Files
zzet--gortex/internal/resolver/workspace_membership_test.go
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

138 lines
5.1 KiB
Go

package resolver
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zzet/gortex/internal/graph"
)
// memberOf builds a WorkspaceMembership that derives a file's
// workspace-member id from the leading two path segments — the test
// stand-in for the indexer's manifest-driven membership index.
func memberOf(filePath string) string {
parts := strings.Split(filePath, "/")
if len(parts) >= 2 && parts[0] == "packages" {
return parts[0] + "/" + parts[1]
}
return ""
}
// TestResolveImport_PrefersSameWorkspaceCandidate pins the
// same-workspace preference: a bare import name that matches a
// directory in two different package-manager workspace members
// resolves to the candidate sharing the importer's own member.
func TestResolveImport_PrefersSameWorkspaceCandidate(t *testing.T) {
g := graph.New()
// Importer lives in workspace member `packages/app`.
g.AddNode(&graph.Node{
ID: "packages/app/src/main.ts::main", Kind: graph.KindFunction, Name: "main",
FilePath: "packages/app/src/main.ts", Language: "typescript",
})
// Two same-named `logger` packages in two different members.
g.AddNode(&graph.Node{
ID: "packages/app/logger/index.ts", Kind: graph.KindFile, Name: "index.ts",
FilePath: "packages/app/logger/index.ts", Language: "typescript",
})
g.AddNode(&graph.Node{
ID: "packages/admin/logger/index.ts", Kind: graph.KindFile, Name: "index.ts",
FilePath: "packages/admin/logger/index.ts", Language: "typescript",
})
edge := &graph.Edge{
From: "packages/app/src/main.ts::main", To: "unresolved::import::logger",
Kind: graph.EdgeImports, FilePath: "packages/app/src/main.ts", Line: 1,
}
g.AddEdge(edge)
r := New(g)
r.SetWorkspaceMembership(memberOf)
r.ResolveAll()
assert.Equal(t, "packages/app/logger/index.ts", edge.To,
"import should resolve to the logger in the importer's own workspace member")
}
// TestResolveImport_NoWorkspaceLookupKeepsBaseline confirms that
// without a workspace lookup the resolver's pre-existing first-hit
// behaviour is unchanged — no regression for non-workspace repos.
func TestResolveImport_NoWorkspaceLookupKeepsBaseline(t *testing.T) {
g := graph.New()
g.AddNode(&graph.Node{
ID: "src/main.ts::main", Kind: graph.KindFunction, Name: "main",
FilePath: "src/main.ts", Language: "typescript",
})
g.AddNode(&graph.Node{
ID: "vendor/logger/index.ts", Kind: graph.KindFile, Name: "index.ts",
FilePath: "vendor/logger/index.ts", Language: "typescript",
})
edge := &graph.Edge{
From: "src/main.ts::main", To: "unresolved::import::logger",
Kind: graph.EdgeImports, FilePath: "src/main.ts", Line: 1,
}
g.AddEdge(edge)
// No SetWorkspaceMembership — the resolver must still resolve the
// single directory match exactly as before the feature.
New(g).ResolveAll()
assert.Equal(t, "vendor/logger/index.ts", edge.To,
"single directory match must resolve regardless of workspace lookup")
}
// TestResolveImport_WorkspaceLookupNoMatchKeepsFirstHit confirms that
// when the importer belongs to no workspace member the resolver falls
// back to its first-hit tie-break rather than failing the import.
func TestResolveImport_WorkspaceLookupNoMatchKeepsFirstHit(t *testing.T) {
g := graph.New()
// Importer is outside every `packages/*` member directory.
g.AddNode(&graph.Node{
ID: "tools/cli/main.ts::main", Kind: graph.KindFunction, Name: "main",
FilePath: "tools/cli/main.ts", Language: "typescript",
})
g.AddNode(&graph.Node{
ID: "packages/app/logger/index.ts", Kind: graph.KindFile, Name: "index.ts",
FilePath: "packages/app/logger/index.ts", Language: "typescript",
})
edge := &graph.Edge{
From: "tools/cli/main.ts::main", To: "unresolved::import::logger",
Kind: graph.EdgeImports, FilePath: "tools/cli/main.ts", Line: 1,
}
g.AddEdge(edge)
r := New(g)
r.SetWorkspaceMembership(memberOf)
r.ResolveAll()
assert.Equal(t, "packages/app/logger/index.ts", edge.To,
"importer outside any workspace member should still resolve via the existing tie-break")
}
// TestPickSameWorkspaceFile_AmbiguousLeavesTieBreak pins the unit
// contract: when several candidates share the importer's workspace the
// helper returns nil so the caller's existing tie-break decides.
func TestPickSameWorkspaceFile_AmbiguousLeavesTieBreak(t *testing.T) {
caller := "packages/app/src/main.ts"
candidates := []*graph.Node{
{ID: "packages/app/a/logger.ts", Kind: graph.KindFile, FilePath: "packages/app/a/logger.ts"},
{ID: "packages/app/b/logger.ts", Kind: graph.KindFile, FilePath: "packages/app/b/logger.ts"},
}
if got := pickSameWorkspaceFile(memberOf, caller, candidates); got != nil {
t.Errorf("two candidates in the importer's workspace should be ambiguous, got %q", got.ID)
}
// A single same-workspace candidate among decoys resolves cleanly.
candidates = []*graph.Node{
{ID: "packages/admin/logger.ts", Kind: graph.KindFile, FilePath: "packages/admin/logger.ts"},
{ID: "packages/app/logger.ts", Kind: graph.KindFile, FilePath: "packages/app/logger.ts"},
}
got := pickSameWorkspaceFile(memberOf, caller, candidates)
if got == nil || got.ID != "packages/app/logger.ts" {
t.Errorf("expected the packages/app candidate, got %v", got)
}
}