feat(fls): ZIP layer of the .fls codec
Reader and writer ported from filius-ipad with the same path policy, limits and error codes; DEFLATE via fflate, cut at the declared size like Apple Compression. 54 tests: the Swift archive rejection tests, DEFLATE edge cases, and all 23 corpus files cross-checked against fflate.unzipSync and round-tripped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
// The 23 real .fls files of the corpus (corpus/README.md): every one opens,
|
||||
// the entries match what an independent ZIP reader extracts, and writing
|
||||
// them back and reading again changes nothing.
|
||||
|
||||
import { readdirSync, readFileSync, statSync } from 'node:fs';
|
||||
import { dirname, join, relative } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { unzipSync } from 'fflate';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { CONFIGURATION_ENTRY_PATH, readFlsArchive, writeFlsArchive } from '../src/index.js';
|
||||
|
||||
const corpusRoot = join(dirname(fileURLToPath(import.meta.url)), '../../../corpus');
|
||||
|
||||
function flsFiles(directory: string): string[] {
|
||||
return readdirSync(directory)
|
||||
.flatMap((name) => {
|
||||
const path = join(directory, name);
|
||||
return statSync(path).isDirectory() ? flsFiles(path) : name.endsWith('.fls') ? [path] : [];
|
||||
})
|
||||
.sort();
|
||||
}
|
||||
|
||||
const files = flsFiles(corpusRoot);
|
||||
|
||||
describe('corpus archives', () => {
|
||||
it('has all 23 files', () => {
|
||||
expect(files).toHaveLength(23);
|
||||
});
|
||||
|
||||
for (const file of files) {
|
||||
it(relative(corpusRoot, file), () => {
|
||||
const bytes = new Uint8Array(readFileSync(file));
|
||||
const contents = readFlsArchive(bytes);
|
||||
|
||||
const independent = unzipSync(bytes);
|
||||
const independentFiles = Object.keys(independent).filter((path) => !path.endsWith('/')).sort();
|
||||
expect(contents.entryPaths).toEqual(independentFiles);
|
||||
expect(contents.configurationXml).toEqual(independent[CONFIGURATION_ENTRY_PATH]);
|
||||
for (const [path, data] of contents.supplementalEntries) expect(data, path).toEqual(independent[path]);
|
||||
|
||||
const rewritten = readFlsArchive(writeFlsArchive(contents.configurationXml, contents.supplementalEntries));
|
||||
expect(rewritten.configurationXml).toEqual(contents.configurationXml);
|
||||
expect(rewritten.supplementalEntries).toEqual(contents.supplementalEntries);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user