diff --git a/test/import-batch.test.js b/test/import-batch.test.js new file mode 100644 index 0000000..0435722 --- /dev/null +++ b/test/import-batch.test.js @@ -0,0 +1,93 @@ +const { test } = require('node:test'); +const assert = require('node:assert/strict'); +const { spawn } = require('node:child_process'); +const fs = require('node:fs'); +const http = require('node:http'); +const os = require('node:os'); +const path = require('node:path'); + +const CLI = path.join(__dirname, '..', 'src', 'cli.js'); + +function run(args, env = {}) { + return new Promise((resolve, reject) => { + const child = spawn(process.execPath, [CLI, ...args], { + env: { ...process.env, ...env }, + }); + let stdout = ''; + let stderr = ''; + child.stdout.setEncoding('utf8'); + child.stderr.setEncoding('utf8'); + child.stdout.on('data', (chunk) => { stdout += chunk; }); + child.stderr.on('data', (chunk) => { stderr += chunk; }); + child.on('error', reject); + child.on('close', (status) => resolve({ status, stdout, stderr })); + }); +} + +test('repo import-batch continues after one item has no source token', async () => { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'stoke-import-batch-')); + const configFile = path.join(dir, 'config.json'); + const manifestFile = path.join(dir, 'manifest.json'); + const emptyPath = path.join(dir, 'bin'); + const forgeToken = 'forge-token-must-not-be-printed'; + const requests = []; + const server = http.createServer((req, res) => { + let body = ''; + req.setEncoding('utf8'); + req.on('data', (chunk) => { body += chunk; }); + req.on('end', () => { + requests.push({ method: req.method, url: req.url, body: JSON.parse(body) }); + res.writeHead(201, { 'Content-Type': 'application/json' }); + res.end(JSON.stringify({ + full_name: 'destination/imported-second', + html_url: 'https://forge.test/destination/imported-second', + })); + }); + }); + await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve)); + fs.mkdirSync(emptyPath); + fs.writeFileSync(configFile, JSON.stringify({ + url: `http://127.0.0.1:${server.address().port}`, + login: 'destination', + token: forgeToken, + })); + fs.writeFileSync(manifestFile, JSON.stringify([ + { name: 'missing-token', from: 'https://github.com/source/first.git', service: 'github' }, + { name: 'imported-second', from: 'https://git.example/source/second.git', service: 'git' }, + ])); + + try { + const result = await run( + ['--config', configFile, 'repo', 'import-batch', '--file', manifestFile], + { PATH: emptyPath, GITHUB_TOKEN: undefined }, + ); + + assert.equal(result.status, 1); + assert.match(result.stderr, /Failed to import missing-token: No GitHub token found\./); + assert.equal(result.stdout, + 'Imported: destination/imported-second -> https://forge.test/destination/imported-second\n' + + '\nBatch complete: 1/2 imported.\n'); + assert.deepEqual(requests, [{ + method: 'POST', + url: '/api/v1/repos/migrate', + body: { + clone_addr: 'https://git.example/source/second.git', + repo_name: 'imported-second', + repo_owner: 'destination', + service: 'git', + private: false, + issues: true, + labels: true, + milestones: true, + pull_requests: true, + releases: true, + wiki: true, + lfs: false, + }, + }]); + assert.doesNotMatch(result.stdout + result.stderr, new RegExp(forgeToken)); + } finally { + await new Promise((resolve) => server.close(resolve)); + fs.rmSync(dir, { recursive: true, force: true }); + } +});