radial sugiyama positioning integration
This commit is contained in:
158
backend_go/hierarchy_layout_bridge_test.go
Normal file
158
backend_go/hierarchy_layout_bridge_test.go
Normal file
@@ -0,0 +1,158 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user