Repository navigation
test(plugin-auth): the SCIM deactivation and last-admin-guard suites run under 10 s (two #23184 rows) - #23697
Conversation
…iting 5.5 s each The two timed-ban faces in scim-deactivation-reconcile-user.test.ts let their administrator's expiry pass by advancing a fake Date (the clock the vendor's session.create hook reads) instead of a real 5.5 s setTimeout. The positive control still proves the moved clock reaches the vendor. Also: the per-file scrypt memo for the sign-in verifies, one password hash per file, and the vendor modules loaded at module top. No case, it line or expect line changes. Claude-Session: https://claude.ai/code/session_017WLmd5in9RXu7AjyHR8fKQ Co-authored-by: Claude <noreply@anthropic.com>
…er-file pool Every boot() with no options in last-admin-guard.test.ts now comes from sqlite-engine-pool.testkit.ts: one booted sqlite ObjectQL engine reused across cases, its rows deleted and its catalog emptied before each use, instead of 110 per-case boots. Engines built with any option still boot fresh per call. The testkit refuses what a raw row reset cannot undo (autonumber counters, organization rows, a changed schema). No case, it line or expect line changes. Claude-Session: https://claude.ai/code/session_017WLmd5in9RXu7AjyHR8fKQ Co-authored-by: Claude <noreply@anthropic.com>
…per file scim-deactivation-reconcile-user.test.ts takes its sqlite engine (the 32 identity objects and the two write guards) from sqlite-engine-pool.testkit.ts, emptied before each case, instead of a schema sync per case. The AuthManager and the SCIM credential stay per case. No case, it line or expect line changes. Claude-Session: https://claude.ai/code/session_017WLmd5in9RXu7AjyHR8fKQ Co-authored-by: Claude <noreply@anthropic.com>
…st-budget-plugin-auth-2
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
席位核对
Verdict: ACCEPT at head
Generated by Claude Code |
Refs #23184 (plugin-auth rows: scim-deactivation-reconcile-user, last-admin-guard)
Clause-②: no
Rows answered
Two rows of the card's
@objectstack/plugin-authlist:src/scim-deactivation-reconcile-user.test.ts, listed at 35.3 s (run38267594064, 14 of 14 window runs over budget);src/last-admin-guard.test.ts, listed at 10.9 s (run38222448090).The pin is the next refresh PR's list: it should name neither file. The card stays open for its other rows: the other three
plugin-authrows (org-create-posture-gate, implicit-account-linking, dev-admin-seed-credential-gate) and theplugin-security,service-datasourceandorganizationsrows.The diff is tests only: two test files and one new test-only helper. No production file, option, export or published byte changes. The helper's symbol has 0 hits in
packages/plugins/plugin-auth/dist(the positive controlregisterLastAdminGuardhas 4), and the package ships onlydist,README.mdandCHANGELOG.md. Everyit(,describe(andexpect(line in both files is byte-identical to the base and in the same order.Where the time went, and what changed
scim-deactivation-reconcile-user.test.ts: 18.7 s to 2.2 s localA probe at the base (temporary, deleted) broke the file's wall down. Two real-time 5.5 s ban-expiry waits took 11.0 s. Production-parameter scrypt took 3.3 s over 29 derivations (10 hashes and 19 sign-in verifies). The schema sync took about 0.9 s (12 boots of 32 objects, about 80 ms each). Vendor module evaluation inside the first case took about 0.8 s. Requests took the rest.
session.createhook asbanExpiresbeforeDate.now(), which is the global clock. SopassTime(ms)fakesDatealone (vi.useFakeTimers({ toFake: ['Date'], shouldAdvanceTime: true })) and sets itTIMED_BAN_WAIT_MSahead. Timers stay real, the clock keeps running from there, and anafterEachrestores the real clock.TIMED_BAN_MSandTIMED_BAN_WAIT_MSare unchanged. The pre-PATCH sign-in still runs on the real clock, so its race margin is the same as before. No production constant changes.src/__tests__/scrypt-memo.testkit.ts) answers each sign-in verify: same password, same salt, same parameters.AuthManagerand the SCIM credential are still built fresh in every case.last-admin-guard.test.ts: 2.4 s to 1.6 s localThis file has no scrypt and no waits. It ran 115 engine boots per run (init plus a schema sync), about 1.05 s of its wall. 90 of them are
boot()with no options, and those now come from the pool. The 25 boots with options (readThrough,maxScan,withIdentityWriteGuard,unguarded) still boot fresh per call, because their hooks differ.The pool:
src/__tests__/sqlite-engine-pool.testkit.ts(new, test-only)The pool keeps one booted sqlite
ObjectQLengine per file. Before handing an engine out again, it deletes every row of every table in one transaction, then runs the suite's ownonReset(last-admin-guardempties its catalog set there).take()hands out an engine no case holds, or boots another, so a case that takes two engines gets two.release()runs inafterEachanddestroy()inafterAll.A raw row reset cannot undo some engine state, and the pool refuses those cases loudly:
autonumberfield is refused at the first boot, because the engine keeps the counter in memory;sys_organizationrow is refused, because the engine memoizes the install's organization set until an engine write to that object invalidates it;Measurements
Each figure is vitest's per-file duration (JSON reporter
endTime - startTime):pnpm exec vitest run --maxWorkers=2 FILE --reporter=verbose --reporter=json, inpackages/plugins/plugin-auth, underos-verify-lock. Runs were on a shared 4-vCPU box: another dev'scheck:*gates ran unlocked beside them, and the load average is given for each set. The before and after runs at the final head were interleaved, with the before leg being the base file copied to a temporary name (deleted afterwards), so both sides saw the same load.c24598658dalone (load 2.4)3545b9c012(load 2.5 to 2.9)3545b9c012CI projection. The per-file CI captures of recent hourly runs are NOT MEASURED:
test-core-timings-N-of-6, the timing-table artifact and the job logs are all served fromproductionresultssa16.blob.core.windows.net, which this container's egress proxy refuses (CONNECT 403). The check-run annotations carry no per-file line. The ratios below therefore come from the card's own readings:Proof that every case still runs and still asserts the same
it(/describe(/expect(lines are byte-identical to the base and in order. The counts are 13 / 6 / 83 for scim and 110 / 23 / 277 for last-admin-guard.expect.getState().assertionCallsper case, at the base and at the head. The per-case counts are identical: 146 assertions over 13 cases, and 283 over 110 cases. No case asserts nothing.scripts/ablation-replace.mjsremoved the clock move (and itsDate.now()line) from the positive control only. The control turned red atexpectSignInAcceptedwith403 BANNED_USER, and the other 12 cases stayed green. The restore was verified (blob0fbcf875dbequals HEAD, andgit diff HEADis empty). So the unmutated control is admitted only because the hook read the moved clock, which is the same evidence that makes the main face's refusal mean something.take()handed out, covering per-table row counts, hook counts per event, middlewares, registered objects, the organization memo, the autonumber counters, the deviation, attestation and violation sets, both migration-flag memos, unavailable datasources and the catalog size. It compared each against a freshly booted engine. last-admin-guard made 90 takes (2 boots, 88 resets) and scim made 12 (1 boot, 11 resets). Every take matched the fresh fingerprint.--sequence.shuffle.testsunder seeds 11, 2026 and 23184. Every run passed 123 of 123 tests.Tests and gates (final head
3545b9c012)pnpm --filter @objectstack/plugin-auth run typecheck: VERDICT command-exit 0.check:test-typecheckis OK with the debt ledger unchanged (10 files / 88 errors / 23 signatures).tsc --listFilesOnly: the 3 changed files are intsconfig.test.json.@objectstack/plugin-authsuite, all 146 files in 6 chunks with--maxWorkers=2: 146 of 146 files passed, with 2861 tests passed and 10 skipped (the skips predate this PR). Every chunk exited 0.--no-inline-config: exit 0.check-nul-bytes: exit 0, and a control-byte self-scan of the 3 files found 0 hits.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 53 commands, the same 53 as the seat's lead. All 53 exit 0, as do the 4 roster families that sit under these paths (check:auth-mount-ledger,check:authz-resolver,check:error-code-casing,check:filter-alias-parity).check:dual-build-cjs-loadsfirst answered PREREQUISITE NOT MET (exit 3) and exited 0 after a fullturbo run build(75 of 75 tasks).--ranreports 53 derived, 53 run, 0 NOT-MEASURED and 0 UNRUN.check:engine-double-contractprinted its repo-wide census line verbatim:UNRECOGNISED [engine-double-contract]: 22 construct(s) in packages, examples declare a scanned verb (delete, update, findOne) alongside engine siblings, and this gate could not read the implementation -- so they are in NEITHER the pinned population nor the ledger.None of the 22 is in a file this PR touches.Acceptance notes
test-core-timings-N-of-6) cannot be read from a cloud container (blob-storage egress is refused), so a seat measuring a test-budget row there reads only the card's numbers and the next refresh. This is an environment fact, not a repo defect.Generated by Claude Code