Skip to content

Commit 7bf8660

Browse files
authored
test: modernize Codex SQLite fixtures (#10)
* feat: resolve provider from Codex profiles * fix: address profile watch review findings * test: make service next-step test portable * fix: harden watch and CI coverage * test: make sqlite home fixtures portable * test: modernize Codex SQLite fixtures * test: cover rollout rewrites in smoke fixture * test: assert sqlite fixture indexes and triggers
1 parent b3d217a commit 7bf8660

2 files changed

Lines changed: 326 additions & 33 deletions

File tree

‎scripts/smoke_release.py‎

Lines changed: 190 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import os
55
import pathlib
66
import shutil
7+
import signal
78
import sqlite3
89
import subprocess
910
import sys
@@ -19,7 +20,10 @@
1920
rollout_path TEXT NOT NULL,
2021
created_at INTEGER NOT NULL,
2122
updated_at INTEGER NOT NULL,
23+
created_at_ms INTEGER,
24+
updated_at_ms INTEGER,
2225
source TEXT NOT NULL,
26+
thread_source TEXT,
2327
model_provider TEXT NOT NULL,
2428
cwd TEXT NOT NULL,
2529
title TEXT NOT NULL,
@@ -39,8 +43,54 @@
3943
memory_mode TEXT NOT NULL DEFAULT 'enabled',
4044
model TEXT,
4145
reasoning_effort TEXT,
42-
agent_path TEXT
46+
agent_path TEXT,
47+
preview TEXT NOT NULL DEFAULT ''
4348
);
49+
CREATE INDEX idx_threads_created_at ON threads(created_at DESC, id DESC);
50+
CREATE INDEX idx_threads_updated_at ON threads(updated_at DESC, id DESC);
51+
CREATE INDEX idx_threads_archived ON threads(archived);
52+
CREATE INDEX idx_threads_source ON threads(source);
53+
CREATE INDEX idx_threads_provider ON threads(model_provider);
54+
CREATE INDEX idx_threads_created_at_ms ON threads(created_at_ms DESC, id DESC);
55+
CREATE INDEX idx_threads_updated_at_ms ON threads(updated_at_ms DESC, id DESC);
56+
CREATE INDEX idx_threads_archived_cwd_created_at_ms
57+
ON threads(archived, cwd, created_at_ms DESC, id DESC);
58+
CREATE INDEX idx_threads_archived_cwd_updated_at_ms
59+
ON threads(archived, cwd, updated_at_ms DESC, id DESC);
60+
CREATE TRIGGER threads_created_at_ms_after_insert
61+
AFTER INSERT ON threads
62+
WHEN NEW.created_at_ms IS NULL
63+
BEGIN
64+
UPDATE threads
65+
SET created_at_ms = NEW.created_at * 1000
66+
WHERE id = NEW.id;
67+
END;
68+
CREATE TRIGGER threads_updated_at_ms_after_insert
69+
AFTER INSERT ON threads
70+
WHEN NEW.updated_at_ms IS NULL
71+
BEGIN
72+
UPDATE threads
73+
SET updated_at_ms = NEW.updated_at * 1000
74+
WHERE id = NEW.id;
75+
END;
76+
CREATE TRIGGER threads_created_at_ms_after_update
77+
AFTER UPDATE OF created_at ON threads
78+
WHEN NEW.created_at != OLD.created_at
79+
AND NEW.created_at_ms IS OLD.created_at_ms
80+
BEGIN
81+
UPDATE threads
82+
SET created_at_ms = NEW.created_at * 1000
83+
WHERE id = NEW.id;
84+
END;
85+
CREATE TRIGGER threads_updated_at_ms_after_update
86+
AFTER UPDATE OF updated_at ON threads
87+
WHEN NEW.updated_at != OLD.updated_at
88+
AND NEW.updated_at_ms IS OLD.updated_at_ms
89+
BEGIN
90+
UPDATE threads
91+
SET updated_at_ms = NEW.updated_at * 1000
92+
WHERE id = NEW.id;
93+
END;
4494
"""
4595

4696
COMMAND_TIMEOUT_SECONDS = 30
@@ -76,6 +126,9 @@ def main() -> int:
76126
print(f"[smoke] sync passed with backup: {backup_path}")
77127
assert_backup_contains_dirty_rows(backup_path)
78128
print("[smoke] backup verification passed")
129+
assert_rollout_provider(codex_home, "rollout-a.jsonl", "1", "openai")
130+
assert_rollout_provider(codex_home, "rollout-b.jsonl", "2", "openai")
131+
print("[smoke] rollout metadata verification passed")
79132
assert_status(binary_path, codex_home, expected_total=3, expected_mismatched=0)
80133
print("[smoke] post-sync status passed")
81134
run_service_install(binary_path, codex_home)
@@ -85,10 +138,11 @@ def main() -> int:
85138
binary_path, codex_home, expected_installed="yes", expected_running="yes"
86139
)
87140
print("[smoke] service reached installed=yes running=yes")
88-
insert_dirty_row(codex_home / "state_5.sqlite")
89-
write_new_session(codex_home)
141+
rollout_path = write_new_session(codex_home)
142+
insert_dirty_row(codex_home / "state_5.sqlite", rollout_path)
90143
print("[smoke] inserted dirty row and wrote new session")
91144
wait_for_reconcile(binary_path, codex_home)
145+
assert_rollout_provider(codex_home, "rollout-d.jsonl", "4", "openai")
92146
print("[smoke] background reconcile passed")
93147
finally:
94148
run_service_uninstall(binary_path, codex_home)
@@ -126,35 +180,98 @@ def prepare_codex_home(codex_home: pathlib.Path) -> None:
126180

127181
sessions_dir = codex_home / "sessions" / "2026" / "04" / "15"
128182
sessions_dir.mkdir(parents=True, exist_ok=True)
129-
(sessions_dir / "rollout-a.jsonl").write_text(
130-
'{"type":"session_meta","payload":{"model_provider":"vm"}}\n',
131-
encoding="utf-8",
132-
)
133-
(sessions_dir / "rollout-b.jsonl").write_text(
134-
'{"type":"session_meta","payload":{"model_provider":"cp"}}\n',
135-
encoding="utf-8",
136-
)
183+
rollout_a = sessions_dir / "rollout-a.jsonl"
184+
rollout_b = sessions_dir / "rollout-b.jsonl"
185+
rollout_c = sessions_dir / "rollout-c.jsonl"
186+
write_rollout(rollout_a, "1", "vm")
187+
write_rollout(rollout_b, "2", "cp")
188+
write_rollout(rollout_c, "3", "openai")
137189

138190
sqlite_path = codex_home / "state_5.sqlite"
139191
connection = sqlite3.connect(sqlite_path)
140192
connection.executescript(SCHEMA_SQL)
141193
connection.executemany(
142194
"""
143195
INSERT INTO threads (
144-
id, rollout_path, created_at, updated_at, source, model_provider, cwd, title,
145-
sandbox_policy, approval_mode
146-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
196+
id, rollout_path, created_at, updated_at, created_at_ms, updated_at_ms,
197+
source, thread_source, model_provider, cwd, title, sandbox_policy, approval_mode,
198+
cli_version, model, reasoning_effort, first_user_message, preview
199+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
147200
""",
148201
[
149-
("1", "/tmp/a", 1, 1, "cli", "vm", "/tmp", "a", "workspace-write", "auto"),
150-
("2", "/tmp/b", 1, 1, "cli", "cp", "/tmp", "b", "workspace-write", "auto"),
151-
("3", "/tmp/c", 1, 1, "cli", "openai", "/tmp", "c", "workspace-write", "auto"),
202+
(
203+
"1",
204+
str(rollout_a),
205+
1,
206+
1,
207+
1000,
208+
1000,
209+
"cli",
210+
"user",
211+
"vm",
212+
"/tmp",
213+
"a",
214+
"workspace-write",
215+
"auto",
216+
"0.0.0-test",
217+
"gpt-5-codex",
218+
"medium",
219+
"a",
220+
"a",
221+
),
222+
(
223+
"2",
224+
str(rollout_b),
225+
1,
226+
1,
227+
1001,
228+
1001,
229+
"cli",
230+
"user",
231+
"cp",
232+
"/tmp",
233+
"b",
234+
"workspace-write",
235+
"auto",
236+
"0.0.0-test",
237+
"gpt-5-codex",
238+
"medium",
239+
"b",
240+
"b",
241+
),
242+
(
243+
"3",
244+
str(rollout_c),
245+
1,
246+
1,
247+
1002,
248+
1002,
249+
"cli",
250+
"user",
251+
"openai",
252+
"/tmp",
253+
"c",
254+
"workspace-write",
255+
"auto",
256+
"0.0.0-test",
257+
"gpt-5-codex",
258+
"medium",
259+
"c",
260+
"c",
261+
),
152262
],
153263
)
154264
connection.commit()
155265
connection.close()
156266

157267

268+
def write_rollout(path: pathlib.Path, thread_id: str, provider: str) -> None:
269+
path.write_text(
270+
f'{{"type":"session_meta","payload":{{"id":"{thread_id}","model_provider":"{provider}"}}}}\n',
271+
encoding="utf-8",
272+
)
273+
274+
158275
def command_env() -> dict[str, str]:
159276
env = os.environ.copy()
160277
env["CODEX_THREADRIPPER_LANG"] = "en"
@@ -213,6 +330,8 @@ def run_sync(binary_path: pathlib.Path, codex_home: pathlib.Path) -> pathlib.Pat
213330
output = run_command(binary_path, codex_home, "sync")
214331
if "Rows updated: 2" not in output:
215332
raise RuntimeError(f"unexpected sync output\n\n{output}")
333+
if "Rollouts updated: 2" not in output:
334+
raise RuntimeError(f"sync did not rewrite rollout metadata\n\n{output}")
216335

217336
backup_line = next(
218337
(line for line in output.splitlines() if line.startswith("Backup: ")),
@@ -223,9 +342,34 @@ def run_sync(binary_path: pathlib.Path, codex_home: pathlib.Path) -> pathlib.Pat
223342
backup_path = pathlib.Path(backup_line.removeprefix("Backup: ").strip())
224343
if not backup_path.exists():
225344
raise RuntimeError(f"backup file is missing: {backup_path}")
345+
journal_line = next(
346+
(line for line in output.splitlines() if line.startswith("Rollout first-line journal: ")),
347+
None,
348+
)
349+
if journal_line is None:
350+
raise RuntimeError(f"missing rollout journal line in sync output\n\n{output}")
351+
journal_path = pathlib.Path(journal_line.removeprefix("Rollout first-line journal: ").strip())
352+
if not journal_path.exists():
353+
raise RuntimeError(f"rollout journal file is missing: {journal_path}")
354+
journal = journal_path.read_text(encoding="utf-8")
355+
if '"thread_id":"1"' not in journal or '"thread_id":"2"' not in journal:
356+
raise RuntimeError(f"rollout journal did not include rewritten threads\n\n{journal}")
226357
return backup_path
227358

228359

360+
def assert_rollout_provider(
361+
codex_home: pathlib.Path, rollout_name: str, thread_id: str, provider: str
362+
) -> None:
363+
rollout_path = codex_home / "sessions" / "2026" / "04" / "15" / rollout_name
364+
first_line = rollout_path.read_text(encoding="utf-8").splitlines()[0]
365+
expected_id = f'"id":"{thread_id}"'
366+
expected_provider = f'"model_provider":"{provider}"'
367+
if expected_id not in first_line or expected_provider not in first_line:
368+
raise RuntimeError(
369+
f"unexpected rollout first line for {rollout_path}\n\n{first_line}"
370+
)
371+
372+
229373
def assert_backup_contains_dirty_rows(backup_path: pathlib.Path) -> None:
230374
connection = sqlite3.connect(backup_path)
231375
mismatched = connection.execute(
@@ -269,28 +413,47 @@ def wait_for_service_status(
269413
)
270414

271415

272-
def insert_dirty_row(sqlite_path: pathlib.Path) -> None:
416+
def insert_dirty_row(sqlite_path: pathlib.Path, rollout_path: pathlib.Path) -> None:
273417
connection = sqlite3.connect(sqlite_path)
274418
connection.execute(
275419
"""
276420
INSERT INTO threads (
277-
id, rollout_path, created_at, updated_at, source, model_provider, cwd, title,
278-
sandbox_policy, approval_mode
279-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
421+
id, rollout_path, created_at, updated_at, created_at_ms, updated_at_ms,
422+
source, thread_source, model_provider, cwd, title, sandbox_policy, approval_mode,
423+
cli_version, model, reasoning_effort, first_user_message, preview
424+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
280425
""",
281-
("4", "/tmp/d", 1, 1, "cli", "vm", "/tmp", "d", "workspace-write", "auto"),
426+
(
427+
"4",
428+
str(rollout_path),
429+
1,
430+
1,
431+
1003,
432+
1003,
433+
"cli",
434+
"user",
435+
"vm",
436+
"/tmp",
437+
"d",
438+
"workspace-write",
439+
"auto",
440+
"0.0.0-test",
441+
"gpt-5-codex",
442+
"medium",
443+
"d",
444+
"d",
445+
),
282446
)
283447
connection.commit()
284448
connection.close()
285449

286450

287-
def write_new_session(codex_home: pathlib.Path) -> None:
451+
def write_new_session(codex_home: pathlib.Path) -> pathlib.Path:
288452
sessions_dir = codex_home / "sessions" / "2026" / "04" / "15"
289453
sessions_dir.mkdir(parents=True, exist_ok=True)
290-
(sessions_dir / "rollout-c.jsonl").write_text(
291-
'{"type":"session_meta","payload":{"model_provider":"vm"}}\n',
292-
encoding="utf-8",
293-
)
454+
rollout_path = sessions_dir / "rollout-d.jsonl"
455+
write_rollout(rollout_path, "4", "vm")
456+
return rollout_path
294457

295458

296459
def wait_for_reconcile(binary_path: pathlib.Path, codex_home: pathlib.Path) -> None:

0 commit comments

Comments
 (0)