Files
DndGamePlayer/app/main/project/zipRead.test.ts
T
Ivan Fontosh 1d051f8bf9 fix: game audio persistence and editor perf
- Keep game/campaign audio assets referenced (no prune)
- Flush pending project save on quit/switch/export to avoid losing campaignAudios
- Control: prevent game music restarts on scene changes; allow always-on controls; handle autoplay-after-scene-audio
- Editor: reduce ReactFlow churn with stable scene card map; lazy/async image decode
- Add contract/unit tests and update test script

Made-with: Cursor
2026-04-22 19:06:16 +08:00

99 lines
2.9 KiB
TypeScript

import assert from 'node:assert/strict';
import fssync from 'node:fs';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import test from 'node:test';
import { ZipFile } from 'yazl';
import { PROJECT_SCHEMA_VERSION } from '../../shared/types';
import { readProjectJsonFromZip } from './yauzlProjectZip';
void test('readProjectJsonFromZip: sequential reads close yauzl (no EMFILE)', async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'dnd-zip-read-'));
const zipPath = path.join(tmp, 'test.dnd.zip');
const minimal = {
id: 'p1',
meta: {
name: 'n',
fileBaseName: 'f',
createdAt: '2020-01-01',
updatedAt: '2020-01-01',
createdWithAppVersion: '1',
appVersion: '1',
schemaVersion: PROJECT_SCHEMA_VERSION,
},
scenes: {},
assets: {},
currentSceneId: null,
currentGraphNodeId: null,
sceneGraphNodes: [],
sceneGraphEdges: [],
};
const zipfile = new ZipFile();
zipfile.addBuffer(Buffer.from(JSON.stringify(minimal), 'utf8'), 'project.json', { compressionLevel: 9 });
const out = fssync.createWriteStream(zipPath);
const done = new Promise<void>((resolve, reject) => {
out.on('close', resolve);
out.on('error', reject);
});
zipfile.outputStream.pipe(out);
zipfile.end();
await done;
for (let i = 0; i < 150; i += 1) {
const p = await readProjectJsonFromZip(zipPath);
assert.equal(p.id, 'p1');
}
await fs.rm(tmp, { recursive: true, force: true });
});
void test('readProjectJsonFromZip: campaignAudios round-trips inside project.json', async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), 'dnd-zip-campaign-'));
const zipPath = path.join(tmp, 'test.dnd.zip');
const assetId = 'audio_asset_1';
const minimal = {
id: 'p1',
meta: {
name: 'n',
fileBaseName: 'f',
createdAt: '2020-01-01',
updatedAt: '2020-01-01',
createdWithAppVersion: '1',
appVersion: '1',
schemaVersion: PROJECT_SCHEMA_VERSION,
},
scenes: {},
assets: {},
campaignAudios: [{ assetId, autoplay: true, loop: false }],
currentSceneId: null,
currentGraphNodeId: null,
sceneGraphNodes: [],
sceneGraphEdges: [],
};
const zipfile = new ZipFile();
zipfile.addBuffer(Buffer.from(JSON.stringify(minimal), 'utf8'), 'project.json', { compressionLevel: 9 });
const out = fssync.createWriteStream(zipPath);
const done = new Promise<void>((resolve, reject) => {
out.on('close', resolve);
out.on('error', reject);
});
zipfile.outputStream.pipe(out);
zipfile.end();
await done;
const p = await readProjectJsonFromZip(zipPath);
assert.ok(Array.isArray((p as { campaignAudios?: unknown }).campaignAudios));
assert.deepEqual(
(p as { campaignAudios: typeof minimal.campaignAudios }).campaignAudios,
minimal.campaignAudios,
);
await fs.rm(tmp, { recursive: true, force: true });
});