mirror of
https://github.com/langgenius/dify.git
synced 2026-03-19 13:47:37 +08:00
Signed-off-by: edvatar <88481784+toroleapinc@users.noreply.github.com> Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: majiayu000 <1835304752@qq.com> Co-authored-by: Poojan <poojan@infocusp.com> Co-authored-by: sahil-infocusp <73810410+sahil-infocusp@users.noreply.github.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: Pandaaaa906 <ye.pandaaaa906@gmail.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: heyszt <270985384@qq.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Ijas <ijas.ahmd.ap@gmail.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: 木之本澪 <kinomotomiovo@gmail.com> Co-authored-by: KinomotoMio <200703522+KinomotoMio@users.noreply.github.com> Co-authored-by: 不做了睡大觉 <64798754+stakeswky@users.noreply.github.com> Co-authored-by: User <user@example.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: edvatar <88481784+toroleapinc@users.noreply.github.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: Leilei <138381132+Inlei@users.noreply.github.com> Co-authored-by: HaKu <104669497+haku-ink@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: wangxiaolei <fatelei@gmail.com> Co-authored-by: Varun Chawla <34209028+veeceey@users.noreply.github.com> Co-authored-by: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Co-authored-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com> Co-authored-by: tda <95275462+tda1017@users.noreply.github.com> Co-authored-by: root <root@DESKTOP-KQLO90N> Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai> Co-authored-by: Niels Kaspers <153818647+nielskaspers@users.noreply.github.com> Co-authored-by: hj24 <mambahj24@gmail.com> Co-authored-by: Tyson Cung <45380903+tysoncung@users.noreply.github.com> Co-authored-by: Stephen Zhou <hi@hyoban.cc> Co-authored-by: FFXN <31929997+FFXN@users.noreply.github.com> Co-authored-by: slegarraga <64795732+slegarraga@users.noreply.github.com> Co-authored-by: 99 <wh2099@pm.me> Co-authored-by: Br1an <932039080@qq.com> Co-authored-by: L1nSn0w <l1nsn0w@qq.com> Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai> Co-authored-by: akkoaya <151345394+akkoaya@users.noreply.github.com> Co-authored-by: 盐粒 Yanli <yanli@dify.ai> Co-authored-by: lif <1835304752@qq.com> Co-authored-by: weiguang li <codingpunk@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: HanWenbo <124024253+hwb96@users.noreply.github.com> Co-authored-by: Coding On Star <447357187@qq.com> Co-authored-by: CodingOnStar <hanxujiang@dify.com> Co-authored-by: Stable Genius <stablegenius043@gmail.com> Co-authored-by: Stable Genius <259448942+stablegenius49@users.noreply.github.com> Co-authored-by: ふるい <46769295+Echo0ff@users.noreply.github.com> Co-authored-by: Xiyuan Chen <52963600+GareArc@users.noreply.github.com>
197 lines
5.3 KiB
TypeScript
197 lines
5.3 KiB
TypeScript
import { convertToMp3 } from '../utils'
|
|
|
|
// ── Hoisted mocks ──
|
|
|
|
const mocks = vi.hoisted(() => {
|
|
const readHeader = vi.fn()
|
|
const encodeBuffer = vi.fn()
|
|
const flush = vi.fn()
|
|
|
|
return { readHeader, encodeBuffer, flush }
|
|
})
|
|
|
|
vi.mock('lamejs', () => ({
|
|
default: {
|
|
WavHeader: {
|
|
readHeader: mocks.readHeader,
|
|
},
|
|
Mp3Encoder: class MockMp3Encoder {
|
|
encodeBuffer = mocks.encodeBuffer
|
|
flush = mocks.flush
|
|
},
|
|
},
|
|
}))
|
|
|
|
vi.mock('lamejs/src/js/BitStream', () => ({ default: {} }))
|
|
vi.mock('lamejs/src/js/Lame', () => ({ default: {} }))
|
|
vi.mock('lamejs/src/js/MPEGMode', () => ({ default: {} }))
|
|
|
|
// ── helpers ──
|
|
|
|
/** Build a fake recorder whose getChannelData returns DataView-like objects with .buffer and .byteLength. */
|
|
function createMockRecorder(opts: {
|
|
channels: number
|
|
sampleRate: number
|
|
leftSamples: number[]
|
|
rightSamples?: number[]
|
|
}) {
|
|
const toDataView = (samples: number[]) => {
|
|
const buf = new ArrayBuffer(samples.length * 2)
|
|
const view = new DataView(buf)
|
|
samples.forEach((v, i) => {
|
|
view.setInt16(i * 2, v, true)
|
|
})
|
|
return view
|
|
}
|
|
|
|
const leftView = toDataView(opts.leftSamples)
|
|
const rightView = opts.rightSamples ? toDataView(opts.rightSamples) : null
|
|
|
|
mocks.readHeader.mockReturnValue({
|
|
channels: opts.channels,
|
|
sampleRate: opts.sampleRate,
|
|
})
|
|
|
|
return {
|
|
getWAV: vi.fn(() => new ArrayBuffer(44)),
|
|
getChannelData: vi.fn(() => ({
|
|
left: leftView,
|
|
right: rightView,
|
|
})),
|
|
}
|
|
}
|
|
|
|
describe('convertToMp3', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('should convert mono WAV data to an MP3 blob', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: [100, 200, 300, 400],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([1, 2, 3]))
|
|
mocks.flush.mockReturnValue(new Int8Array([4, 5]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.type).toBe('audio/mp3')
|
|
expect(mocks.encodeBuffer).toHaveBeenCalled()
|
|
// Mono: encodeBuffer called with only left data
|
|
const firstCall = mocks.encodeBuffer.mock.calls[0]
|
|
expect(firstCall).toHaveLength(1)
|
|
expect(mocks.flush).toHaveBeenCalled()
|
|
})
|
|
|
|
it('should convert stereo WAV data to an MP3 blob', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 2,
|
|
sampleRate: 48000,
|
|
leftSamples: [100, 200],
|
|
rightSamples: [300, 400],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([10, 20]))
|
|
mocks.flush.mockReturnValue(new Int8Array([30]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.type).toBe('audio/mp3')
|
|
// Stereo: encodeBuffer called with left AND right
|
|
const firstCall = mocks.encodeBuffer.mock.calls[0]
|
|
expect(firstCall).toHaveLength(2)
|
|
})
|
|
|
|
it('should skip empty encoded buffers', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: [100, 200],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array(0))
|
|
mocks.flush.mockReturnValue(new Int8Array(0))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.type).toBe('audio/mp3')
|
|
expect(result.size).toBe(0)
|
|
})
|
|
|
|
it('should include flush data when flush returns non-empty buffer', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 22050,
|
|
leftSamples: [1],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array(0))
|
|
mocks.flush.mockReturnValue(new Int8Array([99, 98, 97]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.size).toBe(3)
|
|
})
|
|
|
|
it('should omit flush data when flush returns empty buffer', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: [10, 20],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([1, 2]))
|
|
mocks.flush.mockReturnValue(new Int8Array(0))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
expect(result.size).toBe(2)
|
|
})
|
|
|
|
it('should process multiple chunks when sample count exceeds maxSamples (1152)', () => {
|
|
const samples = Array.from({ length: 2400 }, (_, i) => i % 32767)
|
|
const recorder = createMockRecorder({
|
|
channels: 1,
|
|
sampleRate: 44100,
|
|
leftSamples: samples,
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([1]))
|
|
mocks.flush.mockReturnValue(new Int8Array(0))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(mocks.encodeBuffer.mock.calls.length).toBeGreaterThan(1)
|
|
expect(result).toBeInstanceOf(Blob)
|
|
})
|
|
|
|
it('should encode stereo with right channel subarray', () => {
|
|
const recorder = createMockRecorder({
|
|
channels: 2,
|
|
sampleRate: 44100,
|
|
leftSamples: [100, 200, 300],
|
|
rightSamples: [400, 500, 600],
|
|
})
|
|
|
|
mocks.encodeBuffer.mockReturnValue(new Int8Array([5, 6, 7]))
|
|
mocks.flush.mockReturnValue(new Int8Array([8]))
|
|
|
|
const result = convertToMp3(recorder)
|
|
|
|
expect(result).toBeInstanceOf(Blob)
|
|
for (const call of mocks.encodeBuffer.mock.calls) {
|
|
expect(call).toHaveLength(2)
|
|
expect(call[0]).toBeInstanceOf(Int16Array)
|
|
expect(call[1]).toBeInstanceOf(Int16Array)
|
|
}
|
|
})
|
|
})
|