import assert from 'node:assert/strict' import { readFileSync } from 'node:fs' import { spawnSync } from 'node:child_process' import test from 'node:test' import { extractRemoteTemplate, parseArguments, renderRemoteTemplate, } from './git-incremental-deploy-macos.mjs' test('generated remote deployment payload is valid bash', () => { const source = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8') const payload = extractRemoteTemplate(source).replace(/__[A-Z0-9_]+__/g, "''") const result = spawnSync('bash', ['-n'], { input: payload, encoding: 'utf8', }) assert.equal(result.status, 0, result.stderr || result.stdout) }) test('Mac entry renders every canonical remote deployment token', () => { const source = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8') const template = extractRemoteTemplate(source) const tokens = [...new Set(template.match(/__[A-Z0-9_]+__/g) ?? [])] const values = Object.fromEntries(tokens.map((token, index) => [token, token === '__COMMIT__' ? 'a'.repeat(40) : `value ${index} with quote ' safely escaped`])) const payload = renderRemoteTemplate(template, values) assert.doesNotMatch(payload, /__[A-Z0-9_]+__/) const result = spawnSync('bash', ['-n'], { input: payload, encoding: 'utf8' }) assert.equal(result.status, 0, result.stderr || result.stdout) }) test('Mac entry requires an exact commit outside DryRun', () => { assert.throws(() => parseArguments([]), /正式部署必须/) assert.throws(() => parseArguments(['--commit', 'abc']), /40 位小写/) assert.equal(parseArguments(['--dry-run']).dryRun, true) assert.equal(parseArguments(['--commit', 'b'.repeat(40)]).commit, 'b'.repeat(40)) }) test('deployment SSH hops keep long builds and cutovers alive', () => { const macSource = readFileSync(new URL('./git-incremental-deploy-macos.mjs', import.meta.url), 'utf8') const powerShellSource = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8') const payload = extractRemoteTemplate(powerShellSource) for (const source of [macSource, payload]) { assert.match(source, /ServerAliveInterval=3/) assert.match(source, /ServerAliveCountMax=10/) assert.match(source, /TCPKeepAlive=yes/) } }) test('migration deployment gate recognizes the latest approved schema migration', () => { const source = readFileSync(new URL('./git-incremental-deploy.ps1', import.meta.url), 'utf8') const gateStart = source.indexOf('const expectedMigrationNames = new Set([') const gateEnd = source.indexOf(']);', gateStart) assert.ok(gateStart >= 0 && gateEnd > gateStart, 'migration gate list is missing') const gate = source.slice(gateStart, gateEnd) assert.match(gate, /20260827_add_ding_workflow_detail_fields/) })