Restore an evicted server copy from an online holder

This commit is contained in:
Claude
2026-09-30 07:58:42 +00:00
parent 6d3cefa874
commit 60a28fc593
8 changed files with 112 additions and 7 deletions

View File

@@ -1,6 +1,6 @@
// Device → server intake: hash, validate, (scan), admit (plan 016). Needs ffmpeg.
import { test, expect, beforeAll } from 'bun:test';
import { mkdtempSync, readFileSync, readdirSync } from 'node:fs';
import { mkdtempSync, readFileSync, readdirSync, unlinkSync } from 'node:fs';
import { spawnSync } from 'node:child_process';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
@@ -64,6 +64,27 @@ test('a valid file is hashed by the server, validated, admitted and its uploader
expect(await (await req('/api/p2p/intake', { method: 'POST', body: { videoId: 'upAAAAAAAA1', cid: goodCid, size: good.length } })).json()).toEqual({ ok: true, known: true });
});
test('restore: a known cid is uploaded again only when the server lost its copy', async () => {
let serverHas = true;
const app3 = new Hono();
const p2p = registerP2pRoutes(app3, { cfg, p2pDb, fileForCid: async () => null, sha256Range: async () => '', log: quiet });
const got = [];
registerIntakeRoutes(app3, {
cfg, p2pDb, gate: p2p.gate, requireDevice: p2p.requireDevice, validateMedia, admitFile, log: quiet,
serverHasCid: async () => serverHas,
adopt: async (videoId, path, info) => { got.push(info.sha256); unlinkSync(path); return { adopted: true }; }, // a real adopt moves the file
});
const h = { 'X-Device': dev.deviceId + '.' + dev.secret, 'Content-Type': 'application/json' };
const open = async () => (await app3.request('/api/p2p/intake', { method: 'POST', headers: h, body: JSON.stringify({ videoId: 'upAAAAAAAA1', cid: goodCid, size: good.length, restore: true }) })).json();
expect(await open()).toEqual({ ok: true, known: true }); // server still has it
serverHas = false;
const t = await open();
expect(t.ticket).toMatch(/^[0-9a-f]{32}$/);
const r = await (await app3.request(t.url, { method: 'PUT', headers: { 'X-Device': h['X-Device'] }, body: good })).json();
expect(r).toEqual({ ok: true, cid: goodCid, adopted: true });
expect(got).toEqual([goodCid]);
});
test('claimed cid mismatch, truncated media, and non-media are rejected and deleted', async () => {
const cases = [
[{ cid: 'f'.repeat(64), bytes: good }, 400, /hash mismatch/],