Viewing files at commit 62758990fe31Commit detailsView latest

Browse files
tests/test_scale_suite.py 827 lines · 45.0 KB · python
1
"""Instrument tests for suites/scale (generator, sizer, tiers, op oracles, rows).
2
3
Hermetic: no Oak binary and no network. Git-backed tests use the resolved
4
real git and skip when it is absent. Oak-side oracles are exercised with
5
canned subject output (a stub timed runner), so every op's untimed oracle is
6
tested for both a passing and a failing observation.
7
"""
8
9
from __future__ import annotations
10
11
import hashlib
12
import io
13
import json
14
import re
15
import shutil
16
import subprocess
17
import sys
18
import tempfile
19
import unittest
20
from pathlib import Path
21
from types import SimpleNamespace
22
23
ROOT = Path(__file__).resolve().parents[1]
24
sys.path.insert(0, str(ROOT / "suites" / "scale"))
25
sys.path.insert(0, str(ROOT / "scripts"))
26
27
from oakbench.execution import TimedCapture, run_timed  # noqa: E402
28
from oakbench.rows import validate_rows  # noqa: E402
29
from scalesuite import generator as gen  # noqa: E402
30
from scalesuite import ops as ops_mod  # noqa: E402
31
from scalesuite import runner as runner_mod  # noqa: E402
32
from scalesuite import sizer as sz  # noqa: E402
33
from scalesuite import tiers as tiers_mod  # noqa: E402
34
from scalesuite import workspace as ws  # noqa: E402
35
36
try:
37
    from oakbench.subjects import which_measured_git
38
    GIT = which_measured_git()
39
except Exception:  # pragma: no cover
40
    GIT = shutil.which("git")
41
42
SMALL = gen.GenSpec(name="t", seed="unit-seed", files=60, file_bytes=96, depth=4, fanout=4, commits=30,
43
                    touches_per_commit=3, branches=4, branch_commit_files=2, tags=3, binaries=1,
44
                    binary_bytes=(2 << 20) + 17, binary_revisions=2, ignore_patterns=("build/",),
45
                    conflict_files=3)
46
47
48
def stream_sha(spec: gen.GenSpec) -> str:
49
    buffer = io.BytesIO()
50
    gen.write_stream(spec, buffer)
51
    return hashlib.sha256(buffer.getvalue()).hexdigest()
52
53
54
def git(*args: str, cwd: Path) -> str:
55
    return subprocess.run([GIT, *args], cwd=cwd, check=True, capture_output=True, text=True,
56
                          env=gen.git_env()).stdout
57
58
59
class GeneratorDeterminismTest(unittest.TestCase):
60
    def test_same_spec_same_stream(self) -> None:
61
        self.assertEqual(stream_sha(SMALL), stream_sha(SMALL))
62
63
    def test_seed_changes_stream_and_spec_hash(self) -> None:
64
        other = gen.GenSpec(**{**SMALL.to_json(), "seed": "other-seed", "ignore_patterns": ("build/",)})
65
        self.assertNotEqual(stream_sha(SMALL), stream_sha(other))
66
        self.assertNotEqual(SMALL.spec_sha256(), other.spec_sha256())
67
68
    def test_spec_hash_is_stable_text(self) -> None:
69
        self.assertEqual(SMALL.spec_sha256(), gen.GenSpec(**{**SMALL.to_json(), "ignore_patterns": ("build/",)}).spec_sha256())
70
        self.assertRegex(SMALL.spec_sha256(), r"^[0-9a-f]{64}$")
71
72
    def test_validation(self) -> None:
73
        with self.assertRaises(ValueError):
74
            gen.GenSpec(name="x", seed="s", files=0).validate()
75
        with self.assertRaises(ValueError):
76
            gen.GenSpec(name="x", seed="s", binary_revisions=2, commits=3).validate()
77
        with self.assertRaises(ValueError):
78
            gen.GenSpec(name="x", seed="s", file_bytes=8).validate()
79
80
    def test_every_path_has_exact_depth_and_is_unique(self) -> None:
81
        for spec in (SMALL, gen.GenSpec(name="d", seed="s", files=5000, depth=40, fanout=8),
82
                     gen.GenSpec(name="w", seed="s", files=5000, depth=2, fanout=10),
83
                     gen.GenSpec(name="r", seed="s", files=7, depth=1, fanout=4)):
84
            cache = gen.layout(spec)
85
            paths = [gen.file_path(spec, i, cache) for i in range(spec.files)]
86
            self.assertEqual(len(set(paths)), spec.files)
87
            self.assertTrue(all(p.count("/") + 1 == spec.depth for p in paths), spec)
88
89
    def test_content_unique_sized_and_deterministic(self) -> None:
90
        factory = gen.ContentFactory(SMALL)
91
        blobs = {factory.text(i, "r1") for i in range(SMALL.files)}
92
        self.assertEqual(len(blobs), SMALL.files)
93
        self.assertTrue(all(len(b) == SMALL.file_bytes for b in blobs))
94
        self.assertEqual(factory.text(3, "r9"), gen.ContentFactory(SMALL).text(3, "r9"))
95
        self.assertNotEqual(factory.text(3, "r9"), factory.text(3, "r8"))
96
97
    def test_binary_is_sparse_friendly_and_exact_size(self) -> None:
98
        factory = gen.ContentFactory(SMALL)
99
        data = b"".join(factory.binary_segments(0, 1))
100
        self.assertEqual(len(data), SMALL.binary_bytes)
101
        self.assertEqual(data[gen.SEGMENT_RANDOM:gen.SEGMENT], bytes(gen.SEGMENT - gen.SEGMENT_RANDOM))
102
        self.assertNotEqual(data[:gen.SEGMENT_RANDOM], bytes(gen.SEGMENT_RANDOM))
103
        sha1, _ = gen.binary_sha1_and_sha256(factory, 0, 1)
104
        self.assertEqual(sha1, gen.git_blob_id(data))
105
106
    def test_branch_changes_match_stream_facts(self) -> None:
107
        buffer = io.BytesIO()
108
        facts = gen.write_stream(SMALL, buffer)
109
        self.assertEqual(facts["branch_sample"]["changed_paths"],
110
                         sorted(path for _, path in gen.branch_changes(SMALL, 0)))
111
        self.assertEqual(facts["total_commits"], SMALL.commits + SMALL.branches + 2)
112
113
114
@unittest.skipUnless(GIT, "git not available")
115
class GeneratedFixtureTest(unittest.TestCase):
116
    @classmethod
117
    def setUpClass(cls) -> None:
118
        cls.tmp = Path(tempfile.mkdtemp(prefix="scale-gen-"))
119
        cls.manifest = gen.generate(SMALL, cls.tmp / "a", GIT)
