diff --git a/test/coverage-audit-evidence.test.ts b/test/coverage-audit-evidence.test.ts index 2974d5e8e..347413905 100644 --- a/test/coverage-audit-evidence.test.ts +++ b/test/coverage-audit-evidence.test.ts @@ -1019,3 +1019,23 @@ describe('coverage reads with neighboring display commands', () => { }); }); }); + +describe('coverage audit native evidence: pathspec git displays', () => { + const run = (command: string) => { + const s = synthetic(); + const numberedLines = (text: string) => text.replace(/\n$/, '').split('\n').map((line, i) => `${String(i + 1).padStart(6)}\t${line}`).join('\n'); + Object.assign(block(s, 1), { name: 'Bash', input: { command } }); + block(s, 2).content = `${numberedLines(s.files.source.content)}\n======\n${numberedLines(s.files.tests.content)}\n======\n src/billing.ts | 2 ++\n`; + s.result.transcript.splice(3); + return verdict(s); + }; + test('a ;-list of cat -n reads followed by git diff with a -- pathspec credits both reads (CI 37094035231 shape)', () => { + expect(run('cat -n src/billing.ts; echo ======; cat -n test/billing.test.ts; echo ======; git diff main --stat; echo; git diff main -- src/billing.ts test/billing.test.ts')) + .toMatchObject({ sourceRead: true, testsRead: true }); + }); + test('the pathspec form still cannot write or run helpers', () => { + for (const tail of ['git diff main -- src/billing.ts > out.txt', 'git diff main --output=x -- src/billing.ts', 'git diff main --ext-diff -- src/billing.ts']) { + expect(run(`cat -n src/billing.ts; echo ======; cat -n test/billing.test.ts; ${tail}`), tail).toMatchObject({ sourceRead: false, testsRead: false }); + } + }); +}); diff --git a/test/helpers/coverage-audit-evidence.ts b/test/helpers/coverage-audit-evidence.ts index 1a7e236bc..3d536f084 100644 --- a/test/helpers/coverage-audit-evidence.ts +++ b/test/helpers/coverage-audit-evidence.ts @@ -162,7 +162,7 @@ function readsFile(command: unknown, file: string, cwd: string, output: unknown, // observed display flag. Quoted/concatenated or unknown options may write // files or invoke helpers, so they cannot borrow a read-only classification. const gitDisplay = git !== null && (!git[2] || git[2].split(/\s+/).every(token => - token === (git[1] === 'log' ? '--oneline' : '--stat') || + token === (git[1] === 'log' ? '--oneline' : '--stat') || token === '--' || (git[1] === 'log' && /^-[1-9]\d{0,4}$/.test(token)) || /^[A-Za-z0-9_][A-Za-z0-9_./~^-]*$/.test(token))); return /^(?:cat|grep|head|ls|echo)(?:\s|$)/.test(stage) || stage === 'pwd' || stage === 'wc -l' || stage === 'git ls-files' || stage === "sed 's/^/TESTFILES:/'" || /^\[ -f [A-Za-z0-9_.\/-]+ \]$/.test(stage) || readTargets(stage).length > 0 || gitDisplay || displayAwk;