'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 }); } });