package mindmap import ( "encoding/json" "strings" "testing" "ragflow/internal/utility" ) func TestRenderPrompt(t *testing.T) { got := renderPrompt("THE TEXT") if !strings.Contains(got, "-TEXT-\nTHE TEXT") { t.Fatalf("input_text not substituted: %q", got) } if strings.Contains(got, "{input_text}") { t.Fatalf("placeholder survived: %q", got) } // The verbatim prompt body must be present. if !strings.Contains(got, "Generate a title for user's 'TEXT'。") { t.Fatalf("prompt body drifted") } } func TestPackSections_Budget(t *testing.T) { // budget = max(4096*0.8, 4096-512) = 3584; with the len-based fake // tokenizer each section of 2000 tokens packs one per batch. tok := fakeTok{} sections := []string{strings.Repeat("a", 8000), strings.Repeat("b", 8000), "short"} got := packSections(sections, tok) if len(got) != 2 { t.Fatalf("batches = %d, want 2 (2000+2000 > 3584 splits)", len(got)) } // An oversized section is never split: it forms its own batch. big := packSections([]string{strings.Repeat("x", 20000)}, tok) if len(big) != 1 { t.Fatalf("oversized section must stay whole: %d batches", len(big)) } } type fakeTok struct{} func (fakeTok) NumTokens(s string) int { return len(s) / 4 } func TestTreeToProducts_ParentLinks(t *testing.T) { root := &utility.Node{ID: "root", Children: []*utility.Node{ {ID: "A", Children: []*utility.Node{{ID: "A1"}, {ID: "A2"}}}, {ID: "B"}, }} products := treeToProducts("t1", "d1", root) if len(products) != 5 { t.Fatalf("products = %d, want 5 (root+A+A1+A2+B)", len(products)) } if products[0].Meta["kind"] != "root" || products[0].ParentID != "" { t.Errorf("root product malformed: %+v", products[0].Meta) } if products[1].ParentID != products[0].ID { t.Errorf("A parent link = %q, want root id", products[1].ParentID) } var aID string for _, p := range products { if p.Meta["name"] == "A" { aID = p.ID } } for _, p := range products { if n, _ := p.Meta["name"].(string); n == "A1" || n == "A2" { if p.ParentID != aID { t.Errorf("%s parent = %q, want A id", n, p.ParentID) } } } } func TestSerializeNode_Shape(t *testing.T) { root := &utility.Node{ID: "Top \"quoted\"", Children: []*utility.Node{{ID: "child"}}} js := serializeNode(root) if !strings.HasPrefix(js, `{"id":"Top \"quoted\""`) { t.Errorf("serializeNode = %q", js) } } // TestSerializeNode_EscapesControlChars locks that serializeNode uses // encoding/json: control characters such as CR/LF/TAB/FF are escaped (not // embedded raw), so the output is always valid JSON. func TestSerializeNode_EscapesControlChars(t *testing.T) { root := &utility.Node{ID: "a\rb\tc\fd", Children: []*utility.Node{{ID: "x\ny"}}} js := serializeNode(root) var dec struct { ID string `json:"id"` Children []struct { ID string `json:"id"` } `json:"children"` } if err := json.Unmarshal([]byte(js), &dec); err != nil { t.Fatalf("serializeNode produced invalid JSON: %v (%q)", err, js) } if dec.ID != "a\rb\tc\fd" { t.Errorf("root id round-trip = %q, want %q", dec.ID, "a\rb\tc\fd") } if len(dec.Children) != 1 || dec.Children[0].ID != "x\ny" { t.Errorf("child id round-trip = %+v", dec.Children) } }