CI / lint (push) Successful in 4s
Build images / sign-extension (push) Successful in 4s
CI / extension-version (push) Successful in 4s
Build images / build-agent (push) Successful in 4s
CI / frontend-build (push) Successful in 21s
extension / lint (push) Successful in 25s
CI / backend-lint-and-test (push) Successful in 32s
Build images / build-ml (push) Successful in 2m50s
Build images / build-web (push) Successful in 2m49s
CI / integration (push) Successful in 3m52s
A dim line at the foot of Settings: `FabledCurator 2026.08.28.1249 · dev`. This is no longer a convenience. Milestone 318 stopped publishing version image tags, so an instance's own report is the ONLY answer to "which build is this?" — there is no registry name left to check it against. Note #3127 §5 says it directly: a wrong answer here has no second source to contradict it. Three states, kept distinct because collapsing any two of them lies: not asked yet render nothing asked, no version render "unknown" asked, has a version render it A blank footer reads as "no version", which is a different claim from "I cannot say". And a failed health call deliberately does NOT mark the build loaded — a network blip says nothing about the image, and presenting it as "unknown" would look like a defective build. Carried on /api/health rather than a new route: it answers at the same cost (two module constants, no I/O) and TopNav already fetches it app-wide, so a separate endpoint would mean a second request for two strings. Both fields are OMITTED when unset rather than sent empty. Absence already means "cannot say" — an image predating the field says exactly that by not having the key — so a second spelling would make every reader special-case it. The pre-existing test asserting the body is EXACTLY {"status": "ok"} is what keeps a well-meaning `or ""` default from creeping in. FC_CHANNEL now has one definition. It was read from the environment in extension.py and would have been read again here; the new build_info module holds both, and extension.py binds it as a module-level name so existing tests monkeypatch it exactly as before. Separate from config.py on purpose: those are operator settings meant to be changed, these describe the artifact. Channel sits beside the version, never inside it (rule 149), asserted from both ends. A `-dev` suffix would read as a 0 segment to the extension's parseInt comparator and make every dev build compare equal — #2993 exactly. Not hidden, per the operator and §7: the JS bundle and asset hashes fingerprint the build anyway, and "I'm on 2026.08.28.1249" is the single most useful line in a bug report.
72 lines
2.5 KiB
Python
72 lines
2.5 KiB
Python
"""Health endpoint smoke test."""
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_returns_ok(client):
|
|
response = await client.get("/api/health")
|
|
assert response.status_code == 200
|
|
body = await response.get_json()
|
|
assert body == {"status": "ok"}
|
|
|
|
|
|
# --- build identity (milestone 318 step 6) --------------------------------
|
|
#
|
|
# With no version image tags left, /api/health is the only place an instance
|
|
# says which build it is. Both fields are OMITTED when unset rather than sent
|
|
# empty: absence already means "cannot say" — an image predating the field
|
|
# says exactly that by not having the key — and a second spelling would make
|
|
# every reader special-case it (note #3127 §7).
|
|
#
|
|
# The test above is load-bearing for that: it asserts the body is EXACTLY
|
|
# {"status": "ok"} when nothing is stamped, so a well-meaning `or ""` default
|
|
# fails it.
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_reports_the_build_it_is(client, monkeypatch):
|
|
from backend.app.api import health
|
|
|
|
monkeypatch.setattr(health, "FC_VERSION", "2026.08.28.1249")
|
|
monkeypatch.setattr(health, "FC_CHANNEL", "dev")
|
|
|
|
body = await (await client.get("/api/health")).get_json()
|
|
assert body == {
|
|
"status": "ok",
|
|
"version": "2026.08.28.1249",
|
|
"channel": "dev",
|
|
}
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_keeps_the_channel_out_of_the_version(client, monkeypatch):
|
|
"""Rule 149, asserted rather than assumed.
|
|
|
|
The tempting shortcut is a `-dev` suffix on the version. The extension's
|
|
comparator parses each dotted segment with `parseInt`, so a suffixed
|
|
segment reads as 0 and every dev build compares equal to every other —
|
|
#2993 exactly. Two separate keys cannot express that mistake.
|
|
"""
|
|
from backend.app.api import health
|
|
|
|
monkeypatch.setattr(health, "FC_VERSION", "2026.08.28.1249")
|
|
monkeypatch.setattr(health, "FC_CHANNEL", "dev")
|
|
|
|
body = await (await client.get("/api/health")).get_json()
|
|
assert body["version"] == "2026.08.28.1249"
|
|
assert "dev" not in body["version"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_health_omits_a_channel_it_cannot_name(client, monkeypatch):
|
|
"""A locally-built image has a version but no channel. It must not gain an
|
|
empty one — the key's absence is the answer."""
|
|
from backend.app.api import health
|
|
|
|
monkeypatch.setattr(health, "FC_VERSION", "2026.08.28.1249")
|
|
monkeypatch.setattr(health, "FC_CHANNEL", "")
|
|
|
|
body = await (await client.get("/api/health")).get_json()
|
|
assert body == {"status": "ok", "version": "2026.08.28.1249"}
|