120
        cls.repo = cls.tmp / "a" / "src.git"
121
122
    @classmethod
123
    def tearDownClass(cls) -> None:
124
        shutil.rmtree(cls.tmp, ignore_errors=True)
125
126
    def test_same_seed_same_fixture_hash(self) -> None:
127
        again = gen.generate(SMALL, self.tmp / "b", GIT)
128
        self.assertEqual(again["refs_sha256"], self.manifest["refs_sha256"])
129
        self.assertEqual(again["head_commit"], self.manifest["head_commit"])
130
131
    def test_reuse_is_detected(self) -> None:
132
        self.assertTrue(gen.generate(SMALL, self.tmp / "a", GIT)["reused"])
133
134
    def test_manifest_facts_agree_with_git(self) -> None:
135
        probe = self.manifest["probe_path"]
136
        log = git("log", "--oneline", "main", "--", probe, cwd=self.repo).splitlines()
137
        self.assertEqual(len(log), self.manifest["probe_path_commit_count"])
138
        self.assertEqual(int(git("rev-list", "--count", "main", cwd=self.repo)), SMALL.commits)
139
        changed = git("diff", "--name-only", "main...b00000", cwd=self.repo).split()
140
        self.assertEqual(sorted(changed), self.manifest["branch_sample"]["changed_paths"])
141
        self.assertEqual(len(git("tag", "--list", cwd=self.repo).split()), SMALL.tags)
142
        conflicts = subprocess.run([GIT, "merge-tree", "--write-tree", "--name-only", "bench-ours", "bench-theirs"],
143
                                   cwd=self.repo, capture_output=True, text=True)
144
        self.assertEqual(conflicts.returncode, 1)
145
        self.assertEqual(len(conflicts.stdout.split("\n\n")[0].splitlines()[1:]), SMALL.conflict_files)
146
147
    def test_binary_blob_id_matches_generator(self) -> None:
148
        blob = git("rev-parse", f"b00000:{gen.binary_path(0)}", cwd=self.repo).strip()
149
        # branch b00000 forks from the tip, where the binary is at revision binary_revisions
150
        sha1, _ = gen.binary_sha1_and_sha256(gen.ContentFactory(SMALL), 0, SMALL.binary_revisions)
151
        self.assertEqual(blob, sha1)
152
153
    def test_sizer_values(self) -> None:
154
        stats = sz.measure(self.repo, GIT)
155
        values = stats["values"]
156
        self.assertEqual(stats["checkout_scope"], "all_commits")
157
        self.assertEqual(values["uniqueCommitCount"], self.manifest["total_commits"])
158
        self.assertEqual(values["maxCheckoutPathDepth"], SMALL.depth)
159
        self.assertEqual(values["maxCheckoutBlobCount"], SMALL.files + SMALL.binaries + 1)  # + .gitignore
160
        self.assertEqual(values["referenceCount"], 1 + SMALL.branches + 2 + SMALL.tags)
161
        self.assertEqual(values["maxBlobSize"], SMALL.binary_bytes)
162
        self.assertEqual(values["maxHistoryDepth"], SMALL.commits + 1)
163
        self.assertEqual(values["maxCommitParentCount"], 1)
164
        self.assertEqual(values["uniqueTagCount"], 0)
165
        longest = max(len(p) for p in git("ls-tree", "-r", "--name-only", "main", cwd=self.repo).split())
166
        self.assertEqual(values["maxCheckoutPathLength"], longest)
167
        self.assertEqual(stats["level_of_concern"]["maxCheckoutPathDepth"],
168
                         round(SMALL.depth / 10, 4))
169
        compact = sz.compact(stats)
170
        self.assertEqual(compact["maxCheckoutPathDepth"], SMALL.depth)
171
172
    def test_sizer_ref_tips_scope_and_tag_depth(self) -> None:
173
        clone = self.tmp / "tags.git"
174
        subprocess.run([GIT, "clone", "-q", "--bare", str(self.repo), str(clone)], check=True, env=gen.git_env())
175
        env = {**gen.git_env(), "GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@t"}
176
        subprocess.run([GIT, "-C", str(clone), "tag", "-a", "-m", "one", "a1", "main"], check=True, env=env)
177
        subprocess.run([GIT, "-C", str(clone), "tag", "-a", "-m", "two", "a2", "a1"], check=True, env=env)
178
        stats = sz.measure(clone, GIT, max_checkout_roots=2)
179
        self.assertEqual(stats["checkout_scope"], "ref_tips")
180
        self.assertEqual(stats["values"]["maxTagDepth"], 2)
181
        self.assertEqual(stats["values"]["uniqueTagCount"], 2)
182
        self.assertIsNone(stats["values"]["uniqueTreeEntries"])
183
184
185
@unittest.skipUnless(GIT, "git not available")
186
class RegistryPinTest(unittest.TestCase):
187
    """Cross-machine determinism: regenerating a tier reproduces the pinned identity."""
188
189
    def test_pinned_tiers_regenerate_identically(self) -> None:
190
        registry = json.loads((ROOT / "config" / "fixtures.json").read_text())["fixtures"]
191
        tmp = Path(tempfile.mkdtemp(prefix="scale-pin-"))
192
        try:
193
            checked = 0
194
            for name in ("fresh", "default"):
195
                entry = registry[f"scale-{name}-v1"]
196
                tier = tiers_mod.TIERS[name]
197
                self.assertEqual(entry["spec_sha256"], tier.spec.spec_sha256())
198
                self.assertEqual(entry["seed"], tier.spec.seed)
199
                manifest = gen.generate(tier.spec, tmp / name, GIT)
200
                self.assertEqual(manifest["refs_sha256"], entry["refs_sha256"], name)
201
                self.assertEqual(manifest["head_commit"], entry["head_commit"], name)
202
                checked += 1
203
            self.assertEqual(checked, 2)
204
        finally:
205
            shutil.rmtree(tmp, ignore_errors=True)
206
207
    def test_every_tier_is_registered_with_its_spec_hash(self) -> None:
208
        registry = json.loads((ROOT / "config" / "fixtures.json").read_text())["fixtures"]
209
        for name, tier in tiers_mod.TIERS.items():
210
            self.assertEqual(registry[f"scale-{name}-v1"]["spec_sha256"], tier.spec.spec_sha256(), name)
211
212
213
class SizerPureTest(unittest.TestCase):
214
    def test_parse_tree(self) -> None:
215
        oid = bytes(range(20))
216
        raw = b"100644 a.txt\0" + oid + b"40000 dir\0" + oid[::-1]
217
        self.assertEqual(sz.parse_tree(raw), [(b"100644", b"a.txt", oid), (b"40000", b"dir", oid[::-1])])
