mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 20:15:19 +08:00
feat(ql3): establish 3.0 incubation baseline
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const { test } = require('node:test');
|
||||
const {
|
||||
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS,
|
||||
ClusterControlRouteRegistryConfigurationError,
|
||||
ClusterControlRouteResolutionError,
|
||||
createClusterControlRouteRegistry,
|
||||
isClusterControlRouteRegistry,
|
||||
} = require('@qinglong/cluster-control/routes');
|
||||
|
||||
function metadata(overrides = {}) {
|
||||
return Object.freeze({
|
||||
requestId: 'request-1',
|
||||
method: 'POST',
|
||||
path: '/api/v3/projects/prj_default/runs',
|
||||
query: Object.freeze({}),
|
||||
headers: Object.freeze({}),
|
||||
signal: new AbortController().signal,
|
||||
...overrides,
|
||||
});
|
||||
}
|
||||
|
||||
function definition(overrides = {}) {
|
||||
return {
|
||||
method: 'POST',
|
||||
path: '/api/v3/projects/{projectId}/runs',
|
||||
operationId: 'run.create',
|
||||
permission: 'run.start',
|
||||
projectParameter: 'projectId',
|
||||
handle(request, parameters) {
|
||||
return {
|
||||
statusCode: 202,
|
||||
body: {
|
||||
projectId: request.projectId,
|
||||
parameter: parameters.projectId,
|
||||
},
|
||||
};
|
||||
},
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('compiles one immutable reviewed route and owns its Project scope', async () => {
|
||||
const source = definition({ allowedQuery: ['dry_run'] });
|
||||
const registry = createClusterControlRouteRegistry([source]);
|
||||
source.operationId = 'forged.operation';
|
||||
|
||||
assert.equal(isClusterControlRouteRegistry(registry), true);
|
||||
assert.equal(isClusterControlRouteRegistry({ ...registry }), false);
|
||||
assert.equal(Object.isFrozen(registry), true);
|
||||
assert.equal(registry.contractVersion, 1);
|
||||
assert.equal(registry.size, 1);
|
||||
|
||||
const route = registry.resolve(
|
||||
metadata({ query: Object.freeze({ dry_run: Object.freeze(['true']) }) }),
|
||||
);
|
||||
assert.equal(route.operationId, 'run.create');
|
||||
assert.equal(route.permission, 'run.start');
|
||||
assert.equal(route.projectId, 'prj_default');
|
||||
assert.equal(Object.isFrozen(route), true);
|
||||
assert.deepEqual(
|
||||
await route.handle({
|
||||
request: { ...metadata(), body: { taskId: 'task-1' } },
|
||||
principal: {
|
||||
subject: { type: 'user', id: 'usr_primary' },
|
||||
authenticationId: 'session:1',
|
||||
authenticatedAtMs: 1,
|
||||
expiresAtMs: 2,
|
||||
assurance: 'multi_factor',
|
||||
},
|
||||
operationId: route.operationId,
|
||||
permission: route.permission,
|
||||
projectId: route.projectId,
|
||||
policyFence: { projectVersion: 1, bindingVersion: 1 },
|
||||
}),
|
||||
{
|
||||
statusCode: 202,
|
||||
body: { projectId: 'prj_default', parameter: 'prj_default' },
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects widened, ambiguous and unbounded route definitions at startup', () => {
|
||||
const invalidSets = [
|
||||
[definition({ extra: true })],
|
||||
[definition({ path: '/api/v3/projects/{projectId}/' })],
|
||||
[definition({ path: '/api/v3/projects/%7BprojectId%7D/runs' })],
|
||||
[definition({ projectParameter: 'missing' })],
|
||||
[definition(), definition({ operationId: 'run.create' })],
|
||||
[
|
||||
definition(),
|
||||
definition({
|
||||
path: '/api/v3/projects/fixed/runs',
|
||||
operationId: 'run.create.fixed',
|
||||
}),
|
||||
],
|
||||
];
|
||||
for (const definitions of invalidSets) {
|
||||
assert.throws(
|
||||
() => createClusterControlRouteRegistry(definitions),
|
||||
ClusterControlRouteRegistryConfigurationError,
|
||||
);
|
||||
}
|
||||
|
||||
const tooMany = Array.from(
|
||||
{ length: CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxRoutes + 1 },
|
||||
(_, index) =>
|
||||
definition({
|
||||
path: `/api/v3/routes/route-${index}`,
|
||||
operationId: `route.operation:${index}`,
|
||||
projectParameter: null,
|
||||
}),
|
||||
);
|
||||
assert.throws(
|
||||
() => createClusterControlRouteRegistry(tooMany),
|
||||
ClusterControlRouteRegistryConfigurationError,
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects non-canonical paths and unknown query before returning a route', () => {
|
||||
const registry = createClusterControlRouteRegistry([
|
||||
definition({ allowedQuery: ['dry_run'] }),
|
||||
]);
|
||||
for (const path of [
|
||||
'/api/v3/projects/prj_default/runs/',
|
||||
'/api/v3/projects//runs',
|
||||
'/api/v3/projects/%2e%2e/runs',
|
||||
'/api/v3/projects/prj_default\\runs',
|
||||
]) {
|
||||
assert.throws(
|
||||
() => registry.resolve(metadata({ path })),
|
||||
(error) =>
|
||||
error instanceof ClusterControlRouteResolutionError &&
|
||||
error.statusCode === 400 &&
|
||||
error.code === 'invalid_route_path',
|
||||
);
|
||||
}
|
||||
assert.throws(
|
||||
() =>
|
||||
registry.resolve(
|
||||
metadata({ query: Object.freeze({ debug: Object.freeze(['1']) }) }),
|
||||
),
|
||||
(error) =>
|
||||
error instanceof ClusterControlRouteResolutionError &&
|
||||
error.code === 'invalid_route_query',
|
||||
);
|
||||
assert.equal(
|
||||
registry.resolve(metadata({ method: 'GET' })),
|
||||
null,
|
||||
'method is part of the reviewed route identity',
|
||||
);
|
||||
assert.equal(
|
||||
registry.resolve(metadata({ path: '/api/v3/projects/prj_default/tasks' })),
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('bounds repeated query values and rejects control characters', () => {
|
||||
const registry = createClusterControlRouteRegistry([
|
||||
definition({ allowedQuery: ['cursor'] }),
|
||||
]);
|
||||
for (const values of [
|
||||
[],
|
||||
Array.from(
|
||||
{
|
||||
length:
|
||||
CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryValuesPerParameter + 1,
|
||||
},
|
||||
() => 'value',
|
||||
),
|
||||
['unsafe\u0000value'],
|
||||
['x'.repeat(CLUSTER_CONTROL_ROUTE_REGISTRY_LIMITS.maxQueryValueBytes + 1)],
|
||||
]) {
|
||||
assert.throws(
|
||||
() =>
|
||||
registry.resolve(
|
||||
metadata({ query: Object.freeze({ cursor: Object.freeze(values) }) }),
|
||||
),
|
||||
(error) =>
|
||||
error instanceof ClusterControlRouteResolutionError &&
|
||||
error.code === 'invalid_route_query',
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user