项目文件夹

文件
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

158 行
5.0 KiB
Go

package indexer
import (
"os"
"path/filepath"
"sort"
"testing"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/modules"
"github.com/zzet/gortex/internal/parser"
)
// writeWorkspaceFile materialises a manifest under dir at a
// repo-relative slash path, creating intermediate directories — the
// shared writeFile helper expects an already-existing parent dir.
func writeWorkspaceFile(t *testing.T, dir, rel, content string) {
t.Helper()
abs := filepath.Join(dir, filepath.FromSlash(rel))
require.NoError(t, os.MkdirAll(filepath.Dir(abs), 0o755))
writeFile(t, abs, content)
}
// workspaceMemberTargets indexes dir and returns the sorted To
// endpoints of every EdgePackageWorkspaceMember edge in the graph.
func workspaceMemberTargets(t *testing.T, dir string) []string {
t.Helper()
g := graph.New()
idx := New(g, parser.NewRegistry(), config.Default().Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
var targets []string
for _, e := range g.AllEdges() {
if e.Kind == graph.EdgePackageWorkspaceMember {
targets = append(targets, e.To)
}
}
sort.Strings(targets)
return targets
}
// TestIndex_PackageWorkspaceMembership pins index-time materialisation
// of root→member edges across all three package-manager ecosystems,
// including glob and explicit members, plus the no-regression case.
func TestIndex_PackageWorkspaceMembership(t *testing.T) {
cases := []struct {
name string
files map[string]string
want []string // sorted EdgePackageWorkspaceMember targets
}{
{
name: "npm workspace, glob plus explicit member",
files: map[string]string{
"package.json": `{"name":"root","workspaces":["packages/*","apps/web"]}`,
"packages/ui/package.json": `{"name":"ui"}`,
"packages/core/package.json": `{"name":"core"}`,
"apps/web/package.json": `{"name":"web"}`,
},
want: []string{
"apps/web/package.json",
"packages/core/package.json",
"packages/ui/package.json",
},
},
{
name: "pnpm workspace, glob members",
files: map[string]string{
"pnpm-workspace.yaml": "packages:\n - 'packages/*'\n",
"packages/a/package.json": `{"name":"a"}`,
"packages/b/package.json": `{"name":"b"}`,
},
want: []string{
"packages/a/package.json",
"packages/b/package.json",
},
},
{
name: "cargo workspace, glob plus explicit member",
files: map[string]string{
"Cargo.toml": "[workspace]\nmembers = [\"crates/*\", \"tools/gen\"]\n",
"crates/engine/Cargo.toml": "[package]\nname = \"engine\"\n",
"crates/cli/Cargo.toml": "[package]\nname = \"cli\"\n",
"tools/gen/Cargo.toml": "[package]\nname = \"gen\"\n",
},
want: []string{
"crates/cli/Cargo.toml",
"crates/engine/Cargo.toml",
"tools/gen/Cargo.toml",
},
},
{
name: "non-workspace repo produces no membership edges",
files: map[string]string{
"package.json": `{"name":"solo","version":"1.0.0","dependencies":{"react":"^18"}}`,
"index.js": "module.exports = 1;\n",
},
want: nil,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
dir := t.TempDir()
for rel, content := range c.files {
writeWorkspaceFile(t, dir, rel, content)
}
got := workspaceMemberTargets(t, dir)
if len(got) != len(c.want) {
t.Fatalf("got %d membership edges %v, want %d %v", len(got), got, len(c.want), c.want)
}
for i := range got {
if got[i] != c.want[i] {
t.Errorf("membership edge[%d] = %q, want %q", i, got[i], c.want[i])
}
}
})
}
}
// TestIndex_PackageWorkspaceRootNode verifies the synthetic
// workspace-root node carries the expected kind and meta and that
// every membership edge originates from it.
func TestIndex_PackageWorkspaceRootNode(t *testing.T) {
dir := t.TempDir()
writeWorkspaceFile(t, dir, "package.json", `{"name":"root","workspaces":["packages/*"]}`)
writeWorkspaceFile(t, dir, "packages/a/package.json", `{"name":"a"}`)
writeWorkspaceFile(t, dir, "packages/b/package.json", `{"name":"b"}`)
g := graph.New()
idx := New(g, parser.NewRegistry(), config.Default().Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
rootID := modules.WorkspaceRootNodeID(modules.WorkspaceNPM, ".")
root := g.GetNode(rootID)
require.NotNil(t, root, "workspace root node %s must be materialised", rootID)
require.Equal(t, graph.KindPackage, root.Kind)
if v, _ := root.Meta["package_workspace"].(bool); !v {
t.Errorf("root node should carry package_workspace=true meta")
}
require.Equal(t, "npm", root.Meta["ecosystem"])
var memberEdges int
for _, e := range g.AllEdges() {
if e.Kind != graph.EdgePackageWorkspaceMember {
continue
}
memberEdges++
require.Equal(t, rootID, e.From, "membership edge must originate at the workspace root")
require.Equal(t, graph.OriginASTResolved, e.Origin)
}
require.Equal(t, 2, memberEdges, "expected one membership edge per resolved member")
}