Files
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

133 lines
4.8 KiB
Go

package resolver
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/zzet/gortex/internal/graph"
)
func razorComponent(g graph.Store, id, name, ns string) {
g.AddNode(&graph.Node{
ID: id, Kind: graph.KindType, Name: name, Language: "razor",
Meta: map[string]any{"component": true, "scope_ns": ns},
})
}
func razorUsing(g graph.Store, from, ns string) {
g.AddEdge(&graph.Edge{From: from, To: "unresolved::razor_using::" + ns, Kind: graph.EdgeImports})
}
func razorTypeRef(g graph.Store, fromFile, name string) *graph.Edge {
e := &graph.Edge{
From: fromFile + "::Page", To: "unresolved::" + name,
Kind: graph.EdgeReferences, FilePath: fromFile,
}
g.AddEdge(e)
return e
}
// TestResolveRazorUsings_Cascade pins the _Imports.razor cascade: a `<Counter/>`
// in Components/Page.razor binds to the Counter component via the @using in
// Components/_Imports.razor.
func TestResolveRazorUsings_Cascade(t *testing.T) {
g := graph.New()
seedFile(g, "Components/_Imports.razor", "razor")
seedFile(g, "Components/Page.razor", "razor")
seedFile(g, "App/Widgets/Counter.razor", "razor")
razorUsing(g, "Components/_Imports.razor", "App.Widgets")
razorComponent(g, "Components/Page.razor::Page", "Page", "Components")
razorComponent(g, "App/Widgets/Counter.razor::Counter", "Counter", "App.Widgets")
ref := razorTypeRef(g, "Components/Page.razor", "Counter")
r := New(g)
r.resolveRazorUsings()
assert.Equal(t, "App/Widgets/Counter.razor::Counter", ref.To)
assert.Equal(t, graph.OriginASTResolved, ref.Origin)
assert.Equal(t, "razor_using", ref.Meta["resolved_via"])
}
// TestResolveRazorUsings_DirectUsing pins a direct @using on the file (no
// cascade) binding the reference.
func TestResolveRazorUsings_DirectUsing(t *testing.T) {
g := graph.New()
seedFile(g, "Pages/Home.razor", "razor")
seedFile(g, "Lib/Widget.razor", "razor")
razorUsing(g, "Pages/Home.razor", "Lib")
razorComponent(g, "Pages/Home.razor::Page", "Home", "Pages")
razorComponent(g, "Lib/Widget.razor::Widget", "Widget", "Lib")
ref := razorTypeRef(g, "Pages/Home.razor", "Widget")
r := New(g)
r.resolveRazorUsings()
assert.Equal(t, "Lib/Widget.razor::Widget", ref.To, "direct @using binds without a cascade")
}
// TestResolveRazorUsings_AmbiguousStaysUnresolved pins that a type matching two
// imported namespaces is refused.
func TestResolveRazorUsings_AmbiguousStaysUnresolved(t *testing.T) {
g := graph.New()
seedFile(g, "Components/_Imports.razor", "razor")
seedFile(g, "Components/Page.razor", "razor")
seedFile(g, "A/Counter.razor", "razor")
seedFile(g, "B/Counter.razor", "razor")
razorUsing(g, "Components/_Imports.razor", "A")
razorUsing(g, "Components/_Imports.razor", "B")
razorComponent(g, "Components/Page.razor::Page", "Page", "Components")
razorComponent(g, "A/Counter.razor::Counter", "Counter", "A")
razorComponent(g, "B/Counter.razor::Counter", "Counter", "B")
ref := razorTypeRef(g, "Components/Page.razor", "Counter")
r := New(g)
r.resolveRazorUsings()
assert.Equal(t, "unresolved::Counter", ref.To, "ambiguous across two imported namespaces stays unresolved")
}
// TestResolveRazorUsings_NamespaceNotImported pins that a type whose namespace
// is not in the effective set stays unresolved.
func TestResolveRazorUsings_NamespaceNotImported(t *testing.T) {
g := graph.New()
seedFile(g, "Components/Page.razor", "razor")
seedFile(g, "Other/Counter.razor", "razor")
// Page imports App.Widgets, but Counter lives in Other.
razorUsing(g, "Components/Page.razor", "App.Widgets")
razorComponent(g, "Components/Page.razor::Page", "Page", "Components")
razorComponent(g, "Other/Counter.razor::Counter", "Counter", "Other")
ref := razorTypeRef(g, "Components/Page.razor", "Counter")
r := New(g)
r.resolveRazorUsings()
assert.Equal(t, "unresolved::Counter", ref.To, "type in a non-imported namespace stays unresolved")
}
// TestResolveRazorUsings_CrossFamilyNotBound pins that a Razor reference never
// binds a coincidentally-named TypeScript component, even when its namespace is
// imported — they are in different language families.
func TestResolveRazorUsings_CrossFamilyNotBound(t *testing.T) {
g := graph.New()
seedFile(g, "Components/Page.razor", "razor")
seedFile(g, "src/Counter.tsx", "typescript")
razorUsing(g, "Components/Page.razor", "App.Widgets")
razorComponent(g, "Components/Page.razor::Page", "Page", "Components")
// A TypeScript component named Counter, coincidentally in namespace App.Widgets.
g.AddNode(&graph.Node{
ID: "src/Counter.tsx::Counter", Kind: graph.KindType, Name: "Counter",
Language: "typescript", Meta: map[string]any{"scope_ns": "App.Widgets"},
})
ref := razorTypeRef(g, "Components/Page.razor", "Counter")
r := New(g)
r.resolveRazorUsings()
assert.Equal(t, "unresolved::Counter", ref.To, "razor reference must not bind a TypeScript component")
}