phase: 154_sync_status_freshness
All 4 task files exist in `complete/`, and only `00_phase.md` remains in `todo/`. This is the final verification pass. I'll read through the task files and check the current state of the code.
This commit is contained in:
@@ -149,6 +149,46 @@ positions are distinct and each disappears independently. The node
|
||||
harness gains the two in-flight image rows (both parts at once —
|
||||
the owner's stuck label — and the image-only pictorial PDF) and
|
||||
pins the composed full string byte-for-byte.
|
||||
|
||||
Phase 154 (task 02) extends the Sources-page upload line with the
|
||||
COMPLETION STAMP (the phase's date-qualified freshness): the settled
|
||||
line reads "Uploaded <name> (<stamp>) — press Sync sources to import
|
||||
it." (the fresh 202 settle, the page-local name) or the nameless
|
||||
"Uploaded <stamp> — press Sync sources to import it." (the boot
|
||||
re-render — the stamp comes from the SERVER payload, so the line is
|
||||
self-dating even with the name unknown); a running / legacy / partial
|
||||
payload without timestamps renders the byte-identical pre-phase-154
|
||||
strings (the nameful / nameless "Uploaded … — press Sync sources to
|
||||
import it." lines). The stamp is PRECOMPUTED by the page-local
|
||||
fmtUploadStamp(status) (the per-page duplication idiom — the same A1
|
||||
date-qualifier as sources.js's fmtSyncTime, duplicated, not shared;
|
||||
the rule's boundary set is pinned in test_sync_button.py on a fixed
|
||||
clock): it reads status.finished_at first (the success state always
|
||||
has it on the server), falls back to status.started_at (a defensive
|
||||
guard — a corrupt finished_at degrades to the started_at stamp rather
|
||||
than to no stamp), and yields "" when both are absent or every ISO is
|
||||
unparseable. Both render sites — the poll's success settle and the
|
||||
boot re-attach — pass fmtUploadStamp(status) as the third argument;
|
||||
the NO server change (the /upload/status payload is untouched —
|
||||
started_at / finished_at already shipped, phase 64).
|
||||
|
||||
Phase 154 (task 03) pins the BYTE-IDENTITY of the no-stamp fallbacks
|
||||
against the VERBATIM pre-phase-154 strings: the old 2-arg
|
||||
fmtUploadResult (git history) is pinned in this file as a reference
|
||||
implementation (the name renamed, the body byte-for-byte) and run
|
||||
side by side with the real 3-arg function under node — the no-stamp
|
||||
(nameful / nameless) outputs + the legacy 2-arg call are asserted
|
||||
byte-identical to the reference's output, and the reference's
|
||||
output to the verbatim old line strings (assertable, not assumed).
|
||||
Clock-free: `when` is a precomputed argument, so the no-stamp
|
||||
shapes need no clock at all. The /upload/status full-dict pin
|
||||
(tests/integration/test_git_sources_upload.py, the phase-64
|
||||
precedent) stays UNCHANGED — this phase adds no server keys (the
|
||||
app/ no-touch regression guard). The qualifier's byte-identity
|
||||
(same-day + fallbacks vs the verbatim pre-phase fmtSyncTime) is
|
||||
pinned in test_sync_button.py on the shared fixed clock; the four
|
||||
shapes and the fmtUploadStamp behavior (finished / started /
|
||||
missing / garbage) ride the task-02 pins below.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -1639,23 +1679,42 @@ def test_processing_state_is_bare_for_the_whole_run() -> None:
|
||||
|
||||
|
||||
def test_fmt_upload_result_is_the_ready_for_sync_line() -> None:
|
||||
"""Phase 90 (A2/A3): the result line reads the no-count
|
||||
{"message": "uploaded"} detail and renders "Uploaded <name> —
|
||||
press Sync sources to import it." (the name from the accepted
|
||||
202); without a name (a reload re-render, the 409 re-attach) it
|
||||
renders the nameless variant; the old sync-style count keys
|
||||
"""Phase 90 (A2/A3) extended by Phase 154 (task 02): the result
|
||||
line reads the no-count {"message": "uploaded"} detail and renders
|
||||
the ready-for-sync line in the FOUR phase-154 shapes —
|
||||
"Uploaded <name> (<stamp>) — press Sync sources to import it." (the
|
||||
name + the completion stamp), "Uploaded <name> — press Sync sources
|
||||
to import it." (name, no stamp — BYTE-IDENTICAL to the pre-phase
|
||||
string), "Uploaded <stamp> — press Sync sources to import it." (no
|
||||
name + stamp — the boot re-render, self-dating with the name
|
||||
unknown), "Uploaded — press Sync sources to import it." (no name,
|
||||
no stamp — BYTE-IDENTICAL to the pre-phase string; the running /
|
||||
legacy / partial payload). The non-uploaded detail keeps the
|
||||
unchanged "The upload finished."; the old sync-style count keys
|
||||
(added / updated / unchanged / pruned) are no longer read at all.
|
||||
The 202 branch records the safe name (lastUploadName); the poll's
|
||||
settle and the boot re-attach both render the line from
|
||||
(detail, lastUploadName); the button's idle label is "Upload"."""
|
||||
settle and the boot re-attach both render the line from (detail,
|
||||
lastUploadName, fmtUploadStamp(status)); the button's idle label
|
||||
is "Upload"."""
|
||||
js = _gjs()
|
||||
body = _gfn(js, "fmtUploadResult")
|
||||
assert "function fmtUploadResult(detail, name)" in body
|
||||
assert 'detail.message === "uploaded"' in body
|
||||
assert "Uploaded ${name} — press Sync sources to import it." in body
|
||||
assert "Uploaded — press Sync sources to import it." in body, (
|
||||
"the nameless variant (reload / 409 re-attach)"
|
||||
assert "function fmtUploadResult(detail, name, when)" in body, (
|
||||
"phase 154: the precomputed completion stamp is the third parameter"
|
||||
)
|
||||
assert 'detail.message === "uploaded"' in body
|
||||
# The four shapes (name×stamp, name×no-stamp, no-name×stamp,
|
||||
# no-name×no-stamp).
|
||||
assert "Uploaded ${name} (${when}) — press Sync sources to import it." in body
|
||||
assert "Uploaded ${name} — press Sync sources to import it." in body, (
|
||||
"the no-stamp nameful variant — byte-identical to pre-phase"
|
||||
)
|
||||
assert "Uploaded ${when} — press Sync sources to import it." in body, (
|
||||
"the nameless + stamp variant (the boot re-render — self-dating)"
|
||||
)
|
||||
assert "Uploaded — press Sync sources to import it." in body, (
|
||||
"the nameless no-stamp variant — byte-identical to pre-phase"
|
||||
)
|
||||
assert '"The upload finished."' in body, ("the non-uploaded branch is unchanged")
|
||||
for key in ("added", "updated", "unchanged", "pruned"):
|
||||
assert key not in body, f"the sync-style count {key!r} is gone"
|
||||
# The 202 branch records the safe name for the settled line.
|
||||
@@ -1664,9 +1723,19 @@ def test_fmt_upload_result_is_the_ready_for_sync_line() -> None:
|
||||
i409 = sub.find("r.status === 409")
|
||||
assert "lastUploadName = name" in sub[i202:i409]
|
||||
assert "let lastUploadName = null" in js, "page-local (null after a reload)"
|
||||
# Both render sites pass (detail, lastUploadName).
|
||||
assert "fmtUploadResult(detail, lastUploadName)" in _utick(js)
|
||||
assert "fmtUploadResult(status.detail, lastUploadName)" in _gfn(js, "initUploadStatus")
|
||||
# Both render sites pass (…, lastUploadName, fmtUploadStamp(status)).
|
||||
tick = _utick(js)
|
||||
assert re.search(
|
||||
r"uploadResult\.textContent = fmtUploadResult\(\s*detail,\s*"
|
||||
r"lastUploadName,\s*fmtUploadStamp\(status\)\s*\)",
|
||||
tick,
|
||||
), "the poll's success settle passes the completion stamp (phase 154)"
|
||||
init = _gfn(js, "initUploadStatus")
|
||||
assert re.search(
|
||||
r"uploadResult\.textContent = fmtUploadResult\(\s*status\.detail,\s*"
|
||||
r"lastUploadName,\s*fmtUploadStamp\(status\)\s*\)",
|
||||
init,
|
||||
), "the boot re-attach passes the completion stamp (phase 154)"
|
||||
# The button's idle label (static + the poll's restores).
|
||||
restore = _gfn(js, "restoreUploadButton")
|
||||
assert 'uploadBtn.textContent = "Upload"' in restore
|
||||
@@ -1751,7 +1820,7 @@ def test_upload_polling_decision_tree() -> None:
|
||||
# next-step announce + reload, NO toast.
|
||||
ok = tick[i_ok:i_fail]
|
||||
for line in (
|
||||
"fmtUploadResult(detail, lastUploadName)",
|
||||
"fmtUploadResult(",
|
||||
"uploadResult.hidden = false",
|
||||
'announce("Archive uploaded — press Sync sources to import it.")',
|
||||
'uploadFileInput.value = ""',
|
||||
@@ -1759,6 +1828,11 @@ def test_upload_polling_decision_tree() -> None:
|
||||
"loadSources()",
|
||||
):
|
||||
assert line in ok, f"the success settle must carry {line!r}"
|
||||
assert re.search(
|
||||
r"uploadResult\.textContent = fmtUploadResult\(\s*detail,\s*"
|
||||
r"lastUploadName,\s*fmtUploadStamp\(status\)\s*\)",
|
||||
ok,
|
||||
), "the success settle passes the completion stamp (phase 154)"
|
||||
assert "showUploadToast" not in tick, "no toast in the poll — it fired at the 202 (A2)"
|
||||
# failed: the sanitized error banner + reload.
|
||||
fail = tick[i_fail:]
|
||||
@@ -1815,8 +1889,14 @@ def test_boot_reattach_branches() -> None:
|
||||
assert "uploadError" not in run and "showUploadToast" not in run
|
||||
ok = body[i_ok:i_fail]
|
||||
# The last result line — the nameless variant after a reload
|
||||
# (lastUploadName is null: the safe name was page-local, phase 90).
|
||||
assert "fmtUploadResult(status.detail, lastUploadName)" in ok
|
||||
# (lastUploadName is null: the safe name was page-local, phase 90),
|
||||
# self-dating through the completion stamp (phase 154 — the stamp
|
||||
# comes from the server payload).
|
||||
assert re.search(
|
||||
r"uploadResult\.textContent = fmtUploadResult\(\s*status\.detail,\s*"
|
||||
r"lastUploadName,\s*fmtUploadStamp\(status\)\s*\)",
|
||||
ok,
|
||||
), "the boot re-attach passes the completion stamp (phase 154)"
|
||||
assert "uploadResult.hidden = false" in ok
|
||||
assert "announce(" not in ok, "no announce at boot (A2)"
|
||||
assert "showUploadToast" not in ok, "no toast at boot (A2)"
|
||||
@@ -1860,3 +1940,330 @@ def test_git_sources_html_comment_documents_the_202_contract() -> None:
|
||||
assert "Phase 90" in comment, "the unpack-only rework is documented"
|
||||
assert "UNPACK + REGISTER ONLY" in comment
|
||||
assert "Sync sources" in comment, "the result line points at the RAG page's button"
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# Phase 154 (task 02) — the Sources upload line's completion stamp
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def test_fmt_upload_stamp_is_the_duplicated_qualifier() -> None:
|
||||
"""Phase 154 (task 02): ``fmtUploadStamp(status)`` — the page-local
|
||||
DUPLICATE of sources.js's ``fmtSyncTime`` date-qualifier (the
|
||||
per-page duplication idiom — not shared). It reads
|
||||
``status.finished_at`` first (the success state always has it on
|
||||
the server), falls back to ``status.started_at`` (the defensive
|
||||
guard — a corrupt ``finished_at`` degrades to the ``started_at``
|
||||
stamp rather than to no stamp), and yields ``""`` when both are
|
||||
missing/absent or every ISO is unparseable (the byte-identical
|
||||
pre-phase-154 fallback). The qualifier is the SAME A1 structure as
|
||||
the phase-154 task-01 pin in test_sync_button.py — local calendar
|
||||
dates, ``HH:MM`` same day, ``M/D, h:MM AM/PM`` same year,
|
||||
``M/D/YYYY, h:MM AM/PM`` across years, the 12-hour form WITHOUT a
|
||||
leading zero on the hour, month/day without leading zeros — cited
|
||||
in sources.js, not restated. The ``status || {}`` guard defends a
|
||||
missing status object."""
|
||||
js = _gjs()
|
||||
body = _gfn(js, "fmtUploadStamp")
|
||||
assert "function fmtUploadStamp(status)" in body
|
||||
assert "const s = status || {};" in body, (
|
||||
"defensive: a missing / null status object yields ''"
|
||||
)
|
||||
assert "s.finished_at" in body and "s.started_at" in body
|
||||
i_fin = body.find("s.finished_at")
|
||||
i_st = body.find("s.started_at")
|
||||
assert -1 < i_fin < i_st, "finished_at is read first — the started_at fallback"
|
||||
assert "if (!iso) continue;" in body, (
|
||||
"absent / null timestamps are skipped (the fallback reads)"
|
||||
)
|
||||
assert "if (!Number.isNaN(t.getTime()))" in body, (
|
||||
"an unparseable ISO is skipped — a corrupt finished_at degrades"
|
||||
" to started_at, '' when neither parses"
|
||||
)
|
||||
assert 'if (d === null) return "";' in body, (
|
||||
"both absent (or unparseable) → '' — the byte-identical pre-"
|
||||
"phase-154 fallback"
|
||||
)
|
||||
# The duplicated A1 qualifier (cited in sources.js's fmtSyncTime,
|
||||
# not restated) — the same structure as the task-01 pin.
|
||||
assert "const now = new Date();" in body, "'now' is the page's clock"
|
||||
assert (
|
||||
"d.getFullYear() === now.getFullYear() &&" in body
|
||||
and "d.getMonth() === now.getMonth()" in body
|
||||
and "d.getDate() === now.getDate()" in body
|
||||
), "same local calendar day as now → HH:MM (byte-identical to pre-phase)"
|
||||
assert "d.getHours() % 12 || 12" in body, (
|
||||
"12-hour WITHOUT leading zero on the hour (the 12 o'clock edge)"
|
||||
)
|
||||
assert '"AM"' in body and '"PM"' in body, "the AM/PM family"
|
||||
assert "pad(d.getMinutes())" in body, "MM zero-padded in every form"
|
||||
assert "`${d.getMonth() + 1}/${d.getDate()}`" in body, (
|
||||
"M/D, no leading zeros (same year, different day)"
|
||||
)
|
||||
assert "`${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`" in body, (
|
||||
"M/D/YYYY across years"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_upload_result_four_shapes_under_node() -> None:
|
||||
"""Phase 154 (task 02): the REAL git-sources.js ``fmtUploadResult``
|
||||
under node — the four shapes, ``when`` PRECOMPUTED by the caller
|
||||
(so the shape itself is clock-free):
|
||||
|
||||
* name + stamp: ``Uploaded docs.zip (21:14) — press Sync sources
|
||||
to import it.`` (the fresh 202 settle, the page-local name);
|
||||
* name, no stamp (``""``): ``Uploaded docs.zip — press Sync
|
||||
sources to import it.`` — BYTE-IDENTICAL to the pre-phase-154
|
||||
string (the running / legacy / partial payload);
|
||||
* no name + stamp: ``Uploaded 10/2, 3:14 PM — press Sync sources
|
||||
to import it.`` (the boot re-render — self-dating with the name
|
||||
unknown);
|
||||
* no name, no stamp (``""``): ``Uploaded — press Sync sources to
|
||||
import it.`` — BYTE-IDENTICAL to the pre-phase-154 string;
|
||||
* a legacy 2-arg call (``when`` undefined) renders the pre-phase
|
||||
nameful string (the fallback is falsy-driven);
|
||||
* the non-``uploaded`` detail and a missing detail keep the
|
||||
unchanged ``The upload finished.``."""
|
||||
js = _gjs()
|
||||
fn = _gfn(js, "fmtUploadResult")
|
||||
script = (
|
||||
fn
|
||||
+ "\n"
|
||||
'const S = { message: "uploaded" };\n'
|
||||
'const O = { message: "other" };\n'
|
||||
"console.log(JSON.stringify([\n"
|
||||
' fmtUploadResult(S, "docs.zip", "21:14"),\n'
|
||||
' fmtUploadResult(S, "docs.zip", ""),\n'
|
||||
' fmtUploadResult(S, null, "10/2, 3:14 PM"),\n'
|
||||
' fmtUploadResult(S, null, ""),\n'
|
||||
' fmtUploadResult(S, "docs.zip"),\n'
|
||||
' fmtUploadResult(O, "docs.zip", "21:14"),\n'
|
||||
" fmtUploadResult(null, \"docs.zip\", \"21:14\"),\n"
|
||||
"]));\n"
|
||||
)
|
||||
rows = json.loads(_run_node(script))
|
||||
assert rows == [
|
||||
"Uploaded docs.zip (21:14) — press Sync sources to import it.",
|
||||
# byte-identical to pre-phase (the no-stamp nameful variant):
|
||||
"Uploaded docs.zip — press Sync sources to import it.",
|
||||
"Uploaded 10/2, 3:14 PM — press Sync sources to import it.",
|
||||
# byte-identical to pre-phase (the no-stamp nameless variant):
|
||||
"Uploaded — press Sync sources to import it.",
|
||||
# the legacy 2-arg call (when undefined → falsy → pre-phase):
|
||||
"Uploaded docs.zip — press Sync sources to import it.",
|
||||
"The upload finished.",
|
||||
"The upload finished.",
|
||||
], rows
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# Phase 154 (task 03) — the byte-identity pins (the pre-phase
|
||||
# strings, pinned verbatim — assertable, not assumed)
|
||||
# =====================================================================
|
||||
|
||||
#: The pre-phase-154 fmtUploadResult, pinned VERBATIM from git
|
||||
#: history (the 2-arg no-count formatter — the phase-90 contract
|
||||
#: pre-stamp). Only the function NAME is renamed (so the real + the
|
||||
#: reference run side by side under node); the body is the old code
|
||||
#: byte-for-byte — the two old line strings + "The upload finished."
|
||||
#: live in this constant, so the byte-identity below is assertable,
|
||||
#: not assumed.
|
||||
PRE_PHASE_FMT_UPLOAD_RESULT_JS = """
|
||||
function prePhaseUploadResult(detail, name) {
|
||||
if (detail && detail.message === "uploaded") {
|
||||
return name
|
||||
? `Uploaded ${name} — press Sync sources to import it.`
|
||||
: "Uploaded — press Sync sources to import it.";
|
||||
}
|
||||
return "The upload finished.";
|
||||
}
|
||||
"""
|
||||
|
||||
#: The two pre-phase-154 result-line strings, pinned verbatim as
|
||||
#: constants (the byte-identity targets — the task-03 completion
|
||||
#: criterion). The nameful one is the old template instantiated with
|
||||
#: the test's input name.
|
||||
PRE_PHASE_UPLOAD_LINE_NAMEFUL = "Uploaded docs.zip — press Sync sources to import it."
|
||||
PRE_PHASE_UPLOAD_LINE_NAMELESS = "Uploaded — press Sync sources to import it."
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_upload_result_no_stamp_is_byte_identical_to_pre_phase() -> None:
|
||||
"""Phase 154 (task 03): the two BYTE-IDENTITY fallbacks — the
|
||||
REAL 3-arg ``fmtUploadResult`` and the VERBATIM pre-phase 2-arg
|
||||
formatter (pinned above) run side by side under node (clock-free
|
||||
— ``when`` is precomputed):
|
||||
|
||||
* name + ``when === ""`` → the pre-phase nameful string,
|
||||
byte-identical (the running / legacy / partial payload);
|
||||
* no name + ``when === ""`` → the pre-phase nameless string,
|
||||
byte-identical (the same payload, the name unknown);
|
||||
* the legacy 2-arg call (``when`` undefined → falsy) renders the
|
||||
SAME two byte-identical strings (the old call shape is
|
||||
unchanged in output);
|
||||
* the non-``uploaded`` detail (and a missing detail) keep
|
||||
``The upload finished.`` on BOTH sides (the branch the phase
|
||||
never touched).
|
||||
|
||||
The reference implementation's own output is asserted against the
|
||||
verbatim old strings FIRST (a self-check on the pinned constant —
|
||||
a typo in the pinned function fails here, not silently), so the
|
||||
real-vs-reference identity is an assertion against the verbatim
|
||||
pre-phase strings (the completion criterion), not an
|
||||
assumption."""
|
||||
# Source-level self-check: the pinned constant carries the old
|
||||
# strings verbatim (both line templates + the non-uploaded
|
||||
# branch).
|
||||
assert (
|
||||
"return name\n"
|
||||
" ? `Uploaded ${name} — press Sync sources to import it.`\n"
|
||||
' : "Uploaded — press Sync sources to import it.";'
|
||||
in PRE_PHASE_FMT_UPLOAD_RESULT_JS
|
||||
), "the old nameful + nameless line templates are pinned verbatim"
|
||||
assert '"The upload finished."' in PRE_PHASE_FMT_UPLOAD_RESULT_JS, (
|
||||
"the non-uploaded branch is pinned verbatim"
|
||||
)
|
||||
|
||||
fn = _gfn(_gjs(), "fmtUploadResult")
|
||||
script = (
|
||||
fn
|
||||
+ "\n"
|
||||
+ PRE_PHASE_FMT_UPLOAD_RESULT_JS
|
||||
+ "\n"
|
||||
'const S = { message: "uploaded" };\n'
|
||||
'const O = { message: "other" };\n'
|
||||
"console.log(JSON.stringify({\n"
|
||||
" REAL: [\n"
|
||||
' fmtUploadResult(S, "docs.zip", ""),\n'
|
||||
" fmtUploadResult(S, null, \"\"),\n"
|
||||
' fmtUploadResult(S, "docs.zip"),\n' # legacy 2-arg call
|
||||
" fmtUploadResult(S, null),\n" # legacy 2-arg call
|
||||
' fmtUploadResult(O, "docs.zip", "21:14"),\n'
|
||||
' fmtUploadResult(null, "docs.zip", "21:14"),\n'
|
||||
" ],\n"
|
||||
" PRE: [\n"
|
||||
' prePhaseUploadResult(S, "docs.zip"),\n'
|
||||
" prePhaseUploadResult(S, null),\n"
|
||||
' prePhaseUploadResult(O, "docs.zip"),\n'
|
||||
' prePhaseUploadResult(null, "docs.zip"),\n'
|
||||
" ]\n"
|
||||
"}));\n"
|
||||
)
|
||||
rows = json.loads(_run_node(script))
|
||||
# 1. the pinned reference reproduces the verbatim old strings
|
||||
# (self-check on the constant — assertable, not assumed).
|
||||
assert rows["PRE"] == [
|
||||
PRE_PHASE_UPLOAD_LINE_NAMEFUL,
|
||||
PRE_PHASE_UPLOAD_LINE_NAMELESS,
|
||||
"The upload finished.",
|
||||
"The upload finished.",
|
||||
], rows
|
||||
# 2. the no-stamp + legacy shapes are byte-identical to the
|
||||
# reference (i.e. to the verbatim old strings).
|
||||
assert rows["REAL"][:4] == [
|
||||
PRE_PHASE_UPLOAD_LINE_NAMEFUL, # name + when === ""
|
||||
PRE_PHASE_UPLOAD_LINE_NAMELESS, # no name + when === ""
|
||||
PRE_PHASE_UPLOAD_LINE_NAMEFUL, # legacy 2-arg call
|
||||
PRE_PHASE_UPLOAD_LINE_NAMELESS, # legacy 2-arg call
|
||||
], rows
|
||||
assert rows["REAL"][0] == rows["PRE"][0], (
|
||||
"the nameful no-stamp shape is byte-identical to pre-phase"
|
||||
)
|
||||
assert rows["REAL"][1] == rows["PRE"][1], (
|
||||
"the nameless no-stamp shape is byte-identical to pre-phase"
|
||||
)
|
||||
# 3. the non-uploaded branch is unchanged on both sides.
|
||||
assert rows["REAL"][4] == rows["PRE"][2] == "The upload finished."
|
||||
assert rows["REAL"][5] == rows["PRE"][3] == "The upload finished."
|
||||
|
||||
|
||||
#: Phase 154 (task 02): the fixed clock for the stamp's boundary set —
|
||||
#: the SAME fixed now as test_sync_button.py's task-01 pin (Oct 3,
|
||||
#: 2026, 18:30 LOCAL). The test body never calls `new Date()` for its
|
||||
#: own timing: the fake clock is installed in the driver (global Date
|
||||
#: patched BEFORE the call — `new Date()` → the fixed now,
|
||||
#: `new Date(iso)` → the real parse), and every ISO is
|
||||
#: designator-less (local) so the comparison is
|
||||
#: timezone-independent.
|
||||
|
||||
_UPLOAD_STAMP_NOW_JS = "new RealDate(2026, 9, 3, 18, 30, 0)"
|
||||
|
||||
|
||||
def _upload_stamp_driver(fn_src: str, expr: str) -> str:
|
||||
"""Run `expr` under node with the REAL git-sources.js function +
|
||||
the fake clock (the idiom: the Date patch precedes the call)."""
|
||||
driver = (
|
||||
f"{fn_src}\n"
|
||||
"const RealDate = Date;\n"
|
||||
f"const NOW = {_UPLOAD_STAMP_NOW_JS};\n"
|
||||
"globalThis.Date = function (...args) {\n"
|
||||
" return args.length === 0 ? NOW : new RealDate(...args);\n"
|
||||
"};\n"
|
||||
f"{expr}\n"
|
||||
)
|
||||
proc = subprocess.run(
|
||||
["node", "-e", driver], capture_output=True, text=True, timeout=60
|
||||
)
|
||||
assert proc.returncode == 0, f"node failed: {proc.stderr}"
|
||||
return proc.stdout
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_upload_stamp_behavior_fixed_clock() -> None:
|
||||
"""Phase 154 (task 02): the REAL git-sources.js
|
||||
``fmtUploadStamp`` under node (fake clock, the fixed now Oct 3
|
||||
2026 18:30 local — the same clock as test_sync_button.py's
|
||||
task-01 pin): finished wins over started; started when finished is
|
||||
missing/null; ``""`` when both are absent (or the status object is
|
||||
missing — the defensive guard); a garbage ISO → ``""`` with no
|
||||
fallback (and a corrupt finished_at degrades to the started_at
|
||||
stamp when a started_at is present — the defensive guard); the A1
|
||||
date-qualifier's boundary set (same day ``HH:MM`` — the audit's
|
||||
own ``21:14`` case + the end-of-day edge; yesterday
|
||||
``M/D, h:MM AM/PM`` with the ``12:05 AM`` edge; last year
|
||||
``M/D/YYYY, h:MM AM/PM``). The wall clock never appears in an
|
||||
assertion."""
|
||||
fn = _gfn(_gjs(), "fmtUploadStamp")
|
||||
|
||||
def _lit(status: object) -> str:
|
||||
if status is None:
|
||||
return "null"
|
||||
assert isinstance(status, dict)
|
||||
parts = []
|
||||
for k, v in status.items():
|
||||
if v is None:
|
||||
parts.append(f"{k}: null")
|
||||
else:
|
||||
parts.append(f'{k}: "{v}"')
|
||||
return "{" + ", ".join(parts) + "}"
|
||||
|
||||
# (status, expected) — the expected strings are the pins.
|
||||
cases = [
|
||||
(
|
||||
{"finished_at": "2026-10-03T16:06:00", "started_at": "2026-10-03T16:05:00"},
|
||||
"16:06", # finished wins over started
|
||||
),
|
||||
({"started_at": "2026-10-02T15:14:00"}, "10/2, 3:14 PM"), # no finished → started
|
||||
(
|
||||
{"finished_at": None, "started_at": "2025-10-03T21:12:00"},
|
||||
"10/3/2025, 9:12 PM", # null finished → started, last year
|
||||
),
|
||||
({"finished_at": "garbage"}, ""), # unparseable, no fallback
|
||||
(
|
||||
{"finished_at": "garbage", "started_at": "2026-10-03T16:06:00"},
|
||||
"16:06", # corrupt finished degrades to the started_at stamp
|
||||
),
|
||||
({"finished_at": None, "started_at": None}, ""), # both absent
|
||||
({}, ""), # both missing
|
||||
(None, ""), # the status object itself missing (the guard)
|
||||
({"finished_at": "2026-10-02T00:05:00"}, "10/2, 12:05 AM"), # 12:05 AM edge
|
||||
({"finished_at": "2026-10-03T23:59:00"}, "23:59"), # same day, end-of-day
|
||||
]
|
||||
driver_expr = (
|
||||
"const CASES = [" + ", ".join(_lit(s) for s, _ in cases) + "];\n"
|
||||
"const STAMPS = CASES.map((c) => fmtUploadStamp(c));\n"
|
||||
"console.log(JSON.stringify(STAMPS));"
|
||||
)
|
||||
out = json.loads(_upload_stamp_driver(fn, driver_expr))
|
||||
assert out == [expected for _, expected in cases], out
|
||||
|
||||
@@ -164,8 +164,11 @@ tests/unit/test_frontend_sync_upload.py's node check).
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Callable, Iterator, Sequence
|
||||
@@ -198,6 +201,8 @@ RETIRED_ID = "-".join(("sync", "result")) # built at runtime: 3.12+ folds
|
||||
# adjacent/"+" constant strings at compile time, which would leave the
|
||||
# retired name in the .pyc — the phase gate greps even the bytecode.
|
||||
|
||||
HAVE_NODE = shutil.which("node") is not None
|
||||
|
||||
|
||||
def _text(path: Path) -> str:
|
||||
assert path.is_file(), f"missing frontend file: {path}"
|
||||
@@ -563,6 +568,10 @@ def test_sources_js_terminal_states() -> None:
|
||||
|
||||
success = _body(js, "applySyncSuccess")
|
||||
assert '"Synced"' in success and "fmtSyncTime(status.finished_at)" in success
|
||||
assert 'settleSyncButton(time ? `Synced ${time}` : "Synced")' in success, (
|
||||
"the settle string — phase 154: the qualified stamp rides the SAME"
|
||||
" shape (the qualifier lives inside the formatter)"
|
||||
)
|
||||
|
||||
failure = _body(js, "applySyncFailure")
|
||||
assert 'settleSyncButton("Sync sources")' in failure # retry-ready
|
||||
@@ -578,6 +587,313 @@ def test_sources_js_terminal_states() -> None:
|
||||
|
||||
time = _body(js, "fmtSyncTime")
|
||||
assert "getHours()" in time and "getMinutes()" in time, "local HH:MM of finished_at"
|
||||
assert 'if (Number.isNaN(d.getTime())) return "";' in time, (
|
||||
"the parse guard is UNCHANGED — unparseable ISO → "" (the bare"
|
||||
" 'Synced' settle)"
|
||||
)
|
||||
# Phase 154 (task 01): the DATE-QUALIFIER (A1 rule — the LOCAL
|
||||
# calendar dates, the page's clock; the table dates' family).
|
||||
assert "const now = new Date();" in time, "'now' is the page's clock"
|
||||
assert (
|
||||
"d.getFullYear() === now.getFullYear() &&" in time
|
||||
and "d.getMonth() === now.getMonth()" in time
|
||||
and "d.getDate() === now.getDate()" in time
|
||||
), "same local calendar day as now → HH:MM (byte-identical to pre-phase)"
|
||||
assert "d.getHours() % 12 || 12" in time, (
|
||||
"12-hour WITHOUT leading zero on the hour (the 12 o'clock edge)"
|
||||
)
|
||||
assert '"AM"' in time and '"PM"' in time, "the AM/PM family"
|
||||
assert "pad(d.getMinutes())" in time, "MM zero-padded in every form"
|
||||
assert "`${d.getMonth() + 1}/${d.getDate()}`" in time, (
|
||||
"M/D, no leading zeros (same year, different day)"
|
||||
)
|
||||
assert "`${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`" in time, (
|
||||
"M/D/YYYY across years"
|
||||
)
|
||||
|
||||
|
||||
# Phase 154 (task 01) — the date-qualifier's boundary set, behavioral
|
||||
# under node with a FIXED clock. The test body never calls `new
|
||||
# Date()` for its own timing: the fake clock is installed in the
|
||||
# driver (global Date patched BEFORE the call — `new Date()` → the
|
||||
# fixed now, `new Date(iso)` → the real parse), and every ISO is
|
||||
# designator-less (local) so the comparison is timezone-independent.
|
||||
# The fixed now is Oct 3, 2026, 18:30 LOCAL.
|
||||
|
||||
_STAMP_NOW_JS = "new RealDate(2026, 9, 3, 18, 30, 0)"
|
||||
|
||||
|
||||
def _stamp_driver(fn_src: str, expr: str) -> str:
|
||||
"""Run `expr` under node with the REAL sources.js function + the
|
||||
fake clock (the idiom: the Date patch precedes the call)."""
|
||||
driver = (
|
||||
f"{fn_src}\n"
|
||||
"const RealDate = Date;\n"
|
||||
f"const NOW = {_STAMP_NOW_JS};\n"
|
||||
"globalThis.Date = function (...args) {\n"
|
||||
" return args.length === 0 ? NOW : new RealDate(...args);\n"
|
||||
"};\n"
|
||||
f"{expr}\n"
|
||||
)
|
||||
proc = subprocess.run(
|
||||
["node", "-e", driver], capture_output=True, text=True, timeout=60
|
||||
)
|
||||
assert proc.returncode == 0, f"node failed: {proc.stderr}"
|
||||
return proc.stdout
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_sources_js_fmt_sync_time_qualifier_boundaries_fixed_clock() -> None:
|
||||
"""The REAL sources.js ``fmtSyncTime`` under node (fake clock, the
|
||||
fixed now Oct 3 2026 18:30 local) — the A1 boundary set:
|
||||
same-day → ``HH:MM`` (byte-identical to the pre-phase output —
|
||||
the audit's own ``16:06`` case, plus the midnight / end-of-day
|
||||
edges); yesterday (same year) → ``M/D, h:MM AM/PM`` (no leading
|
||||
zero on the hour — ``3:14 PM``; the ``12:05 AM`` / ``12:07 PM``
|
||||
o'clock edges); last-year same M/D → ``M/D/YYYY, h:MM AM/PM``;
|
||||
garbage / missing ISO → ``""`` (the guard unchanged).
|
||||
The ``null`` edge (``new Date(null)`` IS the epoch — parseable,
|
||||
the guard unchanged) is pinned TZ-independently: ``null`` and the
|
||||
epoch ISO render identically (same instant), in the dated + year
|
||||
form (1969 ≠ the fixed 2026). The wall clock never appears in an
|
||||
assertion."""
|
||||
fn = _body(_text(SOURCES_JS), "fmtSyncTime")
|
||||
# (iso, expected) — the expected strings are the pins.
|
||||
cases = [
|
||||
("2026-10-03T16:06:00", "16:06"), # same day — the audit case
|
||||
("2026-10-03T00:05:00", "00:05"), # same day, midnight edge
|
||||
("2026-10-03T23:59:00", "23:59"), # same day, end-of-day edge
|
||||
("2026-10-02T15:14:00", "10/2, 3:14 PM"), # yesterday, no zero hour
|
||||
("2026-10-02T00:05:00", "10/2, 12:05 AM"), # yesterday, 12:05 AM edge
|
||||
("2026-10-02T12:07:00", "10/2, 12:07 PM"), # yesterday, noon edge
|
||||
("2026-10-02T23:58:00", "10/2, 11:58 PM"), # yesterday, PM minutes
|
||||
("2025-10-03T21:12:00", "10/3/2025, 9:12 PM"), # last year, same M/D
|
||||
("2025-01-03T09:12:00", "1/3/2025, 9:12 AM"), # last year, month 1
|
||||
("garbage", ""), # unparseable → the pre-phase fallback
|
||||
("", ""), # empty → the pre-phase fallback
|
||||
("undefined", ""), # missing ISO (the app-restart case) → ""
|
||||
]
|
||||
iso_literals = []
|
||||
for iso, _ in cases:
|
||||
if iso == "undefined":
|
||||
iso_literals.append("undefined")
|
||||
else:
|
||||
iso_literals.append(f'"{iso}"')
|
||||
driver_expr = (
|
||||
"const CASES = [" + ", ".join(iso_literals) + "];\n"
|
||||
"const STAMPS = CASES.map((c) => fmtSyncTime(c));\n"
|
||||
"const EPOCH = [fmtSyncTime(null), fmtSyncTime(new RealDate(0).toISOString())];\n"
|
||||
"console.log(JSON.stringify({ STAMPS, EPOCH }));"
|
||||
)
|
||||
out = json.loads(_stamp_driver(fn, driver_expr))
|
||||
assert out["STAMPS"] == [expected for _, expected in cases], out
|
||||
assert out["EPOCH"][0] == out["EPOCH"][1], (
|
||||
"null and the epoch ISO render identically (the same instant —"
|
||||
" the guard is unchanged: new Date(null) IS the epoch)"
|
||||
)
|
||||
assert re.fullmatch(
|
||||
r"\d{1,2}/\d{1,2}/\d{4}, \d{1,2}:\d{2} (AM|PM)", out["EPOCH"][0]
|
||||
), "the epoch takes the M/D/YYYY, h:MM AM/PM form (1969 ≠ the fixed year)"
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_sources_js_synced_settle_string_qualifies_the_stamp() -> None:
|
||||
"""The ``applySyncSuccess`` settle string (the pinned shape
|
||||
``time ? `Synced ${time}` : "Synced"`` — the qualifier lives
|
||||
INSIDE the formatter, the shape is unchanged) under node with the
|
||||
fixed clock: a same-day ``finished_at`` settles as
|
||||
``Synced 16:06`` (byte-identical to pre-phase), a non-same-day
|
||||
one settles as ``Synced 10/2, 3:14 PM`` (the date-qualified
|
||||
badge — the phase's ask), and an unparseable/missing ISO settles
|
||||
as the bare ``Synced`` (the existing fallback). The ``null`` edge
|
||||
pins identically to the boundary test (the epoch — the pre-phase
|
||||
rendered ``Synced 00:00`` there)."""
|
||||
fn = _body(_text(SOURCES_JS), "fmtSyncTime")
|
||||
settle = (
|
||||
"const settle = (iso) => {\n"
|
||||
" const t = fmtSyncTime(iso);\n"
|
||||
" return t ? `Synced ${t}` : \"Synced\";\n"
|
||||
"};\n"
|
||||
)
|
||||
iso_literals = [
|
||||
'"2026-10-03T16:06:00"', # same day
|
||||
'"2026-10-02T15:14:00"', # yesterday
|
||||
'"2025-10-03T21:12:00"', # last year
|
||||
'"garbage"', # unparseable
|
||||
"undefined", # missing ISO
|
||||
]
|
||||
driver_expr = (
|
||||
settle
|
||||
+ "const CASES = [" + ", ".join(iso_literals) + "];\n"
|
||||
+ "const SETTLES = CASES.map(settle);\n"
|
||||
+ "const EPOCH = [settle(null), settle(new RealDate(0).toISOString())];\n"
|
||||
+ "console.log(JSON.stringify({ SETTLES, EPOCH }));"
|
||||
)
|
||||
out = json.loads(_stamp_driver(fn, driver_expr))
|
||||
assert out["SETTLES"] == [
|
||||
"Synced 16:06", # same day — byte-identical to pre-phase
|
||||
"Synced 10/2, 3:14 PM", # yesterday — date-qualified
|
||||
"Synced 10/3/2025, 9:12 PM", # last year — date + year
|
||||
"Synced", # garbage — the bare fallback
|
||||
"Synced", # missing ISO — the bare fallback
|
||||
], out
|
||||
assert out["EPOCH"][0] == out["EPOCH"][1], (
|
||||
"settle(null) and the epoch ISO settle identically (the guard"
|
||||
" unchanged)"
|
||||
)
|
||||
assert out["EPOCH"][0].startswith("Synced "), (
|
||||
"the epoch settles with a stamp (new Date(null) is parseable"
|
||||
" — the pre-phase rendered 'Synced 00:00' there)"
|
||||
)
|
||||
|
||||
|
||||
# Phase 154 (task 03) — the qualifier's BYTE-IDENTITY: the pre-phase
|
||||
# fmtSyncTime, pinned VERBATIM from git history (the phase-154
|
||||
# pre-state — the time-only formatter). Only the function NAME is
|
||||
# renamed (so the real + the reference run side by side under node);
|
||||
# the body is the old code byte-for-byte — the old strings (the
|
||||
# `HH:MM` return + the `""` guard) live in this constant, so the
|
||||
# byte-identity below is assertable, not assumed. The reference is
|
||||
# clock-free (it reads no `now` — only the ISO's local time), but
|
||||
# the same fixed-clock driver (the task-01 boundary set's clock) is
|
||||
# used, so the real function's same-day comparison runs on the fixed
|
||||
# now exactly as in the boundary test.
|
||||
PRE_PHASE_FMT_SYNC_TIME_JS = """
|
||||
function prePhaseSyncTime(iso) {
|
||||
const d = new Date(iso);
|
||||
if (Number.isNaN(d.getTime())) return "";
|
||||
const pad = (n) => String(n).padStart(2, "0");
|
||||
return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_sources_js_fmt_sync_time_same_day_is_byte_identical_to_pre_phase() -> None:
|
||||
"""Phase 154 (task 03): the REAL sources.js ``fmtSyncTime`` and
|
||||
the VERBATIM pre-phase formatter (pinned above) run side by side
|
||||
under node with the fixed clock (Oct 3 2026 18:30 local — the
|
||||
task-01 boundary set's clock):
|
||||
|
||||
* the pinned reference's own output is asserted against the
|
||||
verbatim old strings FIRST (a self-check on the constant — a
|
||||
typo in the pinned function fails here, not silently);
|
||||
* EVERY same-day ISO renders BYTE-IDENTICALLY to the pre-phase
|
||||
output (the old ``HH:MM`` — the audit's ``16:06`` case, the
|
||||
midnight / end-of-day edges), and its settle string is
|
||||
byte-identical to the pre-phase settle (``"Synced " + HH:MM``
|
||||
— a same-day badge never moved);
|
||||
* EVERY unparseable/missing ISO (``garbage`` / ``""`` /
|
||||
``undefined``) renders byte-identically to the pre-phase
|
||||
``""`` (the guard unchanged);
|
||||
* the NON-same-day ISOs (yesterday + last year + the
|
||||
``null``/epoch edge — the epoch's local time is a different
|
||||
day AND a different year, so the qualified form applies; its
|
||||
value is TZ-dependent, so it is pinned by SHAPE: the
|
||||
``M/D/YYYY, h:MM AM/PM`` family vs the old ``HH:MM``) are the
|
||||
pinned DIVERGENT set — the qualified form is byte-different
|
||||
from the pre-phase time-only string (the phase's change lives
|
||||
exactly there).
|
||||
|
||||
The wall clock never appears in an assertion."""
|
||||
# Source-level self-check: the pinned constant carries the old
|
||||
# strings verbatim (the `HH:MM` return + the parse guard).
|
||||
assert (
|
||||
"return `${pad(d.getHours())}:${pad(d.getMinutes())}`;"
|
||||
in PRE_PHASE_FMT_SYNC_TIME_JS
|
||||
), "the old HH:MM return is pinned verbatim"
|
||||
assert (
|
||||
'if (Number.isNaN(d.getTime())) return "";' in PRE_PHASE_FMT_SYNC_TIME_JS
|
||||
), "the old parse guard is pinned verbatim"
|
||||
|
||||
fn = _body(_text(SOURCES_JS), "fmtSyncTime")
|
||||
# (js literal, category) — category: same / fallback / diverge.
|
||||
cases = [
|
||||
('"2026-10-03T16:06:00"', "same"), # the audit case
|
||||
('"2026-10-03T00:05:00"', "same"), # midnight edge
|
||||
('"2026-10-03T23:59:00"', "same"), # end-of-day edge
|
||||
('"garbage"', "fallback"),
|
||||
('""', "fallback"), # empty ISO
|
||||
("undefined", "fallback"), # missing ISO (the app-restart case)
|
||||
("null", "diverge"), # the epoch edge — shape-pinned (TZ-dependent)
|
||||
('"2026-10-02T15:14:00"', "diverge"), # yesterday
|
||||
('"2026-10-02T00:05:00"', "diverge"),
|
||||
('"2026-10-02T12:07:00"', "diverge"),
|
||||
('"2026-10-02T23:58:00"', "diverge"),
|
||||
('"2025-10-03T21:12:00"', "diverge"), # last year
|
||||
('"2025-01-03T09:12:00"', "diverge"),
|
||||
]
|
||||
# The verbatim old strings (the pre-phase output — pinned here so
|
||||
# the identity is assertable); the null/epoch value is
|
||||
# TZ-dependent, so its old string is pinned by shape, not value.
|
||||
old = {
|
||||
'"2026-10-03T16:06:00"': "16:06",
|
||||
'"2026-10-03T00:05:00"': "00:05",
|
||||
'"2026-10-03T23:59:00"': "23:59",
|
||||
'"garbage"': "",
|
||||
'""': "",
|
||||
"undefined": "",
|
||||
}
|
||||
driver_expr = (
|
||||
"const CASES = [" + ", ".join(lit for lit, _ in cases) + "];\n"
|
||||
"const PRE = CASES.map((c) => prePhaseSyncTime(c));\n"
|
||||
"const REAL = CASES.map((c) => fmtSyncTime(c));\n"
|
||||
"const settle = (f) => (iso) => {\n"
|
||||
" const t = f(iso);\n"
|
||||
" return t ? `Synced ${t}` : \"Synced\";\n"
|
||||
"};\n"
|
||||
"const PRE_SETTLE = CASES.map((c) => settle(prePhaseSyncTime)(c));\n"
|
||||
"const REAL_SETTLE = CASES.map((c) => settle(fmtSyncTime)(c));\n"
|
||||
"console.log(JSON.stringify({ PRE, REAL, PRE_SETTLE, REAL_SETTLE }));"
|
||||
)
|
||||
out = json.loads(
|
||||
_stamp_driver(fn + "\n" + PRE_PHASE_FMT_SYNC_TIME_JS, driver_expr)
|
||||
)
|
||||
# 1. the pinned reference reproduces the verbatim old strings
|
||||
# (self-check on the constant — assertable, not assumed).
|
||||
for i, (lit, _cat) in enumerate(cases):
|
||||
if lit in old:
|
||||
assert out["PRE"][i] == old[lit], (
|
||||
f"the pre-phase reference for {lit} must be the verbatim old"
|
||||
f" string {old[lit]!r} — got {out['PRE'][i]!r} (a typo in the"
|
||||
" pinned constant?"
|
||||
)
|
||||
null_i = [lit for lit, _ in cases].index("null")
|
||||
assert re.fullmatch(r"\d{2}:\d{2}", out["PRE"][null_i]), (
|
||||
"the null/epoch edge takes the old HH:MM shape on the reference"
|
||||
" side (the pre-phase is time-only — the value is TZ-dependent,"
|
||||
" the shape is not)"
|
||||
)
|
||||
assert out["PRE_SETTLE"][null_i] == "Synced " + out["PRE"][null_i] # old settle
|
||||
assert re.fullmatch(
|
||||
r"\d{1,2}/\d{1,2}/\d{4}, \d{1,2}:\d{2} (AM|PM)", out["REAL"][null_i]
|
||||
), (
|
||||
"the null/epoch edge takes the M/D/YYYY, h:MM AM/PM shape on the"
|
||||
" real side (the epoch is a different day + year — the qualified"
|
||||
" form; the value is TZ-dependent, the shape is not)"
|
||||
)
|
||||
assert out["REAL_SETTLE"][null_i].startswith("Synced ")
|
||||
# 2. the audit case's settle is byte-identical to pre-phase.
|
||||
audit_i = [lit for lit, _ in cases].index('"2026-10-03T16:06:00"')
|
||||
assert out["REAL_SETTLE"][audit_i] == "Synced 16:06", (
|
||||
"the same-day settle is byte-identical to the pre-phase string"
|
||||
)
|
||||
# 3. same-day + fallbacks: byte-identical to the reference, settle
|
||||
# included; the divergent set: byte-different (the qualified form).
|
||||
for i, (lit, cat) in enumerate(cases):
|
||||
if cat == "diverge":
|
||||
assert out["REAL"][i] != out["PRE"][i], (
|
||||
f"{lit} must DIFFER from the pre-phase form (the qualified"
|
||||
f" branch) — got {out['REAL'][i]!r} == {out['PRE'][i]!r}"
|
||||
)
|
||||
assert out["REAL_SETTLE"][i] != out["PRE_SETTLE"][i]
|
||||
else:
|
||||
assert out["REAL"][i] == out["PRE"][i], (
|
||||
f"{lit} must be byte-identical to the pre-phase output"
|
||||
f" (category {cat}) — got {out['REAL'][i]!r} vs"
|
||||
f" {out['PRE'][i]!r}"
|
||||
)
|
||||
assert out["REAL_SETTLE"][i] == out["PRE_SETTLE"][i]
|
||||
|
||||
|
||||
def test_sources_js_failed_error_is_sanitized() -> None:
|
||||
|
||||
Reference in New Issue
Block a user