132 lines
4.9 KiB
JavaScript
132 lines
4.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import crypto from 'node:crypto';
|
|
import test from 'node:test';
|
|
import { createImageMakeClient, createImageMakeClientFromEnv } from './image-make-client.mjs';
|
|
|
|
test('image_make client creates, polls, validates, downloads and acknowledges a job', async () => {
|
|
const image = Buffer.concat([
|
|
Buffer.from('RIFF'),
|
|
Buffer.alloc(4),
|
|
Buffer.from('WEBP'),
|
|
Buffer.from('test-image'),
|
|
]);
|
|
const sha256 = crypto.createHash('sha256').update(image).digest('hex');
|
|
const calls = [];
|
|
const fetchImpl = async (url, init) => {
|
|
const path = new URL(url).pathname;
|
|
calls.push({ path, init });
|
|
if (path === '/v1/generation-jobs') {
|
|
return Response.json({ job_id: 'job-1', status: 'queued' }, { status: 201 });
|
|
}
|
|
if (path === '/v1/generation-jobs/job-1' && init.method !== 'POST') {
|
|
return Response.json({
|
|
job_id: 'job-1',
|
|
status: 'succeeded',
|
|
result: {
|
|
download_url: '/v1/generation-jobs/job-1/result',
|
|
sha256,
|
|
width: 512,
|
|
height: 512,
|
|
},
|
|
});
|
|
}
|
|
if (path.endsWith('/result')) {
|
|
return new Response(image, {
|
|
headers: { 'content-type': 'image/webp', 'x-artifact-sha256': sha256 },
|
|
});
|
|
}
|
|
if (path.endsWith('/acknowledge')) return Response.json({ job_id: 'job-1' });
|
|
throw new Error(`Unexpected request ${path}`);
|
|
};
|
|
const client = createImageMakeClient({
|
|
baseUrl: 'http://127.0.0.1:8080',
|
|
token: 'test-token',
|
|
fetchImpl,
|
|
sleep: async () => {},
|
|
});
|
|
const result = await client.generateImage({
|
|
prompt: 'calm reading room',
|
|
presetId: 'memind_square_illustration',
|
|
idempotencyKey: 'imgreq-test',
|
|
});
|
|
assert.equal(result.jobId, 'job-1');
|
|
assert.equal(result.sha256, sha256);
|
|
assert.deepEqual(result.buffer, image);
|
|
await client.acknowledge(result.jobId, 'asset-1');
|
|
assert.deepEqual(calls.map((call) => call.path), [
|
|
'/v1/generation-jobs',
|
|
'/v1/generation-jobs/job-1',
|
|
'/v1/generation-jobs/job-1/result',
|
|
'/v1/generation-jobs/job-1/acknowledge',
|
|
]);
|
|
assert.equal(calls[0].init.headers.authorization, 'Bearer test-token');
|
|
assert.equal(calls[0].init.headers['idempotency-key'], 'imgreq-test');
|
|
});
|
|
|
|
test('image_make client fails closed on checksum mismatch', async () => {
|
|
const image = Buffer.concat([Buffer.from('RIFF'), Buffer.alloc(4), Buffer.from('WEBPbad')]);
|
|
const client = createImageMakeClient({
|
|
baseUrl: 'http://127.0.0.1:8080', token: 'test-token', sleep: async () => {},
|
|
fetchImpl: async (url) => {
|
|
const path = new URL(url).pathname;
|
|
if (path === '/v1/generation-jobs') return Response.json({ job_id: 'job-1', status: 'running' }, { status: 201 });
|
|
if (path.endsWith('/result')) return new Response(image, { headers: { 'content-type': 'image/webp' } });
|
|
return Response.json({ job_id: 'job-1', status: 'succeeded', result: { download_url: '/v1/generation-jobs/job-1/result', sha256: '0'.repeat(64) } });
|
|
},
|
|
});
|
|
await assert.rejects(
|
|
client.generateImage({ prompt: 'x', presetId: 'memind_square_illustration' }),
|
|
{ code: 'IMAGE_MAKE_CHECKSUM_MISMATCH' },
|
|
);
|
|
});
|
|
|
|
test('image_make client resolves a completed idempotent reuse through status_url', async () => {
|
|
const image = Buffer.concat([Buffer.from('RIFF'), Buffer.alloc(4), Buffer.from('WEBPreused')]);
|
|
const sha256 = crypto.createHash('sha256').update(image).digest('hex');
|
|
const calls = [];
|
|
const client = createImageMakeClient({
|
|
baseUrl: 'http://127.0.0.1:8080', token: 'test-token', sleep: async () => {},
|
|
fetchImpl: async (url) => {
|
|
const path = new URL(url).pathname;
|
|
calls.push(path);
|
|
if (path === '/v1/generation-jobs') {
|
|
return Response.json({
|
|
job_id: 'job-reused',
|
|
status: 'succeeded',
|
|
status_url: '/v1/generation-jobs/job-reused',
|
|
});
|
|
}
|
|
if (path === '/v1/generation-jobs/job-reused') {
|
|
return Response.json({
|
|
job_id: 'job-reused',
|
|
status: 'succeeded',
|
|
result: {
|
|
download_url: '/v1/generation-jobs/job-reused/result',
|
|
sha256,
|
|
},
|
|
});
|
|
}
|
|
if (path.endsWith('/result')) {
|
|
return new Response(image, { headers: { 'content-type': 'image/webp' } });
|
|
}
|
|
throw new Error(`Unexpected request ${path}`);
|
|
},
|
|
});
|
|
const result = await client.generateImage({
|
|
prompt: 'reuse me',
|
|
presetId: 'memind_dark_hero',
|
|
idempotencyKey: 'imgreq-reused',
|
|
});
|
|
assert.equal(result.jobId, 'job-reused');
|
|
assert.deepEqual(calls, [
|
|
'/v1/generation-jobs',
|
|
'/v1/generation-jobs/job-reused',
|
|
'/v1/generation-jobs/job-reused/result',
|
|
]);
|
|
});
|
|
|
|
test('image_make env client stays disabled until URL and token are both configured', () => {
|
|
assert.equal(createImageMakeClientFromEnv({}), null);
|
|
assert.equal(createImageMakeClientFromEnv({ IMAGE_MAKE_BASE_URL: 'http://127.0.0.1:8080' }), null);
|
|
});
|