Skip to content

Commit c90a0b4

Browse files
test(server): share repeated boots in four server test files and budget the 17 files between 10 and 13 s (#28631)
<!-- claude-projects-attribution: {"github_login":"os-project-manager"} --> _Requested by **Leehom** · [project thread](https://claude.ai/code/project/chan_01TenzsRopZj4jMbvjX8uz1X?thread=cmsg_01TenzsRopZj4jMbvjX8uz1XBSZhKKCd4guirWxgj33hr3)_ Part of #23181 (slice 33). The card stays open for later slices: `plugin-auth/src/invitation-delivery-outcome.test.ts` (10.5 s) is left to slice 30's effect, and slices 29–32 hold other files. Clause-②: no Metadata-protocol: none ## 维护者速读 - **改了什么**:17 个非 cli 的服务端测试文件(verify ×6、runtime ×4、rest ×2、plugin-security ×3、plugin-auth ×2、service-automation ×1)。其中 4 个文件把多个用例重复执行的相同启动合并成一次(served-composition 3→1、sys-user-default-row-scope 10→2、meta-history-diff 20→1、meta-draft-read-builder-gate 21→1);17 个文件都在第 2 行加 `// test-budget: 20` 头。 - **为什么改**:卡 #23181 的测试提速职责:CI 里超过 10 s 的测试文件要么提速,要么按裁决 6181973363 B 声明预算。这 17 个文件本地只跑 1.6–6.2 s,CI 读数 10–13 s 来自分片并发负载,剩下的地板是互不相同的真实工作。 - **风险与回滚**:只改测试文件,六个包的 `files[]` 都只发 `dist`、README、CHANGELOG,不发布任何东西;用例名和断言一条没删、没放宽(4 个改动文件 base/head 用例名逐条一致,且在 3 个随机顺序种子下全过);回滚即 revert。 - **先看**:`packages/rest/src/meta-history-diff-authoring-door.test.ts`(共享启动 + 每例前清空 spy);`packages/services/service-automation/src/sys-user-default-row-scope.integration.test.ts`(两个 `beforeAll`);`packages/verify/src/harness.served-composition.test.ts`(共享的 plain 启动)。 - **两分钟验证**:`pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/meta-history-diff-authoring-door.test.ts` → 20/20 通过,文件行约 0.5 s(base 约 2.3 s)。 - **需要你做的**:无,席位落地。 ## What changes The 17 files the order lists from slice 23's offender table (`pm-orders/server/evidence/23181-s23/offenders-after-s23.md`), read from the default reporter's per-file module line in the Test Core shard logs of the scheduled `ci.yml` runs `41199450735`, `41217827347`, `41237094739`, `41255489006` and `41271604267`. "local" is the same file line from one run on this box (`--maxWorkers=1`, base `d94dd3a65d`). | file | 0735 | 7347 | 4739 | 9006 | 4267 | local | tests | boots, base → head | cause | fix | |:--|--:|--:|--:|--:|--:|--:|--:|:--|:--|:--| | `plugin-security/src/rls-check-stored-form.test.ts` | **12.8** | 4.2 | 6.9 | 7.4 | 8.0 | 1.60 | 104 | one engine + SecurityPlugin per (driver, policy), already shared per key | each policy is its cells' subject, on two driver families | header 20 | | `runtime/src/plugin-driver-external-object.spike.test.ts` | 12.0 | 6.2 | 10.3 | **12.4** | 9.1 | 2.89 | 12 | 1 real-kernel boot in `beforeAll` | the boot (cases 0.24 s of 2.89) | header 20 | | `rest/src/meta-history-diff-authoring-door.test.ts` | 9.3 | 10.0 | 11.3 | **12.2** | 11.2 | 2.32 | 20 | 20 → 1 seeded real-stack boots | the same `boot()` (six saves) per case; every case only reads | one boot in `beforeAll`, spies cleared before each case; header 20 | | `plugin-auth/src/walled-owner-operator-stamp.test.ts` | 3.4 | 10.9 | **11.9** | 10.6 | 3.4 | 3.29 | 28 | 13 fresh engines + `AuthManager` | each case's sign-ups (production scrypt) and stamp | header 20 | | `plugin-security/src/rls-stored-list-ordering-fails-closed.test.ts` | 11.3 | 4.2 | **11.9** | 11.3 | 9.8 | 4.39 | 94 | 120 fresh engines | every case asserts on its boot's whole stored table, so none can share | header 20 | | `verify/src/harness.required-providers.test.ts` | **11.4** | 7.8 | 9.2 | 6.7 | 11.1 | 5.87 | 7 | 6 `bootStack` | six configurations, no two the same | header 20 | | `verify/src/harness.served-composition.test.ts` | **10.9** | 6.7 | 8.8 | 10.3 | 9.8 | 5.60 | 7 | 7 → 5 `bootStack` | the plain one-package boot, booted by three read-only cases | one plain boot in `beforeAll`; header 20 | | `plugin-security/src/org-admin-assignment-boundary.posture.test.ts` | **10.9** | 4.2 | 9.2 | 10.5 | 7.9 | 4.33 | 60 | 60 fresh engines | every case writes and reads back the same holder's rows, so none can share | header 20 | | `service-automation/src/sys-user-default-row-scope.integration.test.ts` | 2.0 | 7.5 | 9.9 | **10.6** | 7.1 | 2.79 | 10 | 10 → 2 real-kernel boots | the same `boot()` per case in each describe; the only case writes are `hook_probe` rows no case reads | one boot per describe in `beforeAll`; header 20 | | `runtime/src/installed-plugin-loader.test.ts` | **10.4** | 9.2 | 9.9 | 10.1 | 9.3 | 2.48 | 59 | 2 real Node children | each child's own module resolution is the subject (about 1 s each) | header 20 | | `verify/src/held-session-impersonation-target.pin.test.ts` | 9.7 | 7.1 | 8.5 | **10.4** | 9.7 | 6.19 | 8 | 1 `bootStack` in `beforeAll` | the boot (about 4.3 s of 6.19) plus a session per case | header 20 | | `plugin-auth/src/scim-deactivation-reconcile-user.test.ts` | 5.0 | 7.6 | **10.3** | 6.8 | 4.6 | 1.84 | 13 | pooled engine, fresh `AuthManager` per case | each case's SCIM sequence | header 20 | | `rest/src/meta-draft-read-builder-gate.test.ts` | 5.8 | 6.8 | 7.9 | **10.3** | 5.5 | 2.70 | 21 | 21 → 1 seeded real-stack boots | the same `boot()` (seven saves) per case; every case only reads | one boot in `beforeAll`, `queryDataset` cleared before each case; header 20 | | `verify/src/held-session-platform-admin.pin.test.ts` | **10.3** | 6.1 | 7.2 | 9.8 | 8.6 | 5.34 | 13 | 1 `bootStack` in `beforeAll` | the boot plus a hold per posture state | header 20 | | `runtime/src/app-automation-identity-write-refusal.integration.test.ts` | 10.0 | 4.9 | 9.3 | **10.2** | 7.3 | 3.45 | 100 | 100 fresh engines | each case's refused write is read back as unmoved, so none can share | header 20 | | `runtime/src/sandbox/quickjs-runner.test.ts` | 8.6 | 7.6 | **10.1** | 9.3 | 8.0 | 4.83 | 63 | none | real waits that are the cases' subject (600 / 500 ms host awaits, 250 ms CPU budgets, wall ceilings) | header 20 | | `verify/src/handle.test.ts` | **10.0** | 5.1 | 6.7 | 8.6 | 7.3 | 3.71 | 24 | 1 shared + 2 subject boots | the two extra boots are their cases' subjects | header 20 | **Cause across the set.** Every file reads 1.6–6.2 s locally, and its CI readings spread two to four times across runs of the same code (`walled-owner-operator-stamp` 3.4 s and 11.9 s): the overrun is the Test Core shard's concurrent load on in-process work. No file spawns a CLI; none repeats a boot beyond the four fixed here. **Headers** (ruling 6181973363 letter B, class `spawns the real producer once per single-cause fixture`, the class slice 26 used for in-process boots): N = the slowest of the five CI readings × 1.25, rounded up to 10 s; 20 for all 17. Where the file also changed, the header keeps its pre-change CI reading and says so. All 17 parse with `readTestBudget` (`scripts/measure-test-shard-timings.mjs`): 20 s, line 2. **The shares.** Only where no case writes what another reads, as the order allows: - `harness.served-composition`: the slate case and the cube control only read the kernel; the mail control's send leaves a `sys_email` row only it counts. Its own anti-vacuity line (`onePackage().requires` is undefined) stays. - `sys-user-default-row-scope`: each case reads through its own producer, and asserts on the last `readContexts` entry its own read pushed and on the hook firing it counts from its own `before`. In the `extend` describe the second case's contribution is refused and the case itself pins that the registry is unchanged. - The two rest files: after the seeding every case only reads (`GET` doors, `draftVersion`'s system read; the dataset door hands its query to the `queryDataset` spy, which stores nothing). The protocol spies (history-diff) and `queryDataset` (builder-gate) are cleared in `beforeEach`, as a fresh boot left them. Not changed: no case name, assertion, timeout, tier or project; no source file; no shared helper or testkit (`verify/src/harness.ts`, `boot-warm-up.testkit.ts`, `default-organization.testkit.ts`, `audience-gate-test-support` untouched). ## Tests - **A/B** (one box, one lock hold, base copied beside head as `zzbase.*`, each side in its own vitest invocation, order alternated per round, N=3, `--maxWorkers=1`, default reporter file line; copies deleted after): | file | base (s) | head (s) | change (mean) | |:--|:--|:--|--:| | `meta-history-diff-authoring-door` | 2.39, 2.29, 2.12 | 0.48, 0.45, 0.51 | −78.7% (2.27 → 0.48) | | `meta-draft-read-builder-gate` | 2.51, 2.35, 2.56 | 0.52, 0.56, 0.59 | −77.5% (2.47 → 0.56) | | `sys-user-default-row-scope.integration` | 2.75, 2.83, 2.45 | 0.87, 0.92, 0.73 | −68.5% (2.67 → 0.84) | | `harness.served-composition` | 4.83, 5.43, 5.35 | 3.41, 3.95, 3.51 | −30.4% (5.20 → 3.62) | Every run exit 0, the same test counts both sides (20, 21, 10, 7). `vitest list` on base and head gives the same case names for all four files. - **Order independence of the shares:** each changed file run plain and with `--sequence.shuffle --sequence.seed=11`, `=22`, `=33`: 16 runs, all exit 0 (7/7, 10/10, 20/20, 21/21). - **Header-only files:** all 13 run on base with the full count passing (local readings in the table); the header is a comment line. - No ablation: no tested behaviour changed (test-only). ## Acceptance notes - **Headers on files that read well under 10 s locally.** The 17 headers follow slice 26's precedent (PR #28599), which declared the same class on in-process files from their CI readings. The local readings are in the table so the seat can weigh it: on the four changed files the header carries the pre-change CI reading, and the next refresh reads the new shape. - **`skip-changeset`.** Test-only. All six packages ship `files: ["dist", "README.md", "CHANGELOG.md"]`; after the full build, a source symbol from each package is in its `dist/` (positive control: `bootStack`, `AutomationServicePlugin`, `RestServer`, `QuickJSScriptRunner`, `SecurityPlugin`, `AuthManager`) and a fixture string from each package's changed tests is not (`svc_note_cube`, `qa_star_reader`, `nav_atlas_launch_plan`, `spike_tree`, `qa_due_stored_`, `operator@corp.example`), nor is `test-budget:`. No `files[]` content changes. - **Open lever, not taken here:** `rls-stored-list-ordering-fails-closed` (120 boots) and `org-admin-assignment-boundary.posture` (60) could share a boot per key only if their cases stopped reading the whole table, which would change what those assertions say; left as is. ## Verification All readings on head `9f399f58` (one commit, then one merge of `origin/main` after slices 24, 25, 26 and 28 landed: #28610, #28601, #28599, #28616), after a full `pnpm exec turbo run build --concurrency=2` (118/118). - **Derived gate families:** `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` on the final head → 54 families, each run with its exit code captured before any pipe, all exit 0 (with `git fetch --depth=1` of the commit the teardown-shape self-test names). `--ran` reconciliation: `✓ dispatch-gates --ran: 54 derived famil(ies) accounted for — 54 run, 0 NOT-MEASURED`. List: `pm-orders/server/evidence/23181-s33-gates-ran-9f399f58.txt` in the project files. - **The 17 files on the final head:** 104/104, 12/12, 20/20, 28/28, 94/94, 7/7, 7/7, 60/60, 10/10, 59/59, 8/8, 13/13, 21/21, 13/13, 100/100, 63/63, 24/24 (table order), every run exit 0. - **Typecheck:** `pnpm --filter` the six packages `run typecheck` (tsc + `check:test-typecheck`) exit 0. - **Hand gates:** `pnpm exec eslint --no-inline-config` over the 17 files exit 0 (none of the six packages has a `lint` script; the repo-wide `pnpm lint` is CI's); `node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD` exit 0. - **CI** families (Test Core, lint farm, type-check lanes) are read off this PR's check-runs by the seat. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01FEkQ5mAySGuLppSnAtNXaa Co-authored-by: Claude <noreply@anthropic.com>
1 parent 672bda0 commit c90a0b4

17 files changed

Lines changed: 111 additions & 45 deletions

‎packages/plugins/plugin-auth/src/scim-deactivation-reconcile-user.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 20 — spawns the real producer once per single-cause fixture: a pooled real better-sqlite3 engine and a fresh AuthManager per case, each case's SCIM provision, ban, last-administrator and sign-in sequence its subject; 1.8 s in a local run; slowest CI reading 10.3 s (scheduled full-battery run 41237094739, Test Core shard log) x 1.25, rounded up
23

34
/**
45
* [#14360] SCIM `active: false` disables the account again — end to end,

‎packages/plugins/plugin-auth/src/walled-owner-operator-stamp.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 20 — spawns the real producer once per single-cause fixture: a fresh real ObjectQL engine (better-sqlite3 :memory:) and AuthManager in each of the 13 real-engine cases, its sign-ups (a production scrypt hash per new password) and the stamp or probe they produce the case's subject, beside 15 pure-function cases; 3.3 s in a local run; slowest CI reading 11.9 s (scheduled full-battery run 41237094739, Test Core shard log) x 1.25, rounded up
23

34
/**
45
* [#12751] The walled owner-verified stamp — maintainer ruling 2026-08-28

‎packages/plugins/plugin-security/src/org-admin-assignment-boundary.posture.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 20 — spawns the real producer once per single-cause fixture: a fresh real ObjectQL engine and SecurityPlugin per case across three postures, 60 cases that each write and read back the same holder's assignments, so no boot can be shared; 4.3 s in a local run; slowest CI reading 10.9 s (scheduled full-battery run 41199450735, Test Core shard log) x 1.25, rounded up
23

34
/**
45
* [ADR-0090 D12, amended 2026-10-17] The organization administrator's

‎packages/plugins/plugin-security/src/rls-check-stored-form.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 20 — spawns the real producer once per single-cause fixture: a real ObjectQL engine and SecurityPlugin per (driver, using, check) policy on two SQL driver families (better-sqlite3 and sqlite-wasm, both :memory:), already shared by every cell that names the policy and warmed at module scope, each policy the subject of its cells; 104 cases, 1.6 s in a local run; slowest CI reading 12.8 s (scheduled full-battery run 41199450735, Test Core shard log) x 1.25, rounded up
23

34
/**
45
* [#21109, ruling A] The row-level write `check` judges a `date`, `datetime` or

‎packages/plugins/plugin-security/src/rls-stored-list-ordering-fails-closed.test.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 20 — spawns the real producer once per single-cause fixture: a fresh real ObjectQL engine and SecurityPlugin per (driver, clause, predicate) on two SQL driver families, 120 boots for 94 cases, each case asserting on the whole stored table of its own boot (nothing stored, the row unchanged), so no boot can be shared; 4.4 s in a local run; slowest CI reading 11.9 s (scheduled full-battery run 41237094739, Test Core shard log) x 1.25, rounded up
23

34
/**
45
* [#19886 stage 2e] A row-level policy that ORDERS a field against a bound fails

‎packages/rest/src/meta-draft-read-builder-gate.test.ts‎

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
// test-budget: 20 — spawns the real producer once per single-cause fixture: one real-stack boot (better-sqlite3 :memory:, the real sys_metadata objects, protocol and routes) seeded with seven saves and shared by the 21 cases, which only read, each case's door reads its subject; slowest CI reading 10.3 s (scheduled full-battery run 41255489006, Test Core shard log) x 1.25, rounded up; read before the 21 identical boots became one
23

34
/**
45
* [#20338] A PENDING draft — unpublished authoring work — is read only by a
@@ -47,7 +48,7 @@
4748
* arrive ungated, is `meta-draft-read-door-census.test.ts`.
4849
*/
4950

50-
import { describe, it, expect, afterEach, vi } from 'vitest';
51+
import { describe, it, expect, afterAll, beforeAll, beforeEach, vi } from 'vitest';
5152
import { ObjectQL } from '@objectstack/objectql';
5253
import { SqlDriver } from '@objectstack/driver-sql';
5354
import { ObjectStackProtocolImplementation } from '@objectstack/metadata-protocol';
@@ -115,7 +116,7 @@ const DATASET_DRAFT_ONLY = { ...DATASET, name: 'pipeline', label: 'Pipeline' };
115116
const DRAFT_ONLY_TEXT = ['Atlas (draft)', 'nav_atlas_launch_plan', 'Beacon', 'nav_beacon_home'];
116117

117118
const liveEngines: ObjectQL[] = [];
118-
afterEach(async () => {
119+
afterAll(async () => {
119120
while (liveEngines.length) {
120121
try { await liveEngines.pop()?.destroy(); } catch { /* noop */ }
121122
}
@@ -218,6 +219,20 @@ async function boot() {
218219
return { item, list, drafts, datasetQuery, queryDataset };
219220
}
220221

222+
/**
223+
* One boot, seeded once, for every case: after the seeding every case only
224+
* reads (`GET` on the doors, and the dataset door, whose analytics service is
225+
* the `queryDataset` spy and stores nothing), so no case writes what another
226+
* reads. The spy is cleared before each case, as a boot of its own left it.
227+
*/
228+
let shared: Awaited<ReturnType<typeof boot>>;
229+
beforeAll(async () => {
230+
shared = await boot();
231+
});
232+
beforeEach(() => {
233+
shared?.queryDataset.mockClear();
234+
});
235+
221236
const navIds = (doc: any): string[] => (doc?.navigation ?? []).map((e: any) => e.id);
222237
const envelope = (res: any) => ({ status: res.statusCode, code: res.body?.error?.code ?? res.body?.code });
223238
const text = (res: any): string => JSON.stringify(res.body ?? null);
@@ -233,7 +248,7 @@ const ITEM_SWITCHES: Record<string, Record<string, string>> = {
233248
describe('[#20338] GET /meta/:type/:name — ?state=draft and ?preview=draft', () => {
234249
for (const [label, query] of Object.entries(ITEM_SWITCHES)) {
235250
it(`${label}: a member without an authoring capability reads the PUBLISHED app — the plain read's answer, byte for byte`, async () => {
236-
const { item } = await boot();
251+
const { item } = shared;
237252
const plain = await item('member', 'app', 'atlas');
238253
const res = await item('member', 'app', 'atlas', query);
239254

@@ -245,7 +260,7 @@ describe('[#20338] GET /meta/:type/:name — ?state=draft and ?preview=draft', (
245260
}, 60_000);
246261

247262
it(`${label}: a draft-only app answers the member the plain read's absence — 404, never the draft`, async () => {
248-
const { item } = await boot();
263+
const { item } = shared;
249264
const plain = await item('member', 'app', 'beacon');
250265
const res = await item('member', 'app', 'beacon', query);
251266

@@ -256,7 +271,7 @@ describe('[#20338] GET /meta/:type/:name — ?state=draft and ?preview=draft', (
256271
}, 60_000);
257272

258273
it(`${label}: every builder reads the draft (the control) — Studio, Setup and a metadata author alike`, async () => {
259-
const { item } = await boot();
274+
const { item } = shared;
260275
for (const who of BUILDERS) {
261276
const res = await item(who, 'app', 'atlas', query);
262277
expect(res.statusCode, who).toBe(200);
@@ -271,7 +286,7 @@ describe('[#20338] GET /meta/:type/:name — ?state=draft and ?preview=draft', (
271286
}
272287

273288
it('?state=draft tells a member nothing about whether a draft exists: an app with none answers the same published read, never NO_DRAFT', async () => {
274-
const { item } = await boot();
289+
const { item } = shared;
275290
const plain = await item('member', 'app', 'quiet');
276291
const res = await item('member', 'app', 'quiet', { state: 'draft' });
277292

@@ -285,7 +300,7 @@ describe('[#20338] GET /meta/:type/:name — ?state=draft and ?preview=draft', (
285300

286301
describe('[#20338] GET /meta/:type — ?preview=draft', () => {
287302
it('a member reads the published list — the plain list, byte for byte: no draft-only item, no draft label', async () => {
288-
const { list } = await boot();
303+
const { list } = shared;
289304
const plain = await list('member', 'app');
290305
const res = await list('member', 'app', { preview: 'draft' });
291306

@@ -297,7 +312,7 @@ describe('[#20338] GET /meta/:type — ?preview=draft', () => {
297312
}, 60_000);
298313

299314
it('every builder reads the draft overlay (the control): the pending draft wins, and the draft-only app is listed', async () => {
300-
const { list } = await boot();
315+
const { list } = shared;
301316
for (const who of BUILDERS) {
302317
const res = await list(who, 'app', { preview: 'draft' });
303318
expect(res.statusCode, who).toBe(200);
@@ -317,7 +332,7 @@ describe('[#20338] POST /analytics/dataset/query — previewDrafts', () => {
317332

318333
for (const [label, bodyFlag, query] of SWITCHES) {
319334
it(`${label}: a member's query runs over the PUBLISHED dataset and never asks for draft rows`, async () => {
320-
const { datasetQuery, queryDataset } = await boot();
335+
const { datasetQuery, queryDataset } = shared;
321336
const res = await datasetQuery('member', { datasetName: 'revenue', selection, ...bodyFlag }, query);
322337

323338
expect(res.statusCode).toBe(200);
@@ -328,23 +343,23 @@ describe('[#20338] POST /analytics/dataset/query — previewDrafts', () => {
328343
}, 60_000);
329344

330345
it(`${label}: a draft-only dataset answers the member the door's own 404, and nothing is queried`, async () => {
331-
const { datasetQuery, queryDataset } = await boot();
346+
const { datasetQuery, queryDataset } = shared;
332347
const res = await datasetQuery('member', { datasetName: 'pipeline', selection, ...bodyFlag }, query);
333348

334349
expect(envelope(res)).toEqual({ status: 404, code: 'NOT_FOUND' });
335350
expect(queryDataset).not.toHaveBeenCalled();
336351
}, 60_000);
337352

338353
it(`${label}: an INLINE dataset posted by a member runs over live data, never the pending seed draft's rows`, async () => {
339-
const { datasetQuery, queryDataset } = await boot();
354+
const { datasetQuery, queryDataset } = shared;
340355
const res = await datasetQuery('member', { dataset: { ...DATASET }, selection, ...bodyFlag }, query);
341356

342357
expect(res.statusCode).toBe(200);
343358
expect((queryDataset.mock.calls[0] as unknown as unknown[])[3]).toBeUndefined();
344359
}, 60_000);
345360

346361
it(`${label}: every builder's query runs over the draft dataset, with the draft rows asked for (the control)`, async () => {
347-
const { datasetQuery, queryDataset } = await boot();
362+
const { datasetQuery, queryDataset } = shared;
348363
for (const who of BUILDERS) {
349364
queryDataset.mockClear();
350365
const res = await datasetQuery(who, { datasetName: 'revenue', selection, ...bodyFlag }, query);
@@ -362,7 +377,7 @@ describe('[#20338] POST /analytics/dataset/query — previewDrafts', () => {
362377

363378
describe('[#20338] one predicate: every draft door admits exactly the callers GET /meta/_drafts admits', () => {
364379
it('for each caller, /meta/_drafts refuses them 403 exactly when the item, list and dataset doors serve them the published version', async () => {
365-
const { item, list, drafts, datasetQuery, queryDataset } = await boot();
380+
const { item, list, drafts, datasetQuery, queryDataset } = shared;
366381
for (const who of ['member', ...BUILDERS] as const) {
367382
const listing = await drafts(who);
368383
const admitted = listing.statusCode !== 403;

0 commit comments

Comments
 (0)