bunderstack 0.22.0 → 0.23.0
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.
- package/CHANGELOG.md +45 -0
- package/README.md +2 -1
- package/dist/api/catalog.d.ts +8 -0
- package/dist/api/catalog.d.ts.map +1 -0
- package/dist/api/catalog.js +56 -0
- package/dist/api/catalog.js.map +1 -0
- package/dist/api/context.d.ts +3 -0
- package/dist/api/context.d.ts.map +1 -1
- package/dist/api/context.js +2 -0
- package/dist/api/context.js.map +1 -1
- package/dist/api/registry.d.ts.map +1 -1
- package/dist/api/registry.js +1 -0
- package/dist/api/registry.js.map +1 -1
- package/dist/api/router.d.ts +3 -0
- package/dist/api/router.d.ts.map +1 -1
- package/dist/api/router.js +7 -2
- package/dist/api/router.js.map +1 -1
- package/dist/backend-internals.d.ts +11 -0
- package/dist/backend-internals.d.ts.map +1 -1
- package/dist/backend-internals.js.map +1 -1
- package/dist/backend.d.ts +2 -2
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +18 -5
- package/dist/backend.js.map +1 -1
- package/dist/blueprint.d.ts +21 -1
- package/dist/blueprint.d.ts.map +1 -1
- package/dist/blueprint.js +45 -17
- package/dist/blueprint.js.map +1 -1
- package/dist/env.d.ts +18 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +1 -0
- package/dist/env.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/jobs/worker.d.ts +12 -0
- package/dist/jobs/worker.d.ts.map +1 -1
- package/dist/jobs/worker.js +32 -1
- package/dist/jobs/worker.js.map +1 -1
- package/dist/logging.d.ts +8 -0
- package/dist/logging.d.ts.map +1 -0
- package/dist/logging.js +6 -0
- package/dist/logging.js.map +1 -0
- package/dist/manifest.d.ts +20 -0
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +68 -6
- package/dist/manifest.js.map +1 -1
- package/dist/readiness-probes.d.ts +4 -0
- package/dist/readiness-probes.d.ts.map +1 -0
- package/dist/readiness-probes.js +23 -0
- package/dist/readiness-probes.js.map +1 -0
- package/dist/readiness.d.ts +39 -0
- package/dist/readiness.d.ts.map +1 -0
- package/dist/readiness.js +72 -0
- package/dist/readiness.js.map +1 -0
- package/dist/runtime.d.ts +4 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +32 -4
- package/dist/runtime.js.map +1 -1
- package/dist/testing/auth.d.ts +30 -2
- package/dist/testing/auth.d.ts.map +1 -1
- package/dist/testing/auth.js +103 -14
- package/dist/testing/auth.js.map +1 -1
- package/dist/testing/fixture.d.ts +18 -0
- package/dist/testing/fixture.d.ts.map +1 -1
- package/dist/testing/fixture.js +55 -2
- package/dist/testing/fixture.js.map +1 -1
- package/dist/testing/jobs.d.ts +9 -1
- package/dist/testing/jobs.d.ts.map +1 -1
- package/dist/testing/jobs.js +9 -0
- package/dist/testing/jobs.js.map +1 -1
- package/dist/testing/logs.d.ts +18 -0
- package/dist/testing/logs.d.ts.map +1 -0
- package/dist/testing/logs.js +53 -0
- package/dist/testing/logs.js.map +1 -0
- package/dist/testing.d.ts +5 -4
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/llms.txt +20 -13
- package/package.json +1 -1
package/dist/testing/auth.js
CHANGED
|
@@ -8,6 +8,34 @@ export class TestAuthError extends Error {
|
|
|
8
8
|
this.body = body;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
|
+
const TEST_SESSION_HEADER = 'x-bunderstack-test-session';
|
|
12
|
+
export function createTestSessionRegistry() {
|
|
13
|
+
const sessions = new Map();
|
|
14
|
+
return {
|
|
15
|
+
resolver: {
|
|
16
|
+
api: {
|
|
17
|
+
async getSession({ headers }) {
|
|
18
|
+
const token = headers.get(TEST_SESSION_HEADER);
|
|
19
|
+
if (token)
|
|
20
|
+
return sessions.get(token) ?? null;
|
|
21
|
+
return null;
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
mocked(headers) {
|
|
26
|
+
const token = headers.get(TEST_SESSION_HEADER);
|
|
27
|
+
return token ? sessions.get(token) : undefined;
|
|
28
|
+
},
|
|
29
|
+
mock(user, session = null) {
|
|
30
|
+
const token = crypto.randomUUID();
|
|
31
|
+
sessions.set(token, { user, session });
|
|
32
|
+
return {
|
|
33
|
+
user,
|
|
34
|
+
headers: new Headers({ [TEST_SESSION_HEADER]: token }),
|
|
35
|
+
};
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
11
39
|
export function mockAuthSession(app, resolver) {
|
|
12
40
|
const auth = app.auth;
|
|
13
41
|
if (auth?.api) {
|
|
@@ -22,29 +50,90 @@ function cookieHeaders(response) {
|
|
|
22
50
|
.join('; ');
|
|
23
51
|
return new Headers(cookie ? { cookie } : undefined);
|
|
24
52
|
}
|
|
25
|
-
export function createTestAuth(app) {
|
|
53
|
+
export function createTestAuth(app, email, sessions) {
|
|
54
|
+
const request = async (path, init = {}, headers) => {
|
|
55
|
+
const mergedHeaders = new Headers(headers);
|
|
56
|
+
if (init.body !== undefined && !mergedHeaders.has('content-type')) {
|
|
57
|
+
mergedHeaders.set('content-type', 'application/json');
|
|
58
|
+
}
|
|
59
|
+
return app.handler(new Request(new URL(path, 'http://bunderstack.test'), {
|
|
60
|
+
...init,
|
|
61
|
+
headers: mergedHeaders,
|
|
62
|
+
}));
|
|
63
|
+
};
|
|
64
|
+
const identityFromResponse = async (response) => {
|
|
65
|
+
const body = await response.json().catch(() => null);
|
|
66
|
+
if (!response.ok)
|
|
67
|
+
throw new TestAuthError(response.status, body);
|
|
68
|
+
const user = body?.user;
|
|
69
|
+
if (!user)
|
|
70
|
+
throw new TestAuthError(response.status, body);
|
|
71
|
+
return { user, headers: cookieHeaders(response) };
|
|
72
|
+
};
|
|
73
|
+
const currentSession = async (identity) => {
|
|
74
|
+
const mocked = sessions.mocked(identity.headers);
|
|
75
|
+
if (mocked)
|
|
76
|
+
return mocked;
|
|
77
|
+
const auth = app.auth;
|
|
78
|
+
const result = await auth.api.getSession({ headers: identity.headers });
|
|
79
|
+
if (!result || typeof result !== 'object' || !('user' in result)) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const value = result;
|
|
83
|
+
return value.user
|
|
84
|
+
? { user: value.user, session: value.session ?? null }
|
|
85
|
+
: null;
|
|
86
|
+
};
|
|
26
87
|
return {
|
|
27
88
|
async signUpEmail(input) {
|
|
28
|
-
|
|
89
|
+
return identityFromResponse(await request('/api/auth/sign-up/email', {
|
|
29
90
|
method: 'POST',
|
|
30
|
-
headers: { 'content-type': 'application/json' },
|
|
31
91
|
body: JSON.stringify({
|
|
32
92
|
email: input.email,
|
|
33
93
|
name: input.name,
|
|
34
94
|
password: input.password ?? 'password-123',
|
|
35
95
|
}),
|
|
36
96
|
}));
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
97
|
+
},
|
|
98
|
+
async signInEmail(input) {
|
|
99
|
+
return identityFromResponse(await request('/api/auth/sign-in/email', {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
body: JSON.stringify({
|
|
102
|
+
email: input.email,
|
|
103
|
+
password: input.password ?? 'password-123',
|
|
104
|
+
}),
|
|
105
|
+
}));
|
|
106
|
+
},
|
|
107
|
+
async getSession(identity) {
|
|
108
|
+
return currentSession(identity);
|
|
109
|
+
},
|
|
110
|
+
async signOut(identity) {
|
|
111
|
+
const response = await request('/api/auth/sign-out', { method: 'POST' }, identity.headers);
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
throw new TestAuthError(response.status, await response.json().catch(() => null));
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
async verifyEmail(identity) {
|
|
117
|
+
const message = [...email.sent]
|
|
118
|
+
.reverse()
|
|
119
|
+
.find((candidate) => candidate.to.includes(identity.user.email));
|
|
120
|
+
const content = `${message?.text ?? ''}\n${message?.html ?? ''}`;
|
|
121
|
+
const url = content.match(/https?:\/\/[^\s<>"']+\/api\/auth\/verify-email\?[^\s<>"']+/)?.[0];
|
|
122
|
+
if (!url) {
|
|
123
|
+
throw new Error(`[bunderstack] no verification link captured for ${identity.user.email}`);
|
|
124
|
+
}
|
|
125
|
+
const response = await request(url, {}, identity.headers);
|
|
126
|
+
if (response.status >= 400) {
|
|
127
|
+
throw new TestAuthError(response.status, await response.text());
|
|
128
|
+
}
|
|
129
|
+
const user = (await currentSession(identity))?.user;
|
|
130
|
+
if (!user) {
|
|
131
|
+
throw new Error('[bunderstack] verified identity has no active session');
|
|
132
|
+
}
|
|
133
|
+
return { user, headers: identity.headers };
|
|
134
|
+
},
|
|
135
|
+
mockSession(user, session) {
|
|
136
|
+
return sessions.mock(user, session);
|
|
48
137
|
},
|
|
49
138
|
};
|
|
50
139
|
}
|
package/dist/testing/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/testing/auth.ts"],"names":[],"mappings":"AAeA,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,MAAM,CAAQ;IACd,IAAI,CAAS;IAEtB,YAAY,MAAc,EAAE,IAAa;QACvC,KAAK,CAAC,+BAA+B,MAAM,GAAG,CAAC,CAAA;QAC/C,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF;AAyBD,MAAM,UAAU,eAAe,CAG7B,GAAuB,EACvB,QAGS;IAET,MAAM,IAAI,GAAG,GAAG,CAAC,IAA0C,CAAA;IAC3D,IAAI,IAAI,EAAE,GAAG,EAAE,CAAC;QACd,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,QAEA,CAAA;IACxB,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,QAAkB;IACvC,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC,YAAY,EAAE,CAAA;IAC9C,MAAM,MAAM,GAAG,MAAM;SAClB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACtC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAClD,IAAI,CAAC,IAAI,CAAC,CAAA;IACb,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;AACrD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,GAG9B;IACC,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,KAAK;YACrB,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,OAAO,CAChC,IAAI,OAAO,CAAC,gDAAgD,EAAE;gBAC5D,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,KAAK,EAAE,KAAK,CAAC,KAAK;oBAClB,IAAI,EAAE,KAAK,CAAC,IAAI;oBAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,cAAc;iBAC3C,CAAC;aACH,CAAC,CACH,CAAA;YACD,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAA;YACpD,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,MAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;YAChE,MAAM,IAAI,GAAI,IAAmC,EAAE,IAAI,CAAA;YACvD,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,aAAa,CAAC,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;YACzD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,EAAE,CAAA;QACnD,CAAC;QACD,WAAW,CAAC,IAAI;YACd,eAAe,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;YAC3D,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,OAAO,EAAE,EAAE,CAAA;QACzC,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["export type TestUser = {\n id: string\n email: string\n name: string\n emailVerified?: boolean\n image?: string | null\n createdAt?: string | Date\n updatedAt?: string | Date\n}\n\nexport type TestIdentity = {\n user: TestUser\n headers: Headers\n}\n\nexport class TestAuthError extends Error {\n readonly status: number\n readonly body: unknown\n\n constructor(status: number, body: unknown) {\n super(`Better Auth request failed (${status})`)\n this.name = 'TestAuthError'\n this.status = status\n this.body = body\n }\n}\n\nexport type SignUpEmailInput = {\n email: string\n name: string\n password?: string\n}\n\nexport type TestAuth = {\n signUpEmail(input: SignUpEmailInput): Promise<TestIdentity>\n mockSession<TUser extends TestUser>(\n user: TUser,\n ): TestIdentity & { user: TUser }\n}\n\nexport type AuthSessionResolverLike = {\n api: {\n getSession: (opts: { headers: Headers }) => Promise<unknown>\n }\n}\n\nexport type BunderstackAppLike = {\n auth: unknown\n}\n\nexport function mockAuthSession<\n TUser extends { id: string; email: string; name?: string; role?: string },\n>(\n app: BunderstackAppLike,\n resolver: (opts: { headers: Headers }) => Promise<{\n user: TUser\n session?: { activeOrganizationId?: string | null } | null\n } | null>,\n): void {\n const auth = app.auth as unknown as AuthSessionResolverLike\n if (auth?.api) {\n auth.api.getSession = resolver as unknown as (opts: {\n headers: Headers\n }) => Promise<unknown>\n }\n}\n\nfunction cookieHeaders(response: Response): Headers {\n const values = response.headers.getSetCookie()\n const cookie = values\n .map((value) => value.split(';', 1)[0])\n .filter((value): value is string => Boolean(value))\n .join('; ')\n return new Headers(cookie ? { cookie } : undefined)\n}\n\nexport function createTestAuth(app: {\n handler(request: Request): Promise<Response>\n auth: unknown\n}): TestAuth {\n return {\n async signUpEmail(input) {\n const response = await app.handler(\n new Request('http://bunderstack.test/api/auth/sign-up/email', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n email: input.email,\n name: input.name,\n password: input.password ?? 'password-123',\n }),\n }),\n )\n const body = await response.json().catch(() => null)\n if (!response.ok) throw new TestAuthError(response.status, body)\n const user = (body as { user?: TestUser } | null)?.user\n if (!user) throw new TestAuthError(response.status, body)\n return { user, headers: cookieHeaders(response) }\n },\n mockSession(user) {\n mockAuthSession(app, async () => ({ user, session: null }))\n return { user, headers: new Headers() }\n },\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/testing/auth.ts"],"names":[],"mappings":"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","sourcesContent":["export type TestUser = {\n id: string\n email: string\n name: string\n emailVerified?: boolean\n image?: string | null\n createdAt?: string | Date\n updatedAt?: string | Date\n}\n\nexport type TestIdentity = {\n user: TestUser\n headers: Headers\n}\n\nexport class TestAuthError extends Error {\n readonly status: number\n readonly body: unknown\n\n constructor(status: number, body: unknown) {\n super(`Better Auth request failed (${status})`)\n this.name = 'TestAuthError'\n this.status = status\n this.body = body\n }\n}\n\nexport type SignUpEmailInput = {\n email: string\n name: string\n password?: string\n}\n\nexport type SignInEmailInput = {\n email: string\n password?: string\n}\n\nexport type TestSession = {\n user: TestUser\n session: { activeOrganizationId?: string | null } | null\n}\n\nexport type TestAuth = {\n signUpEmail(input: SignUpEmailInput): Promise<TestIdentity>\n signInEmail(input: SignInEmailInput): Promise<TestIdentity>\n getSession(identity: TestIdentity): Promise<TestSession | null>\n signOut(identity: TestIdentity): Promise<void>\n verifyEmail(identity: TestIdentity): Promise<TestIdentity>\n mockSession<TUser extends TestUser>(\n user: TUser,\n session?: { activeOrganizationId?: string | null } | null,\n ): TestIdentity & { user: TUser }\n}\n\nexport type AuthSessionResolverLike = {\n api: {\n getSession: (opts: { headers: Headers }) => Promise<unknown>\n }\n}\n\nexport type BunderstackAppLike = {\n auth: unknown\n}\n\nconst TEST_SESSION_HEADER = 'x-bunderstack-test-session'\n\nexport type TestSessionRegistry = {\n readonly resolver: AuthSessionResolver\n mocked(headers: Headers): TestSession | undefined\n mock<TUser extends TestUser>(\n user: TUser,\n session?: { activeOrganizationId?: string | null } | null,\n ): TestIdentity & { user: TUser }\n}\n\nexport function createTestSessionRegistry(): TestSessionRegistry {\n const sessions = new Map<string, TestSession>()\n return {\n resolver: {\n api: {\n async getSession({ headers }) {\n const token = headers.get(TEST_SESSION_HEADER)\n if (token) return sessions.get(token) ?? null\n return null\n },\n },\n },\n mocked(headers) {\n const token = headers.get(TEST_SESSION_HEADER)\n return token ? sessions.get(token) : undefined\n },\n mock(user, session = null) {\n const token = crypto.randomUUID()\n sessions.set(token, { user, session })\n return {\n user,\n headers: new Headers({ [TEST_SESSION_HEADER]: token }),\n }\n },\n }\n}\n\nexport function mockAuthSession<\n TUser extends { id: string; email: string; name?: string; role?: string },\n>(\n app: BunderstackAppLike,\n resolver: (opts: { headers: Headers }) => Promise<{\n user: TUser\n session?: { activeOrganizationId?: string | null } | null\n } | null>,\n): void {\n const auth = app.auth as unknown as AuthSessionResolverLike\n if (auth?.api) {\n auth.api.getSession = resolver as unknown as (opts: {\n headers: Headers\n }) => Promise<unknown>\n }\n}\n\nfunction cookieHeaders(response: Response): Headers {\n const values = response.headers.getSetCookie()\n const cookie = values\n .map((value) => value.split(';', 1)[0])\n .filter((value): value is string => Boolean(value))\n .join('; ')\n return new Headers(cookie ? { cookie } : undefined)\n}\n\nexport function createTestAuth(\n app: {\n handler(request: Request): Promise<Response>\n auth: unknown\n },\n email: TestEmail,\n sessions: TestSessionRegistry,\n): TestAuth {\n const request = async (\n path: string,\n init: RequestInit = {},\n headers?: Headers,\n ) => {\n const mergedHeaders = new Headers(headers)\n if (init.body !== undefined && !mergedHeaders.has('content-type')) {\n mergedHeaders.set('content-type', 'application/json')\n }\n return app.handler(\n new Request(new URL(path, 'http://bunderstack.test'), {\n ...init,\n headers: mergedHeaders,\n }),\n )\n }\n\n const identityFromResponse = async (response: Response) => {\n const body = await response.json().catch(() => null)\n if (!response.ok) throw new TestAuthError(response.status, body)\n const user = (body as { user?: TestUser } | null)?.user\n if (!user) throw new TestAuthError(response.status, body)\n return { user, headers: cookieHeaders(response) }\n }\n\n const currentSession = async (\n identity: TestIdentity,\n ): Promise<TestSession | null> => {\n const mocked = sessions.mocked(identity.headers)\n if (mocked) return mocked\n const auth = app.auth as {\n api: {\n getSession(options: { headers: Headers }): Promise<unknown>\n }\n }\n const result = await auth.api.getSession({ headers: identity.headers })\n if (!result || typeof result !== 'object' || !('user' in result)) {\n return null\n }\n const value = result as {\n user?: TestUser\n session?: TestSession['session']\n }\n return value.user\n ? { user: value.user, session: value.session ?? null }\n : null\n }\n\n return {\n async signUpEmail(input) {\n return identityFromResponse(\n await request('/api/auth/sign-up/email', {\n method: 'POST',\n body: JSON.stringify({\n email: input.email,\n name: input.name,\n password: input.password ?? 'password-123',\n }),\n }),\n )\n },\n async signInEmail(input) {\n return identityFromResponse(\n await request('/api/auth/sign-in/email', {\n method: 'POST',\n body: JSON.stringify({\n email: input.email,\n password: input.password ?? 'password-123',\n }),\n }),\n )\n },\n async getSession(identity) {\n return currentSession(identity)\n },\n async signOut(identity) {\n const response = await request(\n '/api/auth/sign-out',\n { method: 'POST' },\n identity.headers,\n )\n if (!response.ok) {\n throw new TestAuthError(\n response.status,\n await response.json().catch(() => null),\n )\n }\n },\n async verifyEmail(identity) {\n const message = [...email.sent]\n .reverse()\n .find((candidate) => candidate.to.includes(identity.user.email))\n const content = `${message?.text ?? ''}\\n${message?.html ?? ''}`\n const url = content.match(\n /https?:\\/\\/[^\\s<>\"']+\\/api\\/auth\\/verify-email\\?[^\\s<>\"']+/,\n )?.[0]\n if (!url) {\n throw new Error(\n `[bunderstack] no verification link captured for ${identity.user.email}`,\n )\n }\n const response = await request(url, {}, identity.headers)\n if (response.status >= 400) {\n throw new TestAuthError(response.status, await response.text())\n }\n const user = (await currentSession(identity))?.user\n if (!user) {\n throw new Error('[bunderstack] verified identity has no active session')\n }\n return { user, headers: identity.headers }\n },\n mockSession(user, session) {\n return sessions.mock(user, session)\n },\n }\n}\nimport type { AuthSessionResolver } from '../access'\nimport type { TestEmail } from './email'\n"]}
|
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"fixture.d.ts","sourceRoot":"","sources":["../../src/testing/fixture.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/fixture.js
CHANGED
|
@@ -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":"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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 { 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"]}
|
package/dist/testing/jobs.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/testing/jobs.js
CHANGED
|
@@ -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);
|
package/dist/testing/jobs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobs.js","sourceRoot":"","sources":["../../src/testing/jobs.ts"],"names":[],"mappings":"
|
|
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
|
package/dist/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,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
|
package/dist/testing.js.map
CHANGED
|
@@ -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;
|
|
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"]}
|