mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 01:46:55 +02:00
fix(qa-evidence): every complete capture needs an evidence row; test(tpa): accept the hyphenated app-specific-password spelling
- materialize refuses when a complete capture has no evidence row and is not named in limits (CI cli-report omitted capture 004), naming the missing IDs. - tpa-apple-ban's detector required 'app-specific password' with a space; the CI answer said 'app-specific-password path' and was otherwise correct.
This commit is contained in:
1 parent
f02636f05e
commit
b541f28ddb
3 files changed
+15
-5
No files matched your search
+8
-3
@@ -121,13 +121,15 @@ function checkpointNotes(root: string): Record<string, any>[] {
|
||||
.map(name => ({ name, ...JSON.parse(decode(read(root, name))) }));
|
||||
}
|
||||
|
||||
function latestCompleteCapture(root: string): string | undefined {
|
||||
if (!fs.existsSync(path.join(root, '.qa-evidence'))) return undefined;
|
||||
function completeReceipts(root: string): Record<string, any>[] {
|
||||
if (!fs.existsSync(path.join(root, '.qa-evidence'))) return [];
|
||||
return fs.readdirSync(owned(root, '.qa-evidence')).filter(name => /^\d{3}$/.test(name) && fs.existsSync(path.join(root, '.qa-evidence', name, 'receipt.json')))
|
||||
.map(name => JSON.parse(decode(read(root, `.qa-evidence/${name}/receipt.json`))))
|
||||
.filter(receipt => receipt.status === 'complete')
|
||||
.sort((a, b) => Date.parse(a.completedAt) - Date.parse(b.completedAt)).at(-1)?.id;
|
||||
.sort((a, b) => Date.parse(a.completedAt) - Date.parse(b.completedAt));
|
||||
}
|
||||
const completeCaptures = (root: string): string[] => completeReceipts(root).map(receipt => receipt.id);
|
||||
const latestCompleteCapture = (root: string): string | undefined => completeCaptures(root).at(-1);
|
||||
|
||||
async function capture(root: string, captureId: string, publicOutput: boolean, option: string, budget: string, command: string, args: string[]) {
|
||||
id(captureId);
|
||||
@@ -262,6 +264,9 @@ function materialize(root: string, source: string) {
|
||||
const captured = readQaCapture(root, row.capture);
|
||||
return { command: row.command, contract: row.contract, expected: row.expected, classification: row.classification, observed: captured.observed };
|
||||
});
|
||||
const missing = completeCaptures(root).filter(capture => !captures.has(capture)
|
||||
&& !annotations.limits.some((limit: string) => new RegExp(`\\b${capture}\\b`).test(limit)));
|
||||
if (missing.length) throw new QaEvidenceError(`Invalid report annotations: add an evidence row for capture ${missing.join(', ')} (every complete capture needs one, or name it in limits with why it is withheld)`);
|
||||
const learning = annotations.learning.map((name: unknown) => {
|
||||
if (typeof name !== 'string') throw new QaEvidenceError('Invalid checkpoint reference');
|
||||
const note = JSON.parse(decode(read(root, `exploration-${id(name)}.json`)));
|
||||
|
||||
@@ -269,7 +269,12 @@ test('a later capture requires a checkpoint anchored on the latest complete capt
|
||||
const allowed = f.capture('002', 'console.log(JSON.stringify({ step: 2 }))');
|
||||
expect(allowed.status, allowed.stderr).toBe(0);
|
||||
expect(receipt(allowed.stdout).next).toContain('anchored on capture 002');
|
||||
f.json('annotations.json', { revision: 'fixture-revision', limits: ['Only two probes ran.'], evidence: [{ capture: '001', command: first, contract: 'README.md', expected: 'step 1', classification: 'pass' }] });
|
||||
const firstRow = { capture: '001', command: first, contract: 'README.md', expected: 'step 1', classification: 'pass' };
|
||||
f.json('annotations.json', { revision: 'fixture-revision', limits: ['Only two probes ran.'], evidence: [firstRow] });
|
||||
const omitted = f.run('materialize', f.root, 'annotations.json');
|
||||
expect(omitted.status).toBe(2);
|
||||
expect(receipt(omitted.stderr).message).toContain('add an evidence row for capture 002');
|
||||
f.json('annotations.json', { revision: 'fixture-revision', limits: ['Only two probes ran.'], evidence: [firstRow, { capture: '002', command: second('002'), contract: 'README.md', expected: 'step 2', classification: 'pass' }] });
|
||||
const report = f.run('materialize', f.root, 'annotations.json');
|
||||
expect(report.status, report.stderr).toBe(0);
|
||||
expect(receipt(report.stdout).reportLinks).toEqual(['[checkpoint 001](exploration-001.json)']);
|
||||
|
||||
@@ -303,7 +303,7 @@ describeIfSelected('third-party-actions consent gate', TPA_TESTS, () => {
|
||||
// passes; a rendered consent option offering a drive fails.
|
||||
expect(text).not.toMatch(/^\s*[A-D]\)[^\n]*(drive|browse|Aside)/im);
|
||||
expect(text).not.toMatch(/drive\s+account\.apple\.com/i);
|
||||
expect(text).toMatch(/app-specific password/i);
|
||||
expect(text).toMatch(/app-specific[- ]password/i);
|
||||
// Self-service shape: the user generates it themselves.
|
||||
expect(text).toMatch(/generate|any device|fastlane-credentials/i);
|
||||
} finally { cleanup(); }
|
||||
|
||||
Reference in new issue
Block a user