@rdlabo/workers-hono-kit 0.2.0 → 0.3.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/README.md +126 -11
- package/dist/ai/gateway.d.ts +54 -16
- package/dist/ai/gateway.js +37 -12
- package/dist/aws/cloudfront.d.ts +23 -5
- package/dist/aws/cloudfront.js +45 -6
- package/dist/aws/secrets-manager.d.ts +38 -4
- package/dist/aws/secrets-manager.js +48 -3
- package/dist/cache/kv-cache.d.ts +173 -10
- package/dist/cache/kv-cache.js +139 -7
- package/dist/db/connection.d.ts +56 -14
- package/dist/db/connection.js +39 -13
- package/dist/db/database.d.ts +159 -23
- package/dist/db/database.js +49 -5
- package/dist/db/index.d.ts +11 -0
- package/dist/db/index.js +11 -2
- package/dist/db/jst.d.ts +89 -6
- package/dist/db/jst.js +89 -23
- package/dist/db/orm-config.d.ts +61 -19
- package/dist/db/orm-config.js +43 -14
- package/dist/db/retry.d.ts +25 -3
- package/dist/db/retry.js +25 -3
- package/dist/db/write-result.d.ts +27 -4
- package/dist/db/write-result.js +22 -1
- package/dist/firebase/firebase-verifier.d.ts +53 -4
- package/dist/firebase/identity-toolkit.d.ts +54 -5
- package/dist/firebase/identity-toolkit.js +51 -0
- package/dist/firebase/jose-firebase-verifier.d.ts +79 -7
- package/dist/firebase/jose-firebase-verifier.js +68 -7
- package/dist/firebase/remote-verifier.d.ts +42 -4
- package/dist/firebase/remote-verifier.js +58 -9
- package/dist/http/app-env.d.ts +41 -8
- package/dist/http/app-env.js +38 -8
- package/dist/http/app-info.d.ts +25 -3
- package/dist/http/app-info.js +16 -2
- package/dist/http/http-status.d.ts +12 -3
- package/dist/http/http-status.js +12 -3
- package/dist/http/nest-error.d.ts +90 -29
- package/dist/http/nest-error.js +59 -18
- package/dist/http/user-protocol.d.ts +23 -3
- package/dist/http/user-protocol.js +14 -2
- package/dist/index.d.ts +15 -0
- package/dist/index.js +14 -3
- package/dist/middleware/auth.d.ts +74 -13
- package/dist/middleware/auth.js +30 -6
- package/dist/middleware/finalize-response.d.ts +30 -0
- package/dist/middleware/finalize-response.js +41 -12
- package/dist/middleware/validation.d.ts +83 -9
- package/dist/middleware/validation.js +52 -9
- package/dist/middleware/zod-coerce.d.ts +56 -2
- package/dist/middleware/zod-coerce.js +68 -9
- package/dist/queue/consumer.d.ts +112 -0
- package/dist/queue/consumer.js +80 -0
- package/dist/queue/send.d.ts +90 -0
- package/dist/queue/send.js +85 -0
- package/dist/stripe/client.d.ts +46 -9
- package/dist/stripe/client.js +41 -3
- package/dist/testing/auth.d.ts +58 -10
- package/dist/testing/auth.js +58 -10
- package/dist/testing/configurable-fake.d.ts +20 -9
- package/dist/testing/configurable-fake.js +23 -11
- package/dist/testing/db.d.ts +81 -12
- package/dist/testing/db.js +23 -1
- package/dist/testing/fakes.d.ts +77 -9
- package/dist/testing/fakes.js +69 -7
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js +10 -5
- package/dist/testing/stripe-fixtures.d.ts +93 -3
- package/dist/testing/stripe-fixtures.js +93 -3
- package/package.json +3 -2
- package/scripts/check-subrequest-fanout.mjs +86 -0
- package/src/ai/gateway.ts +66 -27
- package/src/aws/cloudfront.ts +46 -6
- package/src/aws/secrets-manager.ts +56 -7
- package/src/cache/kv-cache.ts +194 -12
- package/src/db/connection.ts +56 -14
- package/src/db/database.ts +160 -24
- package/src/db/index.ts +11 -2
- package/src/db/jst.ts +89 -23
- package/src/db/orm-config.ts +61 -19
- package/src/db/retry.ts +25 -3
- package/src/db/write-result.ts +27 -4
- package/src/firebase/firebase-verifier.ts +53 -4
- package/src/firebase/identity-toolkit.ts +57 -5
- package/src/firebase/jose-firebase-verifier.ts +79 -9
- package/src/firebase/remote-verifier.ts +58 -9
- package/src/http/app-env.ts +41 -8
- package/src/http/app-info.ts +25 -3
- package/src/http/http-status.ts +12 -3
- package/src/http/nest-error.ts +106 -37
- package/src/http/user-protocol.ts +23 -3
- package/src/index.ts +17 -3
- package/src/middleware/auth.ts +77 -15
- package/src/middleware/finalize-response.ts +41 -12
- package/src/middleware/validation.ts +89 -15
- package/src/middleware/zod-coerce.ts +68 -9
- package/src/queue/consumer.ts +146 -0
- package/src/queue/send.ts +129 -0
- package/src/stripe/client.ts +46 -9
- package/src/testing/auth.ts +58 -10
- package/src/testing/configurable-fake.ts +23 -11
- package/src/testing/db.ts +82 -13
- package/src/testing/fakes.ts +77 -9
- package/src/testing/index.ts +10 -5
- package/src/testing/stripe-fixtures.ts +93 -3
package/dist/testing/db.d.ts
CHANGED
|
@@ -1,37 +1,106 @@
|
|
|
1
1
|
import type { Pool } from 'mysql2/promise';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* テストスキーマは「コミット済み Drizzle マイグレーション」を単一ソースとして構築する
|
|
5
|
-
* (手書き schema.sql ではなく `db:generate` 由来の ./drizzle)。
|
|
3
|
+
* Connection parameters for the test MySQL server.
|
|
6
4
|
*
|
|
7
|
-
*
|
|
5
|
+
* @see {@link CreateTestDbOptions.connection} for how defaults are resolved.
|
|
8
6
|
*/
|
|
9
7
|
export interface TestDbConnection {
|
|
8
|
+
/** Server host. */
|
|
10
9
|
host: string;
|
|
10
|
+
/** Server port. */
|
|
11
11
|
port: number;
|
|
12
|
+
/** User name. */
|
|
12
13
|
user: string;
|
|
14
|
+
/** Password. */
|
|
13
15
|
password: string;
|
|
14
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Options for {@link createTestDb}.
|
|
19
|
+
*/
|
|
15
20
|
export interface CreateTestDbOptions {
|
|
16
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Test database name (e.g. `'app_test'`). To isolate parallel runs per feature, resolve a per-run
|
|
23
|
+
* name on the caller side and pass it here.
|
|
24
|
+
*/
|
|
17
25
|
dbName: string;
|
|
18
|
-
/**
|
|
26
|
+
/**
|
|
27
|
+
* Absolute path to the Drizzle migrations folder. Resolve it on the caller side, e.g.
|
|
28
|
+
* `join(here, '..', 'drizzle')`.
|
|
29
|
+
*/
|
|
19
30
|
migrationsFolder: string;
|
|
20
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Connection overrides. Unspecified fields fall back to environment variables
|
|
33
|
+
* (`DB_HOST`/`DB_PORT`/`DB_USER`/`DB_PASSWORD`), then to `127.0.0.1`/`3306`/`root`/`root`.
|
|
34
|
+
*/
|
|
21
35
|
connection?: Partial<TestDbConnection>;
|
|
22
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Test database handle returned by {@link createTestDb}, bundling schema setup, pooling, and
|
|
39
|
+
* fixture helpers for a single test database.
|
|
40
|
+
*/
|
|
23
41
|
export interface TestDb {
|
|
42
|
+
/** The resolved test database name. */
|
|
24
43
|
readonly dbName: string;
|
|
44
|
+
/** The resolved connection parameters. */
|
|
25
45
|
readonly connection: TestDbConnection;
|
|
26
|
-
/**
|
|
46
|
+
/**
|
|
47
|
+
* Drop and recreate the database, then apply the committed Drizzle migrations to build the schema.
|
|
48
|
+
*
|
|
49
|
+
* @returns A promise that resolves once migrations have been applied.
|
|
50
|
+
*/
|
|
27
51
|
resetSchema(): Promise<void>;
|
|
28
|
-
/**
|
|
52
|
+
/**
|
|
53
|
+
* Create a mysql2 pool connected to the test database.
|
|
54
|
+
*
|
|
55
|
+
* @remarks Call `pool.end()` (e.g. in `afterAll`) to release connections.
|
|
56
|
+
* @returns A connection pool for the test database.
|
|
57
|
+
*/
|
|
29
58
|
createTestPool(): Pool;
|
|
30
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Truncate every base table in the database.
|
|
61
|
+
*
|
|
62
|
+
* @remarks Table names are discovered dynamically from `information_schema`; the
|
|
63
|
+
* `__drizzle_migrations` bookkeeping table is excluded. Foreign-key checks are disabled for the
|
|
64
|
+
* duration so truncation order does not matter.
|
|
65
|
+
* @param pool - Pool connected to the test database.
|
|
66
|
+
*/
|
|
31
67
|
truncateAll(pool: Pool): Promise<void>;
|
|
32
|
-
/**
|
|
68
|
+
/**
|
|
69
|
+
* Insert a single row, mapping column names to values — a generic fixture helper for specs.
|
|
70
|
+
*
|
|
71
|
+
* @param pool - Pool connected to the test database.
|
|
72
|
+
* @param table - Target table name.
|
|
73
|
+
* @param row - Column-name to value map. A no-op if empty.
|
|
74
|
+
*/
|
|
33
75
|
seed(pool: Pool, table: string, row: Record<string, unknown>): Promise<void>;
|
|
34
|
-
/**
|
|
76
|
+
/**
|
|
77
|
+
* Report whether the local MySQL server is reachable.
|
|
78
|
+
*
|
|
79
|
+
* @remarks Useful as a guard, e.g. `describe.skipIf(!(await mysqlReachable()))`.
|
|
80
|
+
* @returns `true` if a connection could be opened, otherwise `false`.
|
|
81
|
+
*/
|
|
35
82
|
mysqlReachable(): Promise<boolean>;
|
|
36
83
|
}
|
|
84
|
+
/**
|
|
85
|
+
* Create a {@link TestDb} handle for a single test database.
|
|
86
|
+
*
|
|
87
|
+
* @remarks
|
|
88
|
+
* The test schema is built from the committed Drizzle migrations as the single source of truth
|
|
89
|
+
* (the `db:generate` output under `./drizzle`), rather than a hand-written `schema.sql`. This helper
|
|
90
|
+
* is Node-only test infrastructure (run under Vitest) and is unrelated to runtime behavior.
|
|
91
|
+
*
|
|
92
|
+
* @param options - Database name, migrations folder, and optional connection overrides. See
|
|
93
|
+
* {@link CreateTestDbOptions}.
|
|
94
|
+
* @returns A handle exposing schema setup, pooling, truncation, seeding, and a reachability probe.
|
|
95
|
+
* @example
|
|
96
|
+
* ```ts
|
|
97
|
+
* const testDb = createTestDb({ dbName: 'app_test', migrationsFolder: join(here, '..', 'drizzle') });
|
|
98
|
+
* beforeAll(async () => {
|
|
99
|
+
* await testDb.resetSchema();
|
|
100
|
+
* });
|
|
101
|
+
* const pool = testDb.createTestPool();
|
|
102
|
+
* beforeEach(() => testDb.truncateAll(pool));
|
|
103
|
+
* afterAll(() => pool.end());
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
37
106
|
export declare function createTestDb(options: CreateTestDbOptions): TestDb;
|
package/dist/testing/db.js
CHANGED
|
@@ -10,6 +10,28 @@ function resolveConnection(override) {
|
|
|
10
10
|
password: override?.password ?? env.DB_PASSWORD ?? 'root',
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a {@link TestDb} handle for a single test database.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* The test schema is built from the committed Drizzle migrations as the single source of truth
|
|
18
|
+
* (the `db:generate` output under `./drizzle`), rather than a hand-written `schema.sql`. This helper
|
|
19
|
+
* is Node-only test infrastructure (run under Vitest) and is unrelated to runtime behavior.
|
|
20
|
+
*
|
|
21
|
+
* @param options - Database name, migrations folder, and optional connection overrides. See
|
|
22
|
+
* {@link CreateTestDbOptions}.
|
|
23
|
+
* @returns A handle exposing schema setup, pooling, truncation, seeding, and a reachability probe.
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* const testDb = createTestDb({ dbName: 'app_test', migrationsFolder: join(here, '..', 'drizzle') });
|
|
27
|
+
* beforeAll(async () => {
|
|
28
|
+
* await testDb.resetSchema();
|
|
29
|
+
* });
|
|
30
|
+
* const pool = testDb.createTestPool();
|
|
31
|
+
* beforeEach(() => testDb.truncateAll(pool));
|
|
32
|
+
* afterAll(() => pool.end());
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
13
35
|
export function createTestDb(options) {
|
|
14
36
|
const { dbName, migrationsFolder } = options;
|
|
15
37
|
const connection = resolveConnection(options.connection);
|
|
@@ -34,7 +56,7 @@ export function createTestDb(options) {
|
|
|
34
56
|
timezone: '+09:00',
|
|
35
57
|
});
|
|
36
58
|
// Pin ONLY_FULL_GROUP_BY on every pooled connection so GROUP BY violations surface in specs
|
|
37
|
-
// regardless of the server's my.cnf (
|
|
59
|
+
// regardless of the server's my.cnf (the policy is centralized here, not left to each server). CONCAT keeps
|
|
38
60
|
// the server's other sql_mode flags and is harmless if ONLY_FULL_GROUP_BY is already present.
|
|
39
61
|
// mysql2 queues this SET ahead of the consumer's first query on each new physical connection.
|
|
40
62
|
pool.on('connection', (conn) => {
|
package/dist/testing/fakes.d.ts
CHANGED
|
@@ -2,34 +2,102 @@ import type { Pool } from 'mysql2/promise';
|
|
|
2
2
|
import type { DisposableDatabase } from '../db/database.js';
|
|
3
3
|
import type { DecodedIdToken, FirebaseVerifier } from '../firebase/firebase-verifier.js';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* In-memory {@link FirebaseVerifier} implementation for offline route tests.
|
|
6
|
+
*
|
|
7
|
+
* Seed fake identities with {@link FakeFirebaseVerifier.register | register(token, { uid })}, then
|
|
8
|
+
* verification resolves the registered decoded token instead of calling a real Firebase backend.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```ts
|
|
12
|
+
* const firebase = new FakeFirebaseVerifier();
|
|
13
|
+
* firebase.register('tok-1', { uid: 'uid-1' });
|
|
14
|
+
* const decoded = await firebase.verifyIdToken('tok-1'); // { uid: 'uid-1' }
|
|
15
|
+
* ```
|
|
7
16
|
*/
|
|
8
17
|
export declare class FakeFirebaseVerifier implements FirebaseVerifier {
|
|
9
18
|
private readonly tokens;
|
|
19
|
+
/** UIDs passed to {@link FakeFirebaseVerifier.deleteUser}, in call order, for assertions. */
|
|
10
20
|
readonly deleted: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Register a fake decoded token so that {@link FakeFirebaseVerifier.verifyIdToken} resolves it.
|
|
23
|
+
*
|
|
24
|
+
* @param token - Token string clients will present.
|
|
25
|
+
* @param record - Decoded token returned on verification (must include `uid`).
|
|
26
|
+
*/
|
|
11
27
|
register(token: string, record: DecodedIdToken): void;
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the decoded token previously registered for `idToken`.
|
|
30
|
+
*
|
|
31
|
+
* @param idToken - Token string to verify.
|
|
32
|
+
* @returns The registered decoded token.
|
|
33
|
+
* @throws Error if the token was never registered.
|
|
34
|
+
*/
|
|
12
35
|
verifyIdToken(idToken: string): Promise<DecodedIdToken>;
|
|
36
|
+
/**
|
|
37
|
+
* Return a minimal user record echoing the requested UID.
|
|
38
|
+
*
|
|
39
|
+
* @param uid - UID to look up.
|
|
40
|
+
* @returns An object containing the `uid` (never `null` in this fake).
|
|
41
|
+
*/
|
|
13
42
|
getUser(uid: string): Promise<{
|
|
14
43
|
uid: string;
|
|
15
44
|
email?: string;
|
|
16
45
|
} | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Record a user deletion by appending the UID to {@link FakeFirebaseVerifier.deleted}.
|
|
48
|
+
*
|
|
49
|
+
* @param uid - UID being deleted.
|
|
50
|
+
*/
|
|
17
51
|
deleteUser(uid: string): Promise<void>;
|
|
18
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Options for {@link createPoolDatabase}.
|
|
55
|
+
*
|
|
56
|
+
* @typeParam TDrizzle - The Drizzle instance type, supplied by the consumer so that type identity is
|
|
57
|
+
* not coupled to this package's copy of `drizzle-orm`.
|
|
58
|
+
*/
|
|
19
59
|
export interface CreatePoolDatabaseOptions<TDrizzle> {
|
|
20
|
-
/**
|
|
60
|
+
/** Test pool used as both primary and replica. */
|
|
21
61
|
pool: Pool;
|
|
22
|
-
/**
|
|
62
|
+
/** Drizzle instance built by the consumer with its own `drizzle-orm`, e.g. `drizzle(pool, { schema, ... })`. */
|
|
23
63
|
orm: TDrizzle;
|
|
24
64
|
}
|
|
25
65
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
66
|
+
* Create a `Database` backed by a single pool used as both primary and replica, suitable for tests.
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* `dispose()` ends the pool. The `orm` is provided by the caller (rather than constructed here) so
|
|
70
|
+
* the returned database uses the consumer's own `drizzle-orm` types, avoiding type-identity clashes
|
|
71
|
+
* across duplicated `drizzle-orm` installs.
|
|
72
|
+
*
|
|
73
|
+
* @typeParam TDrizzle - The Drizzle instance type provided by the consumer.
|
|
74
|
+
* @param options - Pool and Drizzle instance. See {@link CreatePoolDatabaseOptions}.
|
|
75
|
+
* @returns A {@link DisposableDatabase} whose `dispose()` closes the pool.
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* const pool = testDb.createTestPool();
|
|
79
|
+
* const db = createPoolDatabase({ pool, orm: drizzle(pool, { schema }) });
|
|
80
|
+
* // ... run tests ...
|
|
81
|
+
* await db.dispose();
|
|
82
|
+
* ```
|
|
28
83
|
*/
|
|
29
84
|
export declare function createPoolDatabase<TDrizzle>(options: CreatePoolDatabaseOptions<TDrizzle>): DisposableDatabase<TDrizzle>;
|
|
30
85
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
86
|
+
* Create a no-op `Database` stub for routes that never touch the database (e.g. plain GET handlers).
|
|
87
|
+
*
|
|
88
|
+
* @remarks
|
|
89
|
+
* `read` resolves to an empty array, while `write` and `transaction` throw so that any unexpected
|
|
90
|
+
* database access is caught as a misuse. `dispose` is a no-op, so this can stand in for a
|
|
91
|
+
* {@link DisposableDatabase} backing (e.g. a Hyperdrive- or pool-based one) without changes.
|
|
92
|
+
*
|
|
93
|
+
* @typeParam TDrizzle - The Drizzle instance type the consumer expects (defaults to `unknown`).
|
|
94
|
+
* @returns A {@link DisposableDatabase} that reads empty and throws on writes/transactions.
|
|
95
|
+
* @throws Error from `write`/`transaction` if they are accessed.
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* const db = createNoopDatabase();
|
|
99
|
+
* await db.read('SELECT 1'); // []
|
|
100
|
+
* await db.write(async (dz) => dz); // throws: noopDatabase.write accessed unexpectedly
|
|
101
|
+
* ```
|
|
34
102
|
*/
|
|
35
103
|
export declare function createNoopDatabase<TDrizzle = unknown>(): DisposableDatabase<TDrizzle>;
|
package/dist/testing/fakes.js
CHANGED
|
@@ -1,14 +1,37 @@
|
|
|
1
1
|
import { databaseFrom } from '../db/database.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* In-memory {@link FirebaseVerifier} implementation for offline route tests.
|
|
4
|
+
*
|
|
5
|
+
* Seed fake identities with {@link FakeFirebaseVerifier.register | register(token, { uid })}, then
|
|
6
|
+
* verification resolves the registered decoded token instead of calling a real Firebase backend.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const firebase = new FakeFirebaseVerifier();
|
|
11
|
+
* firebase.register('tok-1', { uid: 'uid-1' });
|
|
12
|
+
* const decoded = await firebase.verifyIdToken('tok-1'); // { uid: 'uid-1' }
|
|
13
|
+
* ```
|
|
5
14
|
*/
|
|
6
15
|
export class FakeFirebaseVerifier {
|
|
7
16
|
tokens = new Map();
|
|
17
|
+
/** UIDs passed to {@link FakeFirebaseVerifier.deleteUser}, in call order, for assertions. */
|
|
8
18
|
deleted = [];
|
|
19
|
+
/**
|
|
20
|
+
* Register a fake decoded token so that {@link FakeFirebaseVerifier.verifyIdToken} resolves it.
|
|
21
|
+
*
|
|
22
|
+
* @param token - Token string clients will present.
|
|
23
|
+
* @param record - Decoded token returned on verification (must include `uid`).
|
|
24
|
+
*/
|
|
9
25
|
register(token, record) {
|
|
10
26
|
this.tokens.set(token, record);
|
|
11
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the decoded token previously registered for `idToken`.
|
|
30
|
+
*
|
|
31
|
+
* @param idToken - Token string to verify.
|
|
32
|
+
* @returns The registered decoded token.
|
|
33
|
+
* @throws Error if the token was never registered.
|
|
34
|
+
*/
|
|
12
35
|
async verifyIdToken(idToken) {
|
|
13
36
|
const record = this.tokens.get(idToken);
|
|
14
37
|
if (!record) {
|
|
@@ -16,16 +39,42 @@ export class FakeFirebaseVerifier {
|
|
|
16
39
|
}
|
|
17
40
|
return record;
|
|
18
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Return a minimal user record echoing the requested UID.
|
|
44
|
+
*
|
|
45
|
+
* @param uid - UID to look up.
|
|
46
|
+
* @returns An object containing the `uid` (never `null` in this fake).
|
|
47
|
+
*/
|
|
19
48
|
async getUser(uid) {
|
|
20
49
|
return { uid };
|
|
21
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Record a user deletion by appending the UID to {@link FakeFirebaseVerifier.deleted}.
|
|
53
|
+
*
|
|
54
|
+
* @param uid - UID being deleted.
|
|
55
|
+
*/
|
|
22
56
|
async deleteUser(uid) {
|
|
23
57
|
this.deleted.push(uid);
|
|
24
58
|
}
|
|
25
59
|
}
|
|
26
60
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
61
|
+
* Create a `Database` backed by a single pool used as both primary and replica, suitable for tests.
|
|
62
|
+
*
|
|
63
|
+
* @remarks
|
|
64
|
+
* `dispose()` ends the pool. The `orm` is provided by the caller (rather than constructed here) so
|
|
65
|
+
* the returned database uses the consumer's own `drizzle-orm` types, avoiding type-identity clashes
|
|
66
|
+
* across duplicated `drizzle-orm` installs.
|
|
67
|
+
*
|
|
68
|
+
* @typeParam TDrizzle - The Drizzle instance type provided by the consumer.
|
|
69
|
+
* @param options - Pool and Drizzle instance. See {@link CreatePoolDatabaseOptions}.
|
|
70
|
+
* @returns A {@link DisposableDatabase} whose `dispose()` closes the pool.
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const pool = testDb.createTestPool();
|
|
74
|
+
* const db = createPoolDatabase({ pool, orm: drizzle(pool, { schema }) });
|
|
75
|
+
* // ... run tests ...
|
|
76
|
+
* await db.dispose();
|
|
77
|
+
* ```
|
|
29
78
|
*/
|
|
30
79
|
export function createPoolDatabase(options) {
|
|
31
80
|
const { pool, orm } = options;
|
|
@@ -38,9 +87,22 @@ export function createPoolDatabase(options) {
|
|
|
38
87
|
};
|
|
39
88
|
}
|
|
40
89
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
90
|
+
* Create a no-op `Database` stub for routes that never touch the database (e.g. plain GET handlers).
|
|
91
|
+
*
|
|
92
|
+
* @remarks
|
|
93
|
+
* `read` resolves to an empty array, while `write` and `transaction` throw so that any unexpected
|
|
94
|
+
* database access is caught as a misuse. `dispose` is a no-op, so this can stand in for a
|
|
95
|
+
* {@link DisposableDatabase} backing (e.g. a Hyperdrive- or pool-based one) without changes.
|
|
96
|
+
*
|
|
97
|
+
* @typeParam TDrizzle - The Drizzle instance type the consumer expects (defaults to `unknown`).
|
|
98
|
+
* @returns A {@link DisposableDatabase} that reads empty and throws on writes/transactions.
|
|
99
|
+
* @throws Error from `write`/`transaction` if they are accessed.
|
|
100
|
+
* @example
|
|
101
|
+
* ```ts
|
|
102
|
+
* const db = createNoopDatabase();
|
|
103
|
+
* await db.read('SELECT 1'); // []
|
|
104
|
+
* await db.write(async (dz) => dz); // throws: noopDatabase.write accessed unexpectedly
|
|
105
|
+
* ```
|
|
44
106
|
*/
|
|
45
107
|
export function createNoopDatabase() {
|
|
46
108
|
return {
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test infrastructure for Hono on Cloudflare Workers projects (depends on `mysql2`/`drizzle-orm`).
|
|
3
|
+
*
|
|
4
|
+
* Test-only helpers that are never loaded at runtime. This subpath consolidates the duplicated
|
|
5
|
+
* test boilerplate (test DB setup, in-memory fakes, auth header builders, Stripe fixtures) that
|
|
6
|
+
* tends to be copy-pasted across projects into a single, importable surface.
|
|
7
|
+
*/
|
|
1
8
|
export { createTestDb } from './db.js';
|
|
2
9
|
export type { TestDb, CreateTestDbOptions, TestDbConnection } from './db.js';
|
|
3
10
|
export { FakeFirebaseVerifier, createPoolDatabase, createNoopDatabase } from './fakes.js';
|
package/dist/testing/index.js
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Shared test infrastructure for Hono on Cloudflare Workers projects (depends on `mysql2`/`drizzle-orm`).
|
|
3
|
+
*
|
|
4
|
+
* Test-only helpers that are never loaded at runtime. This subpath consolidates the duplicated
|
|
5
|
+
* test boilerplate (test DB setup, in-memory fakes, auth header builders, Stripe fixtures) that
|
|
6
|
+
* tends to be copy-pasted across projects into a single, importable surface.
|
|
7
|
+
*/
|
|
3
8
|
export { createTestDb } from './db.js';
|
|
4
9
|
export { FakeFirebaseVerifier, createPoolDatabase, createNoopDatabase } from './fakes.js';
|
|
5
|
-
//
|
|
10
|
+
// Authentication test helpers (route-spec header builders and user provisioning).
|
|
6
11
|
export { authHeaders, registerFirebaseToken, provisionUser } from './auth.js';
|
|
7
|
-
//
|
|
12
|
+
// Test double helper (partial-implementation fake that throws explicitly on unconfigured members).
|
|
8
13
|
export { configurableFake } from './configurable-fake.js';
|
|
9
|
-
//
|
|
14
|
+
// Test fixture factories for Stripe objects.
|
|
10
15
|
export { fakeApiList, fakePaymentIntent, fakeStripeEvent, fakeCheckoutSession, fakeCustomer, fakePrice, fakeSubscription, } from './stripe-fixtures.js';
|
|
@@ -1,13 +1,103 @@
|
|
|
1
1
|
import type Stripe from 'stripe';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
3
|
+
* Test fixture factories for Stripe objects.
|
|
4
|
+
*
|
|
5
|
+
* The real SDK types are enormous, so each factory builds only the fields tests typically read,
|
|
6
|
+
* fills them with reasonable defaults, lets you override via `over`, and casts to the Stripe type
|
|
7
|
+
* once at the end. This consolidates the duplicated hand-built dummy `PaymentIntent`/`Event`/... that
|
|
8
|
+
* billing tests tend to reconstruct in every project.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Build a fake Stripe `ApiList` wrapping the given data.
|
|
12
|
+
*
|
|
13
|
+
* @remarks Defaults: `object: 'list'`, `has_more: false`, `url: '/v1/_test'`.
|
|
14
|
+
* @typeParam T - Element type of the list.
|
|
15
|
+
* @param data - Items to place in `data`.
|
|
16
|
+
* @param over - Field overrides merged last.
|
|
17
|
+
* @returns A `Stripe.ApiList<T>` fixture.
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const list = fakeApiList([fakePaymentIntent()]);
|
|
21
|
+
* ```
|
|
6
22
|
*/
|
|
7
23
|
export declare function fakeApiList<T>(data: T[], over?: Partial<Stripe.ApiList<T>>): Stripe.ApiList<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Build a fake Stripe `PaymentIntent`.
|
|
26
|
+
*
|
|
27
|
+
* @remarks Defaults: `id: 'pi_test_1'`, `object: 'payment_intent'`, `amount: 1000`, `currency: 'jpy'`,
|
|
28
|
+
* `status: 'succeeded'`, `created: 1_700_000_000`.
|
|
29
|
+
* @param over - Field overrides merged last.
|
|
30
|
+
* @returns A `Stripe.PaymentIntent` fixture.
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* const pi = fakePaymentIntent({ status: 'requires_payment_method' });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
8
36
|
export declare function fakePaymentIntent(over?: Partial<Stripe.PaymentIntent>): Stripe.PaymentIntent;
|
|
37
|
+
/**
|
|
38
|
+
* Build a fake Stripe webhook `Event` wrapping the given payload.
|
|
39
|
+
*
|
|
40
|
+
* @remarks Defaults: `id: 'evt_test_1'`, `object: 'event'`, `api_version: '2024-06-20'`,
|
|
41
|
+
* `created: 1_700_000_000`, `livemode: false`. The payload is placed at `data.object`.
|
|
42
|
+
* @param type - Event type (e.g. `'payment_intent.succeeded'`), assigned to `type`.
|
|
43
|
+
* @param dataObject - The object placed at `data.object`.
|
|
44
|
+
* @param over - Field overrides merged last.
|
|
45
|
+
* @returns A `Stripe.Event` fixture.
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* const event = fakeStripeEvent('payment_intent.succeeded', fakePaymentIntent());
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
9
51
|
export declare function fakeStripeEvent(type: string, dataObject: unknown, over?: Partial<Stripe.Event>): Stripe.Event;
|
|
52
|
+
/**
|
|
53
|
+
* Build a fake Stripe `Checkout.Session`.
|
|
54
|
+
*
|
|
55
|
+
* @remarks Defaults: `id: 'cs_test_1'`, `object: 'checkout.session'`,
|
|
56
|
+
* `url: 'https://checkout.stripe.test/cs_test_1'`, `mode: 'subscription'`, `status: 'open'`.
|
|
57
|
+
* @param over - Field overrides merged last.
|
|
58
|
+
* @returns A `Stripe.Checkout.Session` fixture.
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* const session = fakeCheckoutSession({ status: 'complete' });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
10
64
|
export declare function fakeCheckoutSession(over?: Partial<Stripe.Checkout.Session>): Stripe.Checkout.Session;
|
|
65
|
+
/**
|
|
66
|
+
* Build a fake Stripe `Customer`.
|
|
67
|
+
*
|
|
68
|
+
* @remarks Defaults: `id: 'cus_test_1'`, `object: 'customer'`, `created: 1_700_000_000`,
|
|
69
|
+
* `livemode: false`.
|
|
70
|
+
* @param over - Field overrides merged last.
|
|
71
|
+
* @returns A `Stripe.Customer` fixture.
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* const customer = fakeCustomer({ email: 'a@example.com' });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
11
77
|
export declare function fakeCustomer(over?: Partial<Stripe.Customer>): Stripe.Customer;
|
|
78
|
+
/**
|
|
79
|
+
* Build a fake Stripe `Price`.
|
|
80
|
+
*
|
|
81
|
+
* @remarks Defaults: `id: 'price_test_1'`, `object: 'price'`, `active: true`, `currency: 'jpy'`,
|
|
82
|
+
* `unit_amount: 1000`.
|
|
83
|
+
* @param over - Field overrides merged last.
|
|
84
|
+
* @returns A `Stripe.Price` fixture.
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* const price = fakePrice({ unit_amount: 2000 });
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
12
90
|
export declare function fakePrice(over?: Partial<Stripe.Price>): Stripe.Price;
|
|
91
|
+
/**
|
|
92
|
+
* Build a fake Stripe `Subscription`.
|
|
93
|
+
*
|
|
94
|
+
* @remarks Defaults: `id: 'sub_test_1'`, `object: 'subscription'`, `status: 'active'`,
|
|
95
|
+
* `customer: 'cus_test_1'`, `created: 1_700_000_000`.
|
|
96
|
+
* @param over - Field overrides merged last.
|
|
97
|
+
* @returns A `Stripe.Subscription` fixture.
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* const sub = fakeSubscription({ status: 'canceled' });
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
13
103
|
export declare function fakeSubscription(over?: Partial<Stripe.Subscription>): Stripe.Subscription;
|
|
@@ -1,7 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Test fixture factories for Stripe objects.
|
|
3
|
+
*
|
|
4
|
+
* The real SDK types are enormous, so each factory builds only the fields tests typically read,
|
|
5
|
+
* fills them with reasonable defaults, lets you override via `over`, and casts to the Stripe type
|
|
6
|
+
* once at the end. This consolidates the duplicated hand-built dummy `PaymentIntent`/`Event`/... that
|
|
7
|
+
* billing tests tend to reconstruct in every project.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Build a fake Stripe `ApiList` wrapping the given data.
|
|
11
|
+
*
|
|
12
|
+
* @remarks Defaults: `object: 'list'`, `has_more: false`, `url: '/v1/_test'`.
|
|
13
|
+
* @typeParam T - Element type of the list.
|
|
14
|
+
* @param data - Items to place in `data`.
|
|
15
|
+
* @param over - Field overrides merged last.
|
|
16
|
+
* @returns A `Stripe.ApiList<T>` fixture.
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const list = fakeApiList([fakePaymentIntent()]);
|
|
20
|
+
* ```
|
|
5
21
|
*/
|
|
6
22
|
export function fakeApiList(data, over = {}) {
|
|
7
23
|
return {
|
|
@@ -12,6 +28,18 @@ export function fakeApiList(data, over = {}) {
|
|
|
12
28
|
...over,
|
|
13
29
|
};
|
|
14
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a fake Stripe `PaymentIntent`.
|
|
33
|
+
*
|
|
34
|
+
* @remarks Defaults: `id: 'pi_test_1'`, `object: 'payment_intent'`, `amount: 1000`, `currency: 'jpy'`,
|
|
35
|
+
* `status: 'succeeded'`, `created: 1_700_000_000`.
|
|
36
|
+
* @param over - Field overrides merged last.
|
|
37
|
+
* @returns A `Stripe.PaymentIntent` fixture.
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const pi = fakePaymentIntent({ status: 'requires_payment_method' });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
15
43
|
export function fakePaymentIntent(over = {}) {
|
|
16
44
|
return {
|
|
17
45
|
id: 'pi_test_1',
|
|
@@ -23,6 +51,20 @@ export function fakePaymentIntent(over = {}) {
|
|
|
23
51
|
...over,
|
|
24
52
|
};
|
|
25
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Build a fake Stripe webhook `Event` wrapping the given payload.
|
|
56
|
+
*
|
|
57
|
+
* @remarks Defaults: `id: 'evt_test_1'`, `object: 'event'`, `api_version: '2024-06-20'`,
|
|
58
|
+
* `created: 1_700_000_000`, `livemode: false`. The payload is placed at `data.object`.
|
|
59
|
+
* @param type - Event type (e.g. `'payment_intent.succeeded'`), assigned to `type`.
|
|
60
|
+
* @param dataObject - The object placed at `data.object`.
|
|
61
|
+
* @param over - Field overrides merged last.
|
|
62
|
+
* @returns A `Stripe.Event` fixture.
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* const event = fakeStripeEvent('payment_intent.succeeded', fakePaymentIntent());
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
26
68
|
export function fakeStripeEvent(type, dataObject, over = {}) {
|
|
27
69
|
return {
|
|
28
70
|
id: 'evt_test_1',
|
|
@@ -35,6 +77,18 @@ export function fakeStripeEvent(type, dataObject, over = {}) {
|
|
|
35
77
|
...over,
|
|
36
78
|
};
|
|
37
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Build a fake Stripe `Checkout.Session`.
|
|
82
|
+
*
|
|
83
|
+
* @remarks Defaults: `id: 'cs_test_1'`, `object: 'checkout.session'`,
|
|
84
|
+
* `url: 'https://checkout.stripe.test/cs_test_1'`, `mode: 'subscription'`, `status: 'open'`.
|
|
85
|
+
* @param over - Field overrides merged last.
|
|
86
|
+
* @returns A `Stripe.Checkout.Session` fixture.
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* const session = fakeCheckoutSession({ status: 'complete' });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
38
92
|
export function fakeCheckoutSession(over = {}) {
|
|
39
93
|
return {
|
|
40
94
|
id: 'cs_test_1',
|
|
@@ -45,6 +99,18 @@ export function fakeCheckoutSession(over = {}) {
|
|
|
45
99
|
...over,
|
|
46
100
|
};
|
|
47
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Build a fake Stripe `Customer`.
|
|
104
|
+
*
|
|
105
|
+
* @remarks Defaults: `id: 'cus_test_1'`, `object: 'customer'`, `created: 1_700_000_000`,
|
|
106
|
+
* `livemode: false`.
|
|
107
|
+
* @param over - Field overrides merged last.
|
|
108
|
+
* @returns A `Stripe.Customer` fixture.
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* const customer = fakeCustomer({ email: 'a@example.com' });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
48
114
|
export function fakeCustomer(over = {}) {
|
|
49
115
|
return {
|
|
50
116
|
id: 'cus_test_1',
|
|
@@ -54,6 +120,18 @@ export function fakeCustomer(over = {}) {
|
|
|
54
120
|
...over,
|
|
55
121
|
};
|
|
56
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Build a fake Stripe `Price`.
|
|
125
|
+
*
|
|
126
|
+
* @remarks Defaults: `id: 'price_test_1'`, `object: 'price'`, `active: true`, `currency: 'jpy'`,
|
|
127
|
+
* `unit_amount: 1000`.
|
|
128
|
+
* @param over - Field overrides merged last.
|
|
129
|
+
* @returns A `Stripe.Price` fixture.
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* const price = fakePrice({ unit_amount: 2000 });
|
|
133
|
+
* ```
|
|
134
|
+
*/
|
|
57
135
|
export function fakePrice(over = {}) {
|
|
58
136
|
return {
|
|
59
137
|
id: 'price_test_1',
|
|
@@ -64,6 +142,18 @@ export function fakePrice(over = {}) {
|
|
|
64
142
|
...over,
|
|
65
143
|
};
|
|
66
144
|
}
|
|
145
|
+
/**
|
|
146
|
+
* Build a fake Stripe `Subscription`.
|
|
147
|
+
*
|
|
148
|
+
* @remarks Defaults: `id: 'sub_test_1'`, `object: 'subscription'`, `status: 'active'`,
|
|
149
|
+
* `customer: 'cus_test_1'`, `created: 1_700_000_000`.
|
|
150
|
+
* @param over - Field overrides merged last.
|
|
151
|
+
* @returns A `Stripe.Subscription` fixture.
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* const sub = fakeSubscription({ status: 'canceled' });
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
67
157
|
export function fakeSubscription(over = {}) {
|
|
68
158
|
return {
|
|
69
159
|
id: 'sub_test_1',
|