Skip to content

Commit 9eaea0b

Browse files
committed
test(ci): fold has-work regression into the existing fingerprint test
Reviewer feedback on #2036: the standalone four-case test duplicated the harness above it and only two states are meaningful for this regression. Reuse the same parsed workflow, temp dir, resolver stub, and Node stub; keep neither-cached (both platforms, has-work=true) and both-cached (empty matrix, has-work=false, release gated). Drops the single-cache permutations, which exercise #1996's unchanged filtering rather than this fix.
1 parent d7f6efd commit 9eaea0b

1 file changed

Lines changed: 44 additions & 112 deletions

File tree

test/ci/trusted-fixture-artifact.test.mjs

Lines changed: 44 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -305,14 +305,22 @@ test('Android APK repack signs the output and preserves its package id', (t) =>
305305
assert.match(mismatch.stderr, /did not preserve the source signing certificate/);
306306
});
307307

308-
test('producer maps each platform to its resolved lookup and matrix artifact name', (t) => {
308+
test('producer maps each platform to its resolved lookup and matrix artifact name, and gates has-work when both are cached', (t) => {
309309
const workflow = parse(fs.readFileSync('.github/workflows/test-app-build-cache.yml', 'utf8'));
310310
const fingerprintStep = workflow.jobs.fingerprint.steps.find((step) => step.id === 'fingerprint');
311+
// #2034: an empty `include` matrix is legal JSON but GitHub Actions rejects it as
312+
// `strategy.matrix`, so `release` must be skipped -- not handed a zero-length
313+
// matrix -- whenever both fixtures are already cached.
314+
assert.equal(
315+
workflow.jobs.fingerprint.outputs['has-work'],
316+
'${{ steps.fingerprint.outputs.has-work }}',
317+
);
318+
assert.equal(workflow.jobs.release.if, "needs.fingerprint.outputs.has-work == 'true'");
319+
311320
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'fixture-producer-name-'));
312321
t.after(() => fs.rmSync(tempRoot, { force: true, recursive: true }));
313322
const actionDir = path.join(tempRoot, '.github/actions/setup-fixture-app');
314323
const binDir = path.join(tempRoot, 'bin');
315-
const outputPath = path.join(tempRoot, 'output');
316324
const resolverLog = path.join(tempRoot, 'resolver-calls');
317325
const nodeLog = path.join(tempRoot, 'node-calls');
318326
fs.mkdirSync(actionDir, { recursive: true });
@@ -326,140 +334,64 @@ test('producer maps each platform to its resolved lookup and matrix artifact nam
326334
'',
327335
].join('\n'),
328336
);
329-
fs.writeFileSync(
330-
path.join(binDir, 'node'),
331-
['#!/bin/sh', 'printf "%s\\n" "$*" >> "$TEST_NODE_LOG"', ''].join('\n'),
332-
);
337+
const writeNodeStub = (cached) =>
338+
fs.writeFileSync(
339+
path.join(binDir, 'node'),
340+
[
341+
'#!/bin/sh',
342+
'printf "%s\\n" "$*" >> "$TEST_NODE_LOG"',
343+
cached ? 'printf "111"' : 'true',
344+
'',
345+
].join('\n'),
346+
);
347+
writeNodeStub(false);
333348
fs.chmodSync(path.join(binDir, 'node'), 0o755);
349+
334350
const run = fingerprintStep.run
335351
.replaceAll('${{ github.event.pull_request.head.sha || github.sha }}', 'current-head')
336352
.replaceAll('${{ github.repository }}', 'octo/repo')
337353
.replaceAll('${{ github.event_name }}', 'pull_request')
338354
.replaceAll('${{ github.event.pull_request.head.repo.full_name }}', 'octo/repo');
339-
const result = spawnSync('bash', ['-c', run], {
340-
cwd: tempRoot,
341-
encoding: 'utf8',
342-
env: {
343-
...process.env,
344-
GITHUB_OUTPUT: outputPath,
345-
PATH: `${binDir}:${process.env.PATH}`,
346-
TEST_NODE_LOG: nodeLog,
347-
TEST_RESOLVER_LOG: resolverLog,
348-
},
349-
});
350-
assert.equal(result.status, 0, result.stderr);
351-
const outputLines = fs.readFileSync(outputPath, 'utf8').trim().split('\n');
352-
const matrixLine = outputLines.find((line) => line.startsWith('matrix='));
353-
const matrix = JSON.parse(matrixLine.slice('matrix='.length));
354-
assert.deepEqual(
355-
matrix.include.map(({ platform, artifactName }) => ({ platform, artifactName })),
356-
[
357-
{ platform: 'ios', artifactName: 'fingerprint.ios-hash.ios' },
358-
{ platform: 'android', artifactName: 'fingerprint.android-hash.android' },
359-
],
360-
);
361-
assert.deepEqual(
362-
outputLines.find((line) => line.startsWith('has-work=')),
363-
'has-work=true',
364-
);
365-
assert.deepEqual(fs.readFileSync(resolverLog, 'utf8').trim().split('\n'), ['ios', 'android']);
366-
assert.deepEqual(fs.readFileSync(nodeLog, 'utf8').trim().split('\n'), [
367-
'.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.ios-hash.ios current-head',
368-
'.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.android-hash.android current-head',
369-
]);
370-
});
371-
372-
test('fingerprint matrix covers all four cache combinations and gates has-work on both-cached', (t) => {
373-
const workflow = parse(fs.readFileSync('.github/workflows/test-app-build-cache.yml', 'utf8'));
374-
const fingerprintStep = workflow.jobs.fingerprint.steps.find((step) => step.id === 'fingerprint');
375-
// #2034: an empty `include` matrix is legal JSON but GitHub Actions rejects
376-
// it as `strategy.matrix`, so the `release` job must be skipped -- not
377-
// handed a zero-length matrix -- whenever both fixtures are cached.
378-
assert.equal(
379-
workflow.jobs.fingerprint.outputs['has-work'],
380-
'${{ steps.fingerprint.outputs.has-work }}',
381-
);
382-
assert.equal(workflow.jobs.release.if, "needs.fingerprint.outputs.has-work == 'true'");
383-
384-
const run = fingerprintStep.run
385-
.replaceAll('${{ github.event.pull_request.head.sha || github.sha }}', 'current-head')
386-
.replaceAll('${{ github.repository }}', 'octo/repo')
387-
.replaceAll('${{ github.event_name }}', 'push')
388-
.replaceAll('${{ github.event.pull_request.head.repo.full_name }}', '');
389-
390-
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'fixture-matrix-combinations-'));
391-
t.after(() => fs.rmSync(tempRoot, { force: true, recursive: true }));
392-
const actionDir = path.join(tempRoot, '.github/actions/setup-fixture-app');
393-
const binDir = path.join(tempRoot, 'bin');
394-
fs.mkdirSync(actionDir, { recursive: true });
395-
fs.mkdirSync(binDir);
396-
fs.writeFileSync(
397-
path.join(actionDir, 'resolve-artifact-name.sh'),
398-
['#!/bin/sh', 'printf "fingerprint.%s-hash.%s\\n" "$1" "$1"', ''].join('\n'),
399-
);
400-
fs.chmodSync(path.join(actionDir, 'resolve-artifact-name.sh'), 0o755);
401-
fs.writeFileSync(
402-
path.join(binDir, 'node'),
403-
[
404-
'#!/bin/sh',
405-
// Invoked as: node trusted-artifact.mjs find <repo> <name> <sha>
406-
'name="$4"',
407-
'case "$name" in',
408-
' *.ios) [ "$TEST_IOS_CACHED" = 1 ] && printf "111"; true ;;',
409-
' *.android) [ "$TEST_ANDROID_CACHED" = 1 ] && printf "222"; true ;;',
410-
'esac',
411-
'',
412-
].join('\n'),
413-
);
414-
fs.chmodSync(path.join(binDir, 'node'), 0o755);
415-
416-
const runFingerprint = (iosCached, androidCached) => {
417-
const outputPath = path.join(tempRoot, `output-${iosCached}-${androidCached}`);
355+
const runFingerprint = (outputName) => {
356+
const outputPath = path.join(tempRoot, outputName);
418357
const result = spawnSync('bash', ['-c', run], {
419358
cwd: tempRoot,
420359
encoding: 'utf8',
421360
env: {
422361
...process.env,
423362
GITHUB_OUTPUT: outputPath,
424363
PATH: `${binDir}:${process.env.PATH}`,
425-
TEST_ANDROID_CACHED: androidCached ? '1' : '0',
426-
TEST_IOS_CACHED: iosCached ? '1' : '0',
364+
TEST_NODE_LOG: nodeLog,
365+
TEST_RESOLVER_LOG: resolverLog,
427366
},
428367
});
429368
assert.equal(result.status, 0, result.stderr);
430369
const lines = fs.readFileSync(outputPath, 'utf8').trim().split('\n');
431-
const matrixLine = lines.find((line) => line.startsWith('matrix='));
432-
const hasWorkLine = lines.find((line) => line.startsWith('has-work='));
433370
return {
434-
hasWork: hasWorkLine.slice('has-work='.length),
435-
matrix: JSON.parse(matrixLine.slice('matrix='.length)),
371+
hasWork: lines.find((line) => line.startsWith('has-work=')),
372+
matrix: JSON.parse(lines.find((line) => line.startsWith('matrix=')).slice('matrix='.length)),
436373
};
437374
};
438375

439-
const bothCached = runFingerprint(true, true);
440-
assert.deepEqual(bothCached.matrix, { include: [] });
441-
assert.equal(bothCached.hasWork, 'false');
442-
443-
const iosOnlyCached = runFingerprint(true, false);
376+
const neitherCached = runFingerprint('output-neither-cached');
444377
assert.deepEqual(
445-
iosOnlyCached.matrix.include.map((entry) => entry.platform),
446-
['android'],
447-
);
448-
assert.equal(iosOnlyCached.hasWork, 'true');
449-
450-
const androidOnlyCached = runFingerprint(false, true);
451-
assert.deepEqual(
452-
androidOnlyCached.matrix.include.map((entry) => entry.platform),
453-
['ios'],
378+
neitherCached.matrix.include.map(({ platform, artifactName }) => ({ platform, artifactName })),
379+
[
380+
{ platform: 'ios', artifactName: 'fingerprint.ios-hash.ios' },
381+
{ platform: 'android', artifactName: 'fingerprint.android-hash.android' },
382+
],
454383
);
455-
assert.equal(androidOnlyCached.hasWork, 'true');
384+
assert.equal(neitherCached.hasWork, 'has-work=true');
385+
assert.deepEqual(fs.readFileSync(resolverLog, 'utf8').trim().split('\n'), ['ios', 'android']);
386+
assert.deepEqual(fs.readFileSync(nodeLog, 'utf8').trim().split('\n'), [
387+
'.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.ios-hash.ios current-head',
388+
'.github/actions/setup-fixture-app/trusted-artifact.mjs find octo/repo fingerprint.android-hash.android current-head',
389+
]);
456390

457-
const neitherCached = runFingerprint(false, false);
458-
assert.deepEqual(
459-
neitherCached.matrix.include.map((entry) => entry.platform),
460-
['ios', 'android'],
461-
);
462-
assert.equal(neitherCached.hasWork, 'true');
391+
writeNodeStub(true);
392+
const bothCached = runFingerprint('output-both-cached');
393+
assert.deepEqual(bothCached.matrix, { include: [] });
394+
assert.equal(bothCached.hasWork, 'has-work=false');
463395
});
464396

465397
test('artifact name resolver scopes both platforms and rejects invalid output', (t) => {

0 commit comments

Comments
 (0)