Files
codegraph/__tests__/vapor-closure-route-body.test.ts
Colby Mchenry 911fcf6033 fix(vapor): a closure route's calls are the route's (#2273)
A Vapor route whose handler is a trailing closure had no handler symbol and
linked to nothing (vapor's own repo: 76 of 80 routes bound to nothing in the
viewer browser pass). The closure is the handler: its body's calls, each once
with its receiver, are referenced from the route, as an Express inline
handler's are.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 22:52:43 +00:00

64 lines
2.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* A Vapor route whose handler is a trailing closure — `app.get("hello") { req
* in … }` — has no handler symbol, so it linked to nothing and the viewer's
* Steps tab drew it alone (vapor's own repo: 76 of 80 routes). The closure is
* the handler: its calls are the route's, as an Express inline handler's are.
* A closure after a non-route call (`if let x = req.parameters.get("x") {`)
* is not a route body.
*/
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import { CodeGraph } from '../src';
let root = '';
let cg: CodeGraph;
beforeAll(async () => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-vapor-closure-'));
const files: Record<string, string> = {
'Package.swift': `// swift-tools-version:5.9
import PackageDescription
let package = Package(name: "App", dependencies: [.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0")])
`,
'Sources/App/Greeter.swift': `import Vapor
enum Greeter {
static func greeting(for name: String) -> String { "hi \\(name)" }
static func audit(_ name: String) {}
}
`,
'Sources/App/routes.swift': `import Vapor
func routes(_ app: Application) throws {
app.get("hello", ":name") { req async throws -> String in
let name = req.parameters.get("name") ?? "world"
Greeter.audit(name)
return Greeter.greeting(for: name)
}
app.webSocket("echo") { req, ws in
ws.onText { ws, text in ws.send(text) }
}
}
`,
};
for (const [rel, content] of Object.entries(files)) {
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
fs.writeFileSync(path.join(root, rel), content);
}
cg = await CodeGraph.init(root, { index: true });
});
afterAll(() => {
cg?.close();
if (root) fs.rmSync(root, { recursive: true, force: true });
});
describe('a Vapor closure route', () => {
it('lends the route its closure’s calls', () => {
const route = cg.getNodesByKind('route').find((r) => r.name === 'GET /hello/:name')!;
expect(route).toBeDefined();
const callees = cg.getOutgoingEdgesFrom([route.id], ['calls']).map((e) => cg.getNode(e.target)!.qualifiedName).sort();
expect(callees).toEqual(['Greeter::audit', 'Greeter::greeting']);
});
});