218
219
    def test_level_of_concern_uses_git_sizer_reference_values(self) -> None:
220
        self.assertEqual(sz.level_of_concern("maxCheckoutBlobCount", 1_000_000), 20.0)
221
        self.assertEqual(sz.level_of_concern("referenceCount", 25_000), 1.0)
222
        self.assertIsNone(sz.level_of_concern("maxBlobSize", None))
223
224
225
class TierTest(unittest.TestCase):
226
    def test_all_14_core_shapes_are_runnable_specs(self) -> None:
227
        shapes = tiers_mod.core_shape_tiers(0.05)
228
        self.assertEqual(len(shapes), 14)
229
        for shape_id, tier in shapes.items():
230
            tier.spec.validate()
231
            self.assertGreaterEqual(tier.spec.branches, 1, shape_id)
232
            self.assertEqual(tier.kind, "shape")
233
        self.assertEqual(shapes["deep-tree"].spec.depth, 64)
234
        self.assertEqual(shapes["conflict-heavy"].spec.conflict_files, 250)
235
        self.assertEqual(shapes["binary-history"].spec.binary_revisions, shapes["binary-history"].spec.commits)
236
        self.assertIn("build/", shapes["ignored-heavy"].spec.ignore_patterns)
237
238
    def test_scale_is_part_of_identity(self) -> None:
239
        a = tiers_mod.core_shape_tiers(0.05)["normal"]
240
        b = tiers_mod.core_shape_tiers(0.1)["normal"]
241
        self.assertNotEqual(a.fixture_id, b.fixture_id)
242
243
    def test_tier_ids_unique_and_xl_opt_in(self) -> None:
244
        ids = [tier.fixture_id for tier in tiers_mod.TIERS.values()]
245
        self.assertEqual(len(ids), len(set(ids)))
246
        with self.assertRaises(SystemExit):
247
            tiers_mod.resolve("xl-tree")
248
        self.assertEqual(tiers_mod.resolve("xl-tree", allow_xl=True).spec.files, 1_000_000)
249
        with self.assertRaises(SystemExit):
250
            tiers_mod.resolve("xl-combined", allow_xl=True)
251
        combined = tiers_mod.resolve("xl-combined", allow_xl=True, allow_xl_combined=True).spec
252
        self.assertGreaterEqual(combined.commits, 100_000)
253
        self.assertGreaterEqual(combined.files, 1_000_000)
254
        self.assertGreaterEqual(combined.depth, 32)
255
        self.assertGreaterEqual(combined.branches, 10_000)
256
        self.assertGreaterEqual(combined.binary_bytes, 2 << 30)
257
        for tier in tiers_mod.TIERS.values():
258
            tier.spec.validate()
259
260
261
class CatalogTest(unittest.TestCase):
262
    def test_scale_yaml_matches_catalog(self) -> None:
263
        self.assertEqual((ROOT / "scenarios" / "scale.yaml").read_text(), ops_mod.catalog_yaml())
264
265
    def test_tperf_refs_are_file_names_only(self) -> None:
266
        pattern = re.compile(r"^p\d{4}-[a-z0-9-]+\.sh$")
267
        for info in ops_mod.OP_INFO.values():
268
            for ref in info.tperf:
269
                self.assertRegex(ref, pattern)
270
271
    def test_units_cover_ops(self) -> None:
272
        self.assertEqual(sorted(ops_mod.ALL_OPS), sorted(ops_mod.OPS))
273
274
    def test_porcelain_paths(self) -> None:
275
        text = ' M a/b.txt\n D c.txt\nR  old.txt -> new.txt\n?? added/x/\nM "sp ace.txt"\n'
276
        self.assertEqual(ops_mod.porcelain_paths(text),
277
                         ["a/b.txt", "c.txt", "old.txt", "new.txt", "added/x/", "sp ace.txt"])
278
279
280
# ---------------------------------------------------------------------------
281
# Oracle tests with canned subject output
282
# ---------------------------------------------------------------------------
283
284
285
def cap(stdout: str = "", returncode: int = 0, raw: bytes | None = None) -> TimedCapture:
286
    data = stdout.encode()
287
    return TimedCapture(elapsed_ms=1.0, returncode=returncode, stdout_text=stdout[:4000], stdout_bytes=len(data),
288
                        stdout_truncated=False, stderr_text="", stderr_bytes=0, stderr_truncated=False,
289
                        stdout_full_text=stdout, stdout_raw=raw if raw is not None else data)
290
291
292
class OracleTest(unittest.TestCase):
293
    def setUp(self) -> None:
294
        self.tmp = Path(tempfile.mkdtemp(prefix="scale-oracle-"))
295
        self.spec = gen.GenSpec(name="o", seed="oracle", files=40, file_bytes=64, depth=3, fanout=4, commits=12,
296
                                branches=2, branch_commit_files=2)
297
        facts = gen.write_stream(self.spec, io.BytesIO())
298
        factory = gen.ContentFactory(self.spec)
299
        for kind in ("git", "oak"):
300
            work = self.tmp / kind / "work"
301
            for i in range(self.spec.files):
302
                path = work / gen.file_path(self.spec, i)
303
                path.parent.mkdir(parents=True, exist_ok=True)
304
                path.write_bytes(factory.text(i, "r1"))
305
        branch_paths = facts["branch_sample"]["changed_paths"]
306
        self.expected = ws.Expected(
307
            main_file_count=self.spec.files, sample={}, probe_path=facts["probe_path"], probe_bytes=b"probe\nx\n",
308
            probe_lines=2, probe_commit_count=facts["probe_path_commit_count"], main_commits=self.spec.commits,
309
            branch="b00000", branch_paths=branch_paths, branch_bytes={}, main_bytes_of_branch_paths={},
310
            conflict={}, protected=[facts["probe_path"], *branch_paths], all_paths_sample_size=0)
311
        tier = tiers_mod.Tier(tier_id="t", kind="tier", spec=self.spec, dirty=tiers_mod.DirtyShape(3, 2, 1, 1))
312
        root = self.tmp
313
        self.prepared = SimpleNamespace(tier=tier, expected=self.expected, state={}, env={}, git_bin=GIT or "git",
314
                                        oak_base_branch="base", oak_branches_created=2,
315
                                        path=lambda *parts: root.joinpath(*parts))
316
        self.untimed_calls: list = []
317
318
    def tearDown(self) -> None:
319
        shutil.rmtree(self.tmp, ignore_errors=True)
320
321
    def ctx(self, kind: str, *outputs, run: int = 1) -> ops_mod.Ctx:
322
        queue = list(outputs)
323
        subject = SimpleNamespace(name=kind, kind=kind, bin=kind)
324
325
        def fake_runner(command, cwd, env, admitted, **kwargs):
326
            item = queue.pop(0)
