feat: add conformance test for authorization server metadata (#170)
This commit is contained in:
parent
cb0f4c3601
commit
050d9cf93a
1 changed files with 479 additions and 10 deletions
146
src/index.ts
146
src/index.ts
|
|
@ -10,6 +10,10 @@ import {
|
|||
printServerSummary,
|
||||
runInteractiveMode
|
||||
} from './runner';
|
||||
import {
|
||||
printAuthorizationServerSummary,
|
||||
runAuthorizationServerConformanceTest
|
||||
} from './runner/authorization-server';
|
||||
import {
|
||||
listScenarios,
|
||||
listClientScenarios,
|
||||
|
|
@ -24,11 +28,17 @@ import {
|
|||
listScenariosForSpec,
|
||||
listClientScenariosForSpec,
|
||||
getScenarioSpecVersions,
|
||||
listClientScenariosForAuthorizationServer,
|
||||
listClientScenariosForAuthorizationServerForSpec,
|
||||
resolveSpecVersion
|
||||
} from './scenarios';
|
||||
import type { SpecVersion } from './scenarios';
|
||||
import { ConformanceCheck } from './types';
|
||||
import { ClientOptionsSchema, ServerOptionsSchema } from './schemas';
|
||||
import {
|
||||
AuthorizationServerOptionsSchema,
|
||||
ClientOptionsSchema,
|
||||
ServerOptionsSchema
|
||||
} from './schemas';
|
||||
import {
|
||||
loadExpectedFailures,
|
||||
evaluateBaseline,
|
||||
|
|
@ -44,12 +54,19 @@ import packageJson from '../package.json';
|
|||
function filterScenariosBySpecVersion(
|
||||
allScenarios: string[],
|
||||
version: SpecVersion,
|
||||
command: 'client' | 'server'
|
||||
command: 'client' | 'server' | 'authorization'
|
||||
): string[] {
|
||||
const versionScenarios =
|
||||
command === 'client'
|
||||
? listScenariosForSpec(version)
|
||||
: listClientScenariosForSpec(version);
|
||||
let versionScenarios: string[];
|
||||
if (command === 'client') {
|
||||
versionScenarios = listScenariosForSpec(version);
|
||||
} else if (command === 'server') {
|
||||
versionScenarios = listClientScenariosForSpec(version);
|
||||
} else if (command === 'authorization') {
|
||||
versionScenarios =
|
||||
listClientScenariosForAuthorizationServerForSpec(version);
|
||||
} else {
|
||||
versionScenarios = [];
|
||||
}
|
||||
const allowed = new Set(versionScenarios);
|
||||
return allScenarios.filter((s) => allowed.has(s));
|
||||
}
|
||||
|
|
@ -437,6 +454,87 @@ program
|
|||
}
|
||||
});
|
||||
|
||||
// Authorization command - tests an authorization server implementation
|
||||
program
|
||||
.command('authorization')
|
||||
.description(
|
||||
'Run conformance tests against an authorization server implementation'
|
||||
)
|
||||
.requiredOption('--url <url>', 'URL of the authorization server issuer')
|
||||
.option('-o, --output-dir <path>', 'Save results to this directory')
|
||||
.option(
|
||||
'--spec-version <version>',
|
||||
'Filter scenarios by spec version (cumulative for date versions)'
|
||||
)
|
||||
.action(async (options) => {
|
||||
try {
|
||||
// Validate options with Zod
|
||||
const validated = AuthorizationServerOptionsSchema.parse(options);
|
||||
const outputDir = options.outputDir;
|
||||
const specVersionFilter = options.specVersion
|
||||
? resolveSpecVersion(options.specVersion)
|
||||
: undefined;
|
||||
|
||||
let scenarios: string[];
|
||||
scenarios = listClientScenariosForAuthorizationServer();
|
||||
if (specVersionFilter) {
|
||||
scenarios = filterScenariosBySpecVersion(
|
||||
scenarios,
|
||||
specVersionFilter,
|
||||
'authorization'
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`Running test (${scenarios.length} scenarios) against ${validated.url}\n`
|
||||
);
|
||||
|
||||
const allResults: { scenario: string; checks: ConformanceCheck[] }[] = [];
|
||||
for (const scenarioName of scenarios) {
|
||||
console.log(`\n=== Running scenario: ${scenarioName} ===`);
|
||||
try {
|
||||
const result = await runAuthorizationServerConformanceTest(
|
||||
validated.url,
|
||||
scenarioName,
|
||||
outputDir
|
||||
);
|
||||
allResults.push({ scenario: scenarioName, checks: result.checks });
|
||||
} catch (error) {
|
||||
console.error(`Failed to run scenario ${scenarioName}:`, error);
|
||||
allResults.push({
|
||||
scenario: scenarioName,
|
||||
checks: [
|
||||
{
|
||||
id: scenarioName,
|
||||
name: scenarioName,
|
||||
description: 'Failed to run scenario',
|
||||
status: 'FAILURE',
|
||||
timestamp: new Date().toISOString(),
|
||||
errorMessage:
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}
|
||||
const { totalFailed } = printAuthorizationServerSummary(allResults);
|
||||
process.exit(totalFailed > 0 ? 1 : 0);
|
||||
} catch (error) {
|
||||
if (error instanceof ZodError) {
|
||||
console.error('Validation error:');
|
||||
error.issues.forEach((err) => {
|
||||
console.error(` ${err.path.join('.')}: ${err.message}`);
|
||||
});
|
||||
console.error('\nAvailable authorization server scenarios:');
|
||||
listClientScenariosForAuthorizationServer().forEach((s) =>
|
||||
console.error(` - ${s}`)
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
console.error('Authorization server test error:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// Tier check command
|
||||
program.addCommand(createTierCheckCommand());
|
||||
|
||||
|
|
@ -446,6 +544,7 @@ program
|
|||
.description('List available test scenarios')
|
||||
.option('--client', 'List client scenarios')
|
||||
.option('--server', 'List server scenarios')
|
||||
.option('--authorization', 'List authorization server scenarios')
|
||||
.option(
|
||||
'--spec-version <version>',
|
||||
'Filter scenarios by spec version (cumulative for date versions)'
|
||||
|
|
@ -455,7 +554,10 @@ program
|
|||
? resolveSpecVersion(options.specVersion)
|
||||
: undefined;
|
||||
|
||||
if (options.server || (!options.client && !options.server)) {
|
||||
if (
|
||||
options.server ||
|
||||
(!options.client && !options.server && !options.authorization)
|
||||
) {
|
||||
console.log('Server scenarios (test against a server):');
|
||||
let serverScenarios = listClientScenarios();
|
||||
if (specVersionFilter) {
|
||||
|
|
@ -471,7 +573,10 @@ program
|
|||
});
|
||||
}
|
||||
|
||||
if (options.client || (!options.client && !options.server)) {
|
||||
if (
|
||||
options.client ||
|
||||
(!options.client && !options.server && !options.authorization)
|
||||
) {
|
||||
if (options.server || (!options.client && !options.server)) {
|
||||
console.log('');
|
||||
}
|
||||
|
|
@ -489,6 +594,31 @@ program
|
|||
console.log(` - ${s}${v ? ` [${v}]` : ''}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
options.authorization ||
|
||||
(!options.authorization && !options.server && !options.client)
|
||||
) {
|
||||
if (!(options.authorization && !options.server && !options.client)) {
|
||||
console.log('');
|
||||
}
|
||||
console.log(
|
||||
'Authorization server scenarios (test against an authorization server):'
|
||||
);
|
||||
let authorizationServerScenarios =
|
||||
listClientScenariosForAuthorizationServer();
|
||||
if (specVersionFilter) {
|
||||
authorizationServerScenarios = filterScenariosBySpecVersion(
|
||||
authorizationServerScenarios,
|
||||
specVersionFilter,
|
||||
'authorization'
|
||||
);
|
||||
}
|
||||
authorizationServerScenarios.forEach((s) => {
|
||||
const v = getScenarioSpecVersions(s);
|
||||
console.log(` - ${s}${v ? ` [${v}]` : ''}`);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
program.parse();
|
||||
|
|
|
|||
Loading…
Reference in a new issue