visual-journal / scripts /keepalive-hf-space.test.mjs
Visual Journal deploy
Deploy 8e0aeb3 to Docker Space
84810e9
Raw
History Blame Contribute Delete
8.91 kB
import assert from 'node:assert/strict';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
import { join } from 'node:path';
import { after, describe, it } from 'node:test';
const repoRoot = fileURLToPath(new URL('..', import.meta.url));
const scriptPath = join(repoRoot, 'scripts/keepalive-hf-space.mjs');
const tempDirectories = [];
after(() => {
for (const directory of tempDirectories) {
rmSync(directory, { recursive: true, force: true, maxRetries: 3 });
}
});
describe('HF Space keepalive script validation', () => {
it('rejects non-integer timeout env values before network access', () => {
const result = runKeepalive({ HF_SPACE_KEEPALIVE_TIMEOUT_MS: '1000abc' });
assert.equal(result.status, 1);
assert.match(result.stderr, /HF_SPACE_KEEPALIVE_TIMEOUT_MS/);
assert.match(result.stderr, /integer/);
assert.equal(result.stdout.trim(), '');
});
it('rejects non-integer retry attempt values before network access', () => {
const result = runKeepalive({ HF_SPACE_KEEPALIVE_MAX_ATTEMPTS: 'two' });
assert.equal(result.status, 1);
assert.match(result.stderr, /HF_SPACE_KEEPALIVE_MAX_ATTEMPTS/);
assert.match(result.stderr, /integer/);
assert.equal(result.stdout.trim(), '');
});
it('rejects a retry cap smaller than the initial retry delay before network access', () => {
const result = runKeepalive({
HF_SPACE_KEEPALIVE_RETRY_DELAY_MS: '10',
HF_SPACE_KEEPALIVE_RETRY_MAX_DELAY_MS: '9'
});
assert.equal(result.status, 1);
assert.match(result.stderr, /HF_SPACE_KEEPALIVE_RETRY_MAX_DELAY_MS/);
assert.match(result.stderr, /greater than or equal to 10/);
assert.equal(result.stdout.trim(), '');
});
it('raises the default retry cap to match a custom initial retry delay', () => {
const preloadPath = writeFetchStub(`
globalThis.setTimeout = (callback) => {
queueMicrotask(callback);
return 0;
};
globalThis.clearTimeout = () => {};
let count = 0;
globalThis.fetch = async () => {
count += 1;
if (count === 1) {
return new Response(JSON.stringify({ error: 'warming' }), {
status: 503,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response(JSON.stringify({ passwordRequired: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
};
`);
const result = runKeepalive(
{
HF_SPACE_KEEPALIVE_MAX_ATTEMPTS: '2',
HF_SPACE_KEEPALIVE_RETRY_DELAY_MS: '40000',
HF_SPACE_KEEPALIVE_TIMEOUT_MS: '1000'
},
['--import', preloadPath]
);
assert.equal(result.status, 0);
assert.match(result.stderr, /"nextRetryDelayMs": 40000/);
assert.match(result.stdout, /"attempt": "2\/2"/);
});
it('rejects keepalive URLs with embedded credentials before network access', () => {
const result = runKeepalive({ HF_SPACE_KEEPALIVE_URL: 'https://user:secret@example-demo.hf.space' });
assert.equal(result.status, 1);
assert.match(result.stderr, /HF_SPACE_KEEPALIVE_URL/);
assert.match(result.stderr, /plain Space origin/);
assert.doesNotMatch(result.stderr, /secret/);
assert.equal(result.stdout.trim(), '');
});
it('retries failed keepalive attempts before reporting success', () => {
const preloadPath = writeFetchStub(`
let count = 0;
globalThis.fetch = async () => {
count += 1;
if (count === 1) {
return new Response(JSON.stringify({ error: 'warming' }), {
status: 503,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response(JSON.stringify({ passwordRequired: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
};
`);
const result = runKeepalive(
{
HF_SPACE_KEEPALIVE_URL: 'https://misonl-visual-journal.hf.space',
HF_SPACE_KEEPALIVE_PATH: '/api/auth-status',
HF_SPACE_KEEPALIVE_MAX_ATTEMPTS: '2',
HF_SPACE_KEEPALIVE_RETRY_DELAY_MS: '1',
HF_SPACE_KEEPALIVE_TIMEOUT_MS: '1000'
},
['--import', preloadPath]
);
assert.equal(result.status, 0);
assert.match(result.stderr, /"attempt": "1\/2"/);
assert.match(result.stderr, /"nextRetryDelayMs": 1/);
assert.match(result.stdout, /"ok": true/);
assert.match(result.stdout, /"attempt": "2\/2"/);
});
it('uses capped exponential retry delays before reporting success', () => {
const preloadPath = writeFetchStub(`
let count = 0;
globalThis.fetch = async () => {
count += 1;
if (count <= 3) {
return new Response(JSON.stringify({ error: 'warming' }), {
status: 503,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response(JSON.stringify({ passwordRequired: true }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
};
`);
const result = runKeepalive(
{
HF_SPACE_KEEPALIVE_MAX_ATTEMPTS: '4',
HF_SPACE_KEEPALIVE_RETRY_DELAY_MS: '1',
HF_SPACE_KEEPALIVE_RETRY_MAX_DELAY_MS: '3',
HF_SPACE_KEEPALIVE_TIMEOUT_MS: '1000'
},
['--import', preloadPath]
);
assert.equal(result.status, 0);
assert.match(result.stderr, /"nextRetryDelayMs": 1/);
assert.match(result.stderr, /"nextRetryDelayMs": 2/);
assert.match(result.stderr, /"nextRetryDelayMs": 3/);
assert.match(result.stdout, /"attempt": "4\/4"/);
});
it('reports failing HTTP status and content type without printing the upstream error body', () => {
const preloadPath = writeFetchStub(`
globalThis.fetch = async () =>
new Response('<html>internal-upstream-detail</html>', {
status: 500,
headers: { 'Content-Type': 'text/html; charset=utf-8' }
});
`);
const result = runKeepalive(
{
HF_SPACE_KEEPALIVE_MAX_ATTEMPTS: '1',
HF_SPACE_KEEPALIVE_TIMEOUT_MS: '1000'
},
['--import', preloadPath]
);
assert.equal(result.status, 1);
assert.match(result.stderr, /HTTP 500/);
assert.match(result.stderr, /text\/html/);
assert.doesNotMatch(result.stderr, /internal-upstream-detail|<html>/);
assert.equal((result.stderr.match(/"ok": false/g) || []).length, 1);
});
it('prints a timeout-specific final error when every attempt aborts', () => {
const preloadPath = writeFetchStub(`
globalThis.fetch = async (url, options) =>
new Promise((resolve, reject) => {
options.signal.addEventListener('abort', () => {
reject(new DOMException('This operation was aborted', 'AbortError'));
});
});
`);
const result = runKeepalive(
{
HF_SPACE_KEEPALIVE_MAX_ATTEMPTS: '1',
HF_SPACE_KEEPALIVE_TIMEOUT_MS: '1000'
},
['--import', preloadPath]
);
assert.equal(result.status, 1);
assert.match(result.stderr, /Keepalive request timed out after 1000ms/);
assert.doesNotMatch(result.stderr, /This operation was aborted/);
assert.equal((result.stderr.match(/"ok": false/g) || []).length, 1);
});
});
function runKeepalive(env, nodeArgs = []) {
return spawnSync(process.execPath, [...nodeArgs, scriptPath], {
cwd: repoRoot,
encoding: 'utf8',
env: { ...process.env, ...env }
});
}
function writeFetchStub(source) {
const directory = mkdtempSync(join(tmpdir(), 'hf-keepalive-fetch-'));
tempDirectories.push(directory);
const filePath = join(directory, 'fetch-stub.mjs');
writeFileSync(filePath, source);
return filePath;
}