bunderstack 0.22.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +2 -1
  3. package/dist/api/context.d.ts +3 -0
  4. package/dist/api/context.d.ts.map +1 -1
  5. package/dist/api/context.js +2 -0
  6. package/dist/api/context.js.map +1 -1
  7. package/dist/backend-internals.d.ts +11 -0
  8. package/dist/backend-internals.d.ts.map +1 -1
  9. package/dist/backend-internals.js.map +1 -1
  10. package/dist/backend.d.ts +2 -2
  11. package/dist/backend.d.ts.map +1 -1
  12. package/dist/backend.js +9 -4
  13. package/dist/backend.js.map +1 -1
  14. package/dist/errors.d.ts.map +1 -1
  15. package/dist/errors.js +4 -2
  16. package/dist/errors.js.map +1 -1
  17. package/dist/jobs/worker.d.ts +12 -0
  18. package/dist/jobs/worker.d.ts.map +1 -1
  19. package/dist/jobs/worker.js +32 -1
  20. package/dist/jobs/worker.js.map +1 -1
  21. package/dist/logging.d.ts +8 -0
  22. package/dist/logging.d.ts.map +1 -0
  23. package/dist/logging.js +6 -0
  24. package/dist/logging.js.map +1 -0
  25. package/dist/runtime.d.ts +4 -0
  26. package/dist/runtime.d.ts.map +1 -1
  27. package/dist/runtime.js +19 -4
  28. package/dist/runtime.js.map +1 -1
  29. package/dist/testing/auth.d.ts +30 -2
  30. package/dist/testing/auth.d.ts.map +1 -1
  31. package/dist/testing/auth.js +103 -14
  32. package/dist/testing/auth.js.map +1 -1
  33. package/dist/testing/fixture.d.ts +18 -0
  34. package/dist/testing/fixture.d.ts.map +1 -1
  35. package/dist/testing/fixture.js +55 -2
  36. package/dist/testing/fixture.js.map +1 -1
  37. package/dist/testing/jobs.d.ts +9 -1
  38. package/dist/testing/jobs.d.ts.map +1 -1
  39. package/dist/testing/jobs.js +9 -0
  40. package/dist/testing/jobs.js.map +1 -1
  41. package/dist/testing/logs.d.ts +18 -0
  42. package/dist/testing/logs.d.ts.map +1 -0
  43. package/dist/testing/logs.js +53 -0
  44. package/dist/testing/logs.js.map +1 -0
  45. package/dist/testing.d.ts +5 -4
  46. package/dist/testing.d.ts.map +1 -1
  47. package/dist/testing.js +1 -1
  48. package/dist/testing.js.map +1 -1
  49. package/llms.txt +20 -13
  50. package/package.json +1 -1
@@ -4,6 +4,7 @@ import type { TestDatabaseStrategy } from '../database/adapter.js';
4
4
  import type { TestSchemaMode } from '../provision.js';
5
5
  import type { TestAuth, TestIdentity } from './auth.js';
6
6
  import type { TestEmail } from './email.js';
7
+ import type { TestLogs, TestLogMode } from './logs.js';
7
8
  import type { TestStorage } from './storage.js';
8
9
  import { type TestJobs } from './jobs.js';
9
10
  export type TestOptions = {
@@ -13,21 +14,38 @@ export type TestOptions = {
13
14
  schema?: TestSchemaMode;
14
15
  strategy?: TestDatabaseStrategy;
15
16
  };
17
+ logs?: TestLogMode;
18
+ };
19
+ export type TestCleanup = () => unknown | Promise<unknown>;
20
+ export type TestSetup<TApp, TContext> = (fixture: TestFixture<TApp>) => TContext | Promise<TContext>;
21
+ export type TestConfigureOptions<TApp, TContext> = TestOptions & {
22
+ setup?: TestSetup<TApp, TContext>;
16
23
  };
17
24
  export type TestFixture<TApp> = AsyncDisposable & {
18
25
  readonly app: TApp;
19
26
  readonly auth: TestAuth;
20
27
  readonly email: TestEmail;
21
28
  readonly jobs: TestJobs;
29
+ readonly logs: TestLogs;
22
30
  readonly storage: TestStorage;
31
+ defer(cleanup: TestCleanup): void;
23
32
  client(identity?: TestIdentity): TApp extends AnyBunderstackApp ? BunderstackClient<TApp> : never;
24
33
  close(): Promise<void>;
25
34
  };
35
+ export type ConfiguredTestFixture<TApp, TContext> = TestFixture<TApp> & {
36
+ readonly context: TContext;
37
+ };
38
+ export type TestFactory<TApp, TContext> = (options?: TestOptions) => Promise<ConfiguredTestFixture<TApp, TContext>>;
39
+ export type TestMethod<TApp> = {
40
+ (options?: TestOptions): Promise<TestFixture<TApp>>;
41
+ configure<TContext = undefined>(options: TestConfigureOptions<TApp, TContext>): TestFactory<TApp, TContext>;
42
+ };
26
43
  type TestableApp = {
27
44
  auth: unknown;
28
45
  handler(request: Request): Promise<Response>;
29
46
  close(): Promise<void>;
30
47
  };
48
+ export declare function configureTestApp<TApp extends TestableApp, TContext = undefined>(backend: BunderstackBackend<TApp>, options: TestConfigureOptions<TApp, TContext>): TestFactory<TApp, TContext>;
31
49
  export declare function createTestApp<TApp extends TestableApp>(backend: BunderstackBackend<TApp>, options?: TestOptions): Promise<TestFixture<TApp>>;
32
50
  export {};
33
51
  //# sourceMappingURL=fixture.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/testing/fixture.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAA;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AACxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAW5C,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAGtD,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;IACxC,QAAQ,CAAC,EAAE;QACT,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAA;QAC7B,MAAM,CAAC,EAAE,cAAc,CAAA;QACvB,QAAQ,CAAC,EAAE,oBAAoB,CAAA;KAChC,CAAA;CACF,CAAA;AAED,MAAM,MAAM,WAAW,CAAC,IAAI,IAAI,eAAe,GAAG;IAChD,QAAQ,CAAC,GAAG,EAAE,IAAI,CAAA;IAClB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;IAC7B,MAAM,CACJ,QAAQ,CAAC,EAAE,YAAY,GACtB,IAAI,SAAS,iBAAiB,GAAG,iBAAiB,CAAC,IAAI,CAAC,GAAG,KAAK,CAAA;IACnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAED,KAAK,WAAW,GAAG;IACjB,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACvB,CAAA;AAQD,wBAAsB,aAAa,CAAC,IAAI,SAAS,WAAW,EAC1D,OAAO,EAAE,kBAAkB,CAAC,IAAI,CAAC,EACjC,OAAO,GAAE,WAAgB,GACxB,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAmH5B"}