327
            return item() if callable(item) else item
328
329
        ctx = ops_mod.Ctx(self.prepared, subject, run, fake_runner, self.tmp / "scratch", 60.0)
330
        ctx.untimed = lambda args, cwd, check=True, binary=None: (  # type: ignore[assignment]
331
            self.untimed_calls.append(list(args)) or SimpleNamespace(returncode=0, stdout=b"", stderr=b""))
332
        ctx.git = lambda args, cwd, check=True: (  # type: ignore[assignment]
333
            self.untimed_calls.append(["git", *args]) or SimpleNamespace(returncode=0, stdout=b"", stderr=b""))
334
        return ctx
335
336
    def run_op(self, op: str, kind: str, *outputs: TimedCapture) -> ops_mod.Outcome:
337
        return ops_mod.execute(self.ctx(kind, *outputs), op)
338
339
    def test_status_clean(self) -> None:
340
        self.assertTrue(self.run_op("status.clean", "git", cap("")).oracle_ok)
341
        self.assertFalse(self.run_op("status.clean", "oak", cap("M a.txt\n")).oracle_ok)
342
343
    def test_log_counts(self) -> None:
344
        lines = "".join(f"h{i} c{i}\n" for i in range(self.spec.commits))
345
        self.assertTrue(self.run_op("log.full", "git", cap(lines)).oracle_ok)
346
        self.assertFalse(self.run_op("log.full", "oak", cap(lines + "extra\n")).oracle_ok)
347
        self.assertTrue(self.run_op("log.n", "oak", cap(lines)).oracle_ok)
348
        self.assertFalse(self.run_op("log.n", "oak", cap("h1 c1\n")).oracle_ok)
349
        probe = "".join(f"p{i}\n" for i in range(self.expected.probe_commit_count))
350
        self.assertTrue(self.run_op("log.path", "git", cap(probe)).oracle_ok)
351
        self.assertFalse(self.run_op("log.path", "oak", cap("No matching commits\n")).oracle_ok)
352
353
    def test_blame(self) -> None:
354
        self.assertTrue(self.run_op("blame", "git", cap("a 1) probe\nb 2) x\n")).oracle_ok)
355
        self.assertFalse(self.run_op("blame", "git", cap("a 1) probe\n")).oracle_ok)
356
        self.assertEqual(self.run_op("blame", "oak").skip_reason, "oak_cli_has_no_blame_or_annotate_command")
357
358
    def test_file_show(self) -> None:
359
        self.assertTrue(self.run_op("file.show", "git", cap(raw=b"probe\nx\n")).oracle_ok)
360
        self.assertFalse(self.run_op("file.show", "git", cap(raw=b"other\n")).oracle_ok)
361
        # oak writes --output; nothing written -> mismatch
362
        self.assertFalse(self.run_op("file.show", "oak", cap("{}")).oracle_ok)
363
364
    def test_branch_list(self) -> None:
365
        self.assertTrue(self.run_op("branch.list", "git", cap("* main\n  b00000\n  b00001\n")).oracle_ok)
366
        self.assertFalse(self.run_op("branch.list", "git", cap("* main\n")).oracle_ok)
367
        listing = json.dumps([{"name": "main"}, {"name": "b00000"}, {"name": "b00001"}])
368
        self.assertTrue(self.run_op("branch.list", "oak", cap(listing)).oracle_ok)
369
        self.assertFalse(self.run_op("branch.list", "oak", cap("not json")).oracle_ok)
370
371
    def test_review_branch_diff(self) -> None:
372
        paths = self.expected.branch_paths
373
        self.assertTrue(self.run_op("review.branch_diff", "git",
374
                                    cap("".join(f"M\t{p}\n" for p in paths))).oracle_ok)
375
        oak_ok = json.dumps({"changed_files": [{"path": p} for p in paths]})
376
        self.assertTrue(self.run_op("review.branch_diff", "oak", cap(oak_ok)).oracle_ok)
377
        self.assertFalse(self.run_op("review.branch_diff", "oak", cap(json.dumps({"changed_files": []}))).oracle_ok)
378
379
    def test_merge_preview_oak(self) -> None:
380
        good = json.dumps({"clean": True, "changed_files": [{"path": p} for p in self.expected.branch_paths],
381
                           "merge_safety": {"certified": False}})
382
        outcome = self.run_op("merge.preview", "oak", cap(good, returncode=7))
383
        self.assertTrue(outcome.oracle_ok)
384
        self.assertFalse(outcome.extra["merge_certified"])
385
        self.assertIn(["switch", "b00000"], self.untimed_calls)
386
        self.assertEqual(self.untimed_calls[-1], ["switch", "base"])  # always switched back
387
        bad = json.dumps({"clean": False, "changed_files": []})
388
        self.assertFalse(self.run_op("merge.preview", "oak", cap(bad)).oracle_ok)
389
        self.assertIsNotNone(self.run_op("merge.preview", "oak", cap(good, returncode=2)).failed)
390
391
    def test_local_merge_ops_skip_for_oak(self) -> None:
392
        self.assertIn("oak_serve_lacks_merge_to_main", self.run_op("merge.local", "oak").skip_reason)
393
        self.assertEqual(self.run_op("merge.conflict", "git").skip_reason, "fixture_has_no_conflict_shape")
394
395
    def _listing(self, kind: str = "git") -> str:
396
        state = self.prepared.state[kind]["dirty"]
397
        text = "".join(f" M {p}\n" for p in state["modified"]) + "".join(f" D {p}\n" for p in state["deleted"])
398
        return text + "".join(f" D {old}\n" for old, _ in state["renamed"]) + "?? added/\n?? renamed/\n"
399
400
    def test_dirty_cycle_status_and_diff_oracles(self) -> None:
401
        good = ops_mod.execute(self.ctx("git", lambda: cap(self._listing())), "status.dirty")
402
        self.assertTrue(good.oracle_ok, good.oracle_detail)
403
        dirty = self.prepared.state["git"]["dirty"]
404
        self.assertEqual(dirty["applied"], [3, 2, 1, 1])
405
        self.assertFalse(set(dirty["modified"]) & set(self.expected.protected))
406
        diff = "".join(f"diff --git a/{p} b/{p}\n" for p in dirty["modified"] + dirty["deleted"])
407
        self.assertTrue(ops_mod.execute(self.ctx("git", cap(diff)), "diff.dirty").oracle_ok)
408
        self.assertFalse(ops_mod.execute(self.ctx("git", cap("diff --git a/x b/x\n")), "diff.dirty").oracle_ok)
409
        partial = ops_mod.execute(self.ctx("oak", lambda: cap(self._listing().split("\n", 1)[1]), run=2),
410
                                  "status.dirty")
411
        self.assertFalse(partial.oracle_ok)
