|
1 | | -const {test}=require('node:test'); |
2 | | -const assert=require('node:assert/strict'); |
3 | | -const {execFileSync}=require('node:child_process'); |
4 | | -const {mkdtempSync,writeFileSync,readFileSync,rmSync}=require('node:fs'); |
5 | | -const {join}=require('node:path'); |
6 | | -const {tmpdir}=require('node:os'); |
7 | | -const update=require('./recent-updates.cjs'); |
| 1 | +const { test } = require('node:test'); |
| 2 | +const assert = require('node:assert/strict'); |
| 3 | +const { execFileSync } = require('node:child_process'); |
| 4 | +const { mkdtempSync, writeFileSync, readFileSync, rmSync } = require('node:fs'); |
| 5 | +const { join } = require('node:path'); |
| 6 | +const { tmpdir } = require('node:os'); |
| 7 | +const update = require('./recent-updates.cjs'); |
8 | 8 |
|
9 | | -test('multi-commit pushes update only English README, keep seven dated features and remain idempotent',async()=>{ |
10 | | - const root=mkdtempSync(join(tmpdir(),'reader-recent-updates-')),previous=process.cwd(); |
11 | | - const git=(...args)=>execFileSync('git',args,{cwd:root,encoding:'utf8',stdio:['ignore','pipe','pipe']}).trim(); |
12 | | - try{ |
13 | | - git('init');git('config','user.name','Contributor');git('config','user.email','contributor@example.com'); |
14 | | - const original='Intro\n\n## Features\n\nKeep features\n\n## Recent updates\n\nOld table\n\n## Quick start\n\nKeep quick start\n'; |
15 | | - writeFileSync(join(root,'README.md'),original);writeFileSync(join(root,'translated.md'),'Keep translation'); |
16 | | - git('add','.');git('commit','-m','initial');const before=git('rev-parse','HEAD'); |
17 | | - for(let n=1;n<=9;n++){ |
18 | | - writeFileSync(join(root,'app.js'),`// feature ${n}\n`);git('add','app.js');git('commit','-m',`feat(reader): feature ${n}`); |
| 9 | +test('multi-commit pushes update only English README, keep seven dated features and remain idempotent', async () => { |
| 10 | + const root = mkdtempSync(join(tmpdir(), 'reader-recent-updates-')), |
| 11 | + previous = process.cwd(); |
| 12 | + const git = (...args) => |
| 13 | + execFileSync('git', args, { |
| 14 | + cwd: root, |
| 15 | + encoding: 'utf8', |
| 16 | + stdio: ['ignore', 'pipe', 'pipe'], |
| 17 | + }).trim(); |
| 18 | + try { |
| 19 | + git('init'); |
| 20 | + git('config', 'user.name', 'Contributor'); |
| 21 | + git('config', 'user.email', 'contributor@example.com'); |
| 22 | + const original = |
| 23 | + 'Intro\n\n## Features\n\nKeep features\n\n## Recent updates\n\nOld table\n\n## Quick start\n\nKeep quick start\n'; |
| 24 | + writeFileSync(join(root, 'README.md'), original); |
| 25 | + writeFileSync(join(root, 'translated.md'), 'Keep translation'); |
| 26 | + git('add', '.'); |
| 27 | + git('commit', '-m', 'initial'); |
| 28 | + const before = git('rev-parse', 'HEAD'); |
| 29 | + for (let n = 1; n <= 9; n++) { |
| 30 | + writeFileSync(join(root, 'app.js'), `// feature ${n}\n`); |
| 31 | + git('add', 'app.js'); |
| 32 | + git('commit', '-m', `feat(reader): feature ${n}`); |
| 33 | + } |
| 34 | + const newest = git('rev-parse', 'HEAD'); |
| 35 | + writeFileSync(join(root, 'README.md'), original + '\nBadge\n'); |
| 36 | + git('add', 'README.md'); |
| 37 | + git('commit', '-m', 'feat: ci badge'); |
| 38 | + writeFileSync(join(root, 'app.js'), '// fix\n'); |
| 39 | + git('add', 'app.js'); |
| 40 | + git('commit', '-m', 'fix: last commit'); |
| 41 | + const after = git('rev-parse', 'HEAD'); |
| 42 | + let calls = 0; |
| 43 | + const outputs = []; |
| 44 | + const github = { |
| 45 | + rest: { |
| 46 | + repos: { |
| 47 | + getCommit: async ({ ref }) => { |
| 48 | + calls++; |
| 49 | + return { |
| 50 | + data: { |
| 51 | + author: { login: 'contributor', html_url: 'https://github.com/contributor' }, |
| 52 | + html_url: `https://github.com/owner/repo/commit/${ref}`, |
| 53 | + }, |
| 54 | + }; |
| 55 | + }, |
| 56 | + }, |
| 57 | + }, |
| 58 | + }; |
| 59 | + const core = { info: () => {}, setOutput: (...args) => outputs.push(args) }; |
| 60 | + const context = { repo: { owner: 'owner', repo: 'repo' }, payload: { before, after } }; |
| 61 | + process.chdir(root); |
| 62 | + await update({ github, context, core }); |
| 63 | + const result = readFileSync('README.md', 'utf8'); |
| 64 | + assert.equal(calls, 7); |
| 65 | + assert.equal((result.match(/^\| \d{4}-\d{2}-\d{2} \|/gm) || []).length, 7); |
| 66 | + assert.ok(result.includes(`[feature 9](https://github.com/owner/repo/commit/${newest})`)); |
| 67 | + assert.ok(!result.includes('ci badge')); |
| 68 | + assert.ok(!result.includes('feature 2]')); |
| 69 | + assert.ok(result.includes('Keep features')); |
| 70 | + assert.ok(result.includes('Keep quick start\n\nBadge')); |
| 71 | + assert.ok(result.includes('[@contributor](https://github.com/contributor)')); |
| 72 | + assert.equal(readFileSync('translated.md', 'utf8'), 'Keep translation'); |
| 73 | + assert.deepEqual(outputs, [['changed', 'true']]); |
| 74 | + outputs.length = 0; |
| 75 | + await update({ github, context, core }); |
| 76 | + assert.deepEqual(outputs, []); |
| 77 | + // A fix-only push performs no API calls or file writes. |
| 78 | + const callCount = calls; |
| 79 | + context.payload.before = git('rev-parse', 'HEAD^'); |
| 80 | + await update({ github, context, core }); |
| 81 | + assert.equal(calls, callCount); |
| 82 | + assert.equal(readFileSync('README.md', 'utf8'), result); |
| 83 | + // New branches have an all-zero before SHA. |
| 84 | + context.payload = { before: '0'.repeat(40), after, created: true }; |
| 85 | + await update({ github, context, core }); |
| 86 | + assert.equal(readFileSync('README.md', 'utf8'), result); |
| 87 | + } finally { |
| 88 | + process.chdir(previous); |
| 89 | + rmSync(root, { recursive: true, force: true }); |
19 | 90 | } |
20 | | - const newest=git('rev-parse','HEAD'); |
21 | | - writeFileSync(join(root,'README.md'),original+'\nBadge\n');git('add','README.md');git('commit','-m','feat: ci badge'); |
22 | | - writeFileSync(join(root,'app.js'),'// fix\n');git('add','app.js');git('commit','-m','fix: last commit');const after=git('rev-parse','HEAD'); |
23 | | - let calls=0;const outputs=[]; |
24 | | - const github={rest:{repos:{getCommit:async({ref})=>{calls++;return {data:{author:{login:'contributor',html_url:'https://github.com/contributor'},html_url:`https://github.com/owner/repo/commit/${ref}`}};}}}}; |
25 | | - const core={info:()=>{},setOutput:(...args)=>outputs.push(args)}; |
26 | | - const context={repo:{owner:'owner',repo:'repo'},payload:{before,after}}; |
27 | | - process.chdir(root); |
28 | | - await update({github,context,core}); |
29 | | - const result=readFileSync('README.md','utf8'); |
30 | | - assert.equal(calls,7);assert.equal((result.match(/^\| \d{4}-\d{2}-\d{2} \|/gm)||[]).length,7); |
31 | | - assert.ok(result.includes(`[feature 9](https://github.com/owner/repo/commit/${newest})`)); |
32 | | - assert.ok(!result.includes('ci badge'));assert.ok(!result.includes('feature 2]')); |
33 | | - assert.ok(result.includes('Keep features'));assert.ok(result.includes('Keep quick start\n\nBadge')); |
34 | | - assert.ok(result.includes('[@contributor](https://github.com/contributor)')); |
35 | | - assert.equal(readFileSync('translated.md','utf8'),'Keep translation'); |
36 | | - assert.deepEqual(outputs,[['changed','true']]); |
37 | | - outputs.length=0;await update({github,context,core});assert.deepEqual(outputs,[]); |
38 | | - // A fix-only push performs no API calls or file writes. |
39 | | - const callCount=calls; |
40 | | - context.payload.before=git('rev-parse','HEAD^');await update({github,context,core}); |
41 | | - assert.equal(calls,callCount);assert.equal(readFileSync('README.md','utf8'),result); |
42 | | - // New branches have an all-zero before SHA. |
43 | | - context.payload={before:'0'.repeat(40),after,created:true}; |
44 | | - await update({github,context,core});assert.equal(readFileSync('README.md','utf8'),result); |
45 | | - }finally{process.chdir(previous);rmSync(root,{recursive:true,force:true});} |
46 | 91 | }); |
0 commit comments