108 lines
4.1 KiB
TypeScript
108 lines
4.1 KiB
TypeScript
import { execFileSync } from 'node:child_process';
|
|
import { randomBytes } from 'node:crypto';
|
|
import { readFile } from 'node:fs/promises';
|
|
import assert from 'node:assert/strict';
|
|
|
|
// Isolated disposable Compose project. Never targets the user's deployment or volumes.
|
|
const project = `hamham-check-${randomBytes(4).toString('hex')}`;
|
|
const port = process.env.VERIFY_PORT ?? '3200';
|
|
const origin = `http://localhost:${port}`;
|
|
const password = randomBytes(30).toString('base64url');
|
|
const env = {
|
|
...process.env,
|
|
POSTGRES_PASSWORD: randomBytes(32).toString('hex'),
|
|
HOST_PORT: port,
|
|
APP_ORIGIN: origin,
|
|
COOKIE_SECURE: 'false',
|
|
};
|
|
const compose = (args: string[], input?: string | Buffer) =>
|
|
execFileSync('docker', ['compose', '-p', project, ...args], {
|
|
env,
|
|
input,
|
|
maxBuffer: 32 * 1024 * 1024,
|
|
stdio: input === undefined ? ['ignore', 'pipe', 'inherit'] : ['pipe', 'pipe', 'inherit'],
|
|
});
|
|
let cookie = '';
|
|
async function request(path: string, method = 'GET', body?: unknown) {
|
|
const response = await fetch(`${origin}/api/${path}`, {
|
|
method,
|
|
headers: {
|
|
Origin: origin,
|
|
Cookie: cookie,
|
|
Connection: 'close',
|
|
...(body === undefined ? {} : { 'Content-Type': 'application/json' }),
|
|
},
|
|
body: body === undefined ? undefined : JSON.stringify(body),
|
|
});
|
|
assert.ok(
|
|
response.ok,
|
|
`${method} ${path}: ${response.status} ${response.ok ? '' : await response.text()}`,
|
|
);
|
|
if (response.headers.get('set-cookie'))
|
|
cookie = response.headers.get('set-cookie')!.split(';')[0];
|
|
return response;
|
|
}
|
|
try {
|
|
console.log(`Compose-Integrationstest: ${project} (${origin})`);
|
|
console.log(compose(['up', '-d', '--build', '--wait', '--wait-timeout', '240']).toString());
|
|
assert.equal(compose(['exec', '-T', 'app', 'id', '-u']).toString().trim(), '1000');
|
|
console.log(
|
|
compose(
|
|
['exec', '-T', 'app', 'npm', 'run', 'admin:create', '--', 'dockercheck'],
|
|
`${password}\n`,
|
|
).toString(),
|
|
);
|
|
await request('auth/login', 'POST', { username: 'dockercheck', password });
|
|
const raw = await readFile('public/examples/kochbuch.json', 'utf8');
|
|
const recipes = await (
|
|
await request('import', 'POST', { raw, format: 'json', preview: false })
|
|
).json();
|
|
assert.equal(recipes.length, 3);
|
|
const imageResponse = await fetch(`${origin}/api/media`, {
|
|
method: 'POST',
|
|
headers: { Origin: origin, Cookie: cookie, Connection: 'close', 'Content-Type': 'image/jpeg' },
|
|
body: await readFile('public/images/garden.jpg'),
|
|
});
|
|
assert.equal(imageResponse.status, 201);
|
|
const { url: image } = await imageResponse.json();
|
|
const before = await fetch(`${origin}${image}`, { headers: { Connection: 'close' } }).then((r) =>
|
|
r.arrayBuffer(),
|
|
);
|
|
await request(`recipes/${recipes[0].id}`, 'PUT', { ...recipes[0], image });
|
|
assert.equal((await (await request('export')).json()).recipes.length, 3);
|
|
console.log('Admin-CLI, Login, Import, Bild-Upload, Rezeptänderung und Export: OK');
|
|
console.log(compose(['restart']).toString());
|
|
console.log(compose(['up', '-d', '--wait', '--wait-timeout', '120']).toString());
|
|
const after = await fetch(`${origin}${image}`, { headers: { Connection: 'close' } }).then((r) =>
|
|
r.arrayBuffer(),
|
|
);
|
|
assert.deepEqual(Buffer.from(after), Buffer.from(before));
|
|
assert.equal((await (await request('recipes')).json()).length, 3);
|
|
console.log('Persistenz nach Neustart: Datenbank, Sitzung und Bildbytes OK');
|
|
const dump = compose(['exec', '-T', 'db', 'pg_dump', '-U', 'hamham', '-d', 'hamham', '-Fc']);
|
|
compose(['exec', '-T', 'db', 'createdb', '-U', 'hamham', 'hamham_restore']);
|
|
compose(['exec', '-T', 'db', 'pg_restore', '-U', 'hamham', '-d', 'hamham_restore'], dump);
|
|
assert.equal(
|
|
compose([
|
|
'exec',
|
|
'-T',
|
|
'db',
|
|
'psql',
|
|
'-U',
|
|
'hamham',
|
|
'-d',
|
|
'hamham_restore',
|
|
'-Atc',
|
|
'SELECT count(*) FROM recipes',
|
|
])
|
|
.toString()
|
|
.trim(),
|
|
'3',
|
|
);
|
|
console.log('PostgreSQL-Backup/Restore und wiederholte Migrationen: OK');
|
|
console.log(compose(['ps']).toString());
|
|
} finally {
|
|
// Only the unique verification project created above is removed, including its test volumes.
|
|
compose(['down', '-v', '--remove-orphans']);
|
|
}
|