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

160 lines
5.4 KiB
Go

package languages
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
// findNode returns the first node with the given ID, or nil.
func dnFindNode(nodes []*graph.Node, id string) *graph.Node {
for _, n := range nodes {
if n.ID == id {
return n
}
}
return nil
}
// hasEdge reports whether an edge of the given kind from->to exists.
func dnHasEdge(edges []*graph.Edge, kind graph.EdgeKind, from, to string) bool {
for _, e := range edges {
if e.Kind == kind && e.From == from && e.To == to {
return true
}
}
return false
}
func TestDotNetProjectExtractor_Csproj(t *testing.T) {
const filePath = "src/MyApp/MyApp.csproj"
src := []byte(`<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Serilog" />
</ItemGroup>
</Project>`)
e := NewDotNetProjectExtractor()
if got := e.Language(); got != "dotnet" {
t.Fatalf("Language() = %q, want %q", got, "dotnet")
}
res, err := e.Extract(filePath, src)
if err != nil {
t.Fatalf("Extract returned error: %v", err)
}
// File node with target_framework meta.
fileNode := dnFindNode(res.Nodes, filePath)
if fileNode == nil {
t.Fatalf("expected file node with ID %q", filePath)
}
if fileNode.Kind != graph.KindFile {
t.Fatalf("file node kind = %q, want %q", fileNode.Kind, graph.KindFile)
}
if fileNode.Language != "dotnet" {
t.Fatalf("file node language = %q, want %q", fileNode.Language, "dotnet")
}
if tf, _ := fileNode.Meta["target_framework"].(string); tf != "net8.0" {
t.Fatalf("target_framework meta = %q, want %q", tf, "net8.0")
}
if k, _ := fileNode.Meta["kind"].(string); k != "dotnet_project" {
t.Fatalf("file node meta kind = %q, want %q", k, "dotnet_project")
}
// ProjectReference resolves `..\Core\Core.csproj` relative to
// `src/MyApp` → `src/Core/Core.csproj` (backslash -> forward slash).
const wantRef = "src/Core/Core.csproj"
if !dnHasEdge(res.Edges, graph.EdgeDependsOn, filePath, wantRef) {
t.Fatalf("expected EdgeDependsOn %q -> %q; edges: %+v", filePath, wantRef, res.Edges)
}
// PackageReference with version → KindModule node + EdgeDependsOnModule.
const wantModule = "module::nuget:Newtonsoft.Json@13.0.1"
mod := dnFindNode(res.Nodes, wantModule)
if mod == nil {
t.Fatalf("expected module node with ID %q", wantModule)
}
if mod.Kind != graph.KindModule {
t.Fatalf("module node kind = %q, want %q", mod.Kind, graph.KindModule)
}
if eco, _ := mod.Meta["ecosystem"].(string); eco != "nuget" {
t.Fatalf("module ecosystem = %q, want %q", eco, "nuget")
}
if !dnHasEdge(res.Edges, graph.EdgeDependsOnModule, filePath, wantModule) {
t.Fatalf("expected EdgeDependsOnModule %q -> %q", filePath, wantModule)
}
// Versionless PackageReference uses the versionless module ID form.
const wantVersionless = "module::nuget:Serilog"
if dnFindNode(res.Nodes, wantVersionless) == nil {
t.Fatalf("expected versionless module node with ID %q", wantVersionless)
}
if !dnHasEdge(res.Edges, graph.EdgeDependsOnModule, filePath, wantVersionless) {
t.Fatalf("expected EdgeDependsOnModule %q -> %q", filePath, wantVersionless)
}
}
func TestDotNetProjectExtractor_Sln(t *testing.T) {
const filePath = "MySolution.sln"
// Two projects; App depends on Lib via a ProjectDependencies block.
const appGUID = "11111111-1111-1111-1111-111111111111"
const libGUID = "22222222-2222-2222-2222-222222222222"
src := []byte(`Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "App", "src\App\App.csproj", "{` + appGUID + `}"
ProjectSection(ProjectDependencies) = postProject
{` + libGUID + `} = {` + libGUID + `}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lib", "src\Lib\Lib.csproj", "{` + libGUID + `}"
EndProject
Global
EndGlobal`)
e := NewDotNetProjectExtractor()
res, err := e.Extract(filePath, src)
if err != nil {
t.Fatalf("Extract returned error: %v", err)
}
// Two project file nodes with resolved (backslash->slash) paths.
const appPath = "src/App/App.csproj"
const libPath = "src/Lib/Lib.csproj"
for _, want := range []string{appPath, libPath} {
n := dnFindNode(res.Nodes, want)
if n == nil {
t.Fatalf("expected project file node %q", want)
}
if n.Kind != graph.KindFile {
t.Fatalf("project node %q kind = %q, want %q", want, n.Kind, graph.KindFile)
}
if g, _ := n.Meta["project_guid"].(string); g == "" {
t.Fatalf("project node %q missing project_guid meta", want)
}
}
// App project_guid should match (upper-cased).
app := dnFindNode(res.Nodes, appPath)
if g, _ := app.Meta["project_guid"].(string); g != "11111111-1111-1111-1111-111111111111" {
t.Fatalf("App project_guid = %q, want upper-cased GUID", g)
}
// Solution file node defines both projects.
if !dnHasEdge(res.Edges, graph.EdgeDefines, filePath, appPath) {
t.Fatalf("expected EdgeDefines %q -> %q", filePath, appPath)
}
if !dnHasEdge(res.Edges, graph.EdgeDefines, filePath, libPath) {
t.Fatalf("expected EdgeDefines %q -> %q", filePath, libPath)
}
// App depends on Lib (resolved via the GUID map).
if !dnHasEdge(res.Edges, graph.EdgeDependsOn, appPath, libPath) {
t.Fatalf("expected EdgeDependsOn %q -> %q; edges: %+v", appPath, libPath, res.Edges)
}
}