# frozen_string_literal: true # Offline tests for merging two per-speaker transcripts (no gems, no network). # Run: ruby components/backend/test/test_dual_transcript.rb require_relative "../lib/helpdesk/dual_transcript" $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 D = Helpdesk::DualTranscript puts "parsing the service's line format" p1 = D.parse("[00:00:03] [Speaker 1]: Dobrý den.\n\n[00:01:05] [Speaker 1]: Děkuji.") eq(p1.length, 2, "two turns parsed") eq(p1[0][:at], 3, "seconds computed from the timestamp") eq(p1[1][:at], 65, "minutes carried into seconds") eq(p1[0][:text], "Dobrý den.", "text without the markup") eq(D.parse("[01:02:03] [Speaker 1]: x")[0][:at], 3723, "hours counted too") puts "a wrapped line belongs to the turn above it, not to nobody" w = D.parse("[00:00:03] [Speaker 1]: první část\ndruhá část\n") eq(w.length, 1, "the continuation does not become its own turn") eq(w[0][:text], "první část druhá část", "and its words are kept") puts "merging two single-speaker channels into a conversation" op = "[00:00:01] [Speaker 1]: Dobrý den, helpdesk.\n[00:00:09] [Speaker 1]: Rozumím." cal = "[00:00:05] [Speaker 1]: Dobrý den, mám problém." m = D.merge(left: op, right: cal) eq(m.lines.reject { |l| l.strip.empty? }.length, 3, "all three turns present") truthy(m.include?("[00:00:01] [Operator]: Dobrý den, helpdesk."), "left channel labelled Operator") truthy(m.include?("[00:00:05] [Caller]: Dobrý den, mám problém."), "right channel labelled Caller") truthy(m.index("[00:00:01]") < m.index("[00:00:05]"), "ordered by time across channels") truthy(m.index("[00:00:05]") < m.index("[00:00:09]"), "and interleaved, not concatenated") falsy(m.include?("Speaker 1"), "the meaningless per-channel speaker numbers are gone") puts "ties resolve to the operator, who normally speaks first" t = D.merge(left: "[00:00:04] [Speaker 1]: otázka", right: "[00:00:04] [Speaker 1]: odpověď") truthy(t.index("Operator") < t.index("Caller"), "same-second turns read question then answer") puts "one silent channel still produces a usable transcript" only_op = D.merge(left: "[00:00:02] [Speaker 1]: haló?", right: "") truthy(only_op.include?("[Operator]: haló?"), "the speaking side survives") falsy(only_op.include?("Caller"), "and no empty turn is invented for the silent side") eq(D.merge(left: "", right: ""), "", "two empty channels -> empty transcript") puts "output keeps the format everything downstream parses" round = D.parse(D.merge(left: "[00:00:07] [Speaker 1]: a", right: "[00:00:08] [Speaker 1]: b")) eq(round.length, 2, "the merged text re-parses as turns") eq(round.map { |e| e[:at] }, [7, 8], "with the timestamps intact") puts "plausible? guards against a split that gained nothing" truthy(D.plausible?("[00:00:01] [Speaker 1]: ahoj", "[00:00:02] [Speaker 1]: nazdar"), "two different speakers is the good case") falsy(D.plausible?("[00:00:01] [Speaker 1]: ahoj", "[00:00:01] [Speaker 1]: ahoj"), "identical channels mean the legs were not really separated") falsy(D.plausible?("[00:00:01] [Speaker 1]: ahoj", ""), "a silent channel is not a plausible split") falsy(D.plausible?("", ""), "nothing at all is not plausible") puts "hms formatting" eq(D.hms(0), "00:00:00", "zero") eq(D.hms(61), "00:01:01", "a minute and a second") eq(D.hms(3661), "01:01:01", "an hour in") puts puts "#{$pass} passed, #{$fail} failed" exit($fail.zero? ? 0 : 1)