412
413
    def test_dirty_cycle_runs_on_throwaway_branch(self) -> None:
414
        ops_mod.execute(self.ctx("oak", lambda: cap(self._listing("oak"))), "status.dirty")
415
        self.assertIn(["switch", "-c", "dirty-oak-r1"], self.untimed_calls)
416
        self.untimed_calls.clear()
417
        out = ops_mod.execute(self.ctx("oak", cap("{}")), "commit.dirty")
418
        self.assertIsNone(out.failed, out.failed)
419
        self.assertEqual(self.untimed_calls[-2:], [["switch", "base"], ["close", "dirty-oak-r1"]])
420
        self.assertEqual(self.prepared.state["oak"]["harness_commits"], 0)
421
422
    def test_oak_harness_branches_are_closed_on_teardown(self) -> None:
423
        self.prepared.state["oak"] = {"harness_commits": 0, "deleted": set(), "server_extra": 0, "branch_seq": 0}
424
        ops_mod.execute(self.ctx("oak", cap("")), "branch.create")
425
        self.assertIn(["close", "new-oak-r1-1"], self.untimed_calls)
426
        listing = json.dumps([{"name": "main"}, {"name": "b00000"}, {"name": "b00001"}])
427
        self.untimed_calls.clear()
428
        seen = []
429
        ctx = self.ctx("oak", cap(listing))
430
        original = ctx._run_timed
431
        ctx._run_timed = lambda command, *a, **k: (seen.append(command), original(command, *a, **k))[1]
432
        ops_mod.execute(ctx, "branch.list")
433
        self.assertIn("--status", seen[0])
434
435
    def test_recover_returns_to_base(self) -> None:
436
        ctx = self.ctx("oak")
437
        self.assertIsNone(ops_mod.recover(ctx))
438
        self.assertEqual(self.untimed_calls[-1], ["switch", "base"])
439
440
    def test_oak_state_oracles_use_independent_reader(self) -> None:
441
        from unittest import mock
442
        from oakbench import oak_sqlite_reader as reader
443
        probe = self.expected.probe_path
444
        self.prepared.state["oak"] = {"harness_commits": 0, "deleted": set(), "server_extra": 0, "branch_seq": 0}
445
        with mock.patch.object(reader, "committed_payloads",
446
                               return_value=({probe: self.expected.probe_bytes}, {"head": "h", "reader_version": "v"})):
447
            outcome = ops_mod.execute(self.ctx("oak", cap("")), "gc")
448
        self.assertEqual(outcome.oracle_reader, "oak_sqlite_reader_independent")
449
        self.assertTrue(outcome.oracle_ok, outcome.oracle_detail)
450
        with mock.patch.object(reader, "committed_payloads", return_value=({probe: b"torn"}, {"head": "h"})):
451
            self.assertFalse(ops_mod.execute(self.ctx("oak", cap("")), "gc").oracle_ok)
452
        with mock.patch.object(reader, "committed_payloads", side_effect=reader.Unavailable("database too big")):
453
            unmeasured = ops_mod.execute(self.ctx("oak", cap("")), "gc")
454
        self.assertIsNone(unmeasured.oracle_ok)
455
        self.assertIsNone(unmeasured.failed)
456
        self.assertIn("reader_unavailable", unmeasured.oracle_detail)
457
458
    def test_commit_needs_dirty_state(self) -> None:
459
        self.assertIn("needs status.dirty", self.run_op("commit.dirty", "git").failed)
460
461
    def test_timed_failure_is_recorded_not_raised(self) -> None:
462
        outcome = self.run_op("status.clean", "oak", cap("", returncode=3))
463
        self.assertIn("exit 3", outcome.failed)
464
        self.assertIsNone(outcome.oracle_ok)
465
466
    def test_check_worktree(self) -> None:
467
        root = self.tmp / "git" / "work"
468
        path = gen.file_path(self.spec, 5)
469
        self.expected.sample[path] = gen.git_blob_id((root / path).read_bytes())
470
        ctx = self.ctx("git")
471
        ok, detail = ops_mod.check_worktree(ctx, root, self.spec.files)
472
        self.assertTrue(ok, detail)
473
        (root / path).write_bytes(b"changed")
474
        ok, detail = ops_mod.check_worktree(ctx, root, self.spec.files)
475
        self.assertFalse(ok)
476
        self.assertEqual(detail["sample_mismatch_count"], 1)
477
478
479
class RowTest(unittest.TestCase):
480
    def prepared(self) -> SimpleNamespace:
481
        tier = tiers_mod.TIERS["fresh"]
482
        fields = {"tier": "fresh", "tier_kind": "tier", "fixture_id": tier.fixture_id,
483
                  "fixture_manifest_sha256": "a" * 64, "fixture_spec_sha256": "b" * 64}
484
        return SimpleNamespace(tier=tier, fixture_fields=lambda: fields, phases={"ingest.oak_import": 1.5},
485
                               notes=[], manifest={"refs_sha256": "a" * 64, "spec_sha256": "b" * 64})
486
487
    def row(self, subject: str, kind: str, op: str, run: int, outcome: ops_mod.Outcome) -> dict:
488
        return runner_mod.build_row(bench_id="b", prepared=self.prepared(),
489
                                    subject=SimpleNamespace(name=subject, kind=kind), op=op, run=run,
490
                                    outcome=outcome, details={}, env_sample={}, lock_wait_ms=0.0,
491
                                    order_position=0, runner_fields={})
492
493
    def test_rows_validate_for_all_outcomes(self) -> None:
494
        ok = ops_mod.Outcome(timed=[ops_mod.Timed("main", ["git", "status"], cap(""))], oracle_ok=True)
495
        skip = ops_mod.Outcome(skip_reason="no_equivalent")
496
        fail = ops_mod.Outcome(timed=[ops_mod.Timed("main", ["oak", "status"], cap("", 2))], failed="exit 2")
497
        bad = ops_mod.Outcome(timed=[ops_mod.Timed("main", ["oak", "log"], cap(""))], oracle_ok=False)
498
        rows = [self.row("git", "git", "status.clean", 1, ok), self.row("oak_installed", "oak", "blame", 1, skip),
499
                self.row("oak_installed", "oak", "status.clean", 1, fail),
500
                self.row("oak_installed", "oak", "log.path", 1, bad)]
501
        rows.append(runner_mod.abort_row(rows[0], "diff.dirty", "status.dirty"))
502
        prepared = self.prepared()
503
        prepared.oak_identity = {"binary_sha256": "c" * 64}
504
        rows += runner_mod.ingest_rows(prepared, "b", {})
505
        self.assertEqual(validate_rows(rows, "scale"), [])
506
        self.assertEqual([r["returncode"] for r in rows[:5]], [0, 77, 1, 1, 77])
