mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-04 10:26:52 +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))) }));
|
.map(name => ({ name, ...JSON.parse(decode(read(root, name))) }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function latestCompleteCapture(root: string): string | undefined {
|
function completeReceipts(root: string): Record<string, any>[] {
|
||||||
if (!fs.existsSync(path.join(root, '.qa-evidence'))) return undefined;
|
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')))
|
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`))))
|
.map(name => JSON.parse(decode(read(root, `.qa-evidence/${name}/receipt.json`))))
|
||||||
.filter(receipt => receipt.status === 'complete')
|
.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[]) {
|
async function capture(root: string, captureId: string, publicOutput: boolean, option: string, budget: string, command: string, args: string[]) {
|
||||||
id(captureId);
|
id(captureId);
|
||||||
@@ -262,6 +264,9 @@ function materialize(root: string, source: string) {
|
|||||||
const captured = readQaCapture(root, row.capture);
|
const captured = readQaCapture(root, row.capture);
|
||||||
return { command: row.command, contract: row.contract, expected: row.expected, classification: row.classification, observed: captured.observed };
|
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) => {
|
const learning = annotations.learning.map((name: unknown) => {
|
||||||
if (typeof name !== 'string') throw new QaEvidenceError('Invalid checkpoint reference');
|
if (typeof name !== 'string') throw new QaEvidenceError('Invalid checkpoint reference');
|
||||||
const note = JSON.parse(decode(read(root, `exploration-${id(name)}.json`)));
|
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 }))');
|
const allowed = f.capture('002', 'console.log(JSON.stringify({ step: 2 }))');
|
||||||
expect(allowed.status, allowed.stderr).toBe(0);
|
expect(allowed.status, allowed.stderr).toBe(0);
|
||||||
expect(receipt(allowed.stdout).next).toContain('anchored on capture 002');
|
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');
|
const report = f.run('materialize', f.root, 'annotations.json');
|
||||||
expect(report.status, report.stderr).toBe(0);
|
expect(report.status, report.stderr).toBe(0);
|
||||||
expect(receipt(report.stdout).reportLinks).toEqual(['[checkpoint 001](exploration-001.json)']);
|
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.
|
// 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(/^\s*[A-D]\)[^\n]*(drive|browse|Aside)/im);
|
||||||
expect(text).not.toMatch(/drive\s+account\.apple\.com/i);
|
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.
|
// Self-service shape: the user generates it themselves.
|
||||||
expect(text).toMatch(/generate|any device|fastlane-credentials/i);
|
expect(text).toMatch(/generate|any device|fastlane-credentials/i);
|
||||||
} finally { cleanup(); }
|
} finally { cleanup(); }
|
||||||
|
|||||||
Reference in new issue
Block a user