1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
|
"""Tests for ratio-transcribe, the client.
ssh and scp are replaced by fakes that act on a temp directory standing in for the
remote home, so the client's real logic (job ids, upload-then-rename, polling,
collecting, the local fallback) runs against a filesystem it can't tell from the host.
"""
import json
import os
import subprocess
from pathlib import Path
import pytest
SCRIPT = Path(__file__).resolve().parent.parent / "src" / "ratio-transcribe"
STATE = ".local/state/meeting-transcribe"
FAKE_SSH = r"""#!/usr/bin/env bash
# ssh [-o k=v]... host command... -> run the command with HOME at the fake remote
[[ -n "${FAKE_SSH_DOWN:-}" ]] && exit 255
no_stdin=""
while [[ "$1" == -* ]]; do [[ "$1" == "-n" ]] && no_stdin=1; [[ "$1" == "-o" ]] && shift; shift; done
shift # host
printf '%s\n' "$*" >> "$FAKE_SSH_LOG"
if [[ -n "$no_stdin" ]]; then
( cd "$FAKE_REMOTE_HOME" && HOME="$FAKE_REMOTE_HOME" bash -c "$*" < /dev/null )
rc=$?
else
( cd "$FAKE_REMOTE_HOME" && HOME="$FAKE_REMOTE_HOME" bash -c "$*" )
rc=$?
cat > /dev/null # like the real ssh, drain whatever stdin the command left behind
fi
if [[ "$*" == *"incoming/"* && "$*" == mv* && -n "${FAKE_ON_SUBMIT:-}" ]]; then
( cd "$FAKE_REMOTE_HOME" && bash -c "$FAKE_ON_SUBMIT" )
fi
exit $rc
"""
FAKE_SCP = r"""#!/usr/bin/env bash
[[ -n "${FAKE_SSH_DOWN:-}" ]] && exit 255
while [[ "$1" == -* ]]; do [[ "$1" == "-o" ]] && shift; shift; done
printf '%s -> %s\n' "$1" "$2" >> "$FAKE_SCP_LOG"
cp "$1" "$FAKE_REMOTE_HOME/${2#*:}"
"""
# What the worker would do, compressed: finish or fail whatever sits in incoming/.
WORKER_OK = f"""cd {STATE}; mkdir -p done; for d in incoming/*/; do id=$(basename "$d");
printf '00:00:00 Speaker A: Hello from the host.\\n' > done/$id.txt; rm -rf "$d"; done"""
WORKER_FAIL = f"""cd {STATE}; mkdir -p failed; for d in incoming/*/; do id=$(basename "$d");
printf 'stage: whisper\\nexit 3\\n' > failed/$id.log; rm -rf "$d"; done"""
WORKER_EMPTY = f"""cd {STATE}; mkdir -p done; for d in incoming/*/; do id=$(basename "$d");
: > done/$id.txt; rm -rf "$d"; done"""
class Rig:
def __init__(self, tmp_path):
self.tmp = tmp_path
self.remote = tmp_path / "remote-home"
self.remote.mkdir()
self.local_home = tmp_path / "local-home"
self.local_home.mkdir()
self.bin = tmp_path / "bin"
self.bin.mkdir()
for name, body in (("ssh", FAKE_SSH), ("scp", FAKE_SCP)):
path = self.bin / name
path.write_text(body)
path.chmod(0o755)
self.ssh_log = tmp_path / "ssh.log"
self.scp_log = tmp_path / "scp.log"
self.audio = tmp_path / "meeting.m4a"
self.audio.write_bytes(b"pretend audio")
def run(self, args=None, on_submit=WORKER_OK, **env_extra):
env = {
"PATH": f"{self.bin}:{os.environ['PATH']}",
"HOME": str(self.local_home),
"FAKE_REMOTE_HOME": str(self.remote),
"FAKE_SSH_LOG": str(self.ssh_log),
"FAKE_SCP_LOG": str(self.scp_log),
"TRANSCRIBE_HOST": "testhost",
"TRANSCRIBE_POLL": "0",
"TRANSCRIBE_TIMEOUT": "5",
}
if on_submit:
env["FAKE_ON_SUBMIT"] = on_submit
env.update({k: str(v) for k, v in env_extra.items()})
if args is None:
args = [str(self.audio)]
return subprocess.run([str(SCRIPT), *args], env=env, capture_output=True, text=True, timeout=60)
def state(self, *parts):
return self.remote.joinpath(STATE, *parts)
def uploads(self):
return self.scp_log.read_text().splitlines() if self.scp_log.exists() else []
@pytest.fixture
def rig(tmp_path):
return Rig(tmp_path)
class TestClientNormal:
def test_ratio_transcribe_new_recording_is_uploaded_and_transcript_printed(self, rig):
"""Normal: upload, wait, print. stdout is the transcript and nothing else."""
result = rig.run()
assert result.returncode == 0, result.stderr
assert result.stdout == "00:00:00 Speaker A: Hello from the host.\n"
assert len(rig.uploads()) == 1
def test_ratio_transcribe_job_file_carries_language_speakers_and_name(self, rig):
"""Normal: the options reach the host inside job.json."""
rig.run(args=[str(rig.audio), "es"], on_submit=None, SPEAKERS=3, TRANSCRIBE_TIMEOUT=0)
jobs = list(rig.state("incoming").glob("*/job.json"))
assert len(jobs) == 1
assert json.loads(jobs[0].read_text()) == {
"language": "es", "speakers": 3, "original_name": "meeting.m4a",
}
assert (jobs[0].parent / "audio.m4a").read_bytes() == b"pretend audio"
def test_ratio_transcribe_finished_job_is_collected_without_uploading_again(self, rig):
"""Normal: rerunning after a dropped connection costs one ssh round trip."""
first = rig.run()
rig.scp_log.unlink()
second = rig.run(on_submit=None)
assert second.returncode == 0
assert second.stdout == first.stdout
assert rig.uploads() == []
def test_ratio_transcribe_job_id_depends_on_audio_and_options(self, rig):
"""Normal: same file with a different speaker count is a different job."""
rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0)
rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0)
rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0, SPEAKERS=3)
assert len(list(rig.state("incoming").iterdir())) == 2
def test_ratio_transcribe_earlier_failure_is_retried(self, rig):
"""Normal: a stale failure log does not block a fresh attempt."""
assert rig.run(on_submit=WORKER_FAIL).returncode == 1
result = rig.run(on_submit=WORKER_OK)
assert result.returncode == 0
assert "Hello from the host" in result.stdout
assert list(rig.state("failed").glob("*.log")) == []
class TestClientBoundary:
def test_ratio_transcribe_job_still_queued_is_not_uploaded_twice(self, rig):
"""Boundary: a second run while the first job waits just joins the wait."""
rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0)
rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0)
assert len(rig.uploads()) == 1
def test_ratio_transcribe_upload_lands_in_incoming_only_by_rename(self, rig):
"""Boundary: the audio is copied into uploading/, never straight into incoming/."""
rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0)
assert "/uploading/" in rig.uploads()[0]
assert list(rig.state("uploading").iterdir()) == []
def test_ratio_transcribe_awkward_filename_survives(self, rig):
"""Boundary: spaces and quotes in the recording's name."""
odd = rig.tmp / "someone's \"weekly\" sync.m4a"
odd.write_bytes(b"x")
result = rig.run(args=[str(odd)], on_submit=None, TRANSCRIBE_TIMEOUT=0)
job = json.loads(next(rig.state("incoming").glob("*/job.json")).read_text())
assert job["original_name"] == "someone's \"weekly\" sync.m4a", result.stderr
def test_ratio_transcribe_colon_in_a_relative_filename_still_uploads(self, rig):
"""Boundary: scp reads "standup 14:30.m4a" as host:path; the client hands it an absolute path."""
(rig.tmp / "standup 14:30.m4a").write_bytes(b"x")
env = {
"PATH": f"{rig.bin}:{os.environ['PATH']}", "HOME": str(rig.local_home),
"FAKE_REMOTE_HOME": str(rig.remote), "FAKE_SSH_LOG": str(rig.ssh_log),
"FAKE_SCP_LOG": str(rig.scp_log), "FAKE_ON_SUBMIT": WORKER_OK,
"TRANSCRIBE_HOST": "testhost", "TRANSCRIBE_POLL": "0", "TRANSCRIBE_TIMEOUT": "5",
}
result = subprocess.run(
[str(SCRIPT), "standup 14:30.m4a"], cwd=rig.tmp, env=env, capture_output=True, text=True, timeout=60,
)
assert result.returncode == 0, result.stderr
assert rig.uploads()[0].startswith("/")
def test_ratio_transcribe_host_unreachable_falls_back_to_local_worker(self, rig):
"""Boundary: offline, the same queue and worker run on this machine."""
worker = rig.tmp / "fake-worker"
worker.write_text(
"#!/usr/bin/env bash\n"
f"cd \"$HOME/{STATE}\" && mkdir -p done && for d in incoming/*/; do id=$(basename \"$d\");\n"
"printf '00:00:00 Speaker A: Local fallback.\\n' > done/$id.txt; rm -rf \"$d\"; done\n"
)
worker.chmod(0o755)
result = rig.run(FAKE_SSH_DOWN=1, TRANSCRIBE_WORKER=str(worker))
assert result.returncode == 0, result.stderr
assert result.stdout == "00:00:00 Speaker A: Local fallback.\n"
assert "local" in result.stderr.lower()
class TestClientStdin:
def test_ratio_transcribe_leaves_the_callers_stdin_alone(self, rig):
"""Boundary: in a `while read` loop the client must not eat the loop's input."""
env = {
"PATH": f"{rig.bin}:{os.environ['PATH']}", "HOME": str(rig.local_home),
"FAKE_REMOTE_HOME": str(rig.remote), "FAKE_SSH_LOG": str(rig.ssh_log),
"FAKE_SCP_LOG": str(rig.scp_log), "FAKE_ON_SUBMIT": WORKER_OK,
"TRANSCRIBE_HOST": "testhost", "TRANSCRIBE_POLL": "0", "TRANSCRIBE_TIMEOUT": "5",
}
result = subprocess.run(
["bash", "-c", '"$0" "$1" > /dev/null 2>&1; cat', str(SCRIPT), str(rig.audio)],
env=env, input="next line of the caller's loop\n", capture_output=True, text=True, timeout=60,
)
assert result.stdout == "next line of the caller's loop\n"
class TestClientError:
def test_ratio_transcribe_no_arguments_prints_usage(self, rig):
"""Error: usage."""
result = rig.run(args=[])
assert result.returncode == 1 and "Usage: ratio-transcribe" in result.stderr and result.stdout == ""
def test_ratio_transcribe_missing_file_fails_before_any_ssh(self, rig):
"""Error: no such recording."""
result = rig.run(args=[str(rig.tmp / "nope.m4a")])
assert result.returncode == 1 and "not found" in result.stderr
assert not rig.ssh_log.exists()
@pytest.mark.parametrize("env", [{"SPEAKERS": "0"}, {"SPEAKERS": "three"}, {"MIN_SPEAKERS": "-1"},
{"MIN_SPEAKERS": "4", "MAX_SPEAKERS": "2"}, {"SPEAKERS": "3", "MAX_SPEAKERS": "5"}])
def test_ratio_transcribe_bad_speaker_settings_are_rejected(self, rig, env):
"""Error: non-positive, non-numeric or contradictory counts."""
result = rig.run(**env)
assert result.returncode == 1 and "speaker" in result.stderr.lower()
assert not rig.ssh_log.exists()
def test_ratio_transcribe_bad_language_is_rejected(self, rig):
"""Error: the language ends up in a job file and a command line."""
result = rig.run(args=[str(rig.audio), "en; rm -rf /"])
assert result.returncode == 1 and "language" in result.stderr.lower()
assert not rig.ssh_log.exists()
def test_ratio_transcribe_failed_job_reports_the_log_and_prints_nothing(self, rig):
"""Error: the host's failure log comes back on stderr."""
result = rig.run(on_submit=WORKER_FAIL)
assert result.returncode == 1
assert result.stdout == ""
assert "stage: whisper" in result.stderr
def test_ratio_transcribe_timeout_says_the_job_is_still_running(self, rig):
"""Error: giving up waiting is not the job failing."""
result = rig.run(on_submit=None, TRANSCRIBE_TIMEOUT=0)
assert result.returncode == 1
assert result.stdout == ""
assert "again" in result.stderr.lower()
def test_ratio_transcribe_empty_transcript_is_a_failure(self, rig):
"""Error: a zero-byte result is never passed off as a transcript."""
result = rig.run(on_submit=WORKER_EMPTY, TRANSCRIBE_TIMEOUT=1)
assert result.returncode == 1
assert result.stdout == ""
def test_ratio_transcribe_offline_without_local_worker_names_what_is_missing(self, rig):
"""Error: no host and nothing installed locally."""
result = rig.run(FAKE_SSH_DOWN=1, TRANSCRIBE_WORKER=str(rig.tmp / "absent"))
assert result.returncode == 1
assert "absent" in result.stderr
|