import test from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; import { productionSettings } from '../scripts/production-settings.mjs'; import { verifyProduction } from '../scripts/verify-production.mjs'; import worker from '../src/index.js'; const SHA = 'a'.repeat(40); const fixture = () => ({ CLOUDFLARE_API_TOKEN: 'fixture-cloudflare-token', SESSION_SECRET: 'fixture-session-secret-'.repeat(3), ABC_API_KEY: 'fixture-abc-key', AUTH_CLIENT_ID: 'production-client', ABC_API_BASE_URL: 'https://feedback.example.test/', ABC_PROJECT_ID: '01900000-0000-7000-8000-000000000001', ABC_CHANNEL_ID: '01900000-0000-7000-8000-000000000002', ORG_CONTEXT_ENABLED: 'true', ORG_CONTEXT_TENANT_IDS: '["038326b6-954a-48a7-a85f-efd83f62b82a"]', ORG_CONTEXT_KEY_ID: 'fixture-org-key', ORG_CONTEXT_KEY_SECRET: 'fixture-org-secret', DEPLOY_COMMIT_SHA: SHA }); test('production preserves dashboard-managed domains without requesting zone routing changes', () => { const config = readFileSync(new URL('../wrangler.production.toml', import.meta.url), 'utf8'); assert.doesNotMatch(config, /^\s*routes?\s*=/m); assert.match(config, /^workers_dev\s*=\s*false\s*$/m); }); test('production requires its own credentials, target IDs, organization lookup and immutable release', () => { for (const name of Object.keys(fixture())) { const env = fixture(); delete env[name]; assert.throws(() => productionSettings(env), new RegExp(name === 'ORG_CONTEXT_TENANT_IDS' ? 'org_context_invalid_tenants' : name)); } assert.throws(() => productionSettings({ ...fixture(), ORG_CONTEXT_ENABLED: 'false' }), /must be true/); assert.throws(() => productionSettings({ ...fixture(), SESSION_SECRET: 'short' }), /at least 32/); assert.throws(() => productionSettings({ ...fixture(), ABC_CHANNEL_ID: '123' }), /UUID/); assert.throws(() => productionSettings({ ...fixture(), DEPLOY_COMMIT_SHA: '../staging' }), /full lowercase/); assert.throws(() => productionSettings({ ...fixture(), ABC_API_BASE_URL: 'https://secret:password@example.test' }), /HTTPS origin/); const settings = productionSettings(fixture()); assert.equal(settings.worker, 'baron-qa-gateway-prod'); assert.equal(settings.bucket, 'baron-qa-write'); assert.equal(settings.vars.ABC_API_BASE_URL, 'https://feedback.example.test'); assert.equal(settings.vars.QA_ASSET_PREFIX, 'releases/' + SHA); for (const name of Object.keys(settings.secrets)) assert.equal(settings.vars[name], undefined); assert.equal(settings.secrets.CLOUDFLARE_API_TOKEN, undefined); }); function withFakeWrangler(body) { const dir = mkdtempSync(join(tmpdir(), 'qa-deploy-test-')); const capture = join(dir, 'commands.jsonl'); writeFileSync(join(dir, 'npx'), `#!/usr/bin/env node const fs = require('node:fs'); const args = process.argv.slice(2); const index = args.indexOf('--secrets-file'); const path = index === -1 ? null : args[index + 1]; fs.appendFileSync(process.env.QA_TEST_CAPTURE, JSON.stringify({ args, path, secrets: path ? JSON.parse(fs.readFileSync(path)) : null, mode: path ? fs.statSync(path).mode & 0o777 : null }) + '\\n'); process.exit(Number(process.env.QA_TEST_EXIT || 0)); `, { mode: 0o700 }); const env = { ...fixture(), PATH: dir + ':' + process.env.PATH, QA_TEST_CAPTURE: capture }; try { body({ dir, env, capture }); } finally { rmSync(dir, { recursive: true, force: true }); } } test('production validation and dry run never register secrets or deploy remotely', () => withFakeWrangler(({ env, capture }) => { const script = fileURLToPath(new URL('../scripts/deploy-production.mjs', import.meta.url)); const validation = spawnSync(process.execPath, [script, '--validate-only'], { env, encoding: 'utf8' }); assert.equal(validation.status, 0, validation.stderr); assert.equal(existsSync(capture), false); const dry = spawnSync(process.execPath, [script, '--dry-run'], { env, encoding: 'utf8' }); assert.equal(dry.status, 0, dry.stderr); const command = JSON.parse(readFileSync(capture, 'utf8')); assert.ok(command.args.includes('--dry-run')); assert.ok(command.args.includes('wrangler.production.toml')); assert.ok(!command.args.some((arg) => ['--route', '--routes', '--domain', '--domains'].includes(arg))); assert.equal(command.path, null); for (const name of ['SESSION_SECRET', 'ABC_API_KEY', 'ORG_CONTEXT_KEY_SECRET']) { assert.ok(!JSON.stringify(command.args).includes(env[name])); assert.ok(!validation.stdout.includes(env[name])); } })); for (const exitCode of [0, 1]) { test(`production uploads secrets with code and removes the private temporary file after exit ${exitCode}`, () => withFakeWrangler(({ env, capture }) => { const result = spawnSync(process.execPath, [fileURLToPath(new URL('../scripts/deploy-production.mjs', import.meta.url))], { env: { ...env, QA_TEST_EXIT: String(exitCode) }, encoding: 'utf8' }); assert.equal(result.status, exitCode); const command = JSON.parse(readFileSync(capture, 'utf8')); assert.equal(command.secrets.ABC_API_KEY, env.ABC_API_KEY); assert.equal(command.mode, 0o600); assert.equal(command.secrets.CLOUDFLARE_API_TOKEN, undefined); assert.ok(!JSON.stringify(command.args).includes(env.ORG_CONTEXT_KEY_SECRET)); assert.equal(existsSync(command.path), false); })); } test('production uploader rejects root uploads and sends every file to the selected commit path', () => withFakeWrangler(({ dir, env, capture }) => { const script = fileURLToPath(new URL('../scripts/upload-r2.sh', import.meta.url)); const refused = spawnSync('bash', [script, 'baron-qa-write'], { env, cwd: dir }); assert.equal(refused.status, 1); assert.equal(existsSync(capture), false); mkdirSync(join(dir, 'egbim')); mkdirSync(join(dir, 'assets')); for (const file of ['index.html', 'write.html', 'detail.html', 'app.js', 'config.js']) writeFileSync(join(dir, 'egbim', file), 'fixture'); writeFileSync(join(dir, 'assets/styles.css'), 'body {}'); mkdirSync(join(dir, 'assets/inquiry')); writeFileSync(join(dir, 'assets/inquiry/inquiry-hero.webp'), 'fixture-image'); writeFileSync(join(dir, 'assets/inquiry/entry.js'), 'fixture-entry'); const uploaded = spawnSync('bash', [script, 'baron-qa-write', 'wrangler.production.toml', 'releases/' + SHA], { env, cwd: dir, encoding: 'utf8' }); assert.equal(uploaded.status, 0, uploaded.stderr); const commands = readFileSync(capture, 'utf8').trim().split('\n').map(JSON.parse); assert.equal(commands.length, 8); const heroUpload = commands.find(command => command.args[5].endsWith('/assets/inquiry/inquiry-hero.webp')); assert.equal(heroUpload.args[heroUpload.args.indexOf('--content-type') + 1], 'image/webp'); const entryUpload = commands.find(command => command.args[5].endsWith('/assets/inquiry/entry.js')); assert.equal(entryUpload.args[entryUpload.args.indexOf('--content-type') + 1], 'application/javascript'); for (const command of commands) { assert.ok(command.args[5].startsWith('baron-qa-write/releases/' + SHA + '/')); assert.ok(command.args.includes('wrangler.production.toml')); assert.ok(!command.args.includes('baron-qa-pages')); } writeFileSync(capture, ''); const staging = spawnSync('bash', [script], { env, cwd: dir, encoding: 'utf8' }); assert.equal(staging.status, 0, staging.stderr); for (const command of readFileSync(capture, 'utf8').trim().split('\n').map(JSON.parse)) { assert.ok(command.args[5].startsWith('baron-qa-pages/')); assert.ok(!command.args[5].includes('releases/')); assert.ok(command.args.includes('wrangler.toml')); } })); for (const prefix of ['', 'releases/' + SHA]) { test(`Worker uses ${prefix ? 'release-specific' : 'legacy staging'} asset keys`, async () => { const response = await worker.fetch(new Request('https://qa.example.test/assets/styles.css'), { QA_ASSET_PREFIX: prefix, QA_BUCKET: { async get(key) { assert.equal(key, (prefix ? prefix + '/' : '') + 'assets/styles.css'); return { body: 'body {}', httpEtag: 'fixture', writeHttpMetadata(headers) { headers.set('content-type', 'text/css'); } }; } } }); assert.equal(response.status, 200); assert.equal(response.headers.get('x-qa-release'), prefix || null); }); } test('post-deploy verification rejects Hello World, old releases and unprotected write pages', async () => { await assert.rejects(verifyProduction(async () => new Response('Hello World!'), SHA)); const http = (oldRelease = false, unprotected = false) => async (url) => { if (url.endsWith('/auth/session')) return Response.json({ authenticated: false, user: null }); if (url.endsWith('/assets/styles.css')) return new Response('body {}', { headers: { 'content-type': 'text/css', 'x-qa-release': oldRelease ? 'releases/old' : 'releases/' + SHA } }); return new Response('', { status: unprotected ? 200 : 302, headers: { location: '/auth/login?return_url=write' } }); }; await assert.rejects(verifyProduction(http(true), SHA), /deployed commit/); await assert.rejects(verifyProduction(http(false, true), SHA), /redirect/); await verifyProduction(http(), SHA); });