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

289 lines
7.6 KiB
Go

package languages
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
func TestSwiftExtractor_ClassWithMethods(t *testing.T) {
src := []byte(`class Server {
var port: Int
func start() {
print("starting")
}
func stop() {
print("stopping")
}
}
`)
e := NewSwiftExtractor()
result, err := e.Extract("server.swift", src)
require.NoError(t, err)
// Class should be extracted as KindType.
types := nodesOfKind(result.Nodes, graph.KindType)
require.Len(t, types, 1)
assert.Equal(t, "Server", types[0].Name)
// Methods inside the class.
methods := nodesOfKind(result.Nodes, graph.KindMethod)
require.Len(t, methods, 2)
names := []string{methods[0].Name, methods[1].Name}
assert.Contains(t, names, "start")
assert.Contains(t, names, "stop")
// Methods should NOT appear as top-level functions.
funcs := nodesOfKind(result.Nodes, graph.KindFunction)
assert.Len(t, funcs, 0)
// The stored property is a field member of the class.
fields := nodesOfKind(result.Nodes, graph.KindField)
require.Len(t, fields, 1)
assert.Equal(t, "port", fields[0].Name)
// EdgeMemberOf edges: two methods plus the property.
memberEdges := edgesOfKind(result.Edges, graph.EdgeMemberOf)
assert.Len(t, memberEdges, 3)
for _, e := range memberEdges {
assert.Equal(t, "server.swift::Server", e.To)
}
}
func TestSwiftExtractor_Struct(t *testing.T) {
src := []byte(`struct Config {
var port: Int
var host: String
}
`)
e := NewSwiftExtractor()
result, err := e.Extract("config.swift", src)
require.NoError(t, err)
types := nodesOfKind(result.Nodes, graph.KindType)
require.Len(t, types, 1)
assert.Equal(t, "Config", types[0].Name)
}
func TestSwiftExtractor_Protocol(t *testing.T) {
src := []byte(`protocol Repository {
func findById(id: String) -> User?
func save(user: User)
}
`)
e := NewSwiftExtractor()
result, err := e.Extract("store.swift", src)
require.NoError(t, err)
ifaces := nodesOfKind(result.Nodes, graph.KindInterface)
require.Len(t, ifaces, 1)
assert.Equal(t, "Repository", ifaces[0].Name)
// Protocol methods in Meta.
methods, ok := ifaces[0].Meta["methods"].([]string)
require.True(t, ok)
assert.Equal(t, []string{"findById", "save"}, methods)
}
func TestSwiftExtractor_Imports(t *testing.T) {
src := []byte(`import Foundation
import UIKit
`)
e := NewSwiftExtractor()
result, err := e.Extract("main.swift", src)
require.NoError(t, err)
imports := edgesOfKind(result.Edges, graph.EdgeImports)
require.Len(t, imports, 2)
}
func TestSwiftExtractor_Enum(t *testing.T) {
src := []byte(`enum Direction {
case north
case south
case east
case west
}
`)
e := NewSwiftExtractor()
result, err := e.Extract("direction.swift", src)
require.NoError(t, err)
types := nodesOfKind(result.Nodes, graph.KindType)
require.Len(t, types, 1)
assert.Equal(t, "Direction", types[0].Name)
assert.Equal(t, "enum", types[0].Meta["kind"])
// Each case becomes a KindVariable member with a member_of edge.
caseNames := map[string]bool{}
for _, n := range result.Nodes {
if n.Kind == graph.KindVariable && n.Meta != nil && n.Meta["kind"] == "enum_case" {
caseNames[n.Name] = true
}
}
assert.Equal(t, map[string]bool{"north": true, "south": true, "east": true, "west": true}, caseNames)
}
func TestSwiftExtractor_TypeFlavor(t *testing.T) {
src := []byte(`class Widget {}
struct Point {}
actor Bank {}
enum Suit { case hearts }
protocol Store {}
`)
e := NewSwiftExtractor()
result, err := e.Extract("flavor.swift", src)
require.NoError(t, err)
widget := nodeByName(result.Nodes, "Widget")
require.NotNil(t, widget)
assert.Equal(t, "class", widget.Meta["type_flavor"])
point := nodeByName(result.Nodes, "Point")
require.NotNil(t, point)
assert.Equal(t, "struct", point.Meta["type_flavor"])
bank := nodeByName(result.Nodes, "Bank")
require.NotNil(t, bank)
assert.Equal(t, "actor", bank.Meta["type_flavor"])
suit := nodeByName(result.Nodes, "Suit")
require.NotNil(t, suit)
assert.Equal(t, "enum", suit.Meta["type_flavor"])
// Dual-write: the legacy enum marker stays beside type_flavor.
assert.Equal(t, "enum", suit.Meta["kind"])
store := nodeByName(result.Nodes, "Store")
require.NotNil(t, store)
assert.Equal(t, graph.KindInterface, store.Kind)
assert.Equal(t, "protocol", store.Meta["type_flavor"])
}
func TestSwiftExtractor_EnumAssociatedValues(t *testing.T) {
// Cases with associated values used to false-match label
// identifiers (`x` in `case labeled(x: Int)`) — confirm only the
// case name itself is captured.
src := []byte(`enum Result {
case ok
case err(code: Int, message: String)
case payload(Data)
}
`)
e := NewSwiftExtractor()
result, err := e.Extract("result.swift", src)
require.NoError(t, err)
caseNames := map[string]bool{}
for _, n := range result.Nodes {
if n.Kind == graph.KindVariable && n.Meta != nil && n.Meta["kind"] == "enum_case" {
caseNames[n.Name] = true
}
}
assert.Equal(t, map[string]bool{"ok": true, "err": true, "payload": true}, caseNames)
}
func TestSwiftExtractor_FreeFunction(t *testing.T) {
src := []byte(`func greet(name: String) -> String {
return "Hello \(name)"
}
`)
e := NewSwiftExtractor()
result, err := e.Extract("greet.swift", src)
require.NoError(t, err)
funcs := nodesOfKind(result.Nodes, graph.KindFunction)
require.Len(t, funcs, 1)
assert.Equal(t, "greet", funcs[0].Name)
}
func TestSwiftExtractor_DocAndVisibility(t *testing.T) {
src := []byte(`/// The greeter.
public class Greeter {
/// Says hi.
public func hello() {}
private func secret() {}
}
/// A protocol.
public protocol Friendly {
func wave()
}
func internalHelper() {}
`)
e := NewSwiftExtractor()
result, err := e.Extract("greeter.swift", src)
require.NoError(t, err)
byID := map[string]*graph.Node{}
for _, n := range result.Nodes {
byID[n.ID] = n
}
greeter := byID["greeter.swift::Greeter"]
require.NotNil(t, greeter)
if greeter.Meta["visibility"] != "public" {
t.Fatalf("Greeter.vis = %q", greeter.Meta["visibility"])
}
if greeter.Meta["doc"] != "The greeter." {
t.Fatalf("Greeter.doc = %q", greeter.Meta["doc"])
}
hello := byID["greeter.swift::Greeter.hello"]
require.NotNil(t, hello)
if hello.Meta["visibility"] != "public" {
t.Fatalf("hello.vis = %q", hello.Meta["visibility"])
}
if hello.Meta["doc"] != "Says hi." {
t.Fatalf("hello.doc = %q", hello.Meta["doc"])
}
secret := byID["greeter.swift::Greeter.secret"]
require.NotNil(t, secret)
if secret.Meta["visibility"] != "private" {
t.Fatalf("secret.vis = %q", secret.Meta["visibility"])
}
friendly := byID["greeter.swift::Friendly"]
require.NotNil(t, friendly)
if friendly.Meta["visibility"] != "public" {
t.Fatalf("Friendly.vis = %q", friendly.Meta["visibility"])
}
internalFn := byID["greeter.swift::internalHelper"]
require.NotNil(t, internalFn)
if internalFn.Meta["visibility"] != "internal" {
t.Fatalf("internalHelper.vis = %q", internalFn.Meta["visibility"])
}
}
func TestSwiftExtractor_FactoryChainReceiver(t *testing.T) {
src := `struct Widget { func withX() -> Widget { return self } }
func builder() -> Widget { return Widget() }
func run() {
builder().withX().build()
}
`
res, err := NewSwiftExtractor().Extract("w.swift", []byte(src))
if err != nil {
t.Fatal(err)
}
var build *graph.Edge
for _, e := range res.Edges {
if e.Kind == graph.EdgeCalls && e.To == "unresolved::*.build" {
build = e
}
}
if build == nil {
t.Fatal("build() call edge not found")
}
if build.Meta["receiver_type"] != "Widget" {
t.Errorf("receiver_type = %v, want Widget (chain builder().withX() returns Widget)", build.Meta["receiver_type"])
}
}