|
STAR
Cal STAR — firmware, DAQ server, and engine design
|
Variables | |
| reason | |
| app = FastAPI() | |
| dict | A = {"X-Auth-Email": "alice@berkeley.edu"} |
| dict | B = {"X-Auth-Email": "bob@berkeley.edu"} |
| dict | OWNER_A = {"owner": A["X-Auth-Email"]} |
| str | BASE = "/api/engine/documents" |
| str | APP_SEGMENT = "engine" |
| dict | DECORATED = {"owner", "ownerName", "mine", "sharedWith", "lockedByMe"} |
Design checkouts: at most one holder, ever. Concurrent editing is not resolved here, it is prevented. The property that matters is the one a single-threaded test cannot show: two people pressing Take at the same instant must not both succeed. That is what `test_only_one_of_many_simultaneous_takes_wins` exists for -- it races real processes through the real `flock`, because a mocked lock proves nothing about a lock. Everything else follows from that: only the holder may save, a checkout lapses if nobody saves for `lock_ttl`, and none of it touches the operations that are not concurrent editing (rename, share, copy).
|
protected |
Definition at line 61 of file test_checkout.py.
Referenced by test_a_checkout_lapses_and_the_next_person_can_take_it(), test_a_lapsed_hold_nobody_took_is_reclaimed_by_its_holder(), test_a_lapsed_hold_someone_else_took_is_not_reclaimed(), test_a_returned_record_says_the_design_is_yours(), test_beat_alone_survives_the_ttl_but_idling_does_not(), test_beat_refreshes_the_hold_without_writing(), test_beat_refuses_when_you_do_not_hold_it(), test_copying_a_design_checks_the_copy_out_to_you(), test_copying_is_not_blocked_by_a_checkout(), test_creating_a_design_checks_it_out_to_you(), test_creating_does_not_touch_anyone_elses_checkout(), test_every_content_write_needs_the_checkout(), test_every_endpoint_that_returns_a_record_returns_a_decorated_one(), test_holder_may_save_and_others_may_not(), test_housekeeping_is_not_blocked_by_someone_elses_checkout(), test_lock_expires_at_is_in_the_future(), test_only_one_of_many_simultaneous_takes_wins(), test_release_is_idempotent_and_holder_only(), test_saving_keeps_the_checkout_alive(), test_saving_without_taking_it_is_refused(), test_take_cannot_proceed_while_the_index_lock_is_held(), test_the_list_reports_the_checkout_without_an_extra_call(), and test_the_previous_holder_is_refused_rather_than_silently_overwriting().
|
protected |
Release a freshly created design, since creating one now checks it out. Several tests below need a design nobody holds; this is what makes that explicit rather than an assumption about what `_create` leaves behind.
Definition at line 67 of file test_checkout.py.
Referenced by test_only_one_of_many_simultaneous_takes_wins(), test_saving_without_taking_it_is_refused(), and test_take_cannot_proceed_while_the_index_lock_is_held().
|
protected |
Definition at line 50 of file test_checkout.py.
|
protected |
One contender, in its own process. Threads would not prove anything: `flock` is held per open file description, so the contention that matters is between processes, exactly as it is between the API's workers. The barrier is the point. Without it the pool tends to run these one after another -- each finishes before the next starts, nobody actually races, and the test passes even with the locking removed. Every process parks here until all of them are ready, so they go for the design together.
Definition at line 92 of file test_checkout.py.
|
protected |
Definition at line 83 of file test_checkout.py.
Referenced by test_a_lapsed_hold_nobody_took_is_reclaimed_by_its_holder(), test_a_lapsed_hold_someone_else_took_is_not_reclaimed(), test_beat_refreshes_the_hold_without_writing(), test_copying_a_design_checks_the_copy_out_to_you(), test_creating_a_design_checks_it_out_to_you(), test_holder_may_save_and_others_may_not(), test_saving_keeps_the_checkout_alive(), test_saving_without_taking_it_is_refused(), and test_the_previous_holder_is_refused_rather_than_silently_overwriting().
|
protected |
Definition at line 78 of file test_checkout.py.
Referenced by test_a_checkout_lapses_and_the_next_person_can_take_it(), test_a_lapsed_hold_nobody_took_is_reclaimed_by_its_holder(), test_a_lapsed_hold_someone_else_took_is_not_reclaimed(), test_beat_alone_survives_the_ttl_but_idling_does_not(), test_beat_refuses_when_you_do_not_hold_it(), test_creating_does_not_touch_anyone_elses_checkout(), test_every_content_write_needs_the_checkout(), test_holder_may_save_and_others_may_not(), test_housekeeping_is_not_blocked_by_someone_elses_checkout(), test_only_one_of_many_simultaneous_takes_wins(), test_release_is_idempotent_and_holder_only(), test_take_cannot_proceed_while_the_index_lock_is_held(), test_the_list_reports_the_checkout_without_an_extra_call(), and test_the_previous_holder_is_refused_rather_than_silently_overwriting().
|
protected |
A single take, in its own process, reporting its status code.
Definition at line 295 of file test_checkout.py.
| test_checkout.client | ( | ) |
Definition at line 57 of file test_checkout.py.
| test_checkout.test_a_checkout_lapses_and_the_next_person_can_take_it | ( | client, | |
| monkeypatch ) |
Definition at line 222 of file test_checkout.py.
| test_checkout.test_a_lapsed_hold_nobody_took_is_reclaimed_by_its_holder | ( | client, | |
| monkeypatch ) |
A lapse with nobody waiting is not a conflict, and must not cost the edit. Every content write needs the checkout, so if nobody has taken the design since it lapsed on you, nobody can have changed it: your next beat or save takes the hold back instead of being refused (see ``DesignStore.claim_for_write``). Refusing it was how edits were lost -- step away past ``lock_ttl``, come back, keep editing, and every save came back 423 with nobody else anywhere near the design.
Definition at line 506 of file test_checkout.py.
| test_checkout.test_a_lapsed_hold_someone_else_took_is_not_reclaimed | ( | client, | |
| monkeypatch ) |
Lapsing still frees the design for whoever acts first. Once B has taken it, A's beat and save are refused -- the two-holders case checkouts exist to prevent.
Definition at line 525 of file test_checkout.py.
| test_checkout.test_a_returned_record_says_the_design_is_yours | ( | client | ) |
The specific failure: `mine` absent reads as false, and the bar then shows your own design as belonging to an undefined owner.
Definition at line 417 of file test_checkout.py.
References _create().
| test_checkout.test_beat_alone_survives_the_ttl_but_idling_does_not | ( | client, | |
| monkeypatch ) |
The end-to-end claim, both directions. Someone interacting without saving must keep the design; someone who has genuinely walked away must lose it, because release is holder-only and the on-close beacon is best-effort, so lapsing is the only thing that recovers a checkout after a crash or a power cut.
Definition at line 481 of file test_checkout.py.
| test_checkout.test_beat_refreshes_the_hold_without_writing | ( | client | ) |
The fix for "it kicked me out while I was working". Before this endpoint the only thing that refreshed a hold was a successful autosave, so panning, measuring or thinking counted as idle. The beat must push the expiry out and must NOT touch the stored payload.
Definition at line 446 of file test_checkout.py.
| test_checkout.test_beat_refuses_when_you_do_not_hold_it | ( | client | ) |
423 is the client's cue to stop beating and show the lost dialog. Shared with B on purpose: an unshared user is refused at 403 long before the lock is consulted, so it would prove nothing about the beat.
Definition at line 469 of file test_checkout.py.
| test_checkout.test_copying_a_design_checks_the_copy_out_to_you | ( | client | ) |
Same reasoning: you take a copy in order to work on it.
Definition at line 368 of file test_checkout.py.
| test_checkout.test_copying_is_not_blocked_by_a_checkout | ( | client | ) |
Definition at line 212 of file test_checkout.py.
References _create().
| test_checkout.test_creating_a_design_checks_it_out_to_you | ( | client | ) |
You made it a moment ago; nobody else can possibly hold it. Without this you create a design, start editing, and every save is refused until you notice a button you had no reason to look at -- which reads as "the thing I just made keeps losing my work".
Definition at line 354 of file test_checkout.py.
| test_checkout.test_creating_does_not_touch_anyone_elses_checkout | ( | client | ) |
A new design carries its own checkout; it must not disturb a design someone else is holding.
Definition at line 378 of file test_checkout.py.
| test_checkout.test_designs_predating_checkouts_read_as_free | ( | client, | |
| tmp_path ) |
Every existing design has no lock fields at all.
Definition at line 286 of file test_checkout.py.
| test_checkout.test_every_content_write_needs_the_checkout | ( | client, | |
| path, | |||
| method, | |||
| body ) |
Definition at line 189 of file test_checkout.py.
| test_checkout.test_every_endpoint_that_returns_a_record_returns_a_decorated_one | ( | client | ) |
Definition at line 396 of file test_checkout.py.
References _create().
| test_checkout.test_holder_may_save_and_others_may_not | ( | client | ) |
Definition at line 159 of file test_checkout.py.
| test_checkout.test_housekeeping_is_not_blocked_by_someone_elses_checkout | ( | client, | |
| name, | |||
| method, | |||
| path, | |||
| body ) |
A checkout guards the design's *content*. Renaming or re-sharing is not concurrent editing, and blocking it would let a stale checkout freeze a design nobody can tidy.
Definition at line 201 of file test_checkout.py.
| test_checkout.test_lock_expires_at_is_in_the_future | ( | client | ) |
`lockExpiresAt` is when the hold lapses, not when it was last beaten. It used to carry the raw heartbeat -- a time already in the past -- so a bar counting down to it read as expired the moment it rendered, which is why nothing ever showed the user how long they had. Fails against that.
Definition at line 430 of file test_checkout.py.
References _create().
| test_checkout.test_only_one_of_many_simultaneous_takes_wins | ( | client, | |
| tmp_path, | |||
| attempt ) |
The whole feature in one assertion, repeated -- a race that passes once has proved nothing.
Definition at line 124 of file test_checkout.py.
| test_checkout.test_release_is_idempotent_and_holder_only | ( | client | ) |
Definition at line 260 of file test_checkout.py.
| test_checkout.test_saving_keeps_the_checkout_alive | ( | client | ) |
`lock_ttl` measures time since the last *save*, so someone actively editing never loses the design out from under themselves.
Definition at line 247 of file test_checkout.py.
| test_checkout.test_saving_without_taking_it_is_refused | ( | client | ) |
Opening a design does not take it, so a save without the checkout must fail rather than quietly claiming the design. Creating one *does* check it out, so this releases first to get at the state a second person opening the design would be in.
Definition at line 170 of file test_checkout.py.
| test_checkout.test_take_cannot_proceed_while_the_index_lock_is_held | ( | client, | |
| tmp_path ) |
Deterministic proof that the compare-and-set is genuinely serialised. The race above is probabilistic: the critical section is sub-millisecond, so contenders often fail to interleave and it can pass even with the locking removed. This one cannot. It grabs the very same `flock` the take path uses, then asserts a take *blocks* until it is released -- which is exactly the property that makes two simultaneous holders impossible.
Definition at line 312 of file test_checkout.py.
| test_checkout.test_the_list_reports_the_checkout_without_an_extra_call | ( | client | ) |
Definition at line 274 of file test_checkout.py.
| test_checkout.test_the_previous_holder_is_refused_rather_than_silently_overwriting | ( | client, | |
| monkeypatch ) |
The dangerous case: A's checkout lapses, B takes it, and A's tab is still autosaving. A must be refused, not allowed to overwrite B.
Definition at line 232 of file test_checkout.py.
| dict test_checkout.A = {"X-Auth-Email": "alice@berkeley.edu"} |
Definition at line 41 of file test_checkout.py.
| test_checkout.app = FastAPI() |
Definition at line 38 of file test_checkout.py.
| str test_checkout.APP_SEGMENT = "engine" |
Definition at line 46 of file test_checkout.py.
| dict test_checkout.B = {"X-Auth-Email": "bob@berkeley.edu"} |
Definition at line 42 of file test_checkout.py.
| str test_checkout.BASE = "/api/engine/documents" |
Definition at line 44 of file test_checkout.py.
| dict test_checkout.DECORATED = {"owner", "ownerName", "mine", "sharedWith", "lockedByMe"} |
Definition at line 393 of file test_checkout.py.
| dict test_checkout.OWNER_A = {"owner": A["X-Auth-Email"]} |
Definition at line 43 of file test_checkout.py.
| test_checkout.reason |
Definition at line 28 of file test_checkout.py.