507
        self.assertEqual(rows[3]["failure"], "oracle_failed")
508
        self.assertFalse(any(r["claim_eligible"] for r in rows))
509
        self.assertTrue(all(r["oracle_timed"] is False for r in rows[:4]))
510
        import publish_gate
511
        for row in rows:
512
            if row["subject_kind"] == "oak" and row["operation"].startswith("ingest."):
513
                self.assertEqual(publish_gate.oak_binary_identity(row), "c" * 64)
514
515
    def test_cell_report_gives_floors_and_mde(self) -> None:
516
        rows = []
517
        for run in range(1, 7):
518
            for subject, kind, base in (("git", "git", 10.0), ("git_aa", "git", 10.0),
519
                                        ("oak_installed", "oak", 20.0), ("oak_installed_aa", "oak", 20.0)):
520
                outcome = ops_mod.Outcome(timed=[ops_mod.Timed("main", [kind], cap(""))], oracle_ok=True)
521
                row = self.row(subject, kind, "status.clean", run, outcome)
522
                row["elapsed_ms"] = base + (run % 3) * 0.2
523
                rows.append(row)
524
        report = runner_mod.cell_report(rows, ["git", "git_aa", "oak_installed", "oak_installed_aa"])
525
        item = report[0]
526
        self.assertIsNotNone(item["git_aa_floor_pct"])
527
        self.assertIsNotNone(item["oak_vs_git_mde_pct_80"])
528
        self.assertIsNotNone(item["oak_vs_oak_mde_pct_80"])
529
        self.assertAlmostEqual(item["oak_vs_git_delta_pct_dev_only"], 100.0, delta=5)
530
        self.assertFalse(item["claim_eligible"])
531
        text = runner_mod.summary_markdown(self.prepared(), report,
532
                                           {"bench_id": "b", "runs": 6, "subjects": ["git"]})
533
        self.assertIn("DEVELOPMENT ONLY", text)
534
        self.assertRegex(text, r"\| 1\.9\dx \|")
535
536
537
@unittest.skipUnless(GIT, "git not available")
538
class GitOnlyScheduleTest(unittest.TestCase):
539
    """End to end on git only: prepare, interleave git + git_aa, every oracle passes."""
540
541
    def test_schedule(self) -> None:
542
        from oakbench.environment import base_env
543
        tmp = Path(tempfile.mkdtemp(prefix="scale-e2e-"))
544
        try:
545
            spec = gen.GenSpec(name="e2e", seed="e2e", files=24, file_bytes=64, depth=3, fanout=4, commits=9,
546
                               branches=2, conflict_files=2)
547
            tier = tiers_mod.Tier(tier_id="e2e", kind="tier", spec=spec, dirty=tiers_mod.DirtyShape(2, 1, 1, 1))
548
            env = base_env(home=tmp / "home")
549
            # default logger (stderr) on purpose: it must be importable and callable
550
            prepared = ws.prepare(tier, tmp / "work", git_bin=GIT, oak_bin="oak", env=env, kinds=("git",))
551
            subjects = [SimpleNamespace(name=n, kind="git", bin=Path(GIT)) for n in ("git", "git_aa")]
552
            try:
553
                rows = runner_mod.run_schedule(prepared, subjects, runs=1, ops=ops_mod.ALL_OPS, bench_id="e2e",
554
                                               seed=1, scratch=tmp / "scratch", details={}, runner_fields={},
555
                                               timeout_s=120, lock_wait_ms=None, log=lambda *_: None)
556
            finally:
557
                prepared.close()
558
            self.assertEqual(validate_rows(rows, "scale"), [])
559
            failed = [(r["operation"], r.get("failure"), r.get("oracle_detail")) for r in rows if r["returncode"] == 1]
560
            self.assertEqual(failed, [])
561
            measured = {r["operation"] for r in rows if r["returncode"] == 0}
562
            self.assertEqual(measured, set(ops_mod.ALL_OPS))
563
            self.assertTrue(all(r["oracle_ok"] for r in rows if r["returncode"] == 0))
564
        finally:
565
            shutil.rmtree(tmp, ignore_errors=True)
566
567
568
if __name__ == "__main__":
569
    unittest.main()
570
571
572
class WatchdogTimingTest(unittest.TestCase):
573
    """QA P5-1: a bounded command must not pay Popen.wait(timeout) poll quantization."""
574
575
    def test_watchdog_bound_does_not_change_timing(self) -> None:
576
        import statistics
577
        env = {"PATH": "/usr/bin:/bin"}
578
        plain, bounded = [], []
579
        for _ in range(25):  # interleaved pairs share host drift
580
            plain.append(run_timed(["/bin/sleep", "0.013"], Path("/"), env, 100).elapsed_ms)
581
            bounded.append(run_timed(["/bin/sleep", "0.013"], Path("/"), env, 100, watchdog_seconds=60.0).elapsed_ms)
582
        delta = statistics.median(bounded) - statistics.median(plain)
583
        self.assertLess(abs(delta), 2.0, (statistics.median(plain), statistics.median(bounded)))
584
585
    def test_watchdog_kills_and_reports_timeout(self) -> None:
586
        capture = run_timed(["/bin/sleep", "5"], Path("/"), {"PATH": "/usr/bin:/bin"}, 100, watchdog_seconds=0.3)
587
        self.assertTrue(capture.timed_out)
588
        self.assertEqual(capture.returncode, 124)
589
        self.assertLess(capture.elapsed_ms, 3000)
590
591
    def test_both_bounds_rejected(self) -> None:
592
        with self.assertRaises(ValueError):
593
            run_timed(["/usr/bin/true"], Path("/"), {}, 10, timeout_seconds=1, watchdog_seconds=1)
594
595
    def test_scale_ops_use_the_watchdog(self) -> None:
596
        seen = {}
597
598
        def fake(command, cwd, env, admitted, **kwargs):
599
            seen.update(kwargs)
600
            return cap("")
601
        subject = SimpleNamespace(name="git", kind="git", bin="git")
602
        prepared = SimpleNamespace(env={}, state={}, path=lambda *p: Path("/"))
603
        ops_mod.Ctx(prepared, subject, 1, fake, Path("/"), 30.0).timed(["status"], Path("/"))
604
        self.assertEqual(seen.get("watchdog_seconds"), 30.0)
605
        self.assertNotIn("timeout_seconds", seen)
606
607
608
def _cell_rows(tier: str, op: str, seed, base_git: float = 100.0, base_oak: float = 700.0) -> list:
609
    rows = []
610
    for run in range(1, 7):
611
        for subject, kind, base in (("git", "git", base_git), ("git_aa", "git", base_git),
612
                                    ("oak_installed", "oak", base_oak), ("oak_installed_aa", "oak", base_oak)):
