Viewing files at commit 1a441edafc71Commit detailsView latest

suites/robustness/robustness/hosted.py 65 lines · 3.1 KB · python
1
"""Hosted-merge invariants requested by the merge owner (o-8f): SPEC, runnable later.
2
3
These need a server that implements ``oak merge`` (hosted squash-merge onto
4
main). Loopback ``oak serve`` 0.105.0 answers merge publication with 404, so
5
today every case is an ``unsupported`` row. When a LOCAL oakspace is available,
6
set ``OAK_BENCH_ROBUSTNESS_OAKSPACE_URL=http://127.0.0.1:<port>`` (loopback
7
only; the harness refuses any other host and never targets oak.space).
8
9
Invariants (base: o-8f's M0a stress harness):
10
11
H1 ``acked_merge_on_main``      every ``oak merge --json`` that reports success
12
    (exit 0, merged commit C) leaves C reachable from main, read back by an
13
    independent reader of the server store (not the merging client).
14
H2 ``main_never_loses_landed``  main's history is append-only across the run:
15
    every commit ever observed as main's head stays an ancestor of the final
16
    main head (checked with the lincheck register model, reads = observed heads).
17
H3 ``push_during_merge_not_dropped`` (M0b) a push to branch B acknowledged
18
    while a merge of B is in flight is either included in the merged result or
19
    remains on B as unmerged work (B not closed over it); never silently dropped.
20
H4 ``concurrent_merges_serialize`` (M0a) K concurrent merges of distinct
21
    branches all land (or fail actionably), main's head sequence is
22
    linearizable, and each landed change's files are in main's final tree.
23
H5 ``ambiguous_merge_not_success`` a merge whose reply is lost (fault proxy
24
    drop_after_send on the merge publication) is never reported as success
25
    without reconciliation, and a retry never double-applies.
26
"""
27
28
from __future__ import annotations
29
30
import ipaddress
31
import os
32
from urllib.parse import urlparse
33
34
ENV = "OAK_BENCH_ROBUSTNESS_OAKSPACE_URL"
35
CASES = (
36
    ("acked_merge_on_main", "merge_hosted"),
37
    ("main_never_loses_landed", "merge_hosted"),
38
    ("push_during_merge_not_dropped", "merge_hosted"),
39
    ("concurrent_merges_serialize", "merge_hosted"),
40
    ("ambiguous_merge_not_success", "merge_hosted"),
41
)
42
43
44
def configured_url() -> tuple[str | None, str]:
45
    url = os.environ.get(ENV)
46
    if not url:
47
        return None, (f"hosted merge invariants are spec-only: set {ENV} to a LOOPBACK oakspace "
48
                      "(loopback oak serve 0.105.0 returns 404 for merge publication)")
49
    host = urlparse(url).hostname or ""
50
    try:
51
        loopback = ipaddress.ip_address(host).is_loopback
52
    except ValueError:
53
        loopback = host == "localhost"
54
    if not loopback:
55
        return None, f"refusing non-loopback hosted target {host!r}: fault/stress cases run against local oakspace only"
56
    return url, ""
57
58
59
def rows_spec() -> list[dict]:
60
    """One unsupported row per hosted invariant until a local oakspace driver exists."""
61
    url, reason = configured_url()
62
    if url is not None:
63
        reason = ("local oakspace configured, but the hosted driver is not implemented in suite v1; "
64
                  "see suites/robustness/robustness/hosted.py for the invariant contract")
65
    return [{"invariant_id": name, "operation": op, "skip_reason": reason} for name, op in CASES]