Files
xinmi/internal/application/repository/retriever/legacygraph/repository_test.go
T
yawan f7fc992f57
Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
WeKnora deployment source with the reviewed wiki identity, summary and recovery fixes
2026-09-22 16:19:06 +08:00

451 lines
21 KiB
Go

package legacygraph
import (
"context"
"encoding/json"
"fmt"
"os"
"slices"
"strings"
"testing"
"time"
"github.com/Tencent/WeKnora/internal/types"
"gorm.io/driver/postgres"
"gorm.io/gorm"
)
func TestMissingGraphScope(t *testing.T) {
r := NewRepository(nil, nil)
if _, err := r.Browse(context.Background(), "kb", "", 0, 0); err == nil {
t.Fatal("missing tenant accepted")
}
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(10059))
if _, err := r.SearchNode(ctx, types.NameSpace{}, []string{"term"}); err == nil {
t.Fatal("missing KB accepted")
}
}
func TestBoundedTerms(t *testing.T) {
terms := boundedTerms([]string{"", " ", "a", "a", strings.Repeat("中", 240), "b", "c", "d", "e", "f", "g", "h"})
if len(terms) != 8 || terms[0] != "a" || len([]rune(terms[1])) != 200 {
t.Fatalf("unbounded terms: %#v", terms)
}
}
func TestSearchNodeKeepsGenericSourcesIsolated(t *testing.T) {
tx, r, ctx := nativeWriteFixture(t)
if err := tx.Exec(`INSERT INTO chunks VALUES('sibling',7,'kb','doc','Evidence about trust and learning.',NULL,true)`).Error; err != nil {
t.Fatal(err)
}
ns := types.NameSpace{KnowledgeBase: "kb", Knowledge: "doc"}
for chunk, target := range map[string]string{"chunk": "First target", "sibling": "Second target"} {
graph := &types.GraphData{
Node: []*types.GraphNode{
{Name: "本品", Chunks: []string{chunk}},
{Name: target, Chunks: []string{chunk}},
},
Relation: []*types.GraphRelation{{Node1: "本品", Node2: target, Type: "关联"}},
}
if err := r.AddGraph(ctx, ns, []*types.GraphData{graph}); err != nil {
t.Fatal(err)
}
}
for chunk, target := range map[string]string{"chunk": "First target", "sibling": "Second target"} {
graph, err := r.SearchNode(ctx, ns, []string{target})
if err != nil || len(graph.Node) != 2 || len(graph.Relation) != 1 {
t.Fatalf("unexpected single-product result: %#v %v", graph, err)
}
for _, node := range graph.Node {
if len(node.Chunks) != 1 || node.Chunks[0] != chunk {
t.Fatalf("same-name neighbor expanded another product: %#v", node)
}
}
if edge := graph.Relation[0]; edge.Node1 != "本品" || edge.Node2 != target {
t.Fatalf("unexpected relation projection: %#v", edge)
}
}
graph, err := r.SearchNode(ctx, ns, []string{"本品"})
if err != nil || len(graph.Node) != 4 || len(graph.Relation) != 2 {
t.Fatalf("same-name nodes were collapsed: %#v %v", graph, err)
}
sources := map[string]bool{}
identities := map[string]bool{}
for _, node := range graph.Node {
if node.Name != "本品" {
continue
}
if len(node.Chunks) != 1 || len(node.Attributes) != 2 {
t.Fatalf("generic source identity was lost: %#v", node)
}
sources[node.Chunks[0]] = true
identities[node.Attributes[1]] = true
}
if len(sources) != 2 || !sources["chunk"] || !sources["sibling"] || len(identities) != 2 {
t.Fatalf("generic products share retrieval identity: sources=%v identities=%v", sources, identities)
}
}
func assertReaderTrustAttributes(t *testing.T, r *Repository, ctx context.Context, want []string) {
t.Helper()
ns := types.NameSpace{KnowledgeBase: "kb", Knowledge: "doc"}
id, _ := nativeID("doc", "entity", "trust")
wantType := "concept"
if len(want) > 0 {
wantType = want[0]
}
wantGraph := append([]string{wantType}, want...)
if len(want) > 0 {
wantGraph = wantGraph[1:]
}
wantGraph = append(wantGraph, fmt.Sprintf("legacy_entity:%d", id))
for _, term := range []string{"Trust", "Learning"} {
graph, err := r.SearchNode(ctx, ns, []string{term})
if err != nil {
t.Fatal(err)
}
var found *types.GraphNode
for _, node := range graph.Node {
if node.Name == "Trust" {
found = node
}
}
if found == nil || !slices.Equal(found.Attributes, wantGraph) {
t.Fatalf("%s returned unsupported native attributes: got=%#v want=%v", term, found, wantGraph)
}
}
for _, entityID := range []int64{0, id} {
page, err := r.Browse(ctx, "kb", "", 0, entityID)
if err != nil {
t.Fatal(err)
}
found := false
for _, node := range page.Nodes {
if node.ID != id {
continue
}
found = true
var payload struct {
Attributes []string `json:"attributes"`
Type string `json:"entity_type"`
}
if err := json.Unmarshal(node.Payload, &payload); err != nil {
t.Fatal(err)
}
if !slices.Equal(payload.Attributes, want) || payload.Type != wantType {
t.Fatalf("browse returned unsupported native attributes: %#v; want %v / %s", payload, want, wantType)
}
}
if !found {
t.Fatal("browse lost the entity's surviving bare reference")
}
}
}
func TestNativeReadersRevalidateSharedAttributeSources(t *testing.T) {
for name, invalidate := range map[string]string{
"changed source": `UPDATE chunks SET content=content || ' changed' WHERE id='chunk' AND tenant_id=7`,
"disabled source": `UPDATE chunks SET is_enabled=false WHERE id='chunk' AND tenant_id=7`,
"deleted source": `UPDATE chunks SET deleted_at=now() WHERE id='chunk' AND tenant_id=7`,
"moved source document": `UPDATE chunks SET knowledge_id='doc2' WHERE id='chunk' AND tenant_id=7`,
"wrong evidence hash": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{source_content_hash}',to_jsonb(repeat('a',64))) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"wrong row hash": `UPDATE native_graph_records SET source_snapshot_sha256=repeat('a',64) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"inactive evidence": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{status}','"inactive"'::jsonb) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"wrong origin": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{origin}','"foreign"'::jsonb) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"wrong tenant": `UPDATE native_graph_records SET tenant_id=8 WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"wrong knowledge base": `UPDATE native_graph_records SET knowledge_base_id='other-kb' WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"wrong document": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{native_knowledge_id}','"doc2"'::jsonb) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"wrong mapping document": `UPDATE native_graph_sources SET native_knowledge_id='doc2' WHERE source_chunk_id='native:chunk'`,
"wrong mapping hash": `UPDATE native_graph_sources SET source_content_hash=repeat('a',64) WHERE source_chunk_id='native:chunk'`,
"inactive mapping": `UPDATE native_graph_sources SET mapping_state='stale' WHERE source_chunk_id='native:chunk'`,
"missing old snapshot": `UPDATE native_graph_records SET payload=payload-'entity_attributes' WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk'`,
"null snapshot": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{entity_attributes}','null'::jsonb) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk' AND payload->>'subject_type'='entity'`,
"null attribute": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{entity_attributes}','[null]'::jsonb) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:chunk' AND payload->>'subject_type'='entity'`,
"conflicting snapshot": `UPDATE native_graph_records SET payload=jsonb_set(payload,'{entity_attributes}','["contradiction"]'::jsonb) WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:sibling' AND payload->>'subject_type'='entity'`,
} {
t.Run(name, func(t *testing.T) {
tx, r, ctx := nativeWriteFixture(t)
if err := tx.Exec(`INSERT INTO chunks VALUES('sibling',7,'kb','doc','Another source about trust and learning.',NULL,true)`).Error; err != nil {
t.Fatal(err)
}
owner, bare := testGraph("chunk"), testGraph("sibling")
owner.Node[0].Attributes = []string{"restriction", "Cannot be taken orally"}
bare.Node[0].Attributes = nil
ns := types.NameSpace{KnowledgeBase: "kb", Knowledge: "doc"}
if err := r.AddGraph(ctx, ns, []*types.GraphData{owner, bare}); err != nil {
t.Fatal(err)
}
assertReaderTrustAttributes(t, r, ctx, owner.Node[0].Attributes)
if err := tx.Exec(invalidate).Error; err != nil {
t.Fatal(err)
}
before := nativeGraphWriteState(t, tx)
assertReaderTrustAttributes(t, r, ctx, nil)
if after := nativeGraphWriteState(t, tx); after != before {
t.Fatal("reader projection changed stored graph records or mappings")
}
})
}
}
func TestNativeReadersKeepVerifiedAttributesBeyondChunkCap(t *testing.T) {
tx, r, ctx := nativeWriteFixture(t)
if err := tx.Exec(`INSERT INTO chunks SELECT id,7,'kb','doc','Another source about trust and learning.',NULL,true FROM unnest(ARRAY['aa','ab','ac','ad']) id`).Error; err != nil {
t.Fatal(err)
}
ns := types.NameSpace{KnowledgeBase: "kb", Knowledge: "doc"}
owner := testGraph("chunk")
owner.Node[0].Attributes = []string{"restriction", "Cannot be taken orally"}
graphs := []*types.GraphData{owner}
for _, id := range []string{"aa", "ab", "ac", "ad"} {
bare := testGraph(id)
bare.Node[0].Attributes = nil
graphs = append(graphs, bare)
}
if err := r.AddGraph(ctx, ns, graphs); err != nil {
t.Fatal(err)
}
// An old bare source cannot erase attributes proved by an explicit source.
if err := tx.Exec(`UPDATE native_graph_records SET payload=payload-'entity_attributes' WHERE source_table='knowledgegraphevidence' AND payload->>'source_chunk_id'='native:aa'`).Error; err != nil {
t.Fatal(err)
}
assertReaderTrustAttributes(t, r, ctx, owner.Node[0].Attributes)
graph, err := r.SearchNode(ctx, ns, []string{"Trust"})
if err != nil {
t.Fatal(err)
}
for _, node := range graph.Node {
if len(node.Chunks) != 4 || (node.Name == "Trust" && !slices.Contains(node.Chunks, "chunk")) {
t.Fatalf("attribute owner was omitted from the bounded references: %#v", node)
}
}
if err := tx.Exec(`UPDATE chunks SET is_enabled=false WHERE id='chunk' AND tenant_id=7`).Error; err != nil {
t.Fatal(err)
}
assertReaderTrustAttributes(t, r, ctx, nil)
}
func TestNativeReadersRespectCopiedSourceMappings(t *testing.T) {
tx, r, ctx := nativeWriteFixture(t)
graph := testGraph("chunk")
graph.Node[0].Attributes = []string{"restriction", "Cannot be taken orally"}
if err := r.AddGraph(ctx, types.NameSpace{KnowledgeBase: "kb", Knowledge: "doc"}, []*types.GraphData{graph}); err != nil {
t.Fatal(err)
}
for _, sql := range []string{
`INSERT INTO knowledges VALUES('copy-doc',7,'other-kb','copy.md',NULL)`,
`INSERT INTO chunks VALUES('copy-chunk',7,'other-kb','copy-doc','Evidence about trust and learning.',NULL,true)`,
`INSERT INTO native_graph_records SELECT tenant_id,'other-kb',source_table,legacy_id,graph_version_id,payload,source_snapshot_sha256,imported_at FROM native_graph_records WHERE tenant_id=7 AND knowledge_base_id='kb'`,
`INSERT INTO native_graph_sources SELECT tenant_id,'other-kb',source_chunk_id,source_file,source_content_hash,excerpt,'copy-doc','copy-chunk',native_content_sha256,mapping_state FROM native_graph_sources WHERE tenant_id=7 AND knowledge_base_id='kb'`,
`UPDATE chunks SET is_enabled=false WHERE id='chunk' AND tenant_id=7`,
} {
if err := tx.Exec(sql).Error; err != nil {
t.Fatal(err)
}
}
ns := types.NameSpace{KnowledgeBase: "other-kb", Knowledge: "copy-doc"}
id, _ := nativeID("doc", "entity", "trust")
found, err := r.SearchNode(ctx, ns, []string{"Trust"})
if err != nil || len(found.Node) != 2 {
t.Fatalf("copied native graph was not retrieved: %#v %v", found, err)
}
for _, node := range found.Node {
if !slices.Equal(node.Chunks, []string{"copy-chunk"}) {
t.Fatalf("copied graph retained an original chunk reference: %#v", node)
}
if node.Name == "Trust" && !slices.Equal(node.Attributes, []string{"restriction", "Cannot be taken orally", fmt.Sprintf("legacy_entity:%d", id)}) {
t.Fatalf("copied source snapshot lost its attributes: %#v", node)
}
}
wrong, err := r.SearchNode(ctx, types.NameSpace{KnowledgeBase: "other-kb", Knowledge: "doc"}, []string{"Trust"})
if err != nil || len(wrong.Node) != 0 {
t.Fatalf("copied graph leaked through the original document scope: %#v %v", wrong, err)
}
page, err := r.Browse(ctx, "other-kb", "", 0, id)
if err != nil || len(page.Nodes) != 1 {
t.Fatalf("copied entity was not browsable: %#v %v", page, err)
}
var payload struct {
Attributes []string `json:"attributes"`
}
if err := json.Unmarshal(page.Nodes[0].Payload, &payload); err != nil || !slices.Equal(payload.Attributes, graph.Node[0].Attributes) {
t.Fatalf("copied browse attributes were not revalidated: %#v %v", payload, err)
}
}
// Fixture mutations are confined to a rolled-back transaction.
func TestAcceptancePreservedGraph(t *testing.T) {
dsn := os.Getenv("GRAPH_TEST_DSN")
if dsn == "" {
t.Skip("GRAPH_TEST_DSN is required for acceptance integration")
}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
tx := db.Begin()
defer tx.Rollback()
if tx.Error != nil {
t.Fatal(tx.Error)
}
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(10059))
r := NewRepository(tx, nil)
var fixture struct {
KB string
Entity int64
Name string
Chunk string
Knowledge string
}
err = tx.Raw(`SELECT r.knowledge_base_id AS kb,r.legacy_id AS entity,r.payload->>'canonical_name' AS name,s.native_chunk_id AS chunk,s.native_knowledge_id AS knowledge FROM native_graph_records r JOIN native_graph_records e ON e.tenant_id=r.tenant_id AND e.knowledge_base_id=r.knowledge_base_id AND e.source_table='knowledgegraphevidence' AND e.payload->>'subject_type'='entity' AND e.payload->>'subject_id'=r.legacy_id::text JOIN native_graph_sources s ON s.tenant_id=e.tenant_id AND s.knowledge_base_id=e.knowledge_base_id AND s.source_chunk_id=e.payload->>'source_chunk_id' WHERE r.tenant_id=10059 AND r.source_table='knowledgeentity' AND r.knowledge_base_id='16c3e59c-4bb6-41cf-ae0a-6d0941b3d032' AND s.native_chunk_id IS NOT NULL ORDER BY r.legacy_id LIMIT 1`).Scan(&fixture).Error
if err != nil || fixture.Chunk == "" {
t.Fatalf("missing mapped fixture: %v", err)
}
overview, err := r.Browse(ctx, fixture.KB, "", 0, fixture.Entity)
if err != nil {
t.Fatal(err)
}
var preservedEntities, preservedMappings int64
if err := tx.Raw(`SELECT count(*) FROM native_graph_records WHERE tenant_id=10059 AND knowledge_base_id=? AND source_table='knowledgeentity' AND payload->>'origin' IS DISTINCT FROM 'native_extraction_v1'`, fixture.KB).Scan(&preservedEntities).Error; err != nil {
t.Fatal(err)
}
if err := tx.Raw(`SELECT count(*) FROM native_graph_sources WHERE tenant_id=10059 AND knowledge_base_id=? AND mapping_state<>'native_extraction' AND native_chunk_id IS NOT NULL`, fixture.KB).Scan(&preservedMappings).Error; err != nil {
t.Fatal(err)
}
if len(overview.Nodes) != 1 || len(overview.Evidence) == 0 || preservedEntities != 2 || preservedMappings != 3 || overview.Counts["knowledgeentity"] < preservedEntities || overview.MappedSources < preservedMappings {
t.Fatalf("preserved graph changed: entities=%d mappings=%d counts=%v", preservedEntities, preservedMappings, overview.Counts)
}
found := false
for _, e := range overview.Evidence {
if e.NativeChunkID == fixture.Chunk {
found = true
}
}
if !found {
t.Fatal("mapped evidence was not returned")
}
graph, err := r.SearchNode(ctx, types.NameSpace{KnowledgeBase: fixture.KB, Knowledge: fixture.Knowledge}, []string{fixture.Name})
if err != nil {
t.Fatal(err)
}
if len(graph.Node) == 0 {
t.Fatal("native graph retrieval produced no nodes")
}
for _, n := range graph.Node {
for _, c := range n.Chunks {
if strings.HasPrefix(c, "chunk-") {
t.Fatal("legacy chunk ID leaked into native retrieval")
}
}
}
other := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(999999999))
denied, err := r.Browse(other, fixture.KB, "", 0, fixture.Entity)
if err != nil || len(denied.Nodes) != 0 || len(denied.Evidence) != 0 {
t.Fatal("cross-tenant graph leak", err)
}
wrongFile, err := r.SearchNode(ctx, types.NameSpace{KnowledgeBase: fixture.KB, Knowledge: "other-file"}, []string{fixture.Name})
if err != nil || len(wrongFile.Node) != 0 {
t.Fatal("file scope was ignored", err)
}
for _, change := range []struct {
name, sql, id string
}{
{"edited chunk", "UPDATE chunks SET content=content || ' stale-check' WHERE id=? AND tenant_id=10059", fixture.Chunk},
{"disabled chunk", "UPDATE chunks SET is_enabled=false WHERE id=? AND tenant_id=10059", fixture.Chunk},
{"deleted chunk", "UPDATE chunks SET deleted_at=now() WHERE id=? AND tenant_id=10059", fixture.Chunk},
{"deleted document", "UPDATE knowledges SET deleted_at=now() WHERE id=? AND tenant_id=10059", fixture.Knowledge},
{"deleted knowledge base", "UPDATE knowledge_bases SET deleted_at=now() WHERE id=? AND tenant_id=10059", fixture.KB},
} {
t.Run(change.name, func(t *testing.T) {
if err := tx.SavePoint("resource_check").Error; err != nil {
t.Fatal(err)
}
defer tx.RollbackTo("resource_check")
if err := tx.Exec(change.sql, change.id).Error; err != nil {
t.Fatal(err)
}
stale, err := r.Browse(ctx, fixture.KB, "", 0, fixture.Entity)
if err != nil {
t.Fatal(err)
}
for _, e := range stale.Evidence {
if e.NativeChunkID == fixture.Chunk {
t.Fatal("unavailable chunk remained linked")
}
}
staleGraph, err := r.SearchNode(ctx, types.NameSpace{KnowledgeBase: fixture.KB}, []string{fixture.Name})
if err != nil {
t.Fatal(err)
}
for _, n := range staleGraph.Node {
for _, c := range n.Chunks {
if c == fixture.Chunk {
t.Fatal("unavailable chunk leaked into retrieval")
}
}
}
})
}
}
func TestAcceptanceLargestGraph(t *testing.T) {
dsn := os.Getenv("GRAPH_TEST_DSN")
if dsn == "" {
t.Skip("GRAPH_TEST_DSN is required for acceptance integration")
}
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
t.Fatal(err)
}
ctx := context.WithValue(context.Background(), types.TenantIDContextKey, uint64(10059))
kb := "bb86b44a-576b-4bf8-a6bf-8f1baca3a845"
r := NewRepository(db, nil)
started := time.Now()
overview, err := r.Browse(ctx, kb, "", 0, 0)
if err != nil {
t.Fatal(err)
}
var preserved struct{ Sources, Mapped int64 }
if err := db.Raw(`SELECT count(*) AS sources,count(native_chunk_id) AS mapped FROM native_graph_sources WHERE tenant_id=10059 AND knowledge_base_id=? AND mapping_state<>'native_extraction'`, kb).Scan(&preserved).Error; err != nil {
t.Fatal(err)
}
if len(overview.Nodes) != 50 || preserved.Sources != 66204 || preserved.Mapped != 7000 || overview.Sources < preserved.Sources || overview.MappedSources < preserved.Mapped {
t.Fatalf("unexpected largest graph overview: preserved=%+v sources=%d mapped=%d", preserved, overview.Sources, overview.MappedSources)
}
t.Logf("largest KB overview: %s", time.Since(started))
started = time.Now()
graph, err := r.SearchNode(ctx, types.NameSpace{KnowledgeBase: kb}, []string{"销售", "客户", "信任", "心法"})
if err != nil {
t.Fatal(err)
}
if len(graph.Node) == 0 || len(graph.Node) > 32 || len(graph.Relation) > 32 {
t.Fatalf("invalid bounded retrieval: %d nodes, %d relations", len(graph.Node), len(graph.Relation))
}
for _, node := range graph.Node {
if len(node.Chunks) == 0 || len(node.Chunks) > 4 {
t.Fatalf("invalid chunk cap: %#v", node)
}
}
t.Logf("largest KB graph retrieval: %s; %d nodes, %d relations", time.Since(started), len(graph.Node), len(graph.Relation))
var relationFixture struct {
KB, From, To, Relation string
}
err = db.Raw(`SELECT r.knowledge_base_id AS kb,a.payload->>'canonical_name' AS "from",b.payload->>'canonical_name' AS "to",r.payload->>'relation_type' AS relation FROM native_graph_records r JOIN native_graph_records a ON a.tenant_id=r.tenant_id AND a.knowledge_base_id=r.knowledge_base_id AND a.source_table='knowledgeentity' AND a.legacy_id=(r.payload->>'from_entity_id')::bigint JOIN native_graph_records b ON b.tenant_id=r.tenant_id AND b.knowledge_base_id=r.knowledge_base_id AND b.source_table='knowledgeentity' AND b.legacy_id=(r.payload->>'to_entity_id')::bigint WHERE r.tenant_id=10059 AND r.source_table='knowledgerelation' AND r.legacy_id=1`).Scan(&relationFixture).Error
if err != nil || relationFixture.KB == "" {
t.Fatalf("missing relation fixture: %v", err)
}
expanded, err := r.SearchNode(ctx, types.NameSpace{KnowledgeBase: relationFixture.KB}, []string{relationFixture.From})
if err != nil {
t.Fatal(err)
}
found := false
for _, relation := range expanded.Relation {
if relation.Node1 == relationFixture.From && relation.Node2 == relationFixture.To && relation.Type == relationFixture.Relation {
found = true
}
}
if !found {
t.Fatal("verified relation neighbor was not expanded")
}
}