Files
aia-website/www/tools/deploy.test.mjs
T

45 lines
2.4 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { spawnSync } from 'node:child_process';
import test from 'node:test';
import { releaseManifest, rsyncArguments, sshTarget } from './deploy.mjs';
const fixturePath = path.join(path.dirname(fileURLToPath(import.meta.url)), 'fixtures', 'contabo-release-manifest-v1.json');
const fixture = JSON.parse(await fs.readFile(fixturePath, 'utf8'));
async function writeContractArtifact(root) {
for (const [relative, contents] of Object.entries(fixture.input.files)) {
const target = path.join(root, relative);
await fs.mkdir(path.dirname(target), { recursive: true });
await fs.writeFile(target, contents);
}
}
test('site manifest follows the pinned merged-Contabo release contract and detects unsafe public output', async (t) => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'aia-deploy-manifest-'));
try {
await writeContractArtifact(root);
const manifest = await releaseManifest(root, fixture.input.sourceCommit);
assert.deepEqual(manifest, fixture.expected);
const serverTool = process.env.AIA_CONTABO_RELEASE_TOOL;
if (serverTool) {
const parity = spawnSync('python3', [serverTool, 'manifest', root, fixture.input.sourceCommit], { encoding: 'utf8' });
assert.equal(parity.status, 0, `Configured Contabo release tool failed: ${parity.stderr}`);
assert.deepEqual(manifest, JSON.parse(parity.stdout));
} else {
t.diagnostic(`Live Contabo-tool parity was not run; this test validates the checked-in schema fixture generated from ${fixture.contaboCommit}. Set AIA_CONTABO_RELEASE_TOOL to the merged website-release.py path for direct parity.`);
}
await fs.mkdir(path.join(root, 'private'));
await assert.rejects(releaseManifest(root, fixture.input.sourceCommit), /Unexpected private artifact/);
} finally { await fs.rm(root, { recursive: true, force: true }); }
});
test('deployment transfer uses the Contabo ownership and mode interface', () => {
assert.deepEqual(rsyncArguments('/tmp/source', 'host:/srv/stage', { remove: true }).slice(0, 6), ['-rlt','--delete','--links','--chown=root:root','--chmod=Du=rwx,Dgo=,Fu=rw,Fgo=','/tmp/source/']);
assert.deepEqual(sshTarget('root@host.example', '/absolute/key').slice(0, 3), ['ssh','-i','/absolute/key']);
assert.throws(() => sshTarget('host;rm -rf /', '/absolute/key'), /invalid/);
});