A small helpdesk system: an office Pixel running a patched GrapheneOS Dialer answers technician calls, records both call legs as separate channels, and a Ruby backend transcribes them through Whisper and files an AI summary against the caller. Squashed to a single commit for sharing. No credentials are included; secrets live outside the repo in /etc/helpdesk/env on the server or a gitignored .claude/env.local locally. See .claude/env.local.example for the shape. Start at README.md, then docs/architecture.md.
118 lines
5.2 KiB
Ruby
118 lines
5.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
# Offline tests for the stereo call-recording splitter (no gems, no fixtures - WAVs are built here).
|
|
# This handles the only copy of a recorded call, so the tests that matter most are the ones proving it
|
|
# refuses to guess: wrong channel count, wrong bit depth, not a WAV at all. Mangling audio silently
|
|
# would be worse than declining to split it.
|
|
# Run: ruby components/backend/test/test_wav_split.rb
|
|
require_relative "../lib/helpdesk/wav_split"
|
|
require "tmpdir"
|
|
|
|
$pass = 0; $fail = 0
|
|
def ok(d); $pass += 1; puts " ok #{d}"; end
|
|
def bad(d, got = nil); $fail += 1; puts " FAIL #{d}#{got.nil? ? '' : " (got: #{got.inspect})"}"; end
|
|
def eq(a, b, d); a == b ? ok(d) : bad(d, a); end
|
|
def truthy(x, d); x ? ok(d) : bad(d, x); end
|
|
def falsy(x, d); x ? bad(d, x) : ok(d); end
|
|
def raises(d) ; yield; bad(d, "no error"); rescue Helpdesk::WavSplit::Error; ok(d); end
|
|
|
|
W = Helpdesk::WavSplit
|
|
|
|
# Build a stereo WAV from explicit per-channel samples, so the test knows exactly what should come out.
|
|
def stereo_wav(left, right, rate: 16_000, bits: 16, channels: 2, fmt: 1, extra_chunk: false)
|
|
pcm = +""
|
|
left.zip(right).each { |l, r| pcm << [l].pack("v") << [r].pack("v") }
|
|
body = +""
|
|
body << "fmt " << [16].pack("V") << [fmt].pack("v") << [channels].pack("v")
|
|
body << [rate].pack("V") << [rate * channels * bits / 8].pack("V")
|
|
body << [channels * bits / 8].pack("v") << [bits].pack("v")
|
|
body << "LIST" << [4].pack("V") << "INFO" if extra_chunk
|
|
body << "data" << [pcm.bytesize].pack("V") << pcm
|
|
"RIFF" + [4 + body.bytesize].pack("V") + "WAVE" + body
|
|
end
|
|
|
|
def samples_of(path)
|
|
raw = File.binread(path)
|
|
info = W.parse(raw)
|
|
raw.byteslice(info[:data_offset], info[:data_size]).unpack("v*")
|
|
end
|
|
|
|
puts "parsing a header"
|
|
h = W.parse(stereo_wav([1, 2], [3, 4]))
|
|
eq(h[:channels], 2, "channel count read")
|
|
eq(h[:sample_rate], 16_000, "sample rate read")
|
|
eq(h[:bits], 16, "bit depth read")
|
|
eq(h[:format], 1, "PCM format read")
|
|
truthy(h[:data_offset] > 0, "data offset located")
|
|
|
|
puts "chunks in any order, unknown ones skipped"
|
|
h2 = W.parse(stereo_wav([1], [2], extra_chunk: true))
|
|
eq(h2[:channels], 2, "a LIST chunk before data does not confuse it")
|
|
truthy(h2[:data_offset] > 0, "data still found after an unknown chunk")
|
|
|
|
puts "splitting puts each speaker in their own file"
|
|
Dir.mktmpdir do |dir|
|
|
src = File.join(dir, "call.wav")
|
|
# left = operator, right = caller; distinct values so a swap would be obvious
|
|
File.binwrite(src, stereo_wav([100, 101, 102], [-100, -101, -102]))
|
|
l = File.join(dir, "op.wav"); r = File.join(dir, "caller.wav")
|
|
res = W.split(src, left_path: l, right_path: r)
|
|
|
|
eq(res[:frames], 3, "frame count reported")
|
|
eq(res[:sample_rate], 16_000, "sample rate carried through")
|
|
eq(samples_of(l), [100, 101, 102], "left channel is the operator, in order")
|
|
eq(samples_of(r), [65_436, 65_435, 65_434], "right channel is the caller (unsigned view of -100..-102)")
|
|
eq(W.parse(File.binread(l))[:channels], 1, "output is mono")
|
|
eq(W.parse(File.binread(r))[:channels], 1, "both outputs are mono")
|
|
eq(W.parse(File.binread(l))[:sample_rate], 16_000, "output keeps the sample rate")
|
|
end
|
|
|
|
puts "a truncated recording is split as far as it goes"
|
|
Dir.mktmpdir do |dir|
|
|
src = File.join(dir, "cut.wav")
|
|
full = stereo_wav([1, 2, 3, 4], [5, 6, 7, 8])
|
|
# A call that ended abruptly: the header still claims the original length.
|
|
File.binwrite(src, full.byteslice(0, full.bytesize - 8))
|
|
l = File.join(dir, "l.wav"); r = File.join(dir, "r.wav")
|
|
res = W.split(src, left_path: l, right_path: r)
|
|
eq(res[:frames], 2, "only the frames actually present are used")
|
|
eq(samples_of(l), [1, 2], "left truncated cleanly")
|
|
eq(samples_of(r), [5, 6], "right truncated cleanly")
|
|
end
|
|
|
|
puts "IT MUST REFUSE what it does not fully understand"
|
|
Dir.mktmpdir do |dir|
|
|
l = File.join(dir, "l.wav"); r = File.join(dir, "r.wav")
|
|
mono = File.join(dir, "mono.wav")
|
|
File.binwrite(mono, stereo_wav([1, 2], [3, 4], channels: 1))
|
|
raises("a mono file is refused, not silently duplicated") { W.split(mono, left_path: l, right_path: r) }
|
|
|
|
deep = File.join(dir, "24bit.wav")
|
|
File.binwrite(deep, stereo_wav([1], [2], bits: 24))
|
|
raises("24-bit audio is refused rather than misread as 16") { W.split(deep, left_path: l, right_path: r) }
|
|
|
|
junk = File.join(dir, "junk.wav")
|
|
File.binwrite(junk, "this is not audio at all")
|
|
raises("a non-RIFF file is refused") { W.split(junk, left_path: l, right_path: r) }
|
|
|
|
empty = File.join(dir, "empty.wav")
|
|
File.binwrite(empty, stereo_wav([], []))
|
|
raises("a file with no frames is refused") { W.split(empty, left_path: l, right_path: r) }
|
|
|
|
falsy(File.exist?(l), "nothing is written when the input is rejected")
|
|
end
|
|
|
|
puts "splittable? answers without raising"
|
|
Dir.mktmpdir do |dir|
|
|
good = File.join(dir, "s.wav"); File.binwrite(good, stereo_wav([1], [2]))
|
|
mono = File.join(dir, "m.wav"); File.binwrite(mono, stereo_wav([1], [2], channels: 1))
|
|
junk = File.join(dir, "j.wav"); File.binwrite(junk, "nope")
|
|
truthy(W.splittable?(good), "stereo PCM is splittable")
|
|
falsy(W.splittable?(mono), "mono is not")
|
|
falsy(W.splittable?(junk), "junk is not, and does not raise")
|
|
falsy(W.splittable?(File.join(dir, "missing.wav")), "a missing file is not, and does not raise")
|
|
end
|
|
|
|
puts
|
|
puts "#{$pass} passed, #{$fail} failed"
|
|
exit($fail.zero? ? 0 : 1)
|