mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
81ec7dd28d
The SDK accepted any number as apiVersion while the host refuses anything but a positive integer no newer than its plugin API, a false green for an author. The manifest patterns, sets and caps now live in the host's protocol/manifest-rules.ts, and gen-plugin-facts writes them and PLUGIN_API_VERSION into the SDK, so check:plugin-facts covers them. One manifest corpus is judged by both validators in a server test (the SDK may only be stricter, case by case), and a typecheck test holds the SDK's plugin API types to the host child's copy, which gains rawBodyBase64.
88 lines
3.3 KiB
TypeScript
88 lines
3.3 KiB
TypeScript
/**
|
|
* One verdict per manifest, whoever judges it: the host's install loader
|
|
* (`parseManifest` with `requireTrek`, what an install answers) and the SDK's
|
|
* `validateManifest` (what `trek-plugin validate` answers) run over the shared corpus
|
|
* in plugin-sdk/test/fixtures/manifest-corpus.ts and must each give the verdict
|
|
* recorded there. The SDK may be stricter than the host only where a case says so;
|
|
* accepting what the host refuses is the false green this test exists to stop.
|
|
*
|
|
* The SDK is a standalone package outside this workspace's rootDir, so both modules
|
|
* are loaded by path at run time rather than through a typed import.
|
|
*/
|
|
import { ManifestError, parseManifest } from '../../../src/nest/plugins/install/manifest';
|
|
|
|
import path from 'node:path';
|
|
import { beforeAll, describe, expect, it } from 'vitest';
|
|
|
|
type Verdict = 'accept' | 'reject';
|
|
interface ManifestCase {
|
|
name: string;
|
|
manifest: unknown;
|
|
host: Verdict;
|
|
sdk?: Verdict;
|
|
why?: string;
|
|
}
|
|
interface SdkValidation {
|
|
ok: boolean;
|
|
errors: string[];
|
|
}
|
|
|
|
const sdkRoot = path.resolve(__dirname, '../../../../plugin-sdk');
|
|
let corpus: ManifestCase[] = [];
|
|
let validateManifest: (raw: unknown) => SdkValidation;
|
|
|
|
beforeAll(async () => {
|
|
({ MANIFEST_CORPUS: corpus } = (await import(path.join(sdkRoot, 'test/fixtures/manifest-corpus.ts'))) as {
|
|
MANIFEST_CORPUS: ManifestCase[];
|
|
});
|
|
({ validateManifest } = (await import(path.join(sdkRoot, 'src/manifest.ts'))) as {
|
|
validateManifest: (raw: unknown) => SdkValidation;
|
|
});
|
|
});
|
|
|
|
function hostVerdict(manifest: unknown): { verdict: Verdict; reason: string } {
|
|
try {
|
|
parseManifest(manifest, { requireTrek: true });
|
|
return { verdict: 'accept', reason: '' };
|
|
} catch (e) {
|
|
// Anything but a ManifestError is a crash in the loader, not a verdict.
|
|
if (!(e instanceof ManifestError)) throw e;
|
|
return { verdict: 'reject', reason: e.message };
|
|
}
|
|
}
|
|
|
|
function sdkVerdict(manifest: unknown): { verdict: Verdict; reason: string } {
|
|
const result = validateManifest(manifest);
|
|
return { verdict: result.ok ? 'accept' : 'reject', reason: result.errors.join('; ') };
|
|
}
|
|
|
|
// Each test lists every case that disagrees, so one run names all of them.
|
|
describe('manifest parity: host loader and SDK validate', () => {
|
|
it('has a corpus to judge', () => {
|
|
expect(corpus.length).toBeGreaterThan(40);
|
|
});
|
|
|
|
it('the host gives every case its recorded verdict', () => {
|
|
const wrong = corpus
|
|
.map((c) => ({ c, got: hostVerdict(c.manifest) }))
|
|
.filter(({ c, got }) => got.verdict !== c.host)
|
|
.map(({ c, got }) => `${c.name}: expected ${c.host}, got ${got.verdict} ${got.reason}`);
|
|
expect(wrong).toEqual([]);
|
|
});
|
|
|
|
it('the SDK gives every case its recorded verdict', () => {
|
|
const wrong = corpus
|
|
.map((c) => ({ c, got: sdkVerdict(c.manifest) }))
|
|
.filter(({ c, got }) => got.verdict !== (c.sdk ?? c.host))
|
|
.map(({ c, got }) => `${c.name}: expected ${c.sdk ?? c.host}, got ${got.verdict} ${got.reason}`);
|
|
expect(wrong).toEqual([]);
|
|
});
|
|
|
|
it('never has the SDK accept what the host refuses', () => {
|
|
const falseGreens = corpus
|
|
.filter((c) => sdkVerdict(c.manifest).verdict === 'accept' && hostVerdict(c.manifest).verdict === 'reject')
|
|
.map((c) => c.name);
|
|
expect(falseGreens).toEqual([]);
|
|
});
|
|
});
|