613
            row = {"scenario": tier, "operation": op, "run": run, "subject": subject, "subject_kind": kind,
614
                   "returncode": 0, "elapsed_ms": base + (run % 3) * 0.5}
615
            if kind == "oak" and seed is not None:
616
                row["oak_server_seed"] = seed
617
            rows.append(row)
618
    return rows
619
620
621
class ComparabilityTest(unittest.TestCase):
622
    """QA P5-2: a snapshot-seeded Oak server is not the repository Git serves."""
623
624
    SUBJECTS = ["git", "git_aa", "oak_installed", "oak_installed_aa"]
625
626
    def test_snapshot_seeded_sync_cells_suppress_oak_vs_git(self) -> None:
627
        rows = []
628
        tiers = ("default", "xl-deep", "xl-history", "xl-refs")
629
        ops = ("push.branch", "pull.ff", "pull.diverged", "clone.full")
630
        for tier in tiers:
631
            for op in ops:
632
                rows += _cell_rows(tier, op, "snapshot_1_commit")
633
            rows += _cell_rows(tier, "status.clean", None)
634
        report = {(c["scenario"], c["operation"]): c for c in runner_mod.cell_report(rows, self.SUBJECTS)}
635
        for tier in tiers:
636
            for op in ops:
637
                cell = report[(tier, op)]
638
                self.assertEqual(cell["oak_vs_git_comparability"], "not_comparable:oak_server_seed_snapshot_1_commit")
639
                self.assertIsNone(cell["oak_vs_git_delta_pct_dev_only"])
640
                self.assertIsNone(cell["oak_vs_git_mde_pct_80"])
641
                self.assertIsNotNone(cell["oak_vs_oak_mde_pct_80"])
642
            comparable = report[(tier, "status.clean")]
643
            self.assertEqual(comparable["oak_vs_git_comparability"], "comparable")
644
            self.assertIsNotNone(comparable["oak_vs_git_delta_pct_dev_only"])
645
        prepared = SimpleNamespace(tier=tiers_mod.TIERS["default"], phases={}, notes=[],
646
                                   manifest={"refs_sha256": "a" * 64, "spec_sha256": "b" * 64})
647
        text = runner_mod.summary_markdown(prepared, [report[("default", "pull.ff")], report[("default", "status.clean")]],
648
                                           {"bench_id": "b", "runs": 6, "subjects": self.SUBJECTS})
649
        pull_line = next(line for line in text.splitlines() if line.startswith("| pull.ff "))
650
        self.assertIn("| n/c |", pull_line)
651
        self.assertIn("oak-vs-oak only", pull_line)
652
        self.assertNotIn("7.00x", pull_line)
653
654
    def test_full_history_seed_stays_comparable(self) -> None:
655
        rows = _cell_rows("fresh", "clone.full", "full_history")
656
        cell = runner_mod.cell_report(rows, self.SUBJECTS)[0]
657
        self.assertEqual(cell["oak_vs_git_comparability"], "comparable")
658
659
660
class CalibrationGateTest(unittest.TestCase):
661
    """QA P5-3 (rounds 1-2): claimability is verified by RECOMPUTING the calibration
662
    campaign from its own JSONL; nothing a record states is trusted."""
663
664
    PIN = "scale-fresh-v1"
665
666
    def setUp(self) -> None:
667
        from oakbench import scale_calibration as cal
668
        self.cal = cal
669
        self.tmp = Path(tempfile.mkdtemp(prefix="scale-cal-"))
670
        self.addCleanup(shutil.rmtree, self.tmp, True)
671
        entry = json.loads((ROOT / "config" / "fixtures.json").read_text())["fixtures"][self.PIN]
672
        self.fixture = {"tier": "fresh", "fixture_id": "scale-fresh-x", "fixture_spec_sha256": entry["spec_sha256"],
673
                        "fixture_manifest_sha256": entry["refs_sha256"], "fixture_pin_status": "verified"}
674
        self.identity = {"runner_id": "runner-1", "host_cpu_count": 14, "host_mem_bytes": 1 << 35}
675
676
    def rows(self, bench_id: str, *, load: float = 0.5, jitter: float = 0.05, fsync_ms: float = 1.0,
677
             identity=None, calibrated=None) -> list:
678
        ident = identity or self.identity
679
        rows = []
680
        for run in range(1, 7):
681
            for op_index in range(12):
682
                for subject, kind in (("git", "git"), ("git_aa", "git"), ("oak_installed", "oak"),
683
                                      ("oak_installed_aa", "oak")):
684
                    rows.append({"bench_id": bench_id, "profile": "scale", "scenario": "fresh",
685
                                 "operation": f"op{op_index}", "run": run, "subject": subject, "subject_kind": kind,
686
                                 "elapsed_ms": 10.0 + op_index + ((run + subject.endswith("_aa")) % 3) * jitter,
687
                                 "returncode": 0, "command": [],
688
                                 "binary_sha256": ("a" if kind == "git" else "b") * 64,
689
                                 "environment_sample": {"loadavg": [load, load, load]},
690
                                 **self.fixture, **ident})
691
        base = {"bench_id": bench_id, "profile": "scale", "scenario": "fresh", **self.fixture}
692
        probe = {"fullfsync_ms": [fsync_ms] * 20, "fullfsync_kind": "F_FULLFSYNC", "create_files": 500,
693
                 "create_elapsed_s": 0.05}
694
        probes = self.cal.probe_rows(probe, base, "start")
695
        for row in probes:
696
            row.update(ident)
697
        rows = probes + rows
698
        if calibrated is not None:
699
            record, facts, reasons = calibrated
700
            for row in rows:
701
                if not row.get("diagnostic"):
702
                    self.cal.stamp(row, record, facts, reasons)
703
        return rows
704
705
    def write(self, name: str, rows: list) -> Path:
706
        path = self.tmp / name
707
        path.write_text("".join(json.dumps(row, sort_keys=True) + "\n" for row in rows))
708
        return path
709
710
    def campaign(self, name: str = "calibration.jsonl", **kwargs):
711
        path = self.write(name, self.rows("cal", **kwargs))
712
        rows = self.cal.load_rows(path)
713
        facts, problems = self.cal.recompute(rows, self.registry())
714
        record = self.cal.seal({**{k: facts.get(k) for k in ("runner_id", "host_cpu_count", "host_mem_bytes",
715
                                                              "oak_binary_sha256", "git_binary_sha256", "aa_cells",
716
                                                              "aa_floor_median_pct", "aa_false_claims")},
717
                                "calibration_rows_path": str(path),
718
                                "calibration_rows_sha256": self.cal.file_sha256(path)})
719
        return path, record, facts, problems
