a73x

internal/shape/build_test.go

Ref:   Size: 2.5 KiB   History

package shape

import (
	"bytes"
	"encoding/json"
	"testing"
)

func sample() []rawPackage {
	return []rawPackage{
		{
			ImportPath: module + "/internal/server/api",
			Doc:        "Package api serves the control plane HTTP API. More text.",
			Imports:    []string{"net/http", module + "/internal/pb", module + "/internal/server/store"},
		},
		{
			ImportPath: module + "/internal/pb",
			Doc:        "",
			Imports:    []string{"google.golang.org/protobuf/runtime/protoimpl"},
		},
	}
}

func TestBuildStripsModuleClassifiesAndKeepsInternalImports(t *testing.T) {
	m := Build(sample())
	if m.Module != module {
		t.Fatalf("Module = %q, want %q", m.Module, module)
	}
	if len(m.Packages) != 2 {
		t.Fatalf("got %d packages, want 2", len(m.Packages))
	}
	// Packages are sorted by import path; find api explicitly.
	var found Package
	for _, p := range m.Packages {
		if p.ImportPath == "internal/server/api" {
			found = p
		}
	}
	if found.Plane != PlaneControl {
		t.Errorf("api plane = %q, want control", found.Plane)
	}
	if found.Synopsis != "Package api serves the control plane HTTP API." {
		t.Errorf("api synopsis = %q", found.Synopsis)
	}
	// stdlib (net/http) and external imports dropped; internal kept + relative + sorted.
	want := []string{"internal/pb", "internal/server/store"}
	if len(found.Imports) != len(want) {
		t.Fatalf("imports = %v, want %v", found.Imports, want)
	}
	for i := range want {
		if found.Imports[i] != want[i] {
			t.Errorf("imports[%d] = %q, want %q", i, found.Imports[i], want[i])
		}
	}
}

// A package with no internal imports must serialize as [] (empty array), not
// null. A nil Go slice marshals to JSON null, which the viewer's JS would try
// to iterate (`for (const imp of null)`) and crash on, blanking the whole
// diagram. So Imports must be a non-nil empty slice.
func TestBuildEmitsEmptyImportsArrayNotNull(t *testing.T) {
	m := Build([]rawPackage{
		{ImportPath: module + "/internal/pb", Imports: []string{"google.golang.org/protobuf/runtime/protoimpl"}},
	})
	if m.Packages[0].Imports == nil {
		t.Fatal("Imports is nil; want non-nil empty slice")
	}
	b, err := json.Marshal(m.Packages[0])
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Contains(b, []byte(`"imports":[]`)) {
		t.Errorf("expected \"imports\":[] in JSON, got: %s", b)
	}
}

func TestBuildIsDeterministicRegardlessOfInputOrder(t *testing.T) {
	a := sample()
	b := []rawPackage{a[1], a[0]} // reversed input order
	ja, _ := json.Marshal(Build(a))
	jb, _ := json.Marshal(Build(b))
	if !bytes.Equal(ja, jb) {
		t.Errorf("Build output depends on input order:\n a=%s\n b=%s", ja, jb)
	}
}