@hot-updater/test-utils 1.0.0-rc.16 → 1.0.0-rc.17
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 +14 -6
- package/dist/index.d.mts +96 -49
- package/dist/index.mjs +542 -825
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -19,8 +19,10 @@ Register three suites against a disposable test database:
|
|
|
19
19
|
plugins costs at the adapter, using `createMeasuredDatabase` from
|
|
20
20
|
`@hot-updater/server/db`. `postgresRowsExamined` and `mysqlRowsExamined` also
|
|
21
21
|
count the rows a SQL database examined.
|
|
22
|
-
- `setupDatabaseTestSuite` runs core, Bundles
|
|
23
|
-
|
|
22
|
+
- `setupDatabaseTestSuite` runs core, Bundles and the Release Catalog contract
|
|
23
|
+
through the admin and client HTTP APIs of `createHotUpdater`, then the suites
|
|
24
|
+
of the server plugins listed in `plugins`, such as the Insights plugin's
|
|
25
|
+
`insightsTestSuite()` from `@hot-updater/server/plugins/insights/testing`.
|
|
24
26
|
|
|
25
27
|
Connect your database's lifecycle and the real server:
|
|
26
28
|
|
|
@@ -31,6 +33,7 @@ import {
|
|
|
31
33
|
createInsightsModel,
|
|
32
34
|
insights,
|
|
33
35
|
} from "@hot-updater/server/plugins/insights";
|
|
36
|
+
import { insightsTestSuite } from "@hot-updater/server/plugins/insights/testing";
|
|
34
37
|
import {
|
|
35
38
|
setupDatabaseTestSuite,
|
|
36
39
|
startHttpTestServer,
|
|
@@ -57,10 +60,15 @@ setupDatabaseTestSuite({
|
|
|
57
60
|
clientAccess: "public",
|
|
58
61
|
}).handlers,
|
|
59
62
|
),
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
// The server above runs insights(), so its suite runs too.
|
|
64
|
+
plugins: [
|
|
65
|
+
insightsTestSuite({
|
|
66
|
+
createModel: (database) =>
|
|
67
|
+
createInsightsModel(
|
|
68
|
+
createDatabasePluginApis(database, [insights()]).insights,
|
|
69
|
+
),
|
|
70
|
+
}),
|
|
71
|
+
],
|
|
64
72
|
});
|
|
65
73
|
```
|
|
66
74
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ArtifactInfo, Bundle, ReleaseCatalog } from "@hot-updater/core";
|
|
2
2
|
import { DatabaseAdapter, DatabaseReadCount, PhysicalTable } from "@hot-updater/plugin-core/internal";
|
|
3
|
-
import { BundleRow, ChannelRow, DeployReleasePolicy, EngineDatabase, HotUpdaterCoreApi,
|
|
3
|
+
import { AggregateBatching, BundleRow, ChannelRow, DeployReleasePolicy, EngineDatabase, HotUpdaterCoreApi, ReleaseRow, StoragePlugin } from "@hot-updater/plugin-core";
|
|
4
4
|
//#region src/httpTestClient.d.ts
|
|
5
5
|
type HttpTestRequestInit = NonNullable<ConstructorParameters<typeof Request>[1]>;
|
|
6
6
|
type HttpTestRequest = (path: string, init?: HttpTestRequestInit) => Promise<Response>;
|
|
@@ -71,37 +71,6 @@ declare const setupCoreAdminTestSuite: (options: {
|
|
|
71
71
|
readonly getClient: () => HttpTestClient;
|
|
72
72
|
}) => void;
|
|
73
73
|
//#endregion
|
|
74
|
-
//#region src/setupInsightsHttpTestSuite.d.ts
|
|
75
|
-
/**
|
|
76
|
-
* The Insights plugin's routes on a database: a client reports an event, and
|
|
77
|
-
* the admin routes read it back. The server must run `insights()`.
|
|
78
|
-
*/
|
|
79
|
-
declare const setupInsightsHttpTestSuite: (options: {
|
|
80
|
-
readonly getClient: () => HttpTestClient;
|
|
81
|
-
}) => void;
|
|
82
|
-
//#endregion
|
|
83
|
-
//#region src/databaseTestRunner.d.ts
|
|
84
|
-
type Awaitable<T> = Promise<T> | T;
|
|
85
|
-
/** How a suite gets a provider's database, and gives it back. */
|
|
86
|
-
type DatabaseTestLifecycle<TDatabase> = {
|
|
87
|
-
readonly name: string;
|
|
88
|
-
/** Creates the provider's tables and settings rows, before `createDatabase`. */
|
|
89
|
-
readonly migrate: () => Awaitable<void>;
|
|
90
|
-
/** The provider's database, as its factory returns it. */
|
|
91
|
-
readonly createDatabase: () => Awaitable<TDatabase>;
|
|
92
|
-
/** Empties every table between tests. */
|
|
93
|
-
readonly reset: (database: TDatabase) => Awaitable<void>;
|
|
94
|
-
readonly dispose: (database: TDatabase) => Awaitable<void>;
|
|
95
|
-
};
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/setupInsightsModelTestSuite.d.ts
|
|
98
|
-
/**
|
|
99
|
-
* The Insights report contract on one database: pass the Insights plugin's
|
|
100
|
-
* model over it, `createInsightsModel(createDatabasePluginApis(database,
|
|
101
|
-
* [insights()]).insights)`, as `createDatabase`.
|
|
102
|
-
*/
|
|
103
|
-
declare const setupInsightsModelTestSuite: (lifecycle: DatabaseTestLifecycle<InsightsModel>) => void;
|
|
104
|
-
//#endregion
|
|
105
74
|
//#region src/setupSemverSatisfiesTestSuite.d.ts
|
|
106
75
|
/**
|
|
107
76
|
*
|
|
@@ -124,29 +93,50 @@ declare const setupSemverSatisfiesTestSuite: ({ semverSatisfies }: {
|
|
|
124
93
|
semverSatisfies: (targetAppVersion: string, currentVersion: string) => Promise<boolean> | boolean;
|
|
125
94
|
}) => void;
|
|
126
95
|
//#endregion
|
|
96
|
+
//#region src/databaseTestRunner.d.ts
|
|
97
|
+
type Awaitable<T> = Promise<T> | T;
|
|
98
|
+
/** How a suite gets a provider's database, and gives it back. */
|
|
99
|
+
type DatabaseTestLifecycle<TDatabase> = {
|
|
100
|
+
readonly name: string;
|
|
101
|
+
/** Creates the provider's tables and settings rows, before `createDatabase`. */
|
|
102
|
+
readonly migrate: () => Awaitable<void>;
|
|
103
|
+
/** The provider's database, as its factory returns it. */
|
|
104
|
+
readonly createDatabase: () => Awaitable<TDatabase>;
|
|
105
|
+
/** Empties every table between tests. */
|
|
106
|
+
readonly reset: (database: TDatabase) => Awaitable<void>;
|
|
107
|
+
readonly dispose: (database: TDatabase) => Awaitable<void>;
|
|
108
|
+
};
|
|
109
|
+
//#endregion
|
|
127
110
|
//#region src/setupDatabaseTestSuite.d.ts
|
|
111
|
+
/**
|
|
112
|
+
* A server plugin's tests on a provider's database, such as the Insights
|
|
113
|
+
* plugin's `insightsTestSuite()`. The provider's HTTP server must run the
|
|
114
|
+
* plugin.
|
|
115
|
+
*/
|
|
116
|
+
interface DatabasePluginTestSuite<TDatabase = unknown> {
|
|
117
|
+
readonly name: string;
|
|
118
|
+
register(context: {
|
|
119
|
+
readonly getClient: () => HttpTestClient;
|
|
120
|
+
readonly getDatabase: () => TDatabase;
|
|
121
|
+
}): void;
|
|
122
|
+
}
|
|
128
123
|
type DatabaseTestSuiteOptions<TDatabase extends EngineDatabase = EngineDatabase> = DatabaseTestLifecycle<TDatabase> & {
|
|
129
124
|
/**
|
|
130
|
-
* Serves the handlers of `createHotUpdater({ database, storage, plugins
|
|
131
|
-
*
|
|
132
|
-
*
|
|
125
|
+
* Serves the handlers of `createHotUpdater({ database, storage, plugins,
|
|
126
|
+
* clientAccess: "public" })` over HTTP, in process or in the runtime the
|
|
127
|
+
* provider deploys to, with the plugins whose suites `plugins` lists.
|
|
133
128
|
*/
|
|
134
129
|
readonly createHttpClient: (options: {
|
|
135
130
|
readonly database: TDatabase;
|
|
136
131
|
readonly storage: readonly StoragePlugin[];
|
|
137
132
|
}) => HttpTestServer | Promise<HttpTestServer>;
|
|
138
|
-
/**
|
|
139
|
-
|
|
140
|
-
* contract on its tables: `(database) => createInsightsModel(
|
|
141
|
-
* createDatabasePluginApis(database, [insights()]).insights)`.
|
|
142
|
-
*/
|
|
143
|
-
readonly createInsightsModel?: (database: TDatabase) => InsightsModel;
|
|
133
|
+
/** The suites of the server plugins the provider opts in to test. */
|
|
134
|
+
readonly plugins?: readonly DatabasePluginTestSuite<TDatabase>[];
|
|
144
135
|
};
|
|
145
136
|
/**
|
|
146
137
|
* A provider's database under Hot Updater's server: core's operations, the
|
|
147
|
-
* Release Catalog contract,
|
|
148
|
-
*
|
|
149
|
-
* Insights report contract in process.
|
|
138
|
+
* Release Catalog contract, and bundles, all through HTTP, on the tables the
|
|
139
|
+
* provider's tooling creates, then each opted-in plugin's suite.
|
|
150
140
|
*/
|
|
151
141
|
declare const setupDatabaseTestSuite: <TDatabase extends EngineDatabase>(options: DatabaseTestSuiteOptions<TDatabase>) => void;
|
|
152
142
|
//#endregion
|
|
@@ -167,9 +157,17 @@ interface DatabaseAdapterConformanceOptions {
|
|
|
167
157
|
readonly maxOps?: number;
|
|
168
158
|
/** Concurrent writers in the contention cases (default 32). */
|
|
169
159
|
readonly writers?: number;
|
|
160
|
+
/**
|
|
161
|
+
* How the backend deletes rows past a table's retention: `prune` (the
|
|
162
|
+
* default) through the adapter, or `ttl`, natively on every item, with no
|
|
163
|
+
* `prune` on the adapter.
|
|
164
|
+
*/
|
|
165
|
+
readonly retention?: "prune" | "ttl";
|
|
170
166
|
}
|
|
171
167
|
declare const conformanceItems: PhysicalTable;
|
|
172
168
|
declare const conformanceCounters: PhysicalTable;
|
|
169
|
+
/** Rows expire 1,000 ms after `at`; a null `at` never expires. */
|
|
170
|
+
declare const conformanceExpiring: PhysicalTable;
|
|
173
171
|
/**
|
|
174
172
|
* The storage-adapter conformance contract (PRD S3 and S4 cases). Every
|
|
175
173
|
* adapter runs it through `verifyAdapter`, which also checks each read and
|
|
@@ -281,11 +279,13 @@ interface ReadBudgetServer {
|
|
|
281
279
|
readonly now: () => number;
|
|
282
280
|
readonly storage: ReadBudgetStorage;
|
|
283
281
|
}): ReadBudgetDatabase;
|
|
284
|
-
/** `
|
|
285
|
-
readonly
|
|
286
|
-
readonly
|
|
282
|
+
/** `toolingTargetOf` from `@hot-updater/server/database`: core's and the plugins' tables to create. */
|
|
283
|
+
toolingTargetOf(plugins: readonly unknown[]): {
|
|
284
|
+
readonly schema: {
|
|
285
|
+
readonly tables: readonly PhysicalTable[];
|
|
286
|
+
};
|
|
287
287
|
};
|
|
288
|
-
/** `[insights(), apiKeys()]`, the
|
|
288
|
+
/** `[insights(), apiKeys()]`, the plugins whose reads have budgets. */
|
|
289
289
|
readonly plugins: readonly unknown[];
|
|
290
290
|
/** `targetBaseCandidateKey` from `@hot-updater/server/db`. */
|
|
291
291
|
targetBaseCandidateKey(target: {
|
|
@@ -325,6 +325,51 @@ interface ReadBudgetSuiteOptions {
|
|
|
325
325
|
*/
|
|
326
326
|
declare const setupReadBudgetTestSuite: (options: ReadBudgetSuiteOptions) => void;
|
|
327
327
|
//#endregion
|
|
328
|
+
//#region src/setupAggregateBatchingTestSuite.d.ts
|
|
329
|
+
interface Module {
|
|
330
|
+
readonly id: string;
|
|
331
|
+
readonly schema: object;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* What the suite takes from `@hot-updater/server/database`; pass that
|
|
335
|
+
* module, which keeps test-utils free of a dependency on the server.
|
|
336
|
+
*/
|
|
337
|
+
interface AggregateBatchingEngine {
|
|
338
|
+
resolveSchema(modules: readonly Module[]): {
|
|
339
|
+
readonly tables: readonly PhysicalTable[];
|
|
340
|
+
};
|
|
341
|
+
readonly aggregateBatchingModule: Module;
|
|
342
|
+
createDatabaseEngine(options: {
|
|
343
|
+
readonly adapter: DatabaseAdapter;
|
|
344
|
+
readonly schema: never;
|
|
345
|
+
readonly batching?: AggregateBatching;
|
|
346
|
+
readonly retry?: {
|
|
347
|
+
readonly attempts?: number;
|
|
348
|
+
};
|
|
349
|
+
}): {
|
|
350
|
+
database(module: Module): unknown;
|
|
351
|
+
flush(): Promise<void>;
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
interface AggregateBatchingSuiteOptions {
|
|
355
|
+
readonly name: string;
|
|
356
|
+
/** `import * as engine from "@hot-updater/server/database"`. */
|
|
357
|
+
readonly engine: AggregateBatchingEngine;
|
|
358
|
+
/** Returns an adapter over an empty store; each case calls it once. */
|
|
359
|
+
readonly createAdapter: () => Promise<{
|
|
360
|
+
readonly adapter: DatabaseAdapter;
|
|
361
|
+
readonly cleanup?: () => Promise<void>;
|
|
362
|
+
}>;
|
|
363
|
+
/** Aggregate rows one transaction changes in the too-large case: more than one write takes. */
|
|
364
|
+
readonly oversizedRows: number;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Aggregates declared `batched` on a key-value store: log rows the store
|
|
368
|
+
* writes with each transaction and a compaction merges once, and a memory
|
|
369
|
+
* buffer, read back exactly as transactional writes read.
|
|
370
|
+
*/
|
|
371
|
+
declare const setupAggregateBatchingTestSuite: ({ name, engine, createAdapter, oversizedRows }: AggregateBatchingSuiteOptions) => void;
|
|
372
|
+
//#endregion
|
|
328
373
|
//#region src/runContentionHarness.d.ts
|
|
329
374
|
/** Delays every adapter call by `ms`, like a round trip to a remote database. */
|
|
330
375
|
declare const withAdapterLatency: (adapter: DatabaseAdapter, ms: number) => DatabaseAdapter;
|
|
@@ -385,6 +430,8 @@ interface PluginTestEngine {
|
|
|
385
430
|
interface HarnessPlugin {
|
|
386
431
|
readonly id: string;
|
|
387
432
|
readonly schema: object;
|
|
433
|
+
/** `false` keeps the declared table names, as `createHotUpdater` does. */
|
|
434
|
+
readonly namespace?: false;
|
|
388
435
|
init(context: never): {
|
|
389
436
|
readonly api: unknown;
|
|
390
437
|
};
|
|
@@ -449,4 +496,4 @@ declare const RELEASE_CATALOG_STORAGE_FIXTURES: Readonly<Record<string, string>>
|
|
|
449
496
|
/** Storage boundary shared by provider and separately spawned server tests. */
|
|
450
497
|
declare const createReleaseCatalogTestStorage: () => StoragePlugin;
|
|
451
498
|
//#endregion
|
|
452
|
-
export { AdminApiTestClient, ContentionOptions, ContentionReport, DatabaseAdapterConformanceContext, DatabaseAdapterConformanceOptions, type DatabaseTestLifecycle, DatabaseTestSuiteOptions, HttpTestClient, HttpTestHandlers, HttpTestRequest, HttpTestRequestInit, HttpTestServer, PluginTestEngine, PluginTestHarnessOptions, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, ReadBudgetAdapterContext, ReadBudgetDatabase, ReadBudgetServer, ReadBudgetStorage, ReadBudgetSuiteOptions, RowsExamined, SqlExecutorLike, SqlQuery, SqlStatementLike, conformanceCounters, conformanceItems, createAdminApiTestClient, createHandlerHttpTestClient, createHttpTestClient, createPluginTestHarness, createReleaseCatalogTestStorage, jsonRequest, mysqlRowsExamined, postgresRowsExamined, releaseCatalogArtifact, releaseCatalogDownloadUrl, runContentionHarness, setupBundleMethodsTestSuite, setupCoreAdminTestSuite, setupDatabaseAdapterConformanceSuite, setupDatabaseTestSuite,
|
|
499
|
+
export { AdminApiTestClient, AggregateBatchingEngine, AggregateBatchingSuiteOptions, ContentionOptions, ContentionReport, DatabaseAdapterConformanceContext, DatabaseAdapterConformanceOptions, DatabasePluginTestSuite, type DatabaseTestLifecycle, DatabaseTestSuiteOptions, HttpTestClient, HttpTestHandlers, HttpTestRequest, HttpTestRequestInit, HttpTestServer, PluginTestEngine, PluginTestHarnessOptions, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, ReadBudgetAdapterContext, ReadBudgetDatabase, ReadBudgetServer, ReadBudgetStorage, ReadBudgetSuiteOptions, RowsExamined, SqlExecutorLike, SqlQuery, SqlStatementLike, conformanceCounters, conformanceExpiring, conformanceItems, createAdminApiTestClient, createHandlerHttpTestClient, createHttpTestClient, createPluginTestHarness, createReleaseCatalogTestStorage, jsonRequest, mysqlRowsExamined, postgresRowsExamined, releaseCatalogArtifact, releaseCatalogDownloadUrl, runContentionHarness, setupAggregateBatchingTestSuite, setupBundleMethodsTestSuite, setupCoreAdminTestSuite, setupDatabaseAdapterConformanceSuite, setupDatabaseTestSuite, setupReadBudgetTestSuite, setupReleaseCatalogTestSuite, setupSemverSatisfiesTestSuite, startHttpTestServer, toDeployBundle, withAdapterLatency };
|