720
721
    def registry(self):
722
        return json.loads((ROOT / "config" / "fixtures.json").read_text())["fixtures"]
723
724
    def gate_text(self, *paths: Path) -> str:
725
        import contextlib
726
        import publish_gate
727
        out = io.StringIO()
728
        with contextlib.redirect_stdout(out), contextlib.redirect_stderr(io.StringIO()):
729
            publish_gate.main([str(p) for p in paths])
730
        return out.getvalue()
731
732
    def published(self, record, facts, problems, **kwargs) -> Path:
733
        return self.write("published.jsonl", self.rows("pub", calibrated=(record, facts, problems), **kwargs))
734
735
    def test_quiet_campaign_recomputes_and_publishes(self) -> None:
736
        cal_path, record, facts, problems = self.campaign()
737
        self.assertEqual(problems, [])
738
        self.assertEqual(facts["aa_cells"], 24)
739
        self.assertEqual(facts["aa_false_claims"], 0)
740
        pub = self.published(record, facts, problems)
741
        rows = self.cal.load_rows(pub)
742
        self.assertTrue(all(r["claim_eligible"] for r in rows if not r.get("diagnostic")))
743
        self.assertEqual(validate_rows(rows, "scale"), [])
744
        text = self.gate_text(pub, cal_path)
745
        self.assertIn("PASS   scale-calibration", text)
746
        self.assertNotIn("FAIL   scale-calibration", text)
747
748
    def test_campaign_jsonl_missing_from_inputs_fails(self) -> None:
749
        cal_path, record, facts, problems = self.campaign()
750
        self.assertIn("FAIL   scale-calibration", self.gate_text(self.published(record, facts, problems)))
751
752
    def test_calibration_jsonl_not_matching_record_sha_fails(self) -> None:
753
        cal_path, record, facts, problems = self.campaign()
754
        pub = self.published(record, facts, problems)
755
        cal_path.write_text(cal_path.read_text().replace('"elapsed_ms": 10.0', '"elapsed_ms": 10.5', 1))
756
        text = self.gate_text(pub, cal_path)
757
        self.assertIn("FAIL   scale-calibration", text)
758
        self.assertIn("calibration campaign rows not provided", text)  # no input matches the record's sha
759
760
    def test_resealed_edited_record_fails(self) -> None:
761
        cal_path, record, facts, problems = self.campaign(jitter=4.0)  # noisy campaign: floors too wide
762
        self.assertTrue(problems)
763
        forged = self.cal.seal({**{k: v for k, v in record.items() if k not in ("record_sha256",)},
764
                                "aa_floor_median_pct": 0.0})
765
        pub = self.published(forged, facts, [])  # stamped as if valid
766
        text = self.gate_text(pub, cal_path)
767
        self.assertIn("FAIL   scale-calibration", text)
768
769
    def test_resealed_record_numbers_are_never_trusted(self) -> None:
770
        cal_path, record, facts, problems = self.campaign()
771
        forged = self.cal.seal({**{k: v for k, v in record.items() if k != "record_sha256"}, "aa_cells": 999})
772
        text = self.gate_text(self.published(forged, facts, []), cal_path)
773
        self.assertIn("FAIL   scale-calibration", text)
774
        self.assertIn("recomputed", text)
775
776
    def test_copied_runner_record_fails(self) -> None:
777
        cal_path, record, facts, problems = self.campaign()
778
        other = {"runner_id": "runner-2", "host_cpu_count": 14, "host_mem_bytes": 1 << 35}
779
        text = self.gate_text(self.published(record, facts, [], identity=other), cal_path)
780
        self.assertIn("FAIL   scale-calibration", text)
781
        resealed = self.cal.seal({**{k: v for k, v in record.items() if k != "record_sha256"},
782
                                  "runner_id": "runner-2"})
783
        text = self.gate_text(self.published(resealed, facts, [], identity=other), cal_path)
784
        self.assertIn("FAIL   scale-calibration", text)
785
786
    def test_host_mismatch_and_loaded_host_fail(self) -> None:
787
        cal_path, record, facts, problems = self.campaign()
788
        bigger = dict(self.identity, host_cpu_count=100000)
789
        self.assertIn("FAIL   scale-calibration", self.gate_text(self.published(record, facts, [], identity=bigger),
790
                                                                   cal_path))
791
        loaded = self.campaign("loaded.jsonl", load=2.5)
792
        self.assertTrue(any("not quiet" in r for r in loaded[3]))
793
        disk = self.campaign("disk.jsonl", fsync_ms=4.0)
794
        self.assertTrue(any("disk contended" in r for r in disk[3]))
795
        text = self.gate_text(self.published(record, facts, [], fsync_ms=4.0), cal_path)
796
        self.assertIn("published run: disk contended", text)
797
798
    def test_qa_forged_record_fixture_fails(self) -> None:
799
        import gzip
800
        forged = self.tmp / "forgedC.fresh.jsonl"
801
        forged.write_bytes(gzip.decompress((ROOT / "tests" / "fixtures" / "scale" /
802
                                            "qa-r2-forgedC.fresh.jsonl.gz").read_bytes()))
803
        rows = self.cal.load_rows(forged)
804
        self.assertTrue(any(r.get("claim_eligible") is True for r in rows))  # the forgery claims eligibility
805
        self.assertIn("FAIL   scale-calibration", self.gate_text(forged))
806
807
    def test_without_record_rows_are_dev_only(self) -> None:
808
        row = self.cal.stamp({"returncode": 0}, None, None, [])
809
        self.assertFalse(row["claim_eligible"])
810
        self.assertEqual(row["evidence_class"], "development_only_loaded_host")
811
812
813
class PinAndTeardownTest(unittest.TestCase):
814
    def test_check_pin(self) -> None:
815
        registry = json.loads((ROOT / "config" / "fixtures.json").read_text())["fixtures"]["scale-default-v1"]
816
        manifest = {"spec_sha256": registry["spec_sha256"], "refs_sha256": registry["refs_sha256"]}
817
        self.assertEqual(gen.check_pin(manifest), "verified")
818
        with self.assertRaises(gen.FixturePinMismatch):
819
            gen.check_pin(dict(manifest, refs_sha256="0" * 64))
820
        self.assertEqual(gen.check_pin({"spec_sha256": "f" * 64, "refs_sha256": "0" * 64}), "unpinned")
821
822
    def test_results_file_naming(self) -> None:
823
        from oakbench.results import ResultsStore
824
        with tempfile.TemporaryDirectory() as tmp:
825
            store = ResultsStore(Path(tmp), "scale", filename_suffix="fresh")
826
            raw, _ = store.write("20260929T000000Z", [], "x")
827
            self.assertEqual(raw.name, "20260929T000000Z.fresh.jsonl")