Files
visualizador_instanciados/backend_go/hierarchy_layout_bridge_test.go
2026-03-23 11:13:27 -03:00

159 lines
5.3 KiB
Go

package main
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestPrepareHierarchyLayoutRequestNormalizesEdges(t *testing.T) {
nodes := []Node{
{ID: 0, TermType: "uri", IRI: "http://example.com/root"},
{ID: 1, TermType: "uri", IRI: "http://example.com/child"},
{ID: 2, TermType: "uri", IRI: "http://example.com/leaf"},
}
preds := NewPredicateDict([]string{"http://www.w3.org/2000/01/rdf-schema#subClassOf"})
edges := []Edge{
{Source: 1, Target: 0, PredicateID: 0},
{Source: 1, Target: 0, PredicateID: 0},
{Source: 2, Target: 2, PredicateID: 0},
{Source: 2, Target: 1, PredicateID: 0},
}
prepared := prepareHierarchyLayoutRequest("http://example.com/root", nodes, edges, preds)
if got, want := len(prepared.Request.Nodes), 3; got != want {
t.Fatalf("len(request.nodes)=%d want %d", got, want)
}
if got, want := len(prepared.Request.Edges), 2; got != want {
t.Fatalf("len(request.edges)=%d want %d", got, want)
}
if prepared.Request.Edges[0].ParentID != 0 || prepared.Request.Edges[0].ChildID != 1 {
t.Fatalf("first normalized edge = %+v, want parent=0 child=1", prepared.Request.Edges[0])
}
if prepared.Request.Edges[1].ParentID != 1 || prepared.Request.Edges[1].ChildID != 2 {
t.Fatalf("second normalized edge = %+v, want parent=1 child=2", prepared.Request.Edges[1])
}
if prepared.Request.Edges[0].PredicateIRI == nil || *prepared.Request.Edges[0].PredicateIRI == "" {
t.Fatalf("expected predicate iri to be preserved")
}
}
func TestApplyHierarchyLayoutResponsePreservesIDsAndRemapsRoutes(t *testing.T) {
nodes := []Node{
{ID: 0, TermType: "uri", IRI: "http://example.com/root"},
{ID: 1, TermType: "uri", IRI: "http://example.com/child"},
{ID: 2, TermType: "uri", IRI: "http://example.com/leaf"},
}
normalizedEdges := []Edge{
{Source: 1, Target: 0, PredicateID: 0},
{Source: 2, Target: 0, PredicateID: 0},
}
response := hierarchyLayoutResponse{
Nodes: []hierarchyLayoutResponseNode{
{NodeID: 0, X: 10, Y: 20},
{NodeID: 2, X: 30, Y: 40},
},
RouteSegments: []hierarchyLayoutResponseRouteSegment{
{
EdgeIndex: 1,
Kind: "spiral",
Points: []hierarchyLayoutResponseRoutePoint{
{X: 10, Y: 20},
{X: 30, Y: 40},
},
},
},
}
result, err := applyHierarchyLayoutResponse(nodes, normalizedEdges, response)
if err != nil {
t.Fatalf("applyHierarchyLayoutResponse returned error: %v", err)
}
if got, want := len(result.Nodes), 2; got != want {
t.Fatalf("len(nodes)=%d want %d", got, want)
}
if result.Nodes[0].ID != 0 || result.Nodes[1].ID != 2 {
t.Fatalf("filtered node ids = [%d %d], want [0 2]", result.Nodes[0].ID, result.Nodes[1].ID)
}
if result.Nodes[0].X != 10 || result.Nodes[0].Y != 20 || result.Nodes[1].X != 30 || result.Nodes[1].Y != 40 {
t.Fatalf("positions were not applied to filtered nodes: %+v", result.Nodes)
}
if got, want := len(result.Edges), 1; got != want {
t.Fatalf("len(edges)=%d want %d", got, want)
}
if result.Edges[0] != normalizedEdges[1] {
t.Fatalf("filtered edge = %+v, want %+v", result.Edges[0], normalizedEdges[1])
}
if got, want := len(result.RouteSegments), 1; got != want {
t.Fatalf("len(route_segments)=%d want %d", got, want)
}
if result.RouteSegments[0].EdgeIndex != 0 {
t.Fatalf("route edge index = %d want 0", result.RouteSegments[0].EdgeIndex)
}
}
func TestRunHierarchyLayoutBridgeUsesConfiguredWorkingDirectory(t *testing.T) {
tmpDir := t.TempDir()
outputPath := filepath.Join(tmpDir, "pwd.txt")
scriptPath := filepath.Join(tmpDir, "bridge.sh")
script := "#!/bin/sh\npwd > \"" + outputPath + "\"\ncat >/dev/null\nprintf '{\"nodes\":[{\"node_id\":1,\"x\":10,\"y\":20,\"level\":0}],\"route_segments\":[]}'\n"
if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil {
t.Fatalf("write script: %v", err)
}
cfg := Config{
HierarchyLayoutBridgeBin: scriptPath,
HierarchyLayoutBridgeWorkdir: tmpDir,
HierarchyLayoutTimeout: 2 * time.Second,
}
response, err := runHierarchyLayoutBridge(context.Background(), cfg, hierarchyLayoutRequest{
RootIRI: "root",
Nodes: []hierarchyLayoutRequestNode{
{NodeID: 1, IRI: "root"},
},
})
if err != nil {
t.Fatalf("runHierarchyLayoutBridge returned error: %v", err)
}
if got, want := len(response.Nodes), 1; got != want {
t.Fatalf("len(response.nodes)=%d want %d", got, want)
}
pwdBytes, err := os.ReadFile(outputPath)
if err != nil {
t.Fatalf("read pwd output: %v", err)
}
if got, want := strings.TrimSpace(string(pwdBytes)), tmpDir; got != want {
t.Fatalf("bridge working directory=%q want %q", got, want)
}
}
func TestRunHierarchyLayoutBridgeReturnsSvgWriteFailure(t *testing.T) {
tmpDir := t.TempDir()
scriptPath := filepath.Join(tmpDir, "bridge_fail.sh")
script := "#!/bin/sh\ncat >/dev/null\necho 'failed to write SVG output: permission denied' >&2\nexit 1\n"
if err := os.WriteFile(scriptPath, []byte(script), 0o755); err != nil {
t.Fatalf("write script: %v", err)
}
cfg := Config{
HierarchyLayoutBridgeBin: scriptPath,
HierarchyLayoutBridgeWorkdir: tmpDir,
HierarchyLayoutTimeout: 2 * time.Second,
}
_, err := runHierarchyLayoutBridge(context.Background(), cfg, hierarchyLayoutRequest{
RootIRI: "root",
})
if err == nil {
t.Fatalf("expected hierarchy layout bridge error")
}
if !strings.Contains(err.Error(), "failed to write SVG output") {
t.Fatalf("error=%q does not mention SVG write failure", err)
}
}