STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_checkout Namespace Reference

Functions

 _isolate (tmp_path, monkeypatch)
 client ()
 _create (client, headers=A, name="Feed system")
 _free (client, doc_id, headers=A, params=None)
 _share (client, doc_id, emails)
 _save (client, doc_id, headers, params=None, config=None)
 _race_take (barrier, results, root, doc_id, email, base, owner_param)
 test_only_one_of_many_simultaneous_takes_wins (client, tmp_path, attempt)
 test_holder_may_save_and_others_may_not (client)
 test_saving_without_taking_it_is_refused (client)
 test_every_content_write_needs_the_checkout (client, path, method, body)
 test_housekeeping_is_not_blocked_by_someone_elses_checkout (client, name, method, path, body)
 test_copying_is_not_blocked_by_a_checkout (client)
 test_a_checkout_lapses_and_the_next_person_can_take_it (client, monkeypatch)
 test_the_previous_holder_is_refused_rather_than_silently_overwriting (client, monkeypatch)
 test_saving_keeps_the_checkout_alive (client)
 test_release_is_idempotent_and_holder_only (client)
 test_the_list_reports_the_checkout_without_an_extra_call (client)
 test_designs_predating_checkouts_read_as_free (client, tmp_path)
 _take_in_child (results, root, doc_id, base, owner_param, email)
 test_take_cannot_proceed_while_the_index_lock_is_held (client, tmp_path)
 test_creating_a_design_checks_it_out_to_you (client)
 test_copying_a_design_checks_the_copy_out_to_you (client)
 test_creating_does_not_touch_anyone_elses_checkout (client)
 test_every_endpoint_that_returns_a_record_returns_a_decorated_one (client)
 test_a_returned_record_says_the_design_is_yours (client)
 test_lock_expires_at_is_in_the_future (client)
 test_beat_refreshes_the_hold_without_writing (client)
 test_beat_refuses_when_you_do_not_hold_it (client)
 test_beat_alone_survives_the_ttl_but_idling_does_not (client, monkeypatch)
 test_a_lapsed_hold_nobody_took_is_reclaimed_by_its_holder (client, monkeypatch)
 test_a_lapsed_hold_someone_else_took_is_not_reclaimed (client, monkeypatch)

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"}

Detailed Description

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).

Function Documentation

◆ _create()

◆ _free()

test_checkout._free ( client,
doc_id,
headers = A,
params = None )
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().

◆ _isolate()

test_checkout._isolate ( tmp_path,
monkeypatch )
protected

Definition at line 50 of file test_checkout.py.

◆ _race_take()

test_checkout._race_take ( barrier,
results,
root,
doc_id,
email,
base,
owner_param )
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.

◆ _save()

◆ _share()

◆ _take_in_child()

test_checkout._take_in_child ( results,
root,
doc_id,
base,
owner_param,
email )
protected
A single take, in its own process, reporting its status code.

Definition at line 295 of file test_checkout.py.

◆ client()

test_checkout.client ( )

Definition at line 57 of file test_checkout.py.

◆ test_a_checkout_lapses_and_the_next_person_can_take_it()

test_checkout.test_a_checkout_lapses_and_the_next_person_can_take_it ( client,
monkeypatch )

Definition at line 222 of file test_checkout.py.

References _create(), and _share().

◆ test_a_lapsed_hold_nobody_took_is_reclaimed_by_its_holder()

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.

References _create(), _save(), and _share().

◆ test_a_lapsed_hold_someone_else_took_is_not_reclaimed()

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.

References _create(), _save(), and _share().

◆ test_a_returned_record_says_the_design_is_yours()

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_beat_alone_survives_the_ttl_but_idling_does_not()

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.

References _create(), and _share().

◆ test_beat_refreshes_the_hold_without_writing()

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.

References _create(), and _save().

◆ test_beat_refuses_when_you_do_not_hold_it()

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.

References _create(), and _share().

◆ test_copying_a_design_checks_the_copy_out_to_you()

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.

References _create(), and _save().

◆ test_copying_is_not_blocked_by_a_checkout()

test_checkout.test_copying_is_not_blocked_by_a_checkout ( client)

Definition at line 212 of file test_checkout.py.

References _create().

◆ test_creating_a_design_checks_it_out_to_you()

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.

References _create(), and _save().

◆ test_creating_does_not_touch_anyone_elses_checkout()

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.

References _create(), and _share().

◆ test_designs_predating_checkouts_read_as_free()

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_every_content_write_needs_the_checkout()

test_checkout.test_every_content_write_needs_the_checkout ( client,
path,
method,
body )

Definition at line 189 of file test_checkout.py.

References _create(), and _share().

◆ test_every_endpoint_that_returns_a_record_returns_a_decorated_one()

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_holder_may_save_and_others_may_not()

test_checkout.test_holder_may_save_and_others_may_not ( client)

Definition at line 159 of file test_checkout.py.

References _create(), _save(), and _share().

◆ test_housekeeping_is_not_blocked_by_someone_elses_checkout()

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.

References _create(), and _share().

◆ test_lock_expires_at_is_in_the_future()

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_only_one_of_many_simultaneous_takes_wins()

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.

References _create(), _free(), and _share().

◆ test_release_is_idempotent_and_holder_only()

test_checkout.test_release_is_idempotent_and_holder_only ( client)

Definition at line 260 of file test_checkout.py.

References _create(), and _share().

◆ test_saving_keeps_the_checkout_alive()

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.

References _create(), and _save().

◆ test_saving_without_taking_it_is_refused()

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.

References _create(), _free(), and _save().

◆ test_take_cannot_proceed_while_the_index_lock_is_held()

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.

References _create(), _free(), and _share().

◆ test_the_list_reports_the_checkout_without_an_extra_call()

test_checkout.test_the_list_reports_the_checkout_without_an_extra_call ( client)

Definition at line 274 of file test_checkout.py.

References _create(), and _share().

◆ test_the_previous_holder_is_refused_rather_than_silently_overwriting()

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.

References _create(), _save(), and _share().

Variable Documentation

◆ A

dict test_checkout.A = {"X-Auth-Email": "alice@berkeley.edu"}

Definition at line 41 of file test_checkout.py.

◆ app

test_checkout.app = FastAPI()

Definition at line 38 of file test_checkout.py.

◆ APP_SEGMENT

str test_checkout.APP_SEGMENT = "engine"

Definition at line 46 of file test_checkout.py.

◆ B

dict test_checkout.B = {"X-Auth-Email": "bob@berkeley.edu"}

Definition at line 42 of file test_checkout.py.

◆ BASE

str test_checkout.BASE = "/api/engine/documents"

Definition at line 44 of file test_checkout.py.

◆ DECORATED

dict test_checkout.DECORATED = {"owner", "ownerName", "mine", "sharedWith", "lockedByMe"}

Definition at line 393 of file test_checkout.py.

◆ OWNER_A

dict test_checkout.OWNER_A = {"owner": A["X-Auth-Email"]}

Definition at line 43 of file test_checkout.py.

◆ reason

test_checkout.reason

Definition at line 28 of file test_checkout.py.