GitNexus/gitnexus/test/unit/data-route-table-routes.test.ts
azizur100389 7f0ab16ffe
Some checks are pending
CodeQL / Analyze (javascript-typescript) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
Gitleaks / gitleaks (push) Waiting to run
Publish / RC guard (marker + release-PR skip) (push) Blocked by required conditions
Publish / Classify release event (push) Waiting to run
Publish / ci (push) Blocked by required conditions
Publish / Publish to npm (push) Blocked by required conditions
Publish / Build & Push RC Docker images (push) Blocked by required conditions
Scorecard / Scorecard analysis (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-cli) (push) Waiting to run
Trivy Image Scan / Trivy (gitnexus-web) (push) Waiting to run
feat(routes): support JS data route tables (#2972)
2026-08-18 04:39:45 +01:00

819 lines
25 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import Parser from 'tree-sitter';
import JavaScript from 'tree-sitter-javascript';
import TypeScript from 'tree-sitter-typescript';
import {
DATA_ROUTE_TABLE_SOURCE,
extractDataRouteTableRoutes,
scanDataRouteTables,
} from '../../src/core/ingestion/route-extractors/data-route-table.js';
import { JAVASCRIPT_HTTP_PLUGIN } from '../../src/core/group/extractors/http-patterns/node.js';
const jsParser = new Parser();
jsParser.setLanguage(JavaScript);
const compact = (source: string) =>
extractDataRouteTableRoutes(jsParser.parse(source), 'src/routes.js').map((route) => ({
path: route.routePath,
method: route.httpMethod,
handler: route.handlerName,
source: route.source,
}));
const dispatch = (table: string) => `
for (const route of ${table}) {
if (route.path === request.path && route.method === request.method) route.handler();
}
`;
describe('data route table extraction', () => {
it('extracts literal entries and preserves method + URL identity', () => {
expect(
compact(`
const apiRoutes = [
{ path: '/users', method: 'get', handler: listUsers },
{ path: '/users', method: 'POST', handler: users.create, auth: true },
];
${dispatch('apiRoutes')}
`),
).toEqual([
{
path: '/users',
method: 'GET',
handler: 'listUsers',
source: DATA_ROUTE_TABLE_SOURCE,
},
{
path: '/users',
method: 'POST',
handler: 'users.create',
source: DATA_ROUTE_TABLE_SOURCE,
},
]);
});
it('supports quoted keys, static templates, and one-level member handlers', () => {
expect(
compact(`
const ROUTE_TABLE = [{
'path': \`/auth/me\`,
'method': 'GET',
'handler': auth.getCurrentUser,
}];
${dispatch('ROUTE_TABLE')}
`),
).toMatchObject([{ path: '/auth/me', method: 'GET', handler: 'auth.getCurrentUser' }]);
});
it('decodes JavaScript escapes in static route strings and quoted keys', () => {
expect(
compact(`
const routes = [{
'p\\u0061th': \`\\/users\`,
method: 'G\\x45T',
handler: escaped,
}];
${dispatch('routes')}
`),
).toMatchObject([{ path: '/users', method: 'GET', handler: 'escaped' }]);
});
it('rejects malformed or legacy-octal route string escapes', () => {
expect(
compact(`
const routes = [{ path: '/\\8users', method: 'GET', handler: malformed }];
${dispatch('routes')}
`),
).toEqual([]);
});
it('rejects executable values on extra route properties', () => {
expect(
compact(`
const routes = [{
path: '/users',
method: 'GET',
handler: listUsers,
metadata: buildMetadata(),
}];
${dispatch('routes')}
`),
).toEqual([]);
});
it('keeps declarative metadata on route entries', () => {
expect(
compact(`
const routes = [{
path: '/users',
method: 'GET',
handler: listUsers,
auth: true,
metadata: { audience: 'staff', flags: ['audit'] },
}];
${dispatch('routes')}
`),
).toMatchObject([{ path: '/users', method: 'GET', handler: 'listUsers' }]);
});
it('allows line and block comments between static route properties', () => {
expect(
compact(`
const routes = [{
path: '/comments',
// The verb remains a direct literal.
method: 'GET',
/* The handler remains a direct designator. */
handler: commented,
}];
${dispatch('routes')}
`),
).toMatchObject([{ path: '/comments', method: 'GET', handler: 'commented' }]);
});
it('has JavaScript / TypeScript grammar parity', () => {
const source = `
type Route = { path: string; method: string; handler: Function };
const routes: Route[] = [{ path: '/typed', method: 'GET', handler: typedHandler }];
${dispatch('routes')}
`;
const tsParser = new Parser();
tsParser.setLanguage(TypeScript.typescript);
expect(scanDataRouteTables(tsParser.parse(source))).toMatchObject([
{ path: '/typed', method: 'GET', handlerName: 'typedHandler' },
]);
});
it.each([
['dynamic path', `const routes = [{ path: runtimePath, method: 'GET', handler: h }]`, 'routes'],
[
'dynamic method',
`const routes = [{ path: '/x', method: runtimeMethod, handler: h }]`,
'routes',
],
[
'called handler',
`const routes = [{ path: '/x', method: 'GET', handler: makeHandler() }]`,
'routes',
],
[
'inline handler',
`const routes = [{ path: '/x', method: 'GET', handler: () => {} }]`,
'routes',
],
[
'spread entry',
`const routes = [{ ...base, path: '/x', method: 'GET', handler: h }]`,
'routes',
],
['computed key', `const routes = [{ ['path']: '/x', method: 'GET', handler: h }]`, 'routes'],
['unknown verb', `const routes = [{ path: '/x', method: 'CONNECT', handler: h }]`, 'routes'],
[
'multi-level member',
`const routes = [{ path: '/x', method: 'GET', handler: services.auth.h }]`,
'routes',
],
[
'non-route binding',
`const requests = [{ path: '/x', method: 'GET', handler: h }]`,
'requests',
],
])('suppresses %s', (_name, source, table) => {
expect(compact(`${source}; ${dispatch(table)}`)).toEqual([]);
});
it('suppresses an unconsumed route-named descriptor table', () => {
expect(
compact(`const mockRoutes = [{ path: '/admin', method: 'DELETE', handler: onResponse }];`),
).toEqual([]);
});
it('does not use a dispatch loop hidden in a nested function', () => {
expect(
compact(`
const routes = [{ path: '/admin', method: 'DELETE', handler: removeAdmin }];
function dispatch(path, method) {
for (const route of routes) {
if (route.path === request.path && route.method === request.method) route.handler();
}
}
`),
).toEqual([]);
});
it('suppresses outbound client tables that pass fields to fetch and then callbacks', () => {
expect(
compact(`
const routeRequests = [
{ path: '/admin', method: 'DELETE', handler: onResponse },
];
for (const route of routeRequests) {
fetch(route.path, { method: route.method }).then(route.handler);
}
`),
).toEqual([]);
});
it('requires path and method comparisons in the same guard as direct dispatch', () => {
expect(
compact(`
const routes = [{ path: '/admin', method: 'DELETE', handler: onResponse }];
for (const route of routes) {
if (route.path) console.log(route.method);
route.handler();
}
`),
).toEqual([]);
});
it('does not treat negated equality as a positive dispatch guard', () => {
expect(
compact(`
const routes = [{ path: '/admin', method: 'DELETE', handler: removeAdmin }];
for (const route of routes) {
if (!(route.path === request.path && route.method === request.method)) {
route.handler();
}
}
`),
).toEqual([]);
});
it('does not treat disjunctive comparisons as a complete dispatch guard', () => {
expect(
compact(`
const routes = [{ path: '/admin', method: 'DELETE', handler: removeAdmin }];
for (const route of routes) {
if (route.path === request.path || route.method === request.method) route.handler();
}
`),
).toEqual([]);
});
it.each([
['direct', 'route.path', 'route.method'],
['parenthesized', '(route.path)', '(route.method)'],
['computed', "route['path']", "route['method']"],
['unary', '+route.path', '+route.method'],
['self-derived', 'normalize(route.path)', 'normalize(route.method)'],
])(
'does not accept %s route-field self-comparisons as dispatch evidence',
(_name, path, method) => {
expect(
compact(`
const routes = [{ path: '/admin', method: 'DELETE', handler: removeAdmin }];
for (const route of routes) {
if (route.path === ${path} && route.method === ${method}) route.handler();
}
`),
).toEqual([]);
},
);
it('suppresses duplicate required keys but preserves route candidates for resolution', () => {
expect(
compact(`
const routes = [
{ path: '/bad', path: '/other', method: 'GET', handler: bad },
{ path: '/once', method: 'GET', handler: first },
{ path: '/once', method: 'GET', handler: second },
];
${dispatch('routes')}
`),
).toMatchObject([
{ path: '/once', method: 'GET', handler: 'first' },
{ path: '/once', method: 'GET', handler: 'second' },
]);
});
it('rejects literal-only guards that select only one table entry', () => {
expect(
compact(`
const routes = [
{ path: '/only', method: 'GET', handler: only },
{ path: '/never', method: 'POST', handler: never },
];
for (const route of routes) {
if (route.path === '/only' && route.method === 'GET') route.handler();
}
`),
).toEqual([]);
});
it('rejects route-derived aliases even when their names resemble request fields', () => {
expect(
compact(`
const routes = [{ path: '/copied', method: 'GET', handler: copied }];
for (const route of routes) {
const copiedPath = route.path;
const copiedMethod = route.method;
if (route.path === copiedPath && route.method === copiedMethod) route.handler();
}
`),
).toEqual([]);
});
it('requires request fields to come from an unshadowed ingress binding', () => {
expect(
compact(`
const request = { path: '/only', method: 'GET' };
const routes = [
{ path: '/only', method: 'GET', handler: only },
{ path: '/never', method: 'POST', handler: never },
];
${dispatch('routes')}
`),
).toEqual([]);
expect(
compact(`
const routes = [{ path: '/nested', method: 'GET', handler: nested }];
for (const route of routes) {
if (
route.path === request.config.path &&
route.method === request.options.method
) route.handler();
}
`),
).toEqual([]);
expect(
compact(`
function dispatchRequest(request) {
const routes = [{ path: '/parameter', method: 'GET', handler: parameterHandler }];
for (const route of routes) {
if (route.path === request.path && route.method === request.method) route.handler();
}
}
`),
).toMatchObject([{ path: '/parameter', method: 'GET', handler: 'parameterHandler' }]);
});
it.each([
['reassigned request parameter', 'request = { path: "/x", method: "GET" };'],
['mutated request fields', 'request.path = "/x"; request.method = "GET";'],
['escaped request parameter', 'observe(request);'],
[
'request mutation through a called closure',
'const overwrite = () => { request.path = "/x"; request.method = "GET"; }; overwrite();',
],
])('rejects an unstable ingress binding: %s', (_name, mutation) => {
expect(
compact(`
function dispatchRequest(request) {
const routes = [{ path: '/x', method: 'GET', handler: handler }];
${mutation}
for (const route of routes) {
if (route.path === request.path && route.method === request.method) route.handler();
}
}
`),
).toEqual([]);
});
it.each([
[
'a route-literal narrowing conjunct',
`route.path === request.path && route.method === request.method && route.path === '/only'`,
],
[
'a statically false conjunct',
`route.path === request.path && route.method === request.method && false`,
],
[
'a statically false equality',
`route.path === request.path && route.method === request.method && 1 === 2`,
],
])('rejects %s', (_name, condition) => {
expect(
compact(`
const routes = [
{ path: '/only', method: 'GET', handler: only },
{ path: '/other', method: 'GET', handler: other },
];
for (const route of routes) {
if (${condition}) route.handler();
}
`),
).toEqual([]);
});
it('rejects a conditionally unreachable handler call in the consequence', () => {
expect(
compact(`
const routes = [{ path: '/x', method: 'GET', handler: unreachable }];
for (const route of routes) {
if (route.path === request.path && route.method === request.method) {
false && route.handler();
}
}
`),
).toEqual([]);
});
it('rejects a handler call after a statically terminating branch', () => {
expect(
compact(`
const routes = [{ path: '/x', method: 'GET', handler: unreachable }];
for (const route of routes) {
if (route.path === request.path && route.method === request.method) {
if (true) return;
route.handler();
}
}
`),
).toEqual([]);
});
it.each(['return', 'throw new Error("stop")', 'break', 'continue'])(
'rejects a handler call after an unconditional %s',
(transfer) => {
expect(
compact(`
const routes = [{ path: '/x', method: 'GET', handler: unreachable }];
for (const route of routes) {
if (route.path === request.path && route.method === request.method) {
${transfer};
route.handler();
}
}
`),
).toEqual([]);
},
);
it.each(['return', 'throw new Error("stop")', 'break', 'continue'])(
'rejects a dispatch guard after an unconditional %s',
(transfer) => {
expect(
compact(`
const routes = [{ path: '/x', method: 'GET', handler: unreachable }];
for (const route of routes) {
${transfer};
if (route.path === request.path && route.method === request.method) {
route.handler();
}
}
`),
).toEqual([]);
},
);
it('rejects a dispatch guard nested in a statically false branch', () => {
expect(
compact(`
const routes = [{ path: '/x', method: 'GET', handler: unreachable }];
for (const route of routes) {
if (false) {
if (route.path === request.path && route.method === request.method) {
route.handler();
}
}
}
`),
).toEqual([]);
});
it('rejects mutation of the loop entry before dispatch', () => {
expect(
compact(`
const routes = [{ path: '/mutated', method: 'GET', handler: mutated }];
for (const route of routes) {
route.path = request.path;
route.method = request.method;
if (route.path === request.path && route.method === request.method) route.handler();
}
`),
).toEqual([]);
expect(
compact(`
const routes = [{ path: '/reassigned', method: 'GET', handler: original }];
for (let route of routes) {
route = replacement;
if (route.path === request.path && route.method === request.method) route.handler();
}
`),
).toEqual([]);
});
it.each([
['deleting an entry field', 'delete route.path;'],
['deleting a wrapped entry field', 'delete (route.path);'],
['writing through a destructuring target', '({ path: route.path } = request);'],
['augmenting the handler field', 'route.handler ||= replacement;'],
['aliasing the entry', 'const alias = route; alias.path = request.path;'],
])('rejects %s', (_name, mutation) => {
expect(
compact(`
const routes = [{ path: '/mutated', method: 'GET', handler: original }];
for (const route of routes) {
${mutation}
if (route.path === request.path && route.method === request.method) route.handler();
}
`),
).toEqual([]);
});
it.each([
[
'a mutable declaration',
`let routes = [{ path: '/stale', method: 'GET', handler: stale }];
${dispatch('routes')}`,
],
[
'a reassigned table',
`const routes = [{ path: '/stale', method: 'GET', handler: stale }];
routes = [];
${dispatch('routes')}`,
],
[
'an aliased table',
`const routes = [{ path: '/stale', method: 'GET', handler: stale }];
const alias = routes;
${dispatch('routes')}`,
],
[
'a truncated table',
`const routes = [{ path: '/stale', method: 'GET', handler: stale }];
routes.length = 0;
${dispatch('routes')}`,
],
])('rejects %s', (_name, source) => {
expect(compact(source)).toEqual([]);
});
it.each([
[
'a reassigned bare handler',
`function original() {}
const routes = [{ path: '/stale', method: 'GET', handler: original }];
original = replacement;
${dispatch('routes')}`,
],
[
'a reassigned member handler',
`const auth = {};
auth.handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a handler owner mutated through an alias',
`const auth = {};
const alias = auth;
alias.handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a handler owner hidden by parentheses',
`const auth = {};
const alias = (auth);
alias.handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a handler owner nested in an initializer',
`const auth = {};
const box = { auth };
box.auth.handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a handler owner stored under an unrelated handler property',
`const auth = {};
const box = { handler: auth };
box.handler.handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a handler owner assigned to an alias',
`const auth = {};
let alias;
alias = auth;
alias.handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'an escaped handler owner',
`const auth = {};
auth.configure();
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a handler owner escaped through a computed member call',
`const auth = {};
auth['configure']();
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a parenthesized handler owner escaped through a member call',
`const auth = {};
(auth).configure();
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
[
'a returned handler owner',
`const auth = {};
function expose() { return auth; }
expose().handle = replacement;
const routes = [{ path: '/stale', method: 'GET', handler: auth.handle }];
${dispatch('routes')}`,
],
])('rejects %s', (_name, source) => {
expect(compact(source)).toEqual([]);
});
it('preserves an unresolved identity tombstone for unsupported duplicate handlers', () => {
expect(
compact(`
function valid() {}
const routes = [
{ path: '/users', method: 'GET', handler: makeHandler() },
{ path: '/users', method: 'GET', handler: valid },
];
${dispatch('routes')}
`),
).toEqual([]);
});
it('tombstones recoverable identities from entries with duplicate route keys', () => {
expect(
compact(`
function first() {}
function second() {}
const routes = [
{ path: '/users', path: '/users', method: 'GET', handler: first },
{ path: '/users', method: 'GET', handler: second },
];
${dispatch('routes')}
`),
).toEqual([]);
});
it('uses the final duplicate route keys when tombstoning an invalid entry', () => {
expect(
compact(`
function first() {}
function second() {}
const routes = [
{ path: '/ignored', path: '/users', method: 'POST', method: 'GET', handler: first },
{ path: '/users', method: 'GET', handler: second },
];
${dispatch('routes')}
`),
).toEqual([]);
});
it('suppresses later candidates when a trailing spread leaves identity unknown', () => {
expect(
compact(`
function first() {}
function second() {}
const routes = [
{ path: '/unknown', method: 'POST', handler: first, ...dynamicRoute },
{ path: '/users', method: 'GET', handler: second },
];
${dispatch('routes')}
`),
).toEqual([]);
});
it.each([
['a dynamic path', `{ path: runtimePath, method: 'GET', handler: first }`],
[
'a computed overwrite',
`{ path: '/other', method: 'GET', handler: first, ['path']: '/users' }`,
],
])('suppresses later candidates after %s', (_name, firstEntry) => {
expect(
compact(`
function first() {}
function second() {}
const routes = [
${firstEntry},
{ path: '/users', method: 'GET', handler: second },
];
${dispatch('routes')}
`),
).toEqual([]);
});
it('rejects tables containing array-level spreads', () => {
expect(
compact(`
function second() {}
const routes = [
...baseRoutes,
{ path: '/users', method: 'GET', handler: second },
];
${dispatch('routes')}
`),
).toEqual([]);
});
it('propagates unresolved identity tombstones across route tables', () => {
expect(
compact(`
function first() {}
function second() {}
const primaryRoutes = [{ path: '/users', method: 'GET', handler: first }];
const fallbackRoutes = [{ path: '/users', method: 'GET', handler: makeHandler() }];
${dispatch('primaryRoutes')}
${dispatch('fallbackRoutes')}
`),
).toEqual([]);
});
it.each([
[
'an unstable duplicate handler binding',
`function first() {}
function second() {}
first = replacement;
const routes = [
{ path: '/users', method: 'GET', handler: first },
{ path: '/users', method: 'GET', handler: second },
];`,
],
[
'a spread-first duplicate entry',
`function second() {}
const routes = [
{ ...base, path: '/users', method: 'GET', handler: first },
{ path: '/users', method: 'GET', handler: second },
];`,
],
])('tombstones %s', (_name, table) => {
expect(compact(`${table} ${dispatch('routes')}`)).toEqual([]);
});
it('rejects shadowed table and loop-entry bindings', () => {
expect(
compact(`
const routes = [{ path: '/outer', method: 'GET', handler: outer }];
{
const routes = [{ path: '/inner', method: 'GET', handler: inner }];
for (const route of routes) {
if (route.path === request.path && route.method === request.method) route.handler();
}
}
`),
).toEqual([]);
expect(
compact(`
const routes = [{ path: '/outer', method: 'GET', handler: outer }];
for (const route of routes) {
{
const route = fakeRoute;
if (route.path === request.path && route.method === request.method) route.handler();
}
}
`),
).toEqual([]);
});
it('suppresses a named import shadowed anywhere in the file', () => {
const detections = JAVASCRIPT_HTTP_PLUGIN.scan(
jsParser.parse(`
import { listUsers } from './handlers.js';
function wrapper(listUsers) { return listUsers; }
const routes = [{ path: '/users', method: 'GET', handler: listUsers }];
${dispatch('routes')}
`),
);
expect(detections).not.toContainEqual(
expect.objectContaining({ framework: DATA_ROUTE_TABLE_SOURCE, path: '/users' }),
);
});
it('feeds the Node group scanner with named-import provenance', () => {
const detections = JAVASCRIPT_HTTP_PLUGIN.scan(
jsParser.parse(`
import { listUsers as handleUsers } from './handlers.js';
const routes = [{ path: '/users', method: 'GET', handler: handleUsers }];
${dispatch('routes')}
`),
);
expect(detections).toContainEqual({
role: 'provider',
framework: DATA_ROUTE_TABLE_SOURCE,
method: 'GET',
path: '/users',
name: 'listUsers',
handlerImport: { name: 'listUsers', module: './handlers.js' },
strictHandlerResolution: true,
line: 3,
confidence: 0.8,
});
});
});