1"""The Layer X routes beyond the happy path (backend/routers/layerx.py).
3The router had one test, a burn that worked. These are the paths that decide whether a person
4loses work or reads a wrong state: the one-job rule, what pruning keeps, pins, names and notes,
5deleting, the listing's index, the study's refusals and the measurement bounds. They run without
6a burn except where a preflight is the thing under test.
9from __future__
import annotations
17sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
19from fastapi
import FastAPI
20from fastapi.testclient
import TestClient
27def api(tmp_path, monkeypatch):
28 monkeypatch.setenv(
"USERDATA_DIR", str(tmp_path))
31 app.include_router(lx.router)
32 app.dependency_overrides[get_session] =
lambda: session
33 yield TestClient(app), session
35 for job_id
in [j.id
for j
in lx._JOBS.values()
if j.user == session.user]:
36 lx._JOBS.pop(job_id,
None)
39def _saved(session, run_id: str, kind: str =
"run", impulse: float = 24000.0) -> lx.Job:
40 job = lx.Job(session.user, lx.Settings(drawing_id=
"d"),
"design", kind=kind)
42 job.status, job.finished =
"done", 1.0
43 job.result = {
"summary": {
"burn_time_s": 3.5,
"total_impulse_Ns": impulse},
"provenance": {
"drawing": {
"name":
"stand"}}}
44 lx._persist(lx._runs_dir(session), job)
50 busy = lx.Job(session.user, lx.Settings(drawing_id=
"d"),
"design")
51 busy.status =
"running"
53 lx._JOBS[busy.id] = busy
54 r = client.post(
"/api/layerx/runs", json={
"drawing_id":
"anything"})
55 assert r.status_code == 409
and busy.id
in r.json()[
"detail"]
60 _saved(session,
"20261002-000001-aaaaaa", impulse=24100.0)
61 runs_dir = lx._runs_dir(session)
62 assert (runs_dir /
"_index" /
"20261002-000001-aaaaaa.json").is_file()
64 (runs_dir /
"20261002-000001-aaaaaa.json").write_text(json.dumps({
"id":
"20261002-000001-aaaaaa",
"result":
"x" * 10}))
65 listed = client.get(
"/api/layerx/runs").
json()
66 assert listed[0][
"summary"][
"total_impulse_Ns"] == 24100.0
67 meta = client.patch(
"/api/layerx/runs/20261002-000001-aaaaaa", json={
"name":
" 600 psia, GN2 ",
"pinned":
True}).
json()
68 assert meta == {
"name":
"600 psia, GN2",
"pinned":
True}
69 listed = client.get(
"/api/layerx/runs").
json()
70 assert listed[0][
"meta"][
"pinned"]
is True
71 assert client.patch(
"/api/layerx/runs/20261002-000009-ffffff", json={
"pinned":
True}).status_code == 404
72 assert client.patch(
"/api/layerx/runs/../../etc", json={
"pinned":
True}).status_code == 404
77 monkeypatch.setattr(lx,
"KEEP_RUNS", 2)
78 _saved(session,
"20261002-000001-aaaaaa")
79 client.patch(
"/api/layerx/runs/20261002-000001-aaaaaa", json={
"pinned":
True})
80 _saved(session,
"20261002-000002-bbbbbb")
81 _saved(session,
"20261002-000003-cccccc", kind=
"trade")
82 _saved(session,
"20261002-000004-dddddd")
83 _saved(session,
"20261002-000005-eeeeee")
84 ids = {r[
"id"]
for r
in client.get(
"/api/layerx/runs").
json()}
85 assert ids == {
"20261002-000001-aaaaaa",
"20261002-000003-cccccc",
"20261002-000004-dddddd",
"20261002-000005-eeeeee"}
86 assert not (lx._runs_dir(session) /
"_index" /
"20261002-000002-bbbbbb.json").exists()
91 _saved(session,
"20261002-000001-aaaaaa")
92 client.patch(
"/api/layerx/runs/20261002-000001-aaaaaa", json={
"note":
"first fire prediction"})
93 assert client.delete(
"/api/layerx/runs/20261002-000001-aaaaaa").
json()[
"deleted"]
94 runs_dir = lx._runs_dir(session)
95 assert not any(runs_dir.rglob(
"20261002-000001-aaaaaa*"))
96 assert client.get(
"/api/layerx/runs").
json() == []
97 live = lx.Job(session.user, lx.Settings(drawing_id=
"d"),
"design")
98 live.status =
"running"
100 lx._JOBS[live.id] = live
101 assert client.delete(f
"/api/layerx/runs/{live.id}").status_code == 409
107 base = {
"target":
"node:OXT",
"parameter":
"volume",
"value": 15.1,
"unit":
"L",
"source":
"weighed"}
108 assert Override.from_dict(base).value == 15.1
109 with pytest.raises(ValueError, match=
"negative"):
110 Override.from_dict({**base,
"value": -1.0})
111 assert Override.from_dict({**base,
"parameter":
"elevation_change",
"value": -0.3}).value == -0.3
112 with pytest.raises(ValueError, match=
"longer than"):
113 Override.from_dict({**base,
"source":
"x" * 400})
117 """The list swapped in the replay's delivered impulse but kept the twin's mean thrust, so impulse
118 over burn time was not the mean it printed (LE4 (3): 23737.9 N s delivered beside 6183.8 N)."""
119 result = {
"summary": {
"total_impulse_Ns": 23729.9,
"mean_thrust_N": 6183.8,
"burn_time_s": 3.8364},
120 "delivered": {
"summary": {
"total_impulse_Ns": 23737.9,
"mean_thrust_N": 6187.4}}}
121 s = lx._listing_summary(result)
122 assert (s[
"total_impulse_Ns"], s[
"mean_thrust_N"]) == (23737.9, 6187.4)
123 assert lx._listing_summary({
"summary": {
"total_impulse_Ns": 1.0,
"mean_thrust_N": 2.0}})[
"mean_thrust_N"] == 2.0
127 """POST /engine-card with center_psia -5 said "the config states no tank pressure" though it stated 578."""
130 with pytest.raises(ValueError, match=
r"center_psia -5\.0 is not an absolute pressure"):
131 card_for_config_text(
"lox_tank: {initial_pressure_psi: 578.0}\n", center_psia=-5.0)
135 """Every Layer X run was recorded as design 'default' whatever design the bar had open: the design
136 bar loads a design with POST /config/load, which keeps the session's first config_path, and the
137 router filed jobs under that file's stem. sweepFor's 'never borrow another design's sweep' was then
138 defeated. A job started with the bar's X-Design-Id is filed under that design's name."""
139 from fastapi
import HTTPException
143 client, session = api
145 records = {(me,
"d1"): {
"id":
"d1",
"name":
"ethalox 6800N doublet 578psia"},
146 (
"carol",
"d2"): {
"id":
"d2",
"name":
"Carol's private design"},
147 (
"dave",
"d3"): {
"id":
"d3",
"name":
"Dave's, shared",
"sharedWith": [me]}}
148 monkeypatch.setattr(documents.store,
"find_record",
lambda owner, doc_id: records.get((owner, doc_id)))
149 monkeypatch.setattr(documents.store,
"read_featured",
lambda:
None)
152 def reserve(session_, settings, kind, design=None):
154 raise HTTPException(status_code=409, detail=
"captured")
156 monkeypatch.setattr(lx,
"_reserve", reserve)
157 body = {
"drawing_id":
"d"}
158 assert client.post(
"/api/layerx/runs", json=body, headers={
"X-Design-Id":
"d1"}).status_code == 409
159 assert client.post(
"/api/layerx/uncertainty", json=body, headers={
"X-Design-Id":
"d1"}).status_code == 409
160 assert filed == [
"ethalox 6800N doublet 578psia"] * 2
162 client.post(
"/api/layerx/runs", json=body, headers={
"X-Design-Id":
"d2",
"X-Design-Owner":
"carol"})
163 client.post(
"/api/layerx/runs", json=body, headers={
"X-Design-Id":
"d3",
"X-Design-Owner":
"dave"})
164 assert filed[2:] == [lx._design_name(session),
"Dave's, shared"]
166 client.post(
"/api/layerx/runs", json=body)
167 assert filed[4] == lx._design_name(session)
171 client, session = api
172 job = lx.Job(session.user, lx.Settings(drawing_id=
"d"),
"default", design_sha256=
"abc123")
173 assert job.view()[
"design"] ==
"default" and job.view()[
"design_sha256"] ==
"abc123"
174 assert job.view(with_result=
False)[
"design_sha256"] ==
"abc123"
178 """A server started at 9ebd9fa0 and never restarted filed its second burn as '170af951-dirty' once
179 other commits landed: git describe was asked per run. The code a process runs is what it imported."""
182 loaded = lx._code_version()
185 stdout =
"170af951-dirty\n"
187 monkeypatch.setattr(subprocess,
"run",
lambda *a, **k: Moved())
188 assert lx._code_version() == loaded
189 assert lx._design_snapshot(type(
"C", (), {
"model_dump":
lambda self, mode=
None: {}})())[
"code"] == loaded
a class to store JSON values
test_one_job_at_a_time_is_a_409_before_any_work(api)
test_a_job_is_filed_under_the_design_that_is_open(api, monkeypatch)
api(tmp_path, monkeypatch)
lx.Job _saved(session, str run_id, str kind="run", float impulse=24000.0)
test_delete_removes_the_run_its_entry_and_its_notes_but_not_a_live_job(api)
test_a_card_centre_that_is_not_a_pressure_names_the_callers_number()
test_pruning_keeps_the_newest_per_kind_and_every_pinned_run(api, monkeypatch)
test_a_run_names_the_code_the_server_loaded_not_the_checkout_now(monkeypatch)
test_the_listing_reads_the_index_and_carries_names_and_pins(api)
test_the_listing_quotes_one_burn_the_delivered_one()
test_a_job_records_the_design_fingerprint_beside_its_name(api)
test_a_restated_measurement_is_bounded()