fix: complete image service integration
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
import { createHmac } from 'node:crypto';
|
||||
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import path from 'node:path';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
|
||||
import { syncImageRepository } from './sync-image-repository.mjs';
|
||||
|
||||
test('signs a scoped Core2026 fallback sync without exposing the secret', async (t) => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), 'sammo-image-sync-'));
|
||||
t.after(() => rm(directory, { recursive: true, force: true }));
|
||||
const secretFile = path.join(directory, 'secret');
|
||||
const secret = 's'.repeat(32);
|
||||
await writeFile(secretFile, `${secret}\n`, { mode: 0o600 });
|
||||
let captured;
|
||||
const result = await syncImageRepository({
|
||||
baseUrl: 'https://sam-image.hided.net/',
|
||||
secretFile,
|
||||
commit: 'a'.repeat(40),
|
||||
now: () => Date.parse('2026-08-08T00:00:00Z'),
|
||||
requestIdFactory: () => 'request-1',
|
||||
fetchImpl: async (url, init) => {
|
||||
captured = { url, init };
|
||||
return new Response(JSON.stringify({ ok: true, changed: false }), { status: 200 });
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(result, { ok: true, changed: false });
|
||||
assert.equal(captured.url, 'https://sam-image.hided.net/v1/sync');
|
||||
assert.equal(captured.init.body, JSON.stringify({ commit: 'a'.repeat(40) }));
|
||||
assert.equal(captured.init.headers['x-image-client'], 'core2026');
|
||||
assert.equal(
|
||||
captured.init.headers['x-image-signature'],
|
||||
createHmac('sha256', secret)
|
||||
.update(`${captured.init.headers['x-image-timestamp']}.request-1.${captured.init.body}`)
|
||||
.digest('hex')
|
||||
);
|
||||
assert.equal(Object.values(captured.init.headers).includes(secret), false);
|
||||
});
|
||||
|
||||
test('rejects invalid commits before reading the secret or making a request', async () => {
|
||||
await assert.rejects(
|
||||
syncImageRepository({
|
||||
secretFile: '/does/not/exist',
|
||||
commit: 'main',
|
||||
fetchImpl: async () => {
|
||||
throw new Error('must not fetch');
|
||||
},
|
||||
}),
|
||||
/Commit must be a full/
|
||||
);
|
||||
});
|
||||
|
||||
test('reports authentication failures without returning a response body', async (t) => {
|
||||
const directory = await mkdtemp(path.join(tmpdir(), 'sammo-image-sync-'));
|
||||
t.after(() => rm(directory, { recursive: true, force: true }));
|
||||
const secretFile = path.join(directory, 'secret');
|
||||
await writeFile(secretFile, 's'.repeat(32), { mode: 0o600 });
|
||||
await assert.rejects(
|
||||
syncImageRepository({
|
||||
secretFile,
|
||||
fetchImpl: async () => new Response('{"reason":"sensitive detail"}', { status: 401 }),
|
||||
}),
|
||||
/HTTP 401/
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user