audiobookshelf/test/server/utils/zipHelpers.test.js

87 lines
3.1 KiB
JavaScript

const { expect } = require('chai')
const sinon = require('sinon')
const { PassThrough } = require('stream')
const path = require('path')
const fs = require('fs')
const os = require('os')
const zipHelpers = require('../../../server/utils/zipHelpers')
const Logger = require('../../../server/Logger')
describe('zipHelpers', () => {
let tmpDir
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'abs-zip-test-'))
sinon.stub(Logger, 'info')
sinon.stub(Logger, 'debug')
})
afterEach(() => {
sinon.restore()
fs.rmSync(tmpDir, { recursive: true, force: true })
})
describe('zipDirectoryPipe', () => {
function makeMockRes() {
const stream = new PassThrough()
stream.attachment = sinon.spy()
// zipDirectoryPipe resolves on 'close'. PassThrough emits 'finish' but not 'close'
// automatically — destroy() triggers 'close', so bridge the two events here.
stream.on('finish', () => stream.destroy())
return stream
}
it('should call res.attachment with the provided filename', async () => {
fs.writeFileSync(path.join(tmpDir, 'book.mp3'), 'audio content')
const res = makeMockRes()
await zipHelpers.zipDirectoryPipe(tmpDir, 'my-book.zip', res)
expect(res.attachment.calledWith('my-book.zip')).to.be.true
})
it('should resolve and produce data for a non-empty directory', async () => {
fs.writeFileSync(path.join(tmpDir, 'chapter1.mp3'), 'audio data chapter 1')
fs.writeFileSync(path.join(tmpDir, 'chapter2.mp3'), 'audio data chapter 2')
const res = makeMockRes()
const chunks = []
res.on('data', (chunk) => chunks.push(chunk))
await zipHelpers.zipDirectoryPipe(tmpDir, 'book.zip', res)
const totalBytes = chunks.reduce((sum, c) => sum + c.length, 0)
expect(totalBytes).to.be.greaterThan(0)
})
it('should resolve for an empty directory', async () => {
const res = makeMockRes()
// Should not throw
await zipHelpers.zipDirectoryPipe(tmpDir, 'empty.zip', res)
})
it('should produce a valid zip containing the expected files', async () => {
fs.writeFileSync(path.join(tmpDir, 'test.txt'), 'Hello audiobookshelf')
const res = makeMockRes()
const chunks = []
res.on('data', (chunk) => chunks.push(chunk))
await zipHelpers.zipDirectoryPipe(tmpDir, 'archive.zip', res)
// ZIP files start with the PK signature (0x50 0x4B)
const combined = Buffer.concat(chunks)
expect(combined[0]).to.equal(0x50) // 'P'
expect(combined[1]).to.equal(0x4b) // 'K'
})
it('should resolve (not throw) when the source directory does not exist', async () => {
// archiver treats a missing directory as a warning (ENOENT), not a fatal error,
// so zipDirectoryPipe resolves with an empty archive rather than rejecting.
const res = makeMockRes()
let threw = false
try {
await zipHelpers.zipDirectoryPipe('/nonexistent/path/that/does/not/exist', 'fail.zip', res)
} catch (e) {
threw = true
}
expect(threw).to.be.false
})
})
})