| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771 |
- import { describe, it, expect } from 'vitest';
- import type { FrameworkResolver, UnresolvedRef } from '../src/resolution/types';
- import type { Node } from '../src/types';
- describe('FrameworkResolver.extract interface', () => {
- it('extract() returns { nodes, references }', () => {
- const resolver: FrameworkResolver = {
- name: 'fake',
- detect: () => true,
- resolve: () => null,
- languages: ['python'],
- extract: (_filePath: string, _content: string) => ({
- nodes: [] as Node[],
- references: [] as UnresolvedRef[],
- }),
- };
- const result = resolver.extract!('foo.py', '');
- expect(result).toEqual({ nodes: [], references: [] });
- });
- });
- import { getApplicableFrameworks } from '../src/resolution/frameworks';
- import type { FrameworkResolver } from '../src/resolution/types';
- describe('getApplicableFrameworks', () => {
- const pyFw: FrameworkResolver = { name: 'py', languages: ['python'], detect: () => true, resolve: () => null };
- const jsFw: FrameworkResolver = { name: 'js', languages: ['javascript', 'typescript'], detect: () => true, resolve: () => null };
- const anyFw: FrameworkResolver = { name: 'any', detect: () => true, resolve: () => null };
- it('filters by language', () => {
- const result = getApplicableFrameworks([pyFw, jsFw, anyFw], 'python');
- expect(result.map(r => r.name)).toEqual(['py', 'any']);
- });
- it('returns anyFw-only when language has no matches', () => {
- const result = getApplicableFrameworks([pyFw, jsFw, anyFw], 'rust');
- expect(result.map(r => r.name)).toEqual(['any']);
- });
- });
- import { djangoResolver } from '../src/resolution/frameworks/python';
- describe('djangoResolver.extract', () => {
- it('extracts route node and reference for path() with CBV.as_view()', () => {
- const src = `
- from django.urls import path
- from users.views import UserListView
- urlpatterns = [
- path('users/', UserListView.as_view(), name='user-list'),
- ]
- `;
- const { nodes, references } = djangoResolver.extract!('users/urls.py', src);
- expect(nodes).toHaveLength(1);
- expect(nodes[0].kind).toBe('route');
- expect(nodes[0].name).toBe('users/');
- expect(references).toHaveLength(1);
- expect(references[0].referenceName).toBe('UserListView');
- expect(references[0].referenceKind).toBe('references');
- expect(references[0].fromNodeId).toBe(nodes[0].id);
- });
- it('extracts route for path() with dotted module.Class.as_view()', () => {
- const src = `from django.urls import path\nfrom api.v1 import views as api_v1_views\nurlpatterns = [path('api/', api_v1_views.UserListView.as_view())]\n`;
- const { nodes, references } = djangoResolver.extract!('api/urls.py', src);
- expect(nodes).toHaveLength(1);
- expect(references[0].referenceName).toBe('UserListView');
- });
- it('extracts route for path() with bare function view', () => {
- const src = `from django.urls import path\nurlpatterns = [path('home/', home_view, name='home')]\n`;
- const { nodes, references } = djangoResolver.extract!('home/urls.py', src);
- expect(references[0].referenceName).toBe('home_view');
- });
- it('extracts route for path() with include()', () => {
- const src = `from django.urls import path, include\nurlpatterns = [path('api/', include('api.urls'))]\n`;
- const { nodes, references } = djangoResolver.extract!('root/urls.py', src);
- expect(nodes).toHaveLength(1);
- expect(nodes[0].kind).toBe('route');
- expect(references[0].referenceName).toBe('api.urls');
- expect(references[0].referenceKind).toBe('imports');
- });
- it('extracts routes for re_path and url', () => {
- const src = `from django.urls import re_path, url\nurlpatterns = [re_path(r'^users/$', UserView), url(r'^old/$', OldView)]\n`;
- const { nodes } = djangoResolver.extract!('legacy/urls.py', src);
- expect(nodes).toHaveLength(2);
- expect(nodes.map(n => n.name)).toEqual(['^users/$', '^old/$']);
- });
- it('returns empty result for a non-urls.py python file', () => {
- const src = `def foo(): return 1\n`;
- const { nodes, references } = djangoResolver.extract!('views.py', src);
- expect(nodes).toEqual([]);
- expect(references).toEqual([]);
- });
- });
- import { flaskResolver, fastapiResolver } from '../src/resolution/frameworks/python';
- describe('flaskResolver.extract', () => {
- it('extracts route and reference from @app.route', () => {
- const src = `
- @app.route('/users')
- def list_users():
- return []
- `;
- const { nodes, references } = flaskResolver.extract!('app.py', src);
- expect(nodes).toHaveLength(1);
- expect(nodes[0].kind).toBe('route');
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('list_users');
- });
- it('extracts blueprint routes', () => {
- const src = `
- @users_bp.route('/<id>', methods=['POST'])
- def create_user(id):
- pass
- `;
- const { nodes, references } = flaskResolver.extract!('routes.py', src);
- expect(nodes[0].name).toBe('POST /<id>');
- expect(references[0].referenceName).toBe('create_user');
- });
- });
- describe('fastapiResolver.extract', () => {
- it('extracts route and reference from @app.get', () => {
- const src = `
- @app.get('/users')
- async def list_users():
- return []
- `;
- const { nodes, references } = fastapiResolver.extract!('main.py', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('list_users');
- });
- it('extracts route from router.post', () => {
- const src = `
- @router.post('/items')
- def create_item(item: Item):
- pass
- `;
- const { nodes, references } = fastapiResolver.extract!('items.py', src);
- expect(nodes[0].name).toBe('POST /items');
- expect(references[0].referenceName).toBe('create_item');
- });
- });
- import { expressResolver } from '../src/resolution/frameworks/express';
- describe('expressResolver.extract', () => {
- it('extracts route with inline handler reference', () => {
- const src = `app.get('/users', listUsers);\n`;
- const { nodes, references } = expressResolver.extract!('routes.ts', src);
- expect(nodes).toHaveLength(1);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('listUsers');
- });
- it('extracts route with router.post and middleware chain', () => {
- const src = `router.post('/items', auth, createItem);\n`;
- const { nodes, references } = expressResolver.extract!('items.ts', src);
- expect(nodes[0].name).toBe('POST /items');
- // Multiple handlers: prefer the LAST one (convention: middleware first, handler last)
- expect(references[0].referenceName).toBe('createItem');
- });
- it('extracts route with controller method reference', () => {
- const src = `app.get('/x', userController.list);\n`;
- const { nodes, references } = expressResolver.extract!('routes.ts', src);
- expect(references[0].referenceName).toBe('list');
- });
- });
- import { laravelResolver } from '../src/resolution/frameworks/laravel';
- describe('laravelResolver.extract', () => {
- it('extracts route with controller tuple syntax', () => {
- const src = `Route::get('/users', [UserController::class, 'index']);\n`;
- const { nodes, references } = laravelResolver.extract!('routes/web.php', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('index');
- });
- it('extracts route with Controller@action syntax', () => {
- const src = `Route::post('/users', 'UserController@store');\n`;
- const { nodes, references } = laravelResolver.extract!('routes/web.php', src);
- expect(references[0].referenceName).toBe('store');
- });
- it('extracts resource route', () => {
- const src = `Route::resource('users', UserController::class);\n`;
- const { nodes, references } = laravelResolver.extract!('routes/web.php', src);
- expect(nodes[0].kind).toBe('route');
- expect(references[0].referenceName).toBe('UserController');
- });
- });
- import { railsResolver } from '../src/resolution/frameworks/ruby';
- describe('railsResolver.extract', () => {
- it('extracts route with controller#action syntax', () => {
- const src = `get '/users', to: 'users#index'\n`;
- const { nodes, references } = railsResolver.extract!('config/routes.rb', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('index');
- });
- it('extracts route without to: keyword', () => {
- const src = `post '/items' => 'items#create'\n`;
- const { nodes, references } = railsResolver.extract!('config/routes.rb', src);
- expect(references[0].referenceName).toBe('create');
- });
- });
- import { springResolver } from '../src/resolution/frameworks/java';
- describe('springResolver.extract', () => {
- it('extracts route with @GetMapping and next method', () => {
- const src = `
- @GetMapping("/users")
- public List<User> listUsers() {
- return users;
- }
- `;
- const { nodes, references } = springResolver.extract!('UserController.java', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('listUsers');
- });
- });
- import { goResolver } from '../src/resolution/frameworks/go';
- describe('goResolver.extract', () => {
- it('extracts route from r.GET', () => {
- const src = `r.GET("/users", listUsers)\n`;
- const { nodes, references } = goResolver.extract!('main.go', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('listUsers');
- });
- it('extracts route from router.HandleFunc', () => {
- const src = `router.HandleFunc("/items", createItem)\n`;
- const { nodes, references } = goResolver.extract!('main.go', src);
- expect(references[0].referenceName).toBe('createItem');
- });
- });
- import { rustResolver } from '../src/resolution/frameworks/rust';
- describe('rustResolver.extract', () => {
- it('extracts route from axum .route with get()', () => {
- const src = `let app = Router::new().route("/users", get(list_users));\n`;
- const { nodes, references } = rustResolver.extract!('main.rs', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('list_users');
- });
- });
- describe('rustResolver.resolve cargo workspace crates', () => {
- it('resolves crate name from workspace member lib.rs', () => {
- const workspaceCargo = `
- [workspace]
- members = ["crates/mytool-core", "crates/mytool-fetcher"]
- `;
- const coreCargo = `
- [package]
- name = "mytool-core"
- version = "0.1.0"
- `;
- const libNode: Node = {
- id: 'module:crates/mytool-core/src/lib.rs:mytool_core:1',
- kind: 'module',
- name: 'mytool_core',
- qualifiedName: 'crates/mytool-core/src/lib.rs::mytool_core',
- filePath: 'crates/mytool-core/src/lib.rs',
- language: 'rust',
- startLine: 1,
- endLine: 1,
- startColumn: 0,
- endColumn: 0,
- updatedAt: Date.now(),
- };
- const context = {
- getNodesInFile: (fp: string) => (fp === 'crates/mytool-core/src/lib.rs' ? [libNode] : []),
- getNodesByName: () => [],
- getNodesByQualifiedName: () => [],
- getNodesByKind: () => [],
- fileExists: (p: string) => (
- p === 'Cargo.toml' ||
- p === 'crates/mytool-core/Cargo.toml' ||
- p === 'crates/mytool-core/src/lib.rs'
- ),
- readFile: (p: string) => {
- if (p === 'Cargo.toml') return workspaceCargo;
- if (p === 'crates/mytool-core/Cargo.toml') return coreCargo;
- return null;
- },
- getProjectRoot: () => '/test',
- getAllFiles: () => [
- 'Cargo.toml',
- 'crates/mytool-core/Cargo.toml',
- 'crates/mytool-core/src/lib.rs',
- ],
- getNodesByLowerName: () => [],
- getImportMappings: () => [],
- };
- const ref = {
- fromNodeId: 'fn:crates/mytool-fetcher/src/main.rs:main:1',
- referenceName: 'mytool_core',
- referenceKind: 'references' as const,
- line: 1,
- column: 1,
- filePath: 'crates/mytool-fetcher/src/main.rs',
- language: 'rust' as const,
- };
- const result = rustResolver.resolve(ref, context);
- expect(result?.targetNodeId).toBe(libNode.id);
- expect(result?.resolvedBy).toBe('framework');
- // Workspace-manifest hits are unambiguous and must beat name-matcher's
- // self-file matches (0.7) so cross-crate `imports` edges materialize.
- expect(result?.confidence).toBeGreaterThanOrEqual(0.9);
- });
- it('resolves crate name from workspace member main.rs when lib.rs is absent', () => {
- const workspaceCargo = `
- [workspace]
- members = [
- "crates/mytool-runner",
- ]
- `;
- const runnerCargo = `
- [package]
- name = "mytool-runner"
- version = "0.1.0"
- `;
- const mainNode: Node = {
- id: 'module:crates/mytool-runner/src/main.rs:mytool_runner:1',
- kind: 'module',
- name: 'mytool_runner',
- qualifiedName: 'crates/mytool-runner/src/main.rs::mytool_runner',
- filePath: 'crates/mytool-runner/src/main.rs',
- language: 'rust',
- startLine: 1,
- endLine: 1,
- startColumn: 0,
- endColumn: 0,
- updatedAt: Date.now(),
- };
- const context = {
- getNodesInFile: (fp: string) => (fp === 'crates/mytool-runner/src/main.rs' ? [mainNode] : []),
- getNodesByName: () => [],
- getNodesByQualifiedName: () => [],
- getNodesByKind: () => [],
- fileExists: (p: string) => (
- p === 'Cargo.toml' ||
- p === 'crates/mytool-runner/Cargo.toml' ||
- p === 'crates/mytool-runner/src/main.rs'
- ),
- readFile: (p: string) => {
- if (p === 'Cargo.toml') return workspaceCargo;
- if (p === 'crates/mytool-runner/Cargo.toml') return runnerCargo;
- return null;
- },
- getProjectRoot: () => '/test',
- getAllFiles: () => [
- 'Cargo.toml',
- 'crates/mytool-runner/Cargo.toml',
- 'crates/mytool-runner/src/main.rs',
- ],
- getNodesByLowerName: () => [],
- getImportMappings: () => [],
- };
- const ref = {
- fromNodeId: 'fn:crates/mytool-runner/src/main.rs:main:1',
- referenceName: 'mytool_runner',
- referenceKind: 'references' as const,
- line: 1,
- column: 1,
- filePath: 'crates/mytool-runner/src/main.rs',
- language: 'rust' as const,
- };
- const result = rustResolver.resolve(ref, context);
- expect(result?.targetNodeId).toBe(mainNode.id);
- expect(result?.resolvedBy).toBe('framework');
- });
- it('resolves crate name when members uses a glob (crates/*)', () => {
- const workspaceCargo = `
- [workspace]
- members = ["crates/*"]
- `;
- const fooCargo = `
- [package]
- name = "mytool-foo"
- version = "0.1.0"
- `;
- const barCargo = `
- [package]
- name = "mytool-bar"
- version = "0.1.0"
- `;
- const fooLib: Node = {
- id: 'module:crates/mytool-foo/src/lib.rs:mytool_foo:1',
- kind: 'module',
- name: 'mytool_foo',
- qualifiedName: 'crates/mytool-foo/src/lib.rs::mytool_foo',
- filePath: 'crates/mytool-foo/src/lib.rs',
- language: 'rust',
- startLine: 1,
- endLine: 1,
- startColumn: 0,
- endColumn: 0,
- updatedAt: Date.now(),
- };
- const barLib: Node = {
- id: 'module:crates/mytool-bar/src/lib.rs:mytool_bar:1',
- kind: 'module',
- name: 'mytool_bar',
- qualifiedName: 'crates/mytool-bar/src/lib.rs::mytool_bar',
- filePath: 'crates/mytool-bar/src/lib.rs',
- language: 'rust',
- startLine: 1,
- endLine: 1,
- startColumn: 0,
- endColumn: 0,
- updatedAt: Date.now(),
- };
- const filesByPath: Record<string, string> = {
- 'Cargo.toml': workspaceCargo,
- 'crates/mytool-foo/Cargo.toml': fooCargo,
- 'crates/mytool-bar/Cargo.toml': barCargo,
- };
- const nodesByFile: Record<string, Node[]> = {
- 'crates/mytool-foo/src/lib.rs': [fooLib],
- 'crates/mytool-bar/src/lib.rs': [barLib],
- };
- const dirsByPath: Record<string, string[]> = {
- '.': ['crates'],
- crates: ['mytool-foo', 'mytool-bar'],
- 'crates/mytool-foo': ['src'],
- 'crates/mytool-bar': ['src'],
- };
- const context = {
- getNodesInFile: (fp: string) => nodesByFile[fp] ?? [],
- getNodesByName: () => [],
- getNodesByQualifiedName: () => [],
- getNodesByKind: () => [],
- fileExists: (p: string) => (
- Object.prototype.hasOwnProperty.call(filesByPath, p) ||
- Object.prototype.hasOwnProperty.call(nodesByFile, p)
- ),
- readFile: (p: string) => filesByPath[p] ?? null,
- getProjectRoot: () => '/test',
- getAllFiles: () => [
- 'Cargo.toml',
- ...Object.keys(filesByPath).filter((p) => p !== 'Cargo.toml'),
- ...Object.keys(nodesByFile),
- ],
- getNodesByLowerName: () => [],
- getImportMappings: () => [],
- listDirectories: (rel: string) => dirsByPath[rel] ?? [],
- };
- const fooRef = {
- fromNodeId: 'fn:crates/mytool-bar/src/lib.rs:other:1',
- referenceName: 'mytool_foo',
- referenceKind: 'references' as const,
- line: 1,
- column: 1,
- filePath: 'crates/mytool-bar/src/lib.rs',
- language: 'rust' as const,
- };
- const barRef = {
- fromNodeId: 'fn:crates/mytool-foo/src/lib.rs:other:1',
- referenceName: 'mytool_bar',
- referenceKind: 'references' as const,
- line: 1,
- column: 1,
- filePath: 'crates/mytool-foo/src/lib.rs',
- language: 'rust' as const,
- };
- expect(rustResolver.resolve(fooRef, context)?.targetNodeId).toBe(fooLib.id);
- expect(rustResolver.resolve(barRef, context)?.targetNodeId).toBe(barLib.id);
- });
- it('resolves crate name when members uses a name glob at root (helix-*)', () => {
- const workspaceCargo = `
- [workspace]
- members = ["helix-*"]
- `;
- const coreCargo = `
- [package]
- name = "helix-core"
- version = "0.1.0"
- `;
- const coreLib: Node = {
- id: 'module:helix-core/src/lib.rs:helix_core:1',
- kind: 'module',
- name: 'helix_core',
- qualifiedName: 'helix-core/src/lib.rs::helix_core',
- filePath: 'helix-core/src/lib.rs',
- language: 'rust',
- startLine: 1,
- endLine: 1,
- startColumn: 0,
- endColumn: 0,
- updatedAt: Date.now(),
- };
- const filesByPath: Record<string, string> = {
- 'Cargo.toml': workspaceCargo,
- 'helix-core/Cargo.toml': coreCargo,
- };
- const nodesByFile: Record<string, Node[]> = {
- 'helix-core/src/lib.rs': [coreLib],
- };
- const dirsByPath: Record<string, string[]> = {
- '.': ['helix-core', 'docs', 'target'],
- 'helix-core': ['src'],
- };
- const context = {
- getNodesInFile: (fp: string) => nodesByFile[fp] ?? [],
- getNodesByName: () => [],
- getNodesByQualifiedName: () => [],
- getNodesByKind: () => [],
- fileExists: (p: string) => (
- Object.prototype.hasOwnProperty.call(filesByPath, p) ||
- Object.prototype.hasOwnProperty.call(nodesByFile, p)
- ),
- readFile: (p: string) => filesByPath[p] ?? null,
- getProjectRoot: () => '/test',
- getAllFiles: () => [
- 'Cargo.toml',
- ...Object.keys(filesByPath).filter((p) => p !== 'Cargo.toml'),
- ...Object.keys(nodesByFile),
- ],
- getNodesByLowerName: () => [],
- getImportMappings: () => [],
- listDirectories: (rel: string) => dirsByPath[rel] ?? [],
- };
- const ref = {
- fromNodeId: 'fn:helix-core/src/lib.rs:other:1',
- referenceName: 'helix_core',
- referenceKind: 'references' as const,
- line: 1,
- column: 1,
- filePath: 'helix-core/src/lib.rs',
- language: 'rust' as const,
- };
- expect(rustResolver.resolve(ref, context)?.targetNodeId).toBe(coreLib.id);
- });
- });
- import { aspnetResolver } from '../src/resolution/frameworks/csharp';
- describe('aspnetResolver.extract', () => {
- it('extracts route from [HttpGet] attribute', () => {
- const src = `
- [HttpGet("/users")]
- public IActionResult ListUsers()
- {
- return Ok();
- }
- `;
- const { nodes, references } = aspnetResolver.extract!('UserController.cs', src);
- expect(nodes[0].name).toBe('GET /users');
- expect(references[0].referenceName).toBe('ListUsers');
- });
- });
- import { vaporResolver } from '../src/resolution/frameworks/swift';
- describe('vaporResolver.extract', () => {
- it('extracts route from app.get with use:', () => {
- const src = `app.get("users", use: listUsers)\n`;
- const { nodes, references } = vaporResolver.extract!('routes.swift', src);
- expect(nodes[0].name).toBe('GET users');
- expect(references[0].referenceName).toBe('listUsers');
- });
- });
- import { reactResolver } from '../src/resolution/frameworks/react';
- import { svelteResolver } from '../src/resolution/frameworks/svelte';
- describe('reactResolver.extract (smoke)', () => {
- it('returns { nodes, references } shape', () => {
- const src = `<Route path="/users" element={<UsersPage/>}/>`;
- const result = reactResolver.extract!('App.tsx', src);
- expect(result).toHaveProperty('nodes');
- expect(result).toHaveProperty('references');
- expect(Array.isArray(result.nodes)).toBe(true);
- expect(Array.isArray(result.references)).toBe(true);
- });
- });
- describe('svelteResolver.extract (smoke)', () => {
- it('returns { nodes, references } shape', () => {
- const result = svelteResolver.extract!('+page.svelte', '');
- expect(result).toHaveProperty('nodes');
- expect(result).toHaveProperty('references');
- });
- });
- // Regression tests: commented-out and docstring route examples must NOT
- // surface as phantom route nodes. These would have failed before the
- // strip-comments wiring (the regex would happily scan comments/docstrings).
- describe('framework extractors ignore commented-out routes', () => {
- it('django: skips line-comment and docstring routes', () => {
- const src = `
- # urls.py example:
- # path('/admin/', AdminPanel.as_view())
- """
- Other routing example:
- path('/users/', UserListView.as_view())
- """
- urlpatterns = [path('/real/', RealView.as_view())]
- `;
- const result = djangoResolver.extract!('app/urls.py', src);
- const urls = result.nodes.map((n) => n.name);
- expect(urls).toEqual(['/real/']);
- });
- it('flask: skips commented-out @app.route', () => {
- const src = `
- # @app.route('/fake')
- # def fake_view():
- # return ''
- @app.route('/real')
- def real_view():
- return ''
- `;
- const { nodes, references } = flaskResolver.extract!('app.py', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['real_view']);
- });
- it('fastapi: skips docstring example routes', () => {
- const src = `
- """
- Example:
- @app.get('/in-docstring')
- async def doc():
- pass
- """
- @app.get('/real')
- async def real_handler():
- return {}
- `;
- const { nodes, references } = fastapiResolver.extract!('main.py', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['real_handler']);
- });
- it('express: skips // and /* */ commented routes', () => {
- const src = `
- // app.get('/fake', fakeHandler);
- /* router.post('/also-fake', otherHandler); */
- app.get('/real', realHandler);
- `;
- const { nodes, references } = expressResolver.extract!('routes.ts', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['realHandler']);
- });
- it('laravel: skips // # and /* */ commented Route::* calls', () => {
- const src = `<?php
- // Route::get('/fake', [FakeController::class, 'index']);
- # Route::get('/also-fake', 'FakeController@show');
- /* Route::post('/another-fake', [X::class, 'y']); */
- Route::get('/real', [RealController::class, 'index']);
- `;
- const { nodes, references } = laravelResolver.extract!('routes/web.php', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['index']);
- });
- it('rails: skips =begin/=end and # commented routes', () => {
- const src = `
- # get '/fake', to: 'fake#index'
- =begin
- get '/also-fake', to: 'fake#show'
- =end
- get '/real', to: 'real#index'
- `;
- const { nodes, references } = railsResolver.extract!('config/routes.rb', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['index']);
- });
- it('spring: skips // and /* */ commented @GetMapping', () => {
- const src = `
- // @GetMapping("/fake")
- // public List<X> fake() { return null; }
- /* @PostMapping("/also-fake")
- public void alsoFake() {} */
- @GetMapping("/real")
- public List<User> listUsers() { return users; }
- `;
- const { nodes, references } = springResolver.extract!('UserController.java', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['listUsers']);
- });
- it('go: skips // and /* */ commented router.METHOD calls', () => {
- const src = `
- // r.GET("/fake", fakeHandler)
- /* r.POST("/also-fake", anotherHandler) */
- r.GET("/real", listUsers)
- `;
- const { nodes, references } = goResolver.extract!('main.go', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['listUsers']);
- });
- it('rust: skips // and nested /* */ commented .route() calls', () => {
- const src = `
- // .route("/fake", get(fake_handler))
- /* outer /* inner .route("/inner-fake", get(x)) */ still .route("/outer-fake", get(y)) */
- let app = Router::new().route("/real", get(list_users));
- `;
- const { nodes, references } = rustResolver.extract!('main.rs', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['list_users']);
- });
- it('aspnet: skips // and /* */ commented [HttpGet] attributes', () => {
- const src = `
- // [HttpGet("/fake")]
- // public IActionResult Fake() { return Ok(); }
- /* [HttpPost("/also-fake")]
- public IActionResult AlsoFake() { return Ok(); } */
- [HttpGet("/real")]
- public IActionResult ListUsers() { return Ok(); }
- `;
- const { nodes, references } = aspnetResolver.extract!('UserController.cs', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET /real']);
- expect(references.map((r) => r.referenceName)).toEqual(['ListUsers']);
- });
- it('vapor: skips // and /* */ commented app.METHOD calls', () => {
- const src = `
- // app.get("fake", use: fakeHandler)
- /* app.post("also-fake", use: anotherHandler) */
- app.get("real", use: listUsers)
- `;
- const { nodes, references } = vaporResolver.extract!('routes.swift', src);
- expect(nodes.map((n) => n.name)).toEqual(['GET real']);
- expect(references.map((r) => r.referenceName)).toEqual(['listUsers']);
- });
- });
|