From 88b261d478ba5e5b093f2280799a1bc6b567b6bf Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Sat, 28 Feb 2026 02:17:35 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20combine=20graph=20visualization=20fixes?= =?UTF-8?q?=20=E2=80=94=20populate=20ArchData,=20fix=20double-encoding,=20?= =?UTF-8?q?fix=20len()?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Combines changes from both fix branches to fully resolve graph rendering: 1. Populate ArchData (domain/subdomain force graph) in renderHomepage 2. Populate entity ChartData (profile chart) in renderEntityPage 3. Fix homepage chart JSON field names (label/topEntries → name/count/slug) 4. Change template.HTML → template.JS for all JSON in `, + data: struct { + Data template.JS + }{ + Data: template.JS(`{"nodes":[{"id":"root","name":"Root"}],"links":[]}`), + }, + scriptID: "test-data", + }, + { + name: "template.JS with nested JSON", + tmplText: ``, + data: struct { + ChartData template.JS + }{ + ChartData: template.JS(`{"taxonomies":[{"label":"Category","topEntries":[{"name":"Web","count":5}]}],"totalEntities":42}`), + }, + scriptID: "chart-data", + }, + { + name: "empty template.JS produces empty output", + tmplText: ``, + data: struct { + ChartData template.JS + }{ + ChartData: template.JS(""), + }, + scriptID: "empty-data", + }, + } + + scriptContentRe := regexp.MustCompile(`]*id="([^"]+)"[^>]*>(.*?)`) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tmpl, err := template.New("test").Parse(tt.tmplText) + if err != nil { + t.Fatalf("failed to parse template: %v", err) + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, tt.data); err != nil { + t.Fatalf("failed to execute template: %v", err) + } + + rendered := buf.String() + + matches := scriptContentRe.FindStringSubmatch(rendered) + if matches == nil { + t.Fatalf("no ` + tmpl, err := template.New("test").Parse(tmplText) + if err != nil { + t.Fatalf("failed to parse template: %v", err) + } + + data := struct { + Data template.HTML + }{ + Data: template.HTML(`{"nodes":[{"id":"root"}]}`), + } + + var buf bytes.Buffer + if err := tmpl.Execute(&buf, data); err != nil { + t.Fatalf("failed to execute template: %v", err) + } + + rendered := buf.String() + + // Extract content between script tags + re := regexp.MustCompile(`]*>(.*?)`) + matches := re.FindStringSubmatch(rendered) + if matches == nil { + t.Fatal("no script tag found") + } + content := matches[1] + + // With template.HTML in a script context, html/template double-encodes it. + // The content should NOT parse as a valid JSON object directly. + var result interface{} + if err := json.Unmarshal([]byte(content), &result); err != nil { + // If it doesn't parse at all, that's also a form of brokenness + t.Logf("template.HTML in script tag produced unparseable content (expected): %s", content) + return + } + + // If it parses as a string, it's the double-encoding we saw + if s, isString := result.(string); isString { + maxLen := len(s) + if maxLen > 60 { + maxLen = 60 + } + t.Logf("Confirmed: template.HTML causes double-encoding in script tags. JSON.parse returns string: %s", s[:maxLen]) + return + } + + // If it somehow parses as an object, that's unexpected for template.HTML in script context + t.Log("Warning: template.HTML in script tag parsed as object — behavior may have changed in this Go version") +} + +// TestLengthWithVariousTypes verifies the reflect-based length function +// works with all slice types including taxonomy.Entry slices. +func TestLengthWithVariousTypes(t *testing.T) { + type CustomStruct struct { + Name string + } + + tests := []struct { + name string + input interface{} + expected int + }{ + {"nil", nil, 0}, + {"empty string", "", 0}, + {"string", "hello", 5}, + {"string slice", []string{"a", "b", "c"}, 3}, + {"empty slice", []string{}, 0}, + {"interface slice", []interface{}{1, "two", 3.0}, 3}, + {"map", map[string]interface{}{"a": 1, "b": 2}, 2}, + {"custom struct slice", []CustomStruct{{Name: "a"}, {Name: "b"}}, 2}, + {"int slice", []int{1, 2, 3, 4, 5}, 5}, + {"map string string", map[string]string{"a": "1"}, 1}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := length(tt.input) + if got != tt.expected { + t.Errorf("length(%v) = %d, want %d", tt.input, got, tt.expected) + } + }) + } +}