feat: add ebook MIME types, fix azw3/mobi detection collision, trust undetectable ebook formats

This commit is contained in:
2026-07-31 13:25:46 +02:00
parent 534630ab4e
commit 977dac35d9
2 changed files with 99 additions and 1 deletions
+79
View File
@@ -113,6 +113,85 @@ describe('outputMimeType — fonts', () => {
});
});
describe('outputMimeType — ebooks', () => {
it('returns the correct MIME type for each ebook target format', () => {
expect(outputMimeType('epub')).toBe('application/epub+zip');
expect(outputMimeType('fb2')).toBe('application/x-fictionbook+xml');
expect(outputMimeType('mobi')).toBe('application/x-mobipocket-ebook');
expect(outputMimeType('azw3')).toBe('application/vnd.amazon.ebook');
expect(outputMimeType('pdb')).toBe('application/vnd.palm');
expect(outputMimeType('lrf')).toBe('application/octet-stream');
expect(outputMimeType('rb')).toBe('application/octet-stream');
expect(outputMimeType('snb')).toBe('application/octet-stream');
expect(outputMimeType('tcr')).toBe('application/octet-stream');
});
});
describe('resolveInputFormat — azw3/mobi collision', () => {
function buildMobiFamilyBuffer() {
// PDB header: 8-byte "BOOKMOBI" type+creator magic at offset 60, shared by both
// real .mobi and real .azw3 files (confirmed in file-type's source).
const buffer = Buffer.alloc(68);
buffer.write('BOOKMOBI', 60, 'ascii');
return buffer;
}
it('confirms file-type reports mobi-family content as ext "mobi" regardless of which of the two formats it is (documents the collision this fix works around)', async () => {
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-mobi-family.bin');
await fs.writeFile(fixturePath, buildMobiFamilyBuffer());
const detected = await detectInputMime(fixturePath);
expect(detected).toEqual({ ext: 'mobi', mime: 'application/x-mobipocket-ebook' });
await fs.unlink(fixturePath);
});
it('accepts a real mobi-family file declared as mobi', async () => {
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-mobi-family.bin');
await fs.writeFile(fixturePath, buildMobiFamilyBuffer());
const result = await resolveInputFormat(fixturePath, 'mobi');
expect(result).toEqual({ mime: 'application/x-mobipocket-ebook', valid: true });
await fs.unlink(fixturePath);
});
it('accepts a real mobi-family file declared as azw3 despite file-type reporting it as mobi', async () => {
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample-mobi-family.bin');
await fs.writeFile(fixturePath, buildMobiFamilyBuffer());
const result = await resolveInputFormat(fixturePath, 'azw3');
expect(result).toEqual({ mime: 'application/x-mobipocket-ebook', valid: true });
await fs.unlink(fixturePath);
});
});
describe('resolveInputFormat — undetectable ebook formats', () => {
const undetectableFormats = {
fb2: 'application/x-fictionbook+xml',
lrf: 'application/octet-stream',
pdb: 'application/vnd.palm',
rb: 'application/octet-stream',
snb: 'application/octet-stream',
tcr: 'application/octet-stream',
};
it.each(Object.entries(undetectableFormats))(
'trusts the declared format for undetectable %s files',
async (declaredFormat, expectedMime) => {
const fixturePath = path.join(import.meta.dirname, 'fixtures', `sample.${declaredFormat}`);
await fs.writeFile(fixturePath, 'arbitrary bytes with no recognizable magic number');
const result = await resolveInputFormat(fixturePath, declaredFormat);
expect(result).toEqual({ mime: expectedMime, valid: true });
await fs.unlink(fixturePath);
}
);
});
describe('resolveInputFormat — dfont', () => {
it('confirms file-type alone misidentifies the dfont fixture as ico (documents the bug this fix works around)', async () => {
const fixturePath = path.join(import.meta.dirname, 'fixtures', 'sample.dfont');