1
+ {"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/testing/fixture.ts"],"names":[],"mappings":"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"}
@@ -3,23 +3,57 @@ import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
4
  import { BACKEND_INTERNALS, } from '../backend-internals.js';
5
5
  import { provisionForTest } from '../provision.js';
6
- import { createTestAuth } from './auth.js';
6
+ import { createTestAuth, createTestSessionRegistry } from './auth.js';
7
7
  import { testClient } from './client.js';
8
8
  import { createTestDatabaseTarget } from './database.js';
9
9
  import { createTestEmail } from './email.js';
10
10
  import { createTestJobs } from './jobs.js';
11
+ import { createTestLogs } from './logs.js';
11
12
  import { createTestStorage, resolveTestBuckets } from './storage.js';
12
13
  const defaultTestEnv = {
13
14
  NODE_ENV: 'test',
14
15
  AUTH_SECRET: 'bunderstack-test-secret',
15
16
  BUNDERSTACK_ROLE: 'web',
16
17
  };
18
+ function mergeTestOptions(defaults, overrides) {
19
+ return {
20
+ ...defaults,
21
+ ...overrides,
22
+ env: defaults.env || overrides.env
23
+ ? { ...defaults.env, ...overrides.env }
24
+ : undefined,
25
+ database: defaults.database || overrides.database
26
+ ? { ...defaults.database, ...overrides.database }
27
+ : undefined,
28
+ };
29
+ }
30
+ export function configureTestApp(backend, options) {
31
+ const { setup, ...defaults } = options;
32
+ return async (overrides = {}) => {
33
+ const fixture = await createTestApp(backend, mergeTestOptions(defaults, overrides));
34
+ try {
35
+ const context = setup ? await setup(fixture) : undefined;
36
+ return Object.assign(fixture, { context });
37
+ }
38
+ catch (cause) {
39
+ try {
40
+ await fixture.close();
41
+ }
42
+ catch (cleanupCause) {
43
+ throw new AggregateError([cause, cleanupCause], '[bunderstack] test fixture setup and cleanup failed');
44
+ }
45
+ throw cause;
46
+ }
47
+ };
48
+ }
17
49
  export async function createTestApp(backend, options = {}) {
18
50
  const internals = backend[BACKEND_INTERNALS];
19
51
  const storageRoot = await mkdtemp(join(tmpdir(), 'bunderstack-storage-'));
20
52
  const resolvedStorage = resolveTestBuckets(internals.declaration.config.storage, storageRoot);
21
53
  const { adapter: emailAdapter, email } = createTestEmail();
22
54
  const storage = createTestStorage(resolvedStorage);
55
+ const sessions = createTestSessionRegistry();
56
+ const { logger, logs } = createTestLogs(options.logs);
23
57
  let target;
24
58
  try {
25
59
  target = await createTestDatabaseTarget(internals.declaration.config.database.adapter, {
@@ -38,6 +72,8 @@ export async function createTestApp(backend, options = {}) {
38
72
  database: target.connection,
39
73
  resolvedStorage,
40
74
  emailAdapter,
75
+ authResolver: sessions.resolver,
76
+ logger,
41
77
  forceMemoryRealtime: true,
42
78
  backgroundAutoStart: false,
43
79
  captureTestingHandle: (handle) => {
@@ -79,12 +115,27 @@ export async function createTestApp(backend, options = {}) {
79
115
  await rm(storageRoot, { recursive: true, force: true });
80
116
  throw new Error('[bunderstack] runtime did not provide test controls');
81
117
  }
82
- const auth = createTestAuth(app);
118
+ const auth = createTestAuth(app, email, sessions);
83
119
  const jobs = createTestJobs(testingHandle);
120
+ const deferred = [];
84
121
  let closePromise;
122
+ const defer = (cleanup) => {
123
+ if (closePromise) {
124
+ throw new Error('[bunderstack] cannot defer cleanup after fixture close');
125
+ }
126
+ deferred.push(cleanup);
127
+ };
85
128
  const close = () => {
86
129
  closePromise ??= (async () => {
87
130
  const errors = [];
131
+ for (const cleanup of deferred.reverse()) {
132
+ try {
133
+ await cleanup();
134
+ }
135
+ catch (error) {
136
+ errors.push(error);
137
+ }
138
+ }
88
139
  try {
89
140
  await app.close();
90
141
  }
@@ -116,7 +167,9 @@ export async function createTestApp(backend, options = {}) {
116
167
  auth,
117
168
  email,
118
169
  jobs,
170
+ logs,
119
171
  storage,
172
+ defer,
120
173
  client: (identity) => testClient(app, identity),
121
174
  close,
122
175
  [Symbol.asyncDispose]: close,
@@ -1 +1 @@
1
- {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../src/testing/fixture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAUhC,OAAO,EACL,iBAAiB,GAElB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAA;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAA;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,cAAc,EAAiB,MAAM,QAAQ,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AA6BjE,MAAM,cAAc,GAAG;IACrB,QAAQ,EAAE,MAAM;IAChB,WAAW,EAAE,yBAAyB;IACtC,gBAAgB,EAAE,KAAK;CACqB,CAAA;AAE9C,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,OAAiC,EACjC,UAAuB,EAAE;IAEzB,MAAM,SAAS,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC5C,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAA;IACzE,MAAM,eAAe,GAAG,kBAAkB,CACxC,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,EACpC,WAAW,CACZ,CAAA;IACD,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,eAAe,EAAE,CAAA;IAC1D,MAAM,OAAO,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAA;IAElD,IAAI,MAAM,CAAA;IACV,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,wBAAwB,CACrC,SAAS,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAC7C;YACE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,QAAQ;YACxC,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,QAAQ;SACrC,CACF,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACvD,MAAM,KAAK,CAAA;IACb,CAAC;IAED,IAAI,GAAqB,CAAA;IACzB,IAAI,aAA+C,CAAA;IACnD,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,SAAS,CAAC,KAAK,CACzB,EAAE,GAAG,cAAc,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,EACrC;YACE,QAAQ,EAAE,MAAM,CAAC,UAAU;YAC3B,eAAe;YACf,YAAY;YACZ,mBAAmB,EAAE,IAAI;YACzB,mBAAmB,EAAE,KAAK;YAC1B,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC/B,aAAa,GAAG,MAAM,CAAA;YACxB,CAAC;SACF,CACF,CAAA;QACD,MAAM,gBAAgB,CAAC,GAAa,EAAE,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,MAAM,CAAC,CAAA;IAC3E,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,aAAa,GAAc,EAAE,CAAA;QACnC,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;YACnB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAC3B,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAA;QACrC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACzD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC;QACD,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,cAAc,CACtB,CAAC,KAAK,EAAE,GAAG,aAAa,CAAC,EACzB,qDAAqD,CACtD,CAAA;QACH,CAAC;QACD,MAAM,KAAK,CAAA;IACb,CAAC;IAED,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;QACjB,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAA;QACnC,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;QACvD,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAA;IACxE,CAAC;IAED,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,CAAC,CAAA;IAChC,MAAM,IAAI,GAAG,cAAc,CAAC,aAAa,CAAC,CAAA;IAC1C,IAAI,YAAuC,CAAA;IAC3C,MAAM,KAAK,GAAG,GAAG,EAAE;QACjB,YAAY,KAAK,CAAC,KAAK,IAAI,EAAE;YAC3B,MAAM,MAAM,GAAc,EAAE,CAAA;YAC5B,IAAI,CAAC;gBACH,MAAM,GAAG,CAAC,KAAK,EAAE,CAAA;YACnB,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAA;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;YACD,IAAI,CAAC;gBACH,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;YACzD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACpB,CAAC;YACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAM,MAAM,CAAC,CAAC,CAAC,CAAA;YACxC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtB,MAAM,IAAI,cAAc,CAAC,MAAM,EAAE,sCAAsC,CAAC,CAAA;YAC1E,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;QACJ,OAAO,YAAY,CAAA;IACrB,CAAC,CAAA;IAED,OAAO;QACL,GAAG;QACH,IAAI;QACJ,KAAK;QACL,IAAI;QACJ,OAAO;QACP,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,UAAU,CAAC,GAAY,EAAE,QAAQ,CAAU;QACjE,KAAK;QACL,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,KAAK;KAC7B,CAAA;AACH,CAAC","sourcesContent":["import { mkdtemp, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\nimport type { BunderstackBackend } from '../backend'\nimport type { AnyBunderstackApp, BunderstackClient } from '../client/rpc-client'\nimport type { TestDatabaseStrategy } from '../database/adapter'\nimport type { TestSchemaMode } from '../provision'\nimport type { TestAuth, TestIdentity } from './auth'\nimport type { TestEmail } from './email'\nimport type { TestStorage } from './storage'\n\nimport {\n BACKEND_INTERNALS,\n type RuntimeTestingHandle,\n} from '../backend-internals'\nimport { provisionForTest } from '../provision'\nimport { createTestAuth } from './auth'\nimport { testClient } from './client'\nimport { createTestDatabaseTarget } from './database'\nimport { createTestEmail } from './email'\nimport { createTestJobs, type TestJobs } from './jobs'\nimport { createTestStorage, resolveTestBuckets } from './storage'\n\nexport type TestOptions = {\n env?: Record<string, string | undefined>\n database?: {\n mode?: 'memory' | 'temporary'\n schema?: TestSchemaMode\n strategy?: TestDatabaseStrategy\n }\n}\n\nexport type TestFixture<TApp> = AsyncDisposable & {\n readonly app: TApp\n readonly auth: TestAuth\n readonly email: TestEmail\n readonly jobs: TestJobs\n readonly storage: TestStorage\n client(\n identity?: TestIdentity,\n ): TApp extends AnyBunderstackApp ? BunderstackClient<TApp> : never\n close(): Promise<void>\n}\n\ntype TestableApp = {\n auth: unknown\n handler(request: Request): Promise<Response>\n close(): Promise<void>\n}\n\nconst defaultTestEnv = {\n NODE_ENV: 'test',\n AUTH_SECRET: 'bunderstack-test-secret',\n BUNDERSTACK_ROLE: 'web',\n} satisfies Record<string, string | undefined>\n\nexport async function createTestApp<TApp extends TestableApp>(\n backend: BunderstackBackend<TApp>,\n options: TestOptions = {},\n): Promise<TestFixture<TApp>> {\n const internals = backend[BACKEND_INTERNALS]\n const storageRoot = await mkdtemp(join(tmpdir(), 'bunderstack-storage-'))\n const resolvedStorage = resolveTestBuckets(\n internals.declaration.config.storage,\n storageRoot,\n )\n const { adapter: emailAdapter, email } = createTestEmail()\n const storage = createTestStorage(resolvedStorage)\n\n let target\n try {\n target = await createTestDatabaseTarget(\n internals.declaration.config.database.adapter,\n {\n mode: options.database?.mode ?? 'memory',\n strategy: options.database?.strategy,\n },\n )\n } catch (cause) {\n await rm(storageRoot, { recursive: true, force: true })\n throw cause\n }\n\n let app: TApp | undefined\n let testingHandle: RuntimeTestingHandle | undefined\n try {\n app = await internals.start(\n { ...defaultTestEnv, ...options.env },\n {\n database: target.connection,\n resolvedStorage,\n emailAdapter,\n forceMemoryRealtime: true,\n backgroundAutoStart: false,\n captureTestingHandle: (handle) => {\n testingHandle = handle\n },\n },\n )\n await provisionForTest(app as object, options.database?.schema ?? 'auto')\n } catch (cause) {\n const cleanupErrors: unknown[] = []\n if (app) {\n try {\n await app.close()\n } catch (error) {\n cleanupErrors.push(error)\n }\n }\n try {\n await target[Symbol.asyncDispose]()\n } catch (error) {\n cleanupErrors.push(error)\n }\n try {\n await rm(storageRoot, { recursive: true, force: true })\n } catch (error) {\n cleanupErrors.push(error)\n }\n if (cleanupErrors.length > 0) {\n throw new AggregateError(\n [cause, ...cleanupErrors],\n '[bunderstack] test fixture setup and cleanup failed',\n )\n }\n throw cause\n }\n\n if (!testingHandle) {\n await app.close()\n await target[Symbol.asyncDispose]()\n await rm(storageRoot, { recursive: true, force: true })\n throw new Error('[bunderstack] runtime did not provide test controls')\n }\n\n const auth = createTestAuth(app)\n const jobs = createTestJobs(testingHandle)\n let closePromise: Promise<void> | undefined\n const close = () => {\n closePromise ??= (async () => {\n const errors: unknown[] = []\n try {\n await app.close()\n } catch (error) {\n errors.push(error)\n }\n try {\n await target[Symbol.asyncDispose]()\n } catch (error) {\n errors.push(error)\n }\n try {\n await rm(storageRoot, { recursive: true, force: true })\n } catch (error) {\n errors.push(error)\n }\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, '[bunderstack] fixture cleanup failed')\n }\n })()\n return closePromise\n }\n\n return {\n app,\n auth,\n email,\n jobs,\n storage,\n client: (identity) => testClient(app as never, identity) as never,\n close,\n [Symbol.asyncDispose]: close,\n }\n}\n"]}
1
+ {"version":3,"file":"fixture.js","sourceRoot":"","sources":["../../src/testing/fixture.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdtemp, rm } from 'node:fs/promises'\nimport { tmpdir } from 'node:os'\nimport { join } from 'node:path'\n\nimport type { BunderstackBackend } from '../backend'\nimport type { AnyBunderstackApp, BunderstackClient } from '../client/rpc-client'\nimport type { TestDatabaseStrategy } from '../database/adapter'\nimport type { TestSchemaMode } from '../provision'\nimport type { TestAuth, TestIdentity } from './auth'\nimport type { TestEmail } from './email'\nimport type { TestLogs, TestLogMode } from './logs'\nimport type { TestStorage } from './storage'\n\nimport {\n BACKEND_INTERNALS,\n type RuntimeTestingHandle,\n} from '../backend-internals'\nimport { provisionForTest } from '../provision'\nimport { createTestAuth, createTestSessionRegistry } from './auth'\nimport { testClient } from './client'\nimport { createTestDatabaseTarget } from './database'\nimport { createTestEmail } from './email'\nimport { createTestJobs, type TestJobs } from './jobs'\nimport { createTestLogs } from './logs'\nimport { createTestStorage, resolveTestBuckets } from './storage'\n\nexport type TestOptions = {\n env?: Record<string, string | undefined>\n database?: {\n mode?: 'memory' | 'temporary'\n schema?: TestSchemaMode\n strategy?: TestDatabaseStrategy\n }\n logs?: TestLogMode\n}\n\nexport type TestCleanup = () => unknown | Promise<unknown>\n\nexport type TestSetup<TApp, TContext> = (\n fixture: TestFixture<TApp>,\n) => TContext | Promise<TContext>\n\nexport type TestConfigureOptions<TApp, TContext> = TestOptions & {\n setup?: TestSetup<TApp, TContext>\n}\n\nexport type TestFixture<TApp> = AsyncDisposable & {\n readonly app: TApp\n readonly auth: TestAuth\n readonly email: TestEmail\n readonly jobs: TestJobs\n readonly logs: TestLogs\n readonly storage: TestStorage\n defer(cleanup: TestCleanup): void\n client(\n identity?: TestIdentity,\n ): TApp extends AnyBunderstackApp ? BunderstackClient<TApp> : never\n close(): Promise<void>\n}\n\nexport type ConfiguredTestFixture<TApp, TContext> = TestFixture<TApp> & {\n readonly context: TContext\n}\n\nexport type TestFactory<TApp, TContext> = (\n options?: TestOptions,\n) => Promise<ConfiguredTestFixture<TApp, TContext>>\n\nexport type TestMethod<TApp> = {\n (options?: TestOptions): Promise<TestFixture<TApp>>\n configure<TContext = undefined>(\n options: TestConfigureOptions<TApp, TContext>,\n ): TestFactory<TApp, TContext>\n}\n\ntype TestableApp = {\n auth: unknown\n handler(request: Request): Promise<Response>\n close(): Promise<void>\n}\n\nconst defaultTestEnv = {\n NODE_ENV: 'test',\n AUTH_SECRET: 'bunderstack-test-secret',\n BUNDERSTACK_ROLE: 'web',\n} satisfies Record<string, string | undefined>\n\nfunction mergeTestOptions(\n defaults: TestOptions,\n overrides: TestOptions,\n): TestOptions {\n return {\n ...defaults,\n ...overrides,\n env:\n defaults.env || overrides.env\n ? { ...defaults.env, ...overrides.env }\n : undefined,\n database:\n defaults.database || overrides.database\n ? { ...defaults.database, ...overrides.database }\n : undefined,\n }\n}\n\nexport function configureTestApp<\n TApp extends TestableApp,\n TContext = undefined,\n>(\n backend: BunderstackBackend<TApp>,\n options: TestConfigureOptions<TApp, TContext>,\n): TestFactory<TApp, TContext> {\n const { setup, ...defaults } = options\n return async (overrides = {}) => {\n const fixture = await createTestApp(\n backend,\n mergeTestOptions(defaults, overrides),\n )\n try {\n const context = setup ? await setup(fixture) : (undefined as TContext)\n return Object.assign(fixture, { context })\n } catch (cause) {\n try {\n await fixture.close()\n } catch (cleanupCause) {\n throw new AggregateError(\n [cause, cleanupCause],\n '[bunderstack] test fixture setup and cleanup failed',\n )\n }\n throw cause\n }\n }\n}\n\nexport async function createTestApp<TApp extends TestableApp>(\n backend: BunderstackBackend<TApp>,\n options: TestOptions = {},\n): Promise<TestFixture<TApp>> {\n const internals = backend[BACKEND_INTERNALS]\n const storageRoot = await mkdtemp(join(tmpdir(), 'bunderstack-storage-'))\n const resolvedStorage = resolveTestBuckets(\n internals.declaration.config.storage,\n storageRoot,\n )\n const { adapter: emailAdapter, email } = createTestEmail()\n const storage = createTestStorage(resolvedStorage)\n const sessions = createTestSessionRegistry()\n const { logger, logs } = createTestLogs(options.logs)\n\n let target\n try {\n target = await createTestDatabaseTarget(\n internals.declaration.config.database.adapter,\n {\n mode: options.database?.mode ?? 'memory',\n strategy: options.database?.strategy,\n },\n )\n } catch (cause) {\n await rm(storageRoot, { recursive: true, force: true })\n throw cause\n }\n\n let app: TApp | undefined\n let testingHandle: RuntimeTestingHandle | undefined\n try {\n app = await internals.start(\n { ...defaultTestEnv, ...options.env },\n {\n database: target.connection,\n resolvedStorage,\n emailAdapter,\n authResolver: sessions.resolver,\n logger,\n forceMemoryRealtime: true,\n backgroundAutoStart: false,\n captureTestingHandle: (handle) => {\n testingHandle = handle\n },\n },\n )\n await provisionForTest(app as object, options.database?.schema ?? 'auto')\n } catch (cause) {\n const cleanupErrors: unknown[] = []\n if (app) {\n try {\n await app.close()\n } catch (error) {\n cleanupErrors.push(error)\n }\n }\n try {\n await target[Symbol.asyncDispose]()\n } catch (error) {\n cleanupErrors.push(error)\n }\n try {\n await rm(storageRoot, { recursive: true, force: true })\n } catch (error) {\n cleanupErrors.push(error)\n }\n if (cleanupErrors.length > 0) {\n throw new AggregateError(\n [cause, ...cleanupErrors],\n '[bunderstack] test fixture setup and cleanup failed',\n )\n }\n throw cause\n }\n\n if (!testingHandle) {\n await app.close()\n await target[Symbol.asyncDispose]()\n await rm(storageRoot, { recursive: true, force: true })\n throw new Error('[bunderstack] runtime did not provide test controls')\n }\n\n const auth = createTestAuth(app, email, sessions)\n const jobs = createTestJobs(testingHandle)\n const deferred: TestCleanup[] = []\n let closePromise: Promise<void> | undefined\n const defer = (cleanup: TestCleanup) => {\n if (closePromise) {\n throw new Error('[bunderstack] cannot defer cleanup after fixture close')\n }\n deferred.push(cleanup)\n }\n const close = () => {\n closePromise ??= (async () => {\n const errors: unknown[] = []\n for (const cleanup of deferred.reverse()) {\n try {\n await cleanup()\n } catch (error) {\n errors.push(error)\n }\n }\n try {\n await app.close()\n } catch (error) {\n errors.push(error)\n }\n try {\n await target[Symbol.asyncDispose]()\n } catch (error) {\n errors.push(error)\n }\n try {\n await rm(storageRoot, { recursive: true, force: true })\n } catch (error) {\n errors.push(error)\n }\n if (errors.length === 1) throw errors[0]\n if (errors.length > 1) {\n throw new AggregateError(errors, '[bunderstack] fixture cleanup failed')\n }\n })()\n return closePromise\n }\n\n return {\n app,\n auth,\n email,\n jobs,\n logs,\n storage,\n defer,\n client: (identity) => testClient(app as never, identity) as never,\n close,\n [Symbol.asyncDispose]: close,\n }\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { RuntimeJobFailure, RuntimeTestingHandle } from '../backend-internals.js';
1
+ import type { RuntimeJobFailure, RuntimeTestJob, RuntimeTestingHandle } from '../backend-internals.js';
2
2
  export type JobRunReport = {
3
3
  ticks: number;
4
4
  claimed: number;
@@ -13,6 +13,11 @@ export type RunUntilIdleOptions = RunNextOptions & {
13
13
  maxTicks?: number;
14
14
  failOnJobError?: boolean;
15
15
  };
16
+ export type TestJob = Readonly<RuntimeTestJob>;
17
+ export type TestJobFilter = {
18
+ name?: string;
19
+ dedupeKey?: string;
20
+ };
16
21
  export declare class TestJobsError extends Error {
17
22
  readonly report: JobRunReport;
18
23
  readonly failures: RuntimeJobFailure[];
@@ -27,6 +32,9 @@ export declare class TestJobsConvergenceError extends Error {
27
32
  export type TestJobs = {
28
33
  runNext(options?: RunNextOptions): Promise<JobRunReport>;
29
34
  runUntilIdle(options?: RunUntilIdleOptions): Promise<JobRunReport>;
35
+ inspect(filter?: TestJobFilter): Promise<readonly TestJob[]>;
36
+ pending(filter?: TestJobFilter): Promise<readonly TestJob[]>;
37
+ failed(filter?: TestJobFilter): Promise<readonly TestJob[]>;
30
38
  };
31
39
  export declare function createTestJobs(handle: RuntimeTestingHandle): TestJobs;
32
40
  //# sourceMappingURL=jobs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,sBAAsB,CAAA;AAE7B,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,CAAA;AAEpD,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,qBAAa,aAAc,SAAQ,KAAK;IAIpC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAC7B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE;IAJxC,SAAkB,IAAI,mBAAkB;gBAG7B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,iBAAiB,EAAE;CAMzC;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IAGrC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAFzC,SAAkB,IAAI,8BAA6B;gBAE9B,MAAM,EAAE,YAAY;CAK1C;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACnE,CAAA;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,QAAQ,CAiDrE"}
1
+ {"version":3,"file":"jobs.d.ts","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,oBAAoB,EACrB,MAAM,sBAAsB,CAAA;AAE7B,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,EAAE,MAAM,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAAE,GAAG,CAAC,EAAE,IAAI,GAAG,MAAM,CAAA;CAAE,CAAA;AAEpD,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG;IACjD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAA;AAE9C,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED,qBAAa,aAAc,SAAQ,KAAK;IAIpC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAC7B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,EAAE;IAJxC,SAAkB,IAAI,mBAAkB;gBAG7B,MAAM,EAAE,YAAY,EACpB,QAAQ,EAAE,iBAAiB,EAAE;CAMzC;AAED,qBAAa,wBAAyB,SAAQ,KAAK;IAGrC,QAAQ,CAAC,MAAM,EAAE,YAAY;IAFzC,SAAkB,IAAI,8BAA6B;gBAE9B,MAAM,EAAE,YAAY;CAK1C;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACxD,YAAY,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAClE,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IAC5D,OAAO,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;IAC5D,MAAM,CAAC,MAAM,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,SAAS,OAAO,EAAE,CAAC,CAAA;CAC5D,CAAA;AAMD,wBAAgB,cAAc,CAAC,MAAM,EAAE,oBAAoB,GAAG,QAAQ,CAoErE"}
@@ -20,7 +20,16 @@ function timestamp(value) {
20
20
  return value === undefined ? Date.now() : new Date(value).getTime();
21
21
  }
22
22
  export function createTestJobs(handle) {
23
+ const list = async (filter = {}, status) => {
24
+ const { jobs } = await handle.inspect(Date.now());
25
+ return Object.freeze(jobs.filter((job) => (!status || job.status === status) &&
26
+ (!filter.name || job.name === filter.name) &&
27
+ (!filter.dedupeKey || job.dedupeKey === filter.dedupeKey)));
28
+ };
23
29
  return {
30
+ inspect: (filter) => list(filter),
31
+ pending: (filter) => list(filter, 'pending'),
32
+ failed: (filter) => list(filter, 'failed'),
24
33
  async runNext(options = {}) {
25
34
  const now = timestamp(options.now);
26
35
  const tick = await handle.tick(now);
@@ -1 +1 @@
1
- {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RuntimeJobFailure,\n RuntimeTestingHandle,\n} from '../backend-internals'\n\nexport type JobRunReport = {\n ticks: number\n claimed: number\n ran: number\n failed: number\n remainingRunnable: number\n}\n\nexport type RunNextOptions = { now?: Date | number }\n\nexport type RunUntilIdleOptions = RunNextOptions & {\n maxTicks?: number\n failOnJobError?: boolean\n}\n\nexport class TestJobsError extends Error {\n override readonly name = 'TestJobsError'\n\n constructor(\n readonly report: JobRunReport,\n readonly failures: RuntimeJobFailure[],\n ) {\n super(\n `[bunderstack] ${failures.length} background job${failures.length === 1 ? '' : 's'} failed: ${failures.map((failure) => `${failure.name}: ${failure.lastError ?? 'unknown error'}`).join('; ')}`,\n )\n }\n}\n\nexport class TestJobsConvergenceError extends Error {\n override readonly name = 'TestJobsConvergenceError'\n\n constructor(readonly report: JobRunReport) {\n super(\n `[bunderstack] background jobs did not become idle after ${report.ticks} ticks (${report.remainingRunnable} still runnable)`,\n )\n }\n}\n\nexport type TestJobs = {\n runNext(options?: RunNextOptions): Promise<JobRunReport>\n runUntilIdle(options?: RunUntilIdleOptions): Promise<JobRunReport>\n}\n\nfunction timestamp(value: Date | number | undefined): number {\n return value === undefined ? Date.now() : new Date(value).getTime()\n}\n\nexport function createTestJobs(handle: RuntimeTestingHandle): TestJobs {\n return {\n async runNext(options = {}) {\n const now = timestamp(options.now)\n const tick = await handle.tick(now)\n const inspection = await handle.inspect(now)\n return {\n ticks: 1,\n ...tick,\n remainingRunnable: inspection.runnable,\n }\n },\n\n async runUntilIdle(options = {}) {\n const now = timestamp(options.now)\n const maxTicks = options.maxTicks ?? 100\n if (!Number.isInteger(maxTicks) || maxTicks <= 0) {\n throw new TypeError('[bunderstack] maxTicks must be a positive integer')\n }\n\n const report: JobRunReport = {\n ticks: 0,\n claimed: 0,\n ran: 0,\n failed: 0,\n remainingRunnable: 0,\n }\n let inspection: Awaited<ReturnType<RuntimeTestingHandle['inspect']>>\n\n do {\n const tick = await handle.tick(now)\n report.ticks++\n report.claimed += tick.claimed\n report.ran += tick.ran\n report.failed += tick.failed\n inspection = await handle.inspect(now)\n report.remainingRunnable = inspection.runnable\n\n if (inspection.runnable > 0 && report.ticks >= maxTicks) {\n throw new TestJobsConvergenceError(report)\n }\n } while (inspection.runnable > 0)\n\n if ((options.failOnJobError ?? true) && inspection.failed.length > 0) {\n throw new TestJobsError(report, inspection.failed)\n }\n return report\n },\n }\n}\n"]}
1
+ {"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n RuntimeJobFailure,\n RuntimeTestJob,\n RuntimeTestingHandle,\n} from '../backend-internals'\n\nexport type JobRunReport = {\n ticks: number\n claimed: number\n ran: number\n failed: number\n remainingRunnable: number\n}\n\nexport type RunNextOptions = { now?: Date | number }\n\nexport type RunUntilIdleOptions = RunNextOptions & {\n maxTicks?: number\n failOnJobError?: boolean\n}\n\nexport type TestJob = Readonly<RuntimeTestJob>\n\nexport type TestJobFilter = {\n name?: string\n dedupeKey?: string\n}\n\nexport class TestJobsError extends Error {\n override readonly name = 'TestJobsError'\n\n constructor(\n readonly report: JobRunReport,\n readonly failures: RuntimeJobFailure[],\n ) {\n super(\n `[bunderstack] ${failures.length} background job${failures.length === 1 ? '' : 's'} failed: ${failures.map((failure) => `${failure.name}: ${failure.lastError ?? 'unknown error'}`).join('; ')}`,\n )\n }\n}\n\nexport class TestJobsConvergenceError extends Error {\n override readonly name = 'TestJobsConvergenceError'\n\n constructor(readonly report: JobRunReport) {\n super(\n `[bunderstack] background jobs did not become idle after ${report.ticks} ticks (${report.remainingRunnable} still runnable)`,\n )\n }\n}\n\nexport type TestJobs = {\n runNext(options?: RunNextOptions): Promise<JobRunReport>\n runUntilIdle(options?: RunUntilIdleOptions): Promise<JobRunReport>\n inspect(filter?: TestJobFilter): Promise<readonly TestJob[]>\n pending(filter?: TestJobFilter): Promise<readonly TestJob[]>\n failed(filter?: TestJobFilter): Promise<readonly TestJob[]>\n}\n\nfunction timestamp(value: Date | number | undefined): number {\n return value === undefined ? Date.now() : new Date(value).getTime()\n}\n\nexport function createTestJobs(handle: RuntimeTestingHandle): TestJobs {\n const list = async (\n filter: TestJobFilter = {},\n status?: TestJob['status'],\n ) => {\n const { jobs } = await handle.inspect(Date.now())\n return Object.freeze(\n jobs.filter(\n (job) =>\n (!status || job.status === status) &&\n (!filter.name || job.name === filter.name) &&\n (!filter.dedupeKey || job.dedupeKey === filter.dedupeKey),\n ),\n )\n }\n\n return {\n inspect: (filter) => list(filter),\n pending: (filter) => list(filter, 'pending'),\n failed: (filter) => list(filter, 'failed'),\n\n async runNext(options = {}) {\n const now = timestamp(options.now)\n const tick = await handle.tick(now)\n const inspection = await handle.inspect(now)\n return {\n ticks: 1,\n ...tick,\n remainingRunnable: inspection.runnable,\n }\n },\n\n async runUntilIdle(options = {}) {\n const now = timestamp(options.now)\n const maxTicks = options.maxTicks ?? 100\n if (!Number.isInteger(maxTicks) || maxTicks <= 0) {\n throw new TypeError('[bunderstack] maxTicks must be a positive integer')\n }\n\n const report: JobRunReport = {\n ticks: 0,\n claimed: 0,\n ran: 0,\n failed: 0,\n remainingRunnable: 0,\n }\n let inspection: Awaited<ReturnType<RuntimeTestingHandle['inspect']>>\n\n do {\n const tick = await handle.tick(now)\n report.ticks++\n report.claimed += tick.claimed\n report.ran += tick.ran\n report.failed += tick.failed\n inspection = await handle.inspect(now)\n report.remainingRunnable = inspection.runnable\n\n if (inspection.runnable > 0 && report.ticks >= maxTicks) {\n throw new TestJobsConvergenceError(report)\n }\n } while (inspection.runnable > 0)\n\n if ((options.failOnJobError ?? true) && inspection.failed.length > 0) {\n throw new TestJobsError(report, inspection.failed)\n }\n return report\n },\n }\n}\n"]}
@@ -0,0 +1,18 @@
1
+ import type { BunderstackLogger, BunderstackLogLevel } from '../logging.js';
2
+ export type TestLogMode = 'capture' | 'inherit' | 'silent';
3
+ export type TestLogEntry = {
4
+ readonly level: BunderstackLogLevel;
5
+ readonly message: string;
6
+ readonly args: readonly unknown[];
7
+ };
8
+ export type TestLogs = {
9
+ readonly entries: readonly Readonly<TestLogEntry>[];
10
+ readonly errors: readonly Readonly<TestLogEntry>[];
11
+ readonly warnings: readonly Readonly<TestLogEntry>[];
12
+ clear(): void;
13
+ };
14
+ export declare function createTestLogs(mode?: TestLogMode): {
15
+ logger: BunderstackLogger;
16
+ logs: TestLogs;
17
+ };
18
+ //# sourceMappingURL=logs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.d.ts","sourceRoot":"","sources":["../../src/testing/logs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAExE,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE1D,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAA;IACnC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAA;IACnD,QAAQ,CAAC,MAAM,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAA;IAClD,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAA;IACpD,KAAK,IAAI,IAAI,CAAA;CACd,CAAA;AAcD,wBAAgB,cAAc,CAAC,IAAI,GAAE,WAAuB,GAAG;IAC7D,MAAM,EAAE,iBAAiB,CAAA;IACzB,IAAI,EAAE,QAAQ,CAAA;CACf,CAuCA"}
@@ -0,0 +1,53 @@
1
+ function render(value) {
2
+ if (value instanceof Error)
3
+ return `${value.name}: ${value.message}`;
4
+ if (typeof value === 'string')
5
+ return value;
6
+ if (value === null)
7
+ return 'null';
8
+ if (value === undefined)
9
+ return 'undefined';
10
+ try {
11
+ return JSON.stringify(value);
12
+ }
13
+ catch {
14
+ return String(value);
15
+ }
16
+ }
17
+ export function createTestLogs(mode = 'capture') {
18
+ const entries = [];
19
+ const write = (level, args) => {
20
+ if (mode !== 'silent') {
21
+ entries.push(Object.freeze({
22
+ level,
23
+ message: args.map(render).join(' '),
24
+ args: Object.freeze([...args]),
25
+ }));
26
+ }
27
+ if (mode === 'inherit')
28
+ console[level](...args);
29
+ };
30
+ const snapshot = (level) => Object.freeze(entries.filter((entry) => !level || entry.level === level).slice());
31
+ return {
32
+ logger: {
33
+ info: (...args) => write('info', args),
34
+ warn: (...args) => write('warn', args),
35
+ error: (...args) => write('error', args),
36
+ },
37
+ logs: {
38
+ get entries() {
39
+ return snapshot();
40
+ },
41
+ get errors() {
42
+ return snapshot('error');
43
+ },
44
+ get warnings() {
45
+ return snapshot('warn');
46
+ },
47
+ clear() {
48
+ entries.length = 0;
49
+ },
50
+ },
51
+ };
52
+ }
53
+ //# sourceMappingURL=logs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logs.js","sourceRoot":"","sources":["../../src/testing/logs.ts"],"names":[],"mappings":"AAiBA,SAAS,MAAM,CAAC,KAAc;IAC5B,IAAI,KAAK,YAAY,KAAK;QAAE,OAAO,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,EAAE,CAAA;IACpE,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAA;IAC3C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,MAAM,CAAA;IACjC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,WAAW,CAAA;IAC3C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAA;IACtB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAoB,SAAS;IAI1D,MAAM,OAAO,GAAmB,EAAE,CAAA;IAClC,MAAM,KAAK,GAAG,CAAC,KAA0B,EAAE,IAAe,EAAE,EAAE;QAC5D,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtB,OAAO,CAAC,IAAI,CACV,MAAM,CAAC,MAAM,CAAC;gBACZ,KAAK;gBACL,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;gBACnC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;aAC/B,CAAC,CACH,CAAA;QACH,CAAC;QACD,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACjD,CAAC,CAAA;IACD,MAAM,QAAQ,GAAG,CAAC,KAA2B,EAAE,EAAE,CAC/C,MAAM,CAAC,MAAM,CACX,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,KAAK,EAAE,CACnE,CAAA;IACH,OAAO;QACL,MAAM,EAAE;YACN,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;YACtC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC;YACtC,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC;SACzC;QACD,IAAI,EAAE;YACJ,IAAI,OAAO;gBACT,OAAO,QAAQ,EAAE,CAAA;YACnB,CAAC;YACD,IAAI,MAAM;gBACR,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAA;YAC1B,CAAC;YACD,IAAI,QAAQ;gBACV,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAA;YACzB,CAAC;YACD,KAAK;gBACH,OAAO,CAAC,MAAM,GAAG,CAAC,CAAA;YACpB,CAAC;SACF;KACF,CAAA;AACH,CAAC","sourcesContent":["import type { BunderstackLogger, BunderstackLogLevel } from '../logging'\n\nexport type TestLogMode = 'capture' | 'inherit' | 'silent'\n\nexport type TestLogEntry = {\n readonly level: BunderstackLogLevel\n readonly message: string\n readonly args: readonly unknown[]\n}\n\nexport type TestLogs = {\n readonly entries: readonly Readonly<TestLogEntry>[]\n readonly errors: readonly Readonly<TestLogEntry>[]\n readonly warnings: readonly Readonly<TestLogEntry>[]\n clear(): void\n}\n\nfunction render(value: unknown): string {\n if (value instanceof Error) return `${value.name}: ${value.message}`\n if (typeof value === 'string') return value\n if (value === null) return 'null'\n if (value === undefined) return 'undefined'\n try {\n return JSON.stringify(value)\n } catch {\n return String(value)\n }\n}\n\nexport function createTestLogs(mode: TestLogMode = 'capture'): {\n logger: BunderstackLogger\n logs: TestLogs\n} {\n const entries: TestLogEntry[] = []\n const write = (level: BunderstackLogLevel, args: unknown[]) => {\n if (mode !== 'silent') {\n entries.push(\n Object.freeze({\n level,\n message: args.map(render).join(' '),\n args: Object.freeze([...args]),\n }),\n )\n }\n if (mode === 'inherit') console[level](...args)\n }\n const snapshot = (level?: BunderstackLogLevel) =>\n Object.freeze(\n entries.filter((entry) => !level || entry.level === level).slice(),\n )\n return {\n logger: {\n info: (...args) => write('info', args),\n warn: (...args) => write('warn', args),\n error: (...args) => write('error', args),\n },\n logs: {\n get entries() {\n return snapshot()\n },\n get errors() {\n return snapshot('error')\n },\n get warnings() {\n return snapshot('warn')\n },\n clear() {\n entries.length = 0\n },\n },\n }\n}\n"]}
package/dist/testing.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- export { createTestApp } from './testing/fixture.js';
2
- export type { TestFixture, TestOptions } from './testing/fixture.js';
1
+ export { configureTestApp, createTestApp } from './testing/fixture.js';
2
+ export type { ConfiguredTestFixture, TestCleanup, TestConfigureOptions, TestFactory, TestFixture, TestMethod, TestOptions, TestSetup, } from './testing/fixture.js';
3
3
  export { mockAuthSession, TestAuthError } from './testing/auth.js';
4
- export type { SignUpEmailInput, TestAuth, TestIdentity, TestUser, } from './testing/auth.js';
4
+ export type { SignInEmailInput, SignUpEmailInput, TestAuth, TestIdentity, TestSession, TestUser, } from './testing/auth.js';
5
5
  export type { CapturedEmail, TestEmail } from './testing/email.js';
6
6
  export type { TestStorage } from './testing/storage.js';
7
+ export type { TestLogEntry, TestLogMode, TestLogs } from './testing/logs.js';
7
8
  export { TestJobsConvergenceError, TestJobsError } from './testing/jobs.js';
8
- export type { JobRunReport, RunNextOptions, RunUntilIdleOptions, TestJobs, } from './testing/jobs.js';
9
+ export type { JobRunReport, RunNextOptions, RunUntilIdleOptions, TestJob, TestJobFilter, TestJobs, } from './testing/jobs.js';
9
10
  export type { TestDatabaseStrategy, TestDatabaseTarget, TestDatabaseTargetOptions, } from './database/adapter.js';
10
11
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACjE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EACV,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACxE,YAAY,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACnE,YAAY,EACV,qBAAqB,EACrB,WAAW,EACX,oBAAoB,EACpB,WAAW,EACX,WAAW,EACX,UAAU,EACV,WAAW,EACX,SAAS,GACV,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAC/D,YAAY,EACV,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,EACR,YAAY,EACZ,WAAW,EACX,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC/D,YAAY,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AACpD,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AACxE,YAAY,EACV,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,OAAO,EACP,aAAa,EACb,QAAQ,GACT,MAAM,gBAAgB,CAAA;AACvB,YAAY,EACV,oBAAoB,EACpB,kBAAkB,EAClB,yBAAyB,GAC1B,MAAM,oBAAoB,CAAA"}
package/dist/testing.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // src/testing.ts — test utilities for Bunderstack applications.
2
- export { createTestApp } from './testing/fixture.js';
2
+ export { configureTestApp, createTestApp } from './testing/fixture.js';
3
3
  export { mockAuthSession, TestAuthError } from './testing/auth.js';
4
4
  export { TestJobsConvergenceError, TestJobsError } from './testing/jobs.js';
5
5
  //# sourceMappingURL=testing.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAEjD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAS/D,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA","sourcesContent":["// src/testing.ts — test utilities for Bunderstack applications.\n\nexport { createTestApp } from './testing/fixture'\nexport type { TestFixture, TestOptions } from './testing/fixture'\nexport { mockAuthSession, TestAuthError } from './testing/auth'\nexport type {\n SignUpEmailInput,\n TestAuth,\n TestIdentity,\n TestUser,\n} from './testing/auth'\nexport type { CapturedEmail, TestEmail } from './testing/email'\nexport type { TestStorage } from './testing/storage'\nexport { TestJobsConvergenceError, TestJobsError } from './testing/jobs'\nexport type {\n JobRunReport,\n RunNextOptions,\n RunUntilIdleOptions,\n TestJobs,\n} from './testing/jobs'\nexport type {\n TestDatabaseStrategy,\n TestDatabaseTarget,\n TestDatabaseTargetOptions,\n} from './database/adapter'\n"]}
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,gEAAgE;AAEhE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAWnE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAY/D,OAAO,EAAE,wBAAwB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA","sourcesContent":["// src/testing.ts — test utilities for Bunderstack applications.\n\nexport { configureTestApp, createTestApp } from './testing/fixture'\nexport type {\n ConfiguredTestFixture,\n TestCleanup,\n TestConfigureOptions,\n TestFactory,\n TestFixture,\n TestMethod,\n TestOptions,\n TestSetup,\n} from './testing/fixture'\nexport { mockAuthSession, TestAuthError } from './testing/auth'\nexport type {\n SignInEmailInput,\n SignUpEmailInput,\n TestAuth,\n TestIdentity,\n TestSession,\n TestUser,\n} from './testing/auth'\nexport type { CapturedEmail, TestEmail } from './testing/email'\nexport type { TestStorage } from './testing/storage'\nexport type { TestLogEntry, TestLogMode, TestLogs } from './testing/logs'\nexport { TestJobsConvergenceError, TestJobsError } from './testing/jobs'\nexport type {\n JobRunReport,\n RunNextOptions,\n RunUntilIdleOptions,\n TestJob,\n TestJobFilter,\n TestJobs,\n} from './testing/jobs'\nexport type {\n TestDatabaseStrategy,\n TestDatabaseTarget,\n TestDatabaseTargetOptions,\n} from './database/adapter'\n"]}
package/llms.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  BUNDERSTACK
2
2
 
3
- A batteries-included backend framework for Bun. Version 0.17 (beta).
3
+ A batteries-included backend framework for Bun. Version 0.22.1.
4
4
  Docs: https://bunderstack.kcrz.dev/docs
5
5
  This file is written for coding agents. It is dense on purpose.
6
6
 
@@ -88,25 +88,32 @@ TESTING
88
88
  Use lexical fixture ownership; do not use a process-wide app registry or a
89
89
  global afterEach hook:
90
90
 
91
+ export const createFixture = backend.test.configure({
92
+ database: { schema: 'push' },
93
+ setup: async (t) => {
94
+ const identity = t.auth.mockSession({
95
+ id: 'user-1', email: 'dev@example.com', name: 'Developer',
96
+ })
97
+ return { identity, client: t.client(identity) }
98
+ },
99
+ })
100
+
91
101
  import { backend } from './backend'
92
102
 
93
103
  test('creates a post', async () => {
94
- await using t = await backend.test({ database: { schema: 'push' } })
95
- const identity = t.auth.mockSession({
96
- id: 'user-1',
97
- email: 'dev@example.com',
98
- name: 'Developer',
99
- })
100
- const client = t.client(identity)
104
+ await using t = await createFixture()
105
+ const { client, identity } = t.context
101
106
  await client.posts.create({ title: 'Hello', userId: identity.user.id })
102
107
  })
103
108
 
104
109
  The fixture owns isolated database, email, storage, realtime, and job state.
105
- Its auth helper creates user/session identities only; application-specific
106
- organization setup belongs in a typed application helper. t.jobs.tick() claims
107
- one snapshot of runnable jobs. t.jobs.settle() repeats snapshots until the
108
- queue converges, a terminal job fails, or its limits are reached; it does not
109
- advance the clock to delayed jobs automatically.
110
+ fixture.defer() registers LIFO async cleanup. fixture.logs captures internal
111
+ runtime logs by default. Auth supports sign-up, sign-in, session lookup, sign-out,
112
+ email verification, and multiple header-scoped mock identities. t.jobs.runNext()
113
+ claims one snapshot of runnable jobs. t.jobs.runUntilIdle() repeats snapshots
114
+ until the queue converges, a terminal job fails, or its limits are reached; it
115
+ does not advance the clock to delayed jobs automatically. inspect(), pending(),
116
+ and failed() expose queue rows for assertions.
110
117
 
111
118
  BASES
112
119
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bunderstack",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "description": "Batteries-included backend framework for Bun: type-safe oRPC APIs, auth, storage, realtime, jobs, email, and validated env from one config.",
5
5
  "keywords": [
6
6
  "backend",