Skip to content

refactor(users): obtain resource tokens via User#resource_token from models - #452

Merged
stakach merged 6 commits into
masterfrom
refactor/user-resource-token-from-models
Oct 8, 2026
Merged

stakach merged 6 commits into
masterfrom
refactor/user-resource-token-from-models

Conversation

@stakach

@stakach stakach commented Oct 8, 2026 •

Copy link
Copy Markdown
Member

Summary

Dependencies

Uses User#resource_token from placeos-models 9.119.0 (PlaceOS/models#333).

Spec stabilisation

These CI flakes were unrelated to the change but fixed here:

  • core overwrote hand-written Redis lookups (system/<id>). It rebuilds each system's module lookup asynchronously after every ControlSystem change, wiping entries the specs had written themselves. This broke public_events register (404), chat_gpt_plugin MCP, websocket bind (missing updates) and systems functions/state. Each spec now saves the system once with its final modules and waits for core's mapping (wait_for_module_lookups).
  • ::Spec.before_each inside describe blocks registered globally. It cleared tables before every example in the suite. The hooks are now scoped to their describe. Group tables are still cleared globally in spec/helper.cr, because group grants attach to the shared spec users.
  • Two more fixes:
    • The websocket debug/ignore specs sent a Faker module name (which can contain spaces) instead of the module id.
    • The uploads storages could collide on random bucket names.
  • Verified: full suite with CI seeds 41906, 37528 and 46552, plus seeds 43126 and 8862: 1177 examples, 0 failures, 0 errors each.

Test plan

  • ./test spec/controllers/users_spec.cr: 45 examples, 0 failures. New examples cover returning a stored token and the 404 when no token is available.

🤖 Generated with Claude Code

…m models

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stakach and others added 2 commits October 8, 2026 14:07
The specs run next to a real core container, and core owns each system's
`system/<id>` module lookup hash in redis. On every ControlSystem change
event it clears that hash, rebuilds it from the modules' resolved names and
publishes `lookup-change`. It does this asynchronously, and placeos-resource
handles each changefeed event in its own fiber, so a system's create and
update events can finish in either order.

Several specs created a system and then wrote the lookup into redis by hand.
Core's rebuild could land at any point after that, wiping the hand-written
entry. Generator modules also get a random Faker `custom_name`, so core's
version (e.g. "card/1") never matched the seeded key ("PublicEvents/1").
Logging the hash in the register spec shows it: {"PublicEvents/1" => id}
at request time, {"card/1" => id} three seconds later.

- public_events register (404) and chat_gpt_plugin MCP endpoint (missing
  prompt): the "PublicEvents/1" / "LLM/1" lookup was gone by the time the
  request ran.
- websocket bind (got 2 of 3 updates): core's rebuild removed the module's
  lookup and published `lookup-change`. Session's subscriptions remapped
  the binding, found no module, unsubscribed, and later publishes were
  never delivered. Delaying the first publish by 3s makes it fail every
  time.
- systems functions (found in a full run with seed 41906): same race.

Fix: save each system once, with its final module list, so core gets a
single event. Give modules the resolved name the spec expects. Then wait
for core's mapping with the new `wait_for_module_lookups` helper instead of
seeding it. That waits on the actual condition, so no sleeps are needed.

The websocket debug/ignore specs also flaked, for a separate reason. They
sent the module's resolved name, which is a Faker noun such as "hard
drive". Session#debug treats that value as a module id and puts it in
core's debug websocket path, so a name with a space gets a 400 handshake.
They now send the module id, which is what the API expects.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`::Spec.before_each` registers a hook on the root context, so every one
written inside a `describe` ran before every example in the whole suite.
Before each example, ControlSystem/Module/Driver, Storage, signage, oauth
and group tables were each wiped many times over. Each ControlSystem delete
also sent core a changefeed event, adding to the core lag behind the lookup
races fixed in the previous commit.

- Every `::Spec.before_each` inside a describe is now a plain `before_each`
  that runs only for that describe. The module-level hook in systems_spec
  moves inside `describe Systems`.
- Group clearing really is needed suite-wide. Group grants attach to the
  shared, cached spec users, so a group left over from an earlier example
  gives a "regular" user permissions it shouldn't have. With the hooks
  scoped, asset_categories "fails to create if a regular user" got 201
  (seed 41906). helper.cr's global hook now calls `clear_group_tables`
  once, and the 17 duplicate `before_each { clear_group_tables }` hooks
  are removed. A static check found that all 315 group-creating examples
  already sit under a group-clearing hook.
- signage_ai_providers_spec builds the domain's storage through
  `setup_signage_ai`. It only avoided leftover storages because uploads_spec
  cleared the table globally; without that it collided with a leftover
  (seed 43126). Its hook now clears playlist items, uploads and storages,
  as signage_ai_spec's does.
- uploads_spec: the two storages in the storage_id/default listing specs
  took bucket names from Faker's 24 hacker nouns, so they collided about 1
  time in 24 ("authority_id need to be unique"). They now use distinct
  random bucket names.
- systems_spec "with core": the state specs wrote the lookup by hand for a
  system created at load time, which core's rebuild (or a delete event)
  could wipe. They now use the save-once + `wait_for_module_lookups`
  pattern through a shared `mapped_module_system` helper, as the functions
  spec does. The `get_sys`/`get_driver`/`setup_system` helpers are no
  longer used, so they are removed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The concurrent deletes cascade into one another and can deadlock, which
aborted a CI run after every example had passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The chat manager pings every socket on a 30s timer, so a ping could
land during the example and be counted as an update. Wait for the
expected signals rather than sleeping a fixed interval.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@stakach
stakach merged commit 9a49e56 into master Oct 8, 2026
11 checks passed
@stakach
stakach deleted the refactor/user-resource-token-from-models branch October 8, 2026 07:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant