@stardeck-customer-apps/testing 0.3.1 → 0.4.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/SKILL.md +50 -0
- package/dist/index.d.mts +110 -67
- package/dist/index.d.ts +110 -67
- package/dist/index.js +754 -231
- package/dist/index.mjs +752 -233
- package/dist/next/headers-shim.js +10 -1
- package/dist/next/headers-shim.mjs +10 -1
- package/dist/setup.js +502 -111
- package/dist/setup.mjs +502 -111
- package/package.json +12 -3
package/SKILL.md
CHANGED
|
@@ -67,6 +67,42 @@ describeWorkflow("checkout", () => {
|
|
|
67
67
|
});
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
+
Payments, storage, and messaging:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { paymentsHandler } from "@stardeck-customer-apps/payments-sdk";
|
|
74
|
+
import { PaymentsServerClient } from "@stardeck-customer-apps/payments-sdk/server";
|
|
75
|
+
import { StorageClient } from "@stardeck-customer-apps/storage-sdk/server";
|
|
76
|
+
import { createIntegrationsClient } from "@stardeck-customer-apps/integrations-sdk";
|
|
77
|
+
|
|
78
|
+
// Stripe checkout + webhook fulfillment
|
|
79
|
+
const payments = new PaymentsServerClient({ /* reads CONTROL_PLANE_URL + DEPLOYMENT_SECRET from env */ });
|
|
80
|
+
const session = await payments.createCheckout({ /* lineItems, successUrl, cancelUrl */ });
|
|
81
|
+
const { POST } = paymentsHandler({
|
|
82
|
+
onStripeWebhook: async (event) => {
|
|
83
|
+
if (event.type === "checkout.session.completed") {
|
|
84
|
+
await app.query(`UPDATE orders SET status = 'paid' WHERE session_id = $1`, [
|
|
85
|
+
(event.data as { object: { id: string } }).object.id,
|
|
86
|
+
]);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
await app.payments.deliverStripeEvent(POST, {
|
|
91
|
+
type: "checkout.session.completed",
|
|
92
|
+
data: { object: { id: session.id } },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// File upload
|
|
96
|
+
const storage = new StorageClient();
|
|
97
|
+
await storage.upload(new File(["hi"], "note.txt", { type: "text/plain" }));
|
|
98
|
+
expect(app.storage.latest()?.filename).toBe("note.txt");
|
|
99
|
+
|
|
100
|
+
// LINE notification
|
|
101
|
+
const integrations = createIntegrationsClient();
|
|
102
|
+
await integrations.line.push("U123", { type: "text", text: "Your order shipped" });
|
|
103
|
+
expect(app.messages.channel("line").to("U123")[0].body.text).toMatch(/shipped/i);
|
|
104
|
+
```
|
|
105
|
+
|
|
70
106
|
## API
|
|
71
107
|
|
|
72
108
|
- `createTestApp(options)` — boots PGlite + the control-plane simulator and
|
|
@@ -88,6 +124,13 @@ describeWorkflow("checkout", () => {
|
|
|
88
124
|
- `app.identities` — the platform-identity directory created through
|
|
89
125
|
integrations-sdk `client.identities`: `.get(id)`, `.links(id)`, `.all()`,
|
|
90
126
|
`.count`, `.clear()`.
|
|
127
|
+
- `app.payments` — checkouts created through payments-sdk:
|
|
128
|
+
`.checkouts`, `.latest()`, `.setProducts()`, `.markPaid(id)`,
|
|
129
|
+
`.deliverStripeEvent(handler, event)`, `.deliverBeamEvent(handler, event)`,
|
|
130
|
+
`.count`, `.clear()`.
|
|
131
|
+
- `app.storage` — uploads through storage-sdk: `.uploads`, `.latest()`, `.count`, `.clear()`.
|
|
132
|
+
- `app.messages` — outbound Slack/LINE/Facebook sends:
|
|
133
|
+
`.all()`, `.latest()`, `.to(recipient)`, `.channel("line")`, `.count`, `.clear()`.
|
|
91
134
|
- `app.query(sql, params?)` / `app.db` — direct database access for
|
|
92
135
|
assertions and seeding.
|
|
93
136
|
- `callRoute(handler, opts)` — invoke an App Router route handler with a real
|
|
@@ -111,6 +154,13 @@ describeWorkflow("checkout", () => {
|
|
|
111
154
|
persons, attach channel links) — served offline by the simulated directory;
|
|
112
155
|
`update` replaces the `profile` object (not a merge), like the control plane.
|
|
113
156
|
Inspect via `app.identities`. Merge/archive are dashboard-only — not simulated.
|
|
157
|
+
- `PaymentsServerClient` (Stripe checkout + Beam payment links, product list) —
|
|
158
|
+
captured in `app.payments`; fulfill via `markPaid` (poll) or
|
|
159
|
+
`deliverStripeEvent` / `deliverBeamEvent` (webhook push).
|
|
160
|
+
- `StorageClient` (upload/list/get/patch/delete, presigned upload) — captured
|
|
161
|
+
in `app.storage` against the simulated `STORAGE_URL` host.
|
|
162
|
+
- `client.slack` / `client.line` / `client.facebook` send endpoints — captured
|
|
163
|
+
in `app.messages`.
|
|
114
164
|
- `next/headers` (`headers()`/`cookies()`) inside handlers under `callRoute`.
|
|
115
165
|
|
|
116
166
|
## Rules
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
import * as _stardeck_customer_apps_payments_sdk from '@stardeck-customer-apps/payments-sdk';
|
|
2
|
+
import * as next_server from 'next/server';
|
|
1
3
|
import { PGlite } from '@electric-sql/pglite';
|
|
2
|
-
import { ModuleSchemaOp, ModuleDataPort, IdentityClient } from '@stardeck-customer-apps/core';
|
|
3
4
|
|
|
5
|
+
type RouteHandler<Req extends Request = Request, P = Record<string, string | string[]>> = (request: Req, context: {
|
|
6
|
+
params: Promise<P>;
|
|
7
|
+
}) => Promise<Response> | Response;
|
|
8
|
+
interface CallRouteOptions {
|
|
9
|
+
/** Request path, e.g. "/api/orders". Defaults to "/api/test-route". */
|
|
10
|
+
path?: string;
|
|
11
|
+
method?: string;
|
|
12
|
+
/** JSON body. Implies POST unless `method` is set. */
|
|
13
|
+
body?: unknown;
|
|
14
|
+
searchParams?: Record<string, string>;
|
|
15
|
+
/** Next.js dynamic segment params, e.g. { id: "123" }. */
|
|
16
|
+
params?: Record<string, string | string[]>;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
cookies?: Record<string, string>;
|
|
19
|
+
/** Override the active test user for this call only. */
|
|
20
|
+
user?: TestUser | null;
|
|
21
|
+
}
|
|
22
|
+
declare function callRoute<Req extends Request = Request, P = Record<string, string | string[]>>(handler: RouteHandler<Req, P>, options?: CallRouteOptions): Promise<Response>;
|
|
23
|
+
|
|
24
|
+
type PaymentsWebhookHandler = RouteHandler<next_server.NextRequest, Record<string, string | string[] | undefined>>;
|
|
4
25
|
/**
|
|
5
26
|
* The user shape returned by project-auth's `getSession()`. Mirrors the
|
|
6
27
|
* `UserSchema` in @stardeck-customer-apps/project-auth — keep in sync.
|
|
@@ -85,6 +106,84 @@ interface TestDirectory {
|
|
|
85
106
|
clear(): void;
|
|
86
107
|
get count(): number;
|
|
87
108
|
}
|
|
109
|
+
interface CapturedCheckout {
|
|
110
|
+
id: string;
|
|
111
|
+
url: string;
|
|
112
|
+
provider: "stripe" | "beam";
|
|
113
|
+
options: Record<string, unknown>;
|
|
114
|
+
mode?: "payment" | "subscription";
|
|
115
|
+
metadata?: Record<string, string>;
|
|
116
|
+
createdAt: Date;
|
|
117
|
+
}
|
|
118
|
+
interface TestPayments {
|
|
119
|
+
get checkouts(): CapturedCheckout[];
|
|
120
|
+
latest(): CapturedCheckout | undefined;
|
|
121
|
+
setProducts(products: _stardeck_customer_apps_payments_sdk.Product[]): void;
|
|
122
|
+
markPaid(id: string): void;
|
|
123
|
+
setSessionStatus(id: string, status: Partial<_stardeck_customer_apps_payments_sdk.CheckoutSessionStatus>): void;
|
|
124
|
+
setPaymentLinkStatus(id: string, status: _stardeck_customer_apps_payments_sdk.BeamPaymentLinkStatus): void;
|
|
125
|
+
deliverStripeEvent(handler: PaymentsWebhookHandler, event: {
|
|
126
|
+
type: string;
|
|
127
|
+
data: unknown;
|
|
128
|
+
accountId?: string;
|
|
129
|
+
}, options?: {
|
|
130
|
+
path?: string;
|
|
131
|
+
deploymentSecret?: string;
|
|
132
|
+
}): Promise<Response>;
|
|
133
|
+
deliverBeamEvent(handler: PaymentsWebhookHandler, event: {
|
|
134
|
+
type: string;
|
|
135
|
+
payload: Record<string, unknown>;
|
|
136
|
+
}, options?: {
|
|
137
|
+
path?: string;
|
|
138
|
+
deploymentSecret?: string;
|
|
139
|
+
}): Promise<Response>;
|
|
140
|
+
clear(): void;
|
|
141
|
+
get count(): number;
|
|
142
|
+
}
|
|
143
|
+
interface CapturedUpload {
|
|
144
|
+
id: string;
|
|
145
|
+
key: string;
|
|
146
|
+
filename: string;
|
|
147
|
+
contentType: string;
|
|
148
|
+
sizeBytes: number;
|
|
149
|
+
url: string;
|
|
150
|
+
uploadedAt: string;
|
|
151
|
+
isPublic: boolean;
|
|
152
|
+
metadata?: Record<string, string>;
|
|
153
|
+
method: string;
|
|
154
|
+
path: string;
|
|
155
|
+
}
|
|
156
|
+
interface TestStorage {
|
|
157
|
+
get uploads(): CapturedUpload[];
|
|
158
|
+
latest(): CapturedUpload | undefined;
|
|
159
|
+
clear(): void;
|
|
160
|
+
get count(): number;
|
|
161
|
+
}
|
|
162
|
+
interface CapturedMessage {
|
|
163
|
+
channel: "slack" | "line" | "facebook";
|
|
164
|
+
recipient: string;
|
|
165
|
+
body: {
|
|
166
|
+
text?: string;
|
|
167
|
+
blocks?: unknown[];
|
|
168
|
+
threadTs?: string;
|
|
169
|
+
messagingType?: string;
|
|
170
|
+
tag?: string;
|
|
171
|
+
};
|
|
172
|
+
connectionId?: string;
|
|
173
|
+
sentAt: Date;
|
|
174
|
+
}
|
|
175
|
+
interface TestMessages {
|
|
176
|
+
all(): CapturedMessage[];
|
|
177
|
+
latest(): CapturedMessage | undefined;
|
|
178
|
+
to(recipient: string): CapturedMessage[];
|
|
179
|
+
channel(kind: "slack" | "line" | "facebook"): {
|
|
180
|
+
all(): CapturedMessage[];
|
|
181
|
+
latest(): CapturedMessage | undefined;
|
|
182
|
+
to(recipient: string): CapturedMessage[];
|
|
183
|
+
};
|
|
184
|
+
clear(): void;
|
|
185
|
+
get count(): number;
|
|
186
|
+
}
|
|
88
187
|
interface TestAppOptions {
|
|
89
188
|
/**
|
|
90
189
|
* Path to the schema.sql snapshot generated by
|
|
@@ -114,6 +213,12 @@ interface TestApp {
|
|
|
114
213
|
inbox: TestInbox;
|
|
115
214
|
/** Inspect the simulated platform-identity directory (`client.identities`). */
|
|
116
215
|
identities: TestDirectory;
|
|
216
|
+
/** Captured checkouts and webhook delivery helpers for payments-sdk. */
|
|
217
|
+
payments: TestPayments;
|
|
218
|
+
/** Captured file uploads from storage-sdk. */
|
|
219
|
+
storage: TestStorage;
|
|
220
|
+
/** Captured outbound messages from integrations-sdk messaging channels. */
|
|
221
|
+
messages: TestMessages;
|
|
117
222
|
/** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
|
|
118
223
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
119
224
|
/**
|
|
@@ -141,71 +246,6 @@ interface TestApp {
|
|
|
141
246
|
*/
|
|
142
247
|
declare function createTestApp(options?: TestAppOptions): Promise<TestApp>;
|
|
143
248
|
|
|
144
|
-
/** The minimal slice of a module definition the harness needs to stand it up. */
|
|
145
|
-
interface MountableModule {
|
|
146
|
-
schema: ModuleSchemaOp[];
|
|
147
|
-
}
|
|
148
|
-
interface ModuleSeedDeps {
|
|
149
|
-
/** Raw-SQL data port over the harness PGlite — what the module engine reads/writes through. */
|
|
150
|
-
data: ModuleDataPort;
|
|
151
|
-
/** Identity client backed by the simulated platform identity directory. */
|
|
152
|
-
identities: IdentityClient;
|
|
153
|
-
}
|
|
154
|
-
interface CreateModuleAppOptions {
|
|
155
|
-
/** Modules whose install schema is rendered to DDL and applied to the test DB. */
|
|
156
|
-
modules: MountableModule[];
|
|
157
|
-
/**
|
|
158
|
-
* Seed run once after the schema is applied, and again on every `reset()`.
|
|
159
|
-
* Receives the same data port + identity client the module engine uses, so a
|
|
160
|
-
* module's own `seed*` function drops straight in.
|
|
161
|
-
*/
|
|
162
|
-
seed?: (deps: ModuleSeedDeps) => Promise<void>;
|
|
163
|
-
/** Allow real outbound network (default false). */
|
|
164
|
-
allowNetwork?: boolean;
|
|
165
|
-
}
|
|
166
|
-
interface ModuleApp extends ModuleSeedDeps {
|
|
167
|
-
/** The underlying harness app (db, identities inspector, callRoute, asUser, …). */
|
|
168
|
-
app: TestApp;
|
|
169
|
-
/** Reset to a clean slate: drop + re-apply the module schema, clear the sim, re-run the seed. */
|
|
170
|
-
reset(): Promise<void>;
|
|
171
|
-
/** Tear down (restore global fetch, close the db). */
|
|
172
|
-
close(): Promise<void>;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Stand a capability module up on the in-process platform simulator — the **L2**
|
|
176
|
-
* harness. It renders the module's install schema to real DDL (the SAME renderer
|
|
177
|
-
* the control plane uses, drift-tested in apps/web), applies it to PGlite, wires a
|
|
178
|
-
* raw-SQL data port over it, and a real `integrations-sdk` identity client talking
|
|
179
|
-
* to the simulated identity directory (so scoping + governance behave as in prod).
|
|
180
|
-
*
|
|
181
|
-
* That's a near-end-to-end test of a module's runtime — schema + data port +
|
|
182
|
-
* identity integration + engine — minus the HTTP/RSC shell (those belong to a
|
|
183
|
-
* real-deploy smoke). The same seams back the local dev playground.
|
|
184
|
-
*
|
|
185
|
-
* Adding a module costs one co-located test file calling this with its own
|
|
186
|
-
* definition + seed; there is no per-module harness to maintain.
|
|
187
|
-
*/
|
|
188
|
-
declare function createModuleApp(options: CreateModuleAppOptions): Promise<ModuleApp>;
|
|
189
|
-
|
|
190
|
-
type RouteHandler<Req extends Request, P> = (request: Req, context: {
|
|
191
|
-
params: Promise<P>;
|
|
192
|
-
}) => Promise<Response> | Response;
|
|
193
|
-
interface CallRouteOptions {
|
|
194
|
-
/** Request path, e.g. "/api/orders". Defaults to "/api/test-route". */
|
|
195
|
-
path?: string;
|
|
196
|
-
method?: string;
|
|
197
|
-
/** JSON body. Implies POST unless `method` is set. */
|
|
198
|
-
body?: unknown;
|
|
199
|
-
searchParams?: Record<string, string>;
|
|
200
|
-
/** Next.js dynamic segment params, e.g. { id: "123" }. */
|
|
201
|
-
params?: Record<string, string | string[]>;
|
|
202
|
-
headers?: Record<string, string>;
|
|
203
|
-
cookies?: Record<string, string>;
|
|
204
|
-
/** Override the active test user for this call only. */
|
|
205
|
-
user?: TestUser | null;
|
|
206
|
-
}
|
|
207
|
-
declare function callRoute<Req extends Request = Request, P = Record<string, string | string[]>>(handler: RouteHandler<Req, P>, options?: CallRouteOptions): Promise<Response>;
|
|
208
|
-
|
|
209
249
|
/**
|
|
210
250
|
* Tags a describe block as covering a named critical workflow ("checkout",
|
|
211
251
|
* "booking", "inventory"). The platform parses the `workflow:` prefix out of
|
|
@@ -219,6 +259,8 @@ declare function parseWorkflowName(describeTitle: string): string | null;
|
|
|
219
259
|
|
|
220
260
|
declare const CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
|
|
221
261
|
declare const DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
262
|
+
declare const STORAGE_TEST_URL = "https://storage.stardeck.test";
|
|
263
|
+
declare const STORAGE_TEST_HOST = "storage.stardeck.test";
|
|
222
264
|
declare const TEST_ENV_DEFAULTS: {
|
|
223
265
|
readonly CONTROL_PLANE_URL: "https://control-plane.stardeck.test";
|
|
224
266
|
readonly DEPLOYMENT_SECRET: "stardeck-test-deployment-secret";
|
|
@@ -226,8 +268,9 @@ declare const TEST_ENV_DEFAULTS: {
|
|
|
226
268
|
readonly PROJECT_ID: "00000000-0000-4000-8000-00000000000b";
|
|
227
269
|
readonly DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c";
|
|
228
270
|
readonly DATA_STORE_URL: "postgresql://test:test@db.stardeck.test/main";
|
|
271
|
+
readonly STORAGE_URL: "https://storage.stardeck.test";
|
|
229
272
|
};
|
|
230
273
|
/** Default location of the DDL snapshot written by `generate-types`. */
|
|
231
274
|
declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
|
|
232
275
|
|
|
233
|
-
export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type
|
|
276
|
+
export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedUpload, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestDirectory, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
import * as _stardeck_customer_apps_payments_sdk from '@stardeck-customer-apps/payments-sdk';
|
|
2
|
+
import * as next_server from 'next/server';
|
|
1
3
|
import { PGlite } from '@electric-sql/pglite';
|
|
2
|
-
import { ModuleSchemaOp, ModuleDataPort, IdentityClient } from '@stardeck-customer-apps/core';
|
|
3
4
|
|
|
5
|
+
type RouteHandler<Req extends Request = Request, P = Record<string, string | string[]>> = (request: Req, context: {
|
|
6
|
+
params: Promise<P>;
|
|
7
|
+
}) => Promise<Response> | Response;
|
|
8
|
+
interface CallRouteOptions {
|
|
9
|
+
/** Request path, e.g. "/api/orders". Defaults to "/api/test-route". */
|
|
10
|
+
path?: string;
|
|
11
|
+
method?: string;
|
|
12
|
+
/** JSON body. Implies POST unless `method` is set. */
|
|
13
|
+
body?: unknown;
|
|
14
|
+
searchParams?: Record<string, string>;
|
|
15
|
+
/** Next.js dynamic segment params, e.g. { id: "123" }. */
|
|
16
|
+
params?: Record<string, string | string[]>;
|
|
17
|
+
headers?: Record<string, string>;
|
|
18
|
+
cookies?: Record<string, string>;
|
|
19
|
+
/** Override the active test user for this call only. */
|
|
20
|
+
user?: TestUser | null;
|
|
21
|
+
}
|
|
22
|
+
declare function callRoute<Req extends Request = Request, P = Record<string, string | string[]>>(handler: RouteHandler<Req, P>, options?: CallRouteOptions): Promise<Response>;
|
|
23
|
+
|
|
24
|
+
type PaymentsWebhookHandler = RouteHandler<next_server.NextRequest, Record<string, string | string[] | undefined>>;
|
|
4
25
|
/**
|
|
5
26
|
* The user shape returned by project-auth's `getSession()`. Mirrors the
|
|
6
27
|
* `UserSchema` in @stardeck-customer-apps/project-auth — keep in sync.
|
|
@@ -85,6 +106,84 @@ interface TestDirectory {
|
|
|
85
106
|
clear(): void;
|
|
86
107
|
get count(): number;
|
|
87
108
|
}
|
|
109
|
+
interface CapturedCheckout {
|
|
110
|
+
id: string;
|
|
111
|
+
url: string;
|
|
112
|
+
provider: "stripe" | "beam";
|
|
113
|
+
options: Record<string, unknown>;
|
|
114
|
+
mode?: "payment" | "subscription";
|
|
115
|
+
metadata?: Record<string, string>;
|
|
116
|
+
createdAt: Date;
|
|
117
|
+
}
|
|
118
|
+
interface TestPayments {
|
|
119
|
+
get checkouts(): CapturedCheckout[];
|
|
120
|
+
latest(): CapturedCheckout | undefined;
|
|
121
|
+
setProducts(products: _stardeck_customer_apps_payments_sdk.Product[]): void;
|
|
122
|
+
markPaid(id: string): void;
|
|
123
|
+
setSessionStatus(id: string, status: Partial<_stardeck_customer_apps_payments_sdk.CheckoutSessionStatus>): void;
|
|
124
|
+
setPaymentLinkStatus(id: string, status: _stardeck_customer_apps_payments_sdk.BeamPaymentLinkStatus): void;
|
|
125
|
+
deliverStripeEvent(handler: PaymentsWebhookHandler, event: {
|
|
126
|
+
type: string;
|
|
127
|
+
data: unknown;
|
|
128
|
+
accountId?: string;
|
|
129
|
+
}, options?: {
|
|
130
|
+
path?: string;
|
|
131
|
+
deploymentSecret?: string;
|
|
132
|
+
}): Promise<Response>;
|
|
133
|
+
deliverBeamEvent(handler: PaymentsWebhookHandler, event: {
|
|
134
|
+
type: string;
|
|
135
|
+
payload: Record<string, unknown>;
|
|
136
|
+
}, options?: {
|
|
137
|
+
path?: string;
|
|
138
|
+
deploymentSecret?: string;
|
|
139
|
+
}): Promise<Response>;
|
|
140
|
+
clear(): void;
|
|
141
|
+
get count(): number;
|
|
142
|
+
}
|
|
143
|
+
interface CapturedUpload {
|
|
144
|
+
id: string;
|
|
145
|
+
key: string;
|
|
146
|
+
filename: string;
|
|
147
|
+
contentType: string;
|
|
148
|
+
sizeBytes: number;
|
|
149
|
+
url: string;
|
|
150
|
+
uploadedAt: string;
|
|
151
|
+
isPublic: boolean;
|
|
152
|
+
metadata?: Record<string, string>;
|
|
153
|
+
method: string;
|
|
154
|
+
path: string;
|
|
155
|
+
}
|
|
156
|
+
interface TestStorage {
|
|
157
|
+
get uploads(): CapturedUpload[];
|
|
158
|
+
latest(): CapturedUpload | undefined;
|
|
159
|
+
clear(): void;
|
|
160
|
+
get count(): number;
|
|
161
|
+
}
|
|
162
|
+
interface CapturedMessage {
|
|
163
|
+
channel: "slack" | "line" | "facebook";
|
|
164
|
+
recipient: string;
|
|
165
|
+
body: {
|
|
166
|
+
text?: string;
|
|
167
|
+
blocks?: unknown[];
|
|
168
|
+
threadTs?: string;
|
|
169
|
+
messagingType?: string;
|
|
170
|
+
tag?: string;
|
|
171
|
+
};
|
|
172
|
+
connectionId?: string;
|
|
173
|
+
sentAt: Date;
|
|
174
|
+
}
|
|
175
|
+
interface TestMessages {
|
|
176
|
+
all(): CapturedMessage[];
|
|
177
|
+
latest(): CapturedMessage | undefined;
|
|
178
|
+
to(recipient: string): CapturedMessage[];
|
|
179
|
+
channel(kind: "slack" | "line" | "facebook"): {
|
|
180
|
+
all(): CapturedMessage[];
|
|
181
|
+
latest(): CapturedMessage | undefined;
|
|
182
|
+
to(recipient: string): CapturedMessage[];
|
|
183
|
+
};
|
|
184
|
+
clear(): void;
|
|
185
|
+
get count(): number;
|
|
186
|
+
}
|
|
88
187
|
interface TestAppOptions {
|
|
89
188
|
/**
|
|
90
189
|
* Path to the schema.sql snapshot generated by
|
|
@@ -114,6 +213,12 @@ interface TestApp {
|
|
|
114
213
|
inbox: TestInbox;
|
|
115
214
|
/** Inspect the simulated platform-identity directory (`client.identities`). */
|
|
116
215
|
identities: TestDirectory;
|
|
216
|
+
/** Captured checkouts and webhook delivery helpers for payments-sdk. */
|
|
217
|
+
payments: TestPayments;
|
|
218
|
+
/** Captured file uploads from storage-sdk. */
|
|
219
|
+
storage: TestStorage;
|
|
220
|
+
/** Captured outbound messages from integrations-sdk messaging channels. */
|
|
221
|
+
messages: TestMessages;
|
|
117
222
|
/** Convenience for raw SQL: `app.query("SELECT ...", [param])`. */
|
|
118
223
|
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<T[]>;
|
|
119
224
|
/**
|
|
@@ -141,71 +246,6 @@ interface TestApp {
|
|
|
141
246
|
*/
|
|
142
247
|
declare function createTestApp(options?: TestAppOptions): Promise<TestApp>;
|
|
143
248
|
|
|
144
|
-
/** The minimal slice of a module definition the harness needs to stand it up. */
|
|
145
|
-
interface MountableModule {
|
|
146
|
-
schema: ModuleSchemaOp[];
|
|
147
|
-
}
|
|
148
|
-
interface ModuleSeedDeps {
|
|
149
|
-
/** Raw-SQL data port over the harness PGlite — what the module engine reads/writes through. */
|
|
150
|
-
data: ModuleDataPort;
|
|
151
|
-
/** Identity client backed by the simulated platform identity directory. */
|
|
152
|
-
identities: IdentityClient;
|
|
153
|
-
}
|
|
154
|
-
interface CreateModuleAppOptions {
|
|
155
|
-
/** Modules whose install schema is rendered to DDL and applied to the test DB. */
|
|
156
|
-
modules: MountableModule[];
|
|
157
|
-
/**
|
|
158
|
-
* Seed run once after the schema is applied, and again on every `reset()`.
|
|
159
|
-
* Receives the same data port + identity client the module engine uses, so a
|
|
160
|
-
* module's own `seed*` function drops straight in.
|
|
161
|
-
*/
|
|
162
|
-
seed?: (deps: ModuleSeedDeps) => Promise<void>;
|
|
163
|
-
/** Allow real outbound network (default false). */
|
|
164
|
-
allowNetwork?: boolean;
|
|
165
|
-
}
|
|
166
|
-
interface ModuleApp extends ModuleSeedDeps {
|
|
167
|
-
/** The underlying harness app (db, identities inspector, callRoute, asUser, …). */
|
|
168
|
-
app: TestApp;
|
|
169
|
-
/** Reset to a clean slate: drop + re-apply the module schema, clear the sim, re-run the seed. */
|
|
170
|
-
reset(): Promise<void>;
|
|
171
|
-
/** Tear down (restore global fetch, close the db). */
|
|
172
|
-
close(): Promise<void>;
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* Stand a capability module up on the in-process platform simulator — the **L2**
|
|
176
|
-
* harness. It renders the module's install schema to real DDL (the SAME renderer
|
|
177
|
-
* the control plane uses, drift-tested in apps/web), applies it to PGlite, wires a
|
|
178
|
-
* raw-SQL data port over it, and a real `integrations-sdk` identity client talking
|
|
179
|
-
* to the simulated identity directory (so scoping + governance behave as in prod).
|
|
180
|
-
*
|
|
181
|
-
* That's a near-end-to-end test of a module's runtime — schema + data port +
|
|
182
|
-
* identity integration + engine — minus the HTTP/RSC shell (those belong to a
|
|
183
|
-
* real-deploy smoke). The same seams back the local dev playground.
|
|
184
|
-
*
|
|
185
|
-
* Adding a module costs one co-located test file calling this with its own
|
|
186
|
-
* definition + seed; there is no per-module harness to maintain.
|
|
187
|
-
*/
|
|
188
|
-
declare function createModuleApp(options: CreateModuleAppOptions): Promise<ModuleApp>;
|
|
189
|
-
|
|
190
|
-
type RouteHandler<Req extends Request, P> = (request: Req, context: {
|
|
191
|
-
params: Promise<P>;
|
|
192
|
-
}) => Promise<Response> | Response;
|
|
193
|
-
interface CallRouteOptions {
|
|
194
|
-
/** Request path, e.g. "/api/orders". Defaults to "/api/test-route". */
|
|
195
|
-
path?: string;
|
|
196
|
-
method?: string;
|
|
197
|
-
/** JSON body. Implies POST unless `method` is set. */
|
|
198
|
-
body?: unknown;
|
|
199
|
-
searchParams?: Record<string, string>;
|
|
200
|
-
/** Next.js dynamic segment params, e.g. { id: "123" }. */
|
|
201
|
-
params?: Record<string, string | string[]>;
|
|
202
|
-
headers?: Record<string, string>;
|
|
203
|
-
cookies?: Record<string, string>;
|
|
204
|
-
/** Override the active test user for this call only. */
|
|
205
|
-
user?: TestUser | null;
|
|
206
|
-
}
|
|
207
|
-
declare function callRoute<Req extends Request = Request, P = Record<string, string | string[]>>(handler: RouteHandler<Req, P>, options?: CallRouteOptions): Promise<Response>;
|
|
208
|
-
|
|
209
249
|
/**
|
|
210
250
|
* Tags a describe block as covering a named critical workflow ("checkout",
|
|
211
251
|
* "booking", "inventory"). The platform parses the `workflow:` prefix out of
|
|
@@ -219,6 +259,8 @@ declare function parseWorkflowName(describeTitle: string): string | null;
|
|
|
219
259
|
|
|
220
260
|
declare const CONTROL_PLANE_TEST_URL = "https://control-plane.stardeck.test";
|
|
221
261
|
declare const DATA_STORE_TEST_HOST = "db.stardeck.test";
|
|
262
|
+
declare const STORAGE_TEST_URL = "https://storage.stardeck.test";
|
|
263
|
+
declare const STORAGE_TEST_HOST = "storage.stardeck.test";
|
|
222
264
|
declare const TEST_ENV_DEFAULTS: {
|
|
223
265
|
readonly CONTROL_PLANE_URL: "https://control-plane.stardeck.test";
|
|
224
266
|
readonly DEPLOYMENT_SECRET: "stardeck-test-deployment-secret";
|
|
@@ -226,8 +268,9 @@ declare const TEST_ENV_DEFAULTS: {
|
|
|
226
268
|
readonly PROJECT_ID: "00000000-0000-4000-8000-00000000000b";
|
|
227
269
|
readonly DEPLOYMENT_ID: "00000000-0000-4000-8000-00000000000c";
|
|
228
270
|
readonly DATA_STORE_URL: "postgresql://test:test@db.stardeck.test/main";
|
|
271
|
+
readonly STORAGE_URL: "https://storage.stardeck.test";
|
|
229
272
|
};
|
|
230
273
|
/** Default location of the DDL snapshot written by `generate-types`. */
|
|
231
274
|
declare const DEFAULT_SCHEMA_PATH = "./src/generated/data-store-schema.sql";
|
|
232
275
|
|
|
233
|
-
export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type
|
|
276
|
+
export { CONTROL_PLANE_TEST_URL, type CallRouteOptions, type CapturedCheckout, type CapturedEmail, type CapturedIdentity, type CapturedIdentityLink, type CapturedMessage, type CapturedUpload, DATA_STORE_TEST_HOST, DEFAULT_SCHEMA_PATH, STORAGE_TEST_HOST, STORAGE_TEST_URL, type SessionTokens, TEST_ENV_DEFAULTS, type TestApp, type TestAppOptions, type TestDirectory, type TestInbox, type TestMessages, type TestPayments, type TestStorage, type TestUser, WORKFLOW_NAME_PREFIX, callRoute, createTestApp, describeWorkflow, parseWorkflowName };
|