Files
chimera-Public/test/server/ch-0301-import-upload-http.test.js
T

51 lines
3.6 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const crypto = require('crypto');
const fs = require('fs');
const os = require('os');
const path = require('path');
const test = require('node:test');
const { createAppServer } = require('../../src/server/app-server');
test('streaming import routes require auth and CSRF and return resumable offsets', async () => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-upload-http-'));
fs.mkdirSync(path.join(root, 'cache'), { recursive: true });
const values = new Map();
const settings = { get: (key, fallback) => values.has(key) ? values.get(key) : fallback, set: (key, value) => values.set(key, value) };
const secretValues = new Map();
const secrets = { get: key => secretValues.get(key), set: (key, value) => secretValues.set(key, value), delete: key => secretValues.delete(key) };
const registry = { describe: () => [], invoke: async () => ({}), isMutating: () => false, requestBodyLimit: () => 1024 };
const logger = { info() {}, warn() {}, error() {}, audit() {} };
const app = createAppServer({ appRoot: path.resolve(__dirname, '../..'), paths: { root, cache: path.join(root, 'cache') }, settings, secrets, registry, logger, ftp: { disconnect: async () => {} }, capabilities: { features: {} }, version: 'test' });
const address = await app.listen(0, '127.0.0.1');
const base = `http://127.0.0.1:${address.port}`;
const common = { Host: `127.0.0.1:${address.port}`, Origin: base, 'Content-Type': 'application/json' };
try {
const denied = await fetch(`${base}/api/import/uploads`, { method: 'POST', headers: common, body: JSON.stringify({ files: [{ relativePath: 'game.pkg', size: 4 }] }) });
assert.equal(denied.status, 401);
const setup = await fetch(`${base}/api/setup`, { method: 'POST', headers: common, body: JSON.stringify({ username: 'uploader', password: 'secure-password-123', allowedRoots: [root], safetyConfirmed: true }) });
assert.equal(setup.status, 201);
const cookie = setup.headers.get('set-cookie').split(';')[0];
const csrf = (await setup.json()).csrfToken;
const missingCsrf = await fetch(`${base}/api/import/uploads`, { method: 'POST', headers: { ...common, Cookie: cookie }, body: JSON.stringify({ files: [{ relativePath: 'game.pkg', size: 4 }] }) });
assert.equal(missingCsrf.status, 403);
const createdResponse = await fetch(`${base}/api/import/uploads`, { method: 'POST', headers: { ...common, Cookie: cookie, 'X-CSRF-Token': csrf }, body: JSON.stringify({ files: [{ relativePath: 'folder/game.pkg', size: 4 }] }) });
assert.equal(createdResponse.status, 201);
const created = (await createdResponse.json()).result;
assert.equal(JSON.stringify(created).includes(root), false);
const chunkUrl = `${base}/api/import/uploads/${created.id}/files/${created.files[0].id}`;
const chunk = Buffer.from('data');
const chunkHeaders = { Host: common.Host, Origin: base, Cookie: cookie, 'X-CSRF-Token': csrf, 'Upload-Offset': '0', 'X-Chunk-SHA256': crypto.createHash('sha256').update(chunk).digest('hex'), 'Content-Type': 'application/octet-stream' };
const deniedChunk = await fetch(chunkUrl, { method: 'PUT', headers: { ...chunkHeaders, 'X-CSRF-Token': '' }, body: chunk });
assert.equal(deniedChunk.status, 403);
const uploaded = await fetch(chunkUrl, { method: 'PUT', headers: chunkHeaders, body: chunk });
assert.equal(uploaded.status, 200);
assert.equal(uploaded.headers.get('upload-offset'), '4');
assert.equal((await uploaded.json()).result.state, 'uploaded');
} finally {
await app.close();
fs.rmSync(root, { recursive: true, force: true });
}
});