@hot-updater/test-utils 1.0.0-rc.16 → 1.0.0-rc.21
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 +104 -17
- package/dist/index.d.mts +236 -158
- package/dist/index.mjs +3051 -1704
- package/dist/node.cjs +210 -0
- package/dist/node.d.cts +49 -4
- package/dist/node.d.mts +49 -4
- package/dist/node.mjs +203 -1
- package/dist/react-native.cjs +177 -0
- package/dist/react-native.d.cts +104 -0
- package/dist/react-native.d.mts +104 -0
- package/dist/react-native.mjs +174 -0
- package/package.json +44 -6
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ArtifactInfo, Bundle
|
|
2
|
-
import { DatabaseAdapter,
|
|
3
|
-
import {
|
|
1
|
+
import { ArtifactInfo, Bundle } from "@hot-updater/protocol";
|
|
2
|
+
import { AnyHotUpdaterPlugin, BundlePatchRow, BundleRow, ChannelRow, ClientAuth, DatabaseAdapter, DeployReleasePolicy, EngineDatabase, HotUpdaterCoreApi, HotUpdaterDatabase, KeyValueStore, ModuleSchema, PhysicalTable, PluginApis, ReadMeasurement, ReleaseRow, StorageAdapter, StorageOperation } from "@hot-updater/plugin-core";
|
|
3
|
+
import { BundleEventRow, InsightsModel } from "@hot-updater/plugin-insights/server";
|
|
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
|
-
readonly storage: readonly
|
|
131
|
+
readonly storage: readonly StorageAdapter[];
|
|
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
|
|
@@ -177,9 +175,38 @@ declare const conformanceCounters: PhysicalTable;
|
|
|
177
175
|
*/
|
|
178
176
|
declare const setupDatabaseAdapterConformanceSuite: (options: DatabaseAdapterConformanceOptions) => void;
|
|
179
177
|
//#endregion
|
|
178
|
+
//#region src/setupStorageAdapterTestSuite.d.ts
|
|
179
|
+
interface StorageAdapterTestSuiteOptions {
|
|
180
|
+
readonly name: string;
|
|
181
|
+
/**
|
|
182
|
+
* Returns the adapter over an empty bucket or base path. Every case creates
|
|
183
|
+
* its own and calls `cleanup` after it.
|
|
184
|
+
*/
|
|
185
|
+
readonly createStorage: () => Promise<{
|
|
186
|
+
readonly storage: StorageAdapter;
|
|
187
|
+
/**
|
|
188
|
+
* The base path the adapter stores keys below. With it, every URI `put`
|
|
189
|
+
* returns must have the key `<basePath>/<key>`.
|
|
190
|
+
*/
|
|
191
|
+
readonly basePath?: string;
|
|
192
|
+
readonly cleanup?: () => Promise<void>;
|
|
193
|
+
}>;
|
|
194
|
+
/**
|
|
195
|
+
* The operations the adapter implements. The suite requires them, with
|
|
196
|
+
* `put` and `get`, and skips the cases of every other operation. Without
|
|
197
|
+
* it, the suite skips the cases of the operations the adapter lacks.
|
|
198
|
+
*/
|
|
199
|
+
readonly operations?: readonly StorageOperation[];
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* The storage adapter contract that deploy, patch, the Console, the server,
|
|
203
|
+
* and `storage prune` rely on. Every official storage adapter runs it.
|
|
204
|
+
*/
|
|
205
|
+
declare const setupStorageAdapterTestSuite: (options: StorageAdapterTestSuiteOptions) => void;
|
|
206
|
+
//#endregion
|
|
180
207
|
//#region src/sqlRowsExamined.d.ts
|
|
181
208
|
/**
|
|
182
|
-
* Rows a SQL database examined for the reads a
|
|
209
|
+
* Rows a SQL database examined for the reads a database adapter made: what
|
|
183
210
|
* read-budget suites compare with the rows the adapter read.
|
|
184
211
|
*/
|
|
185
212
|
interface RowsExamined {
|
|
@@ -239,70 +266,17 @@ declare const mysqlRowsExamined: (query: SqlQuery) => {
|
|
|
239
266
|
};
|
|
240
267
|
//#endregion
|
|
241
268
|
//#region src/setupReadBudgetTestSuite.d.ts
|
|
242
|
-
/** Core's API with the client routes' reads, which the public API leaves out. */
|
|
243
|
-
type ReadBudgetCore = HotUpdaterCoreApi & {
|
|
244
|
-
getReleaseCatalog(input: {
|
|
245
|
-
readonly strategy: "APP_VERSION";
|
|
246
|
-
readonly platform: "ios" | "android";
|
|
247
|
-
readonly channelKey: string;
|
|
248
|
-
readonly appVersion: string;
|
|
249
|
-
}): Promise<ReleaseCatalog | null>;
|
|
250
|
-
getArtifactInfo(targetBundleId: string, currentBundleId: string, artifactProtocolVersion: 1): Promise<ArtifactInfo | null>;
|
|
251
|
-
latestReleaseId(scopeKey: string): Promise<string | null>;
|
|
252
|
-
};
|
|
253
|
-
/** How core reads bundle manifests and resolves file URLs. */
|
|
254
|
-
interface ReadBudgetStorage {
|
|
255
|
-
readonly readStorageText?: (storageUri: string) => Promise<string | null>;
|
|
256
|
-
readonly resolveFileUrl: (storageUri: string | null) => Promise<string | null>;
|
|
257
|
-
}
|
|
258
|
-
/** What `createMeasuredDatabase` returns: core and the plugins on one engine in verify mode. */
|
|
259
|
-
interface ReadBudgetDatabase {
|
|
260
|
-
readonly core: ReadBudgetCore;
|
|
261
|
-
readonly api: Readonly<Record<string, unknown>>;
|
|
262
|
-
readonly clientAuth?: {
|
|
263
|
-
authenticate(headers: Headers): Promise<boolean>;
|
|
264
|
-
};
|
|
265
|
-
measureReads<T>(read: () => Promise<T>): Promise<{
|
|
266
|
-
readonly result: T;
|
|
267
|
-
readonly adapter: DatabaseReadCount;
|
|
268
|
-
readonly engine: {
|
|
269
|
-
readonly calls: number;
|
|
270
|
-
readonly rows: number;
|
|
271
|
-
};
|
|
272
|
-
}>;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* What the suite takes from `@hot-updater/server`; pass those exports, which
|
|
276
|
-
* keeps test-utils free of a dependency on the server.
|
|
277
|
-
*/
|
|
278
|
-
interface ReadBudgetServer {
|
|
279
|
-
/** `createMeasuredDatabase` from `@hot-updater/server/db`. */
|
|
280
|
-
createMeasuredDatabase(adapter: DatabaseAdapter, plugins: readonly unknown[], options: {
|
|
281
|
-
readonly now: () => number;
|
|
282
|
-
readonly storage: ReadBudgetStorage;
|
|
283
|
-
}): ReadBudgetDatabase;
|
|
284
|
-
/** `builtInSchema` from `@hot-updater/server/database`: the tables to create. */
|
|
285
|
-
readonly builtInSchema: {
|
|
286
|
-
readonly tables: readonly PhysicalTable[];
|
|
287
|
-
};
|
|
288
|
-
/** `[insights(), apiKeys()]`, the built-in plugins whose reads have budgets. */
|
|
289
|
-
readonly plugins: readonly unknown[];
|
|
290
|
-
/** `targetBaseCandidateKey` from `@hot-updater/server/db`. */
|
|
291
|
-
targetBaseCandidateKey(target: {
|
|
292
|
-
readonly channel: string;
|
|
293
|
-
readonly platform: "ios" | "android";
|
|
294
|
-
readonly fingerprintHash: string | null;
|
|
295
|
-
readonly appVersion: string | null;
|
|
296
|
-
}): string | null;
|
|
297
|
-
}
|
|
298
269
|
interface ReadBudgetAdapterContext {
|
|
270
|
+
/**
|
|
271
|
+
* Core's tables, those of the plugins the suite measures, and the settings
|
|
272
|
+
* table, where the retention passes keep their lease.
|
|
273
|
+
*/
|
|
299
274
|
readonly tables: readonly PhysicalTable[];
|
|
300
275
|
/** Test-only: the adapter must fetch at most this many rows per native page. */
|
|
301
276
|
readonly nativePageSize: number;
|
|
302
277
|
}
|
|
303
278
|
interface ReadBudgetSuiteOptions {
|
|
304
279
|
readonly name: string;
|
|
305
|
-
readonly server: ReadBudgetServer;
|
|
306
280
|
/** Returns an adapter over freshly created, empty tables. */
|
|
307
281
|
readonly createAdapter: (context: ReadBudgetAdapterContext) => Promise<{
|
|
308
282
|
readonly adapter: DatabaseAdapter;
|
|
@@ -317,14 +291,33 @@ interface ReadBudgetSuiteOptions {
|
|
|
317
291
|
}>;
|
|
318
292
|
}
|
|
319
293
|
/**
|
|
320
|
-
* The read-budget suite (PRD S4) on one backend: core and the
|
|
321
|
-
* plugins
|
|
322
|
-
* two rows. Every API in the read-budget list reads
|
|
323
|
-
* adapter and at the engine; with `examined`, the
|
|
324
|
-
* rows than the adapter read, within the backend's
|
|
294
|
+
* The read-budget suite (PRD S4) on one backend: core and the server's
|
|
295
|
+
* built-in plugins on `createMeasuredDatabase` over the backend's adapter,
|
|
296
|
+
* native pages capped at two rows. Every API in the read-budget list reads
|
|
297
|
+
* exactly its budget at the adapter and at the engine; with `examined`, the
|
|
298
|
+
* database examines no more rows than the adapter read, within the backend's
|
|
299
|
+
* native multipliers. It loads `@hot-updater/server`, an optional peer.
|
|
325
300
|
*/
|
|
326
301
|
declare const setupReadBudgetTestSuite: (options: ReadBudgetSuiteOptions) => void;
|
|
327
302
|
//#endregion
|
|
303
|
+
//#region src/setupAggregateBatchingTestSuite.d.ts
|
|
304
|
+
interface AggregateBatchingSuiteOptions {
|
|
305
|
+
readonly name: string;
|
|
306
|
+
/** Returns an adapter over an empty store; each case calls it once. */
|
|
307
|
+
readonly createAdapter: () => Promise<{
|
|
308
|
+
readonly adapter: DatabaseAdapter;
|
|
309
|
+
readonly cleanup?: () => Promise<void>;
|
|
310
|
+
}>;
|
|
311
|
+
/** Aggregate rows one transaction changes in the too-large case: more than one write takes. */
|
|
312
|
+
readonly oversizedRows: number;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Aggregates declared `batched` on a key-value store: log rows the store
|
|
316
|
+
* writes with each transaction and a compaction merges once, and a memory
|
|
317
|
+
* buffer, read back exactly as transactional writes read.
|
|
318
|
+
*/
|
|
319
|
+
declare const setupAggregateBatchingTestSuite: ({ name, createAdapter, oversizedRows }: AggregateBatchingSuiteOptions) => void;
|
|
320
|
+
//#endregion
|
|
328
321
|
//#region src/runContentionHarness.d.ts
|
|
329
322
|
/** Delays every adapter call by `ms`, like a round trip to a remote database. */
|
|
330
323
|
declare const withAdapterLatency: (adapter: DatabaseAdapter, ms: number) => DatabaseAdapter;
|
|
@@ -351,75 +344,160 @@ interface ContentionReport {
|
|
|
351
344
|
*/
|
|
352
345
|
declare const runContentionHarness: (options: ContentionOptions) => Promise<ContentionReport>;
|
|
353
346
|
//#endregion
|
|
354
|
-
//#region src/
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
readonly
|
|
358
|
-
readonly
|
|
347
|
+
//#region src/createMeasuredDatabase.d.ts
|
|
348
|
+
/** How core reads bundle manifests and resolves the file URLs of `storage://` URIs. */
|
|
349
|
+
interface MeasuredDatabaseStorage {
|
|
350
|
+
readonly readStorageText?: (storageUri: string) => Promise<string | null>;
|
|
351
|
+
readonly resolveFileUrl: (storageUri: string) => Promise<string>;
|
|
359
352
|
}
|
|
360
|
-
interface
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
353
|
+
interface MeasuredDatabaseOptions {
|
|
354
|
+
/**
|
|
355
|
+
* The plugins' clock, which they read through `ctx.now`. Core's
|
|
356
|
+
* timestamps and the retention passes run on `Date.now`.
|
|
357
|
+
*/
|
|
358
|
+
readonly now?: () => number;
|
|
359
|
+
/**
|
|
360
|
+
* The storage of `storage://` URIs, for reads that resolve them, such as
|
|
361
|
+
* artifact resolution; without it those reads fail.
|
|
362
|
+
*/
|
|
363
|
+
readonly storage?: MeasuredDatabaseStorage;
|
|
364
|
+
}
|
|
365
|
+
/** Core and the plugins' APIs on `createHotUpdater`, with the database's read meter. */
|
|
366
|
+
interface MeasuredDatabase<TApi = Readonly<Record<string, unknown>>> {
|
|
367
|
+
readonly core: HotUpdaterCoreApi;
|
|
368
|
+
readonly api: TApi;
|
|
369
|
+
/** The client-route policy, when a plugin provides one. */
|
|
370
|
+
readonly clientAuth?: ClientAuth;
|
|
371
|
+
/** Runs `read` and reports what it read at the adapter and at the engine. */
|
|
372
|
+
measureReads<T>(read: () => Promise<T>): Promise<ReadMeasurement<T>>;
|
|
370
373
|
}
|
|
371
374
|
/**
|
|
372
|
-
*
|
|
373
|
-
*
|
|
375
|
+
* Core and the plugins over `adapter` as `createHotUpdater` runs them: what
|
|
376
|
+
* read-budget suites measure. The database is `meterReads` over
|
|
377
|
+
* `verifyAdapter(adapter)`, so every adapter call is checked against the
|
|
378
|
+
* contract and `measureReads` reports each call's reads; the adapter runs
|
|
379
|
+
* as given, without the schema fence. It loads `@hot-updater/server`, an
|
|
380
|
+
* optional peer, when called.
|
|
374
381
|
*/
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
};
|
|
379
|
-
createDatabaseEngine(options: {
|
|
380
|
-
readonly adapter: DatabaseAdapter;
|
|
381
|
-
readonly schema: never;
|
|
382
|
-
readonly verify?: boolean;
|
|
383
|
-
}): HarnessEngine;
|
|
384
|
-
}
|
|
382
|
+
declare const createMeasuredDatabase: <const TPlugins extends readonly AnyHotUpdaterPlugin[]>(adapter: DatabaseAdapter, plugins: TPlugins, options?: MeasuredDatabaseOptions) => Promise<MeasuredDatabase<PluginApis<TPlugins>>>;
|
|
383
|
+
//#endregion
|
|
384
|
+
//#region src/createPluginTestHarness.d.ts
|
|
385
385
|
interface HarnessPlugin {
|
|
386
386
|
readonly id: string;
|
|
387
|
-
readonly
|
|
387
|
+
readonly schemaVersion: string;
|
|
388
|
+
readonly schema: ModuleSchema;
|
|
389
|
+
/** `false` keeps the declared table names, as `createHotUpdater` does. */
|
|
390
|
+
readonly namespace?: false;
|
|
388
391
|
init(context: never): {
|
|
389
392
|
readonly api: unknown;
|
|
390
393
|
};
|
|
391
394
|
}
|
|
392
395
|
interface PluginTestHarnessOptions {
|
|
393
|
-
/** `import * as engine from "@hot-updater/server/database"`. */
|
|
394
|
-
readonly engine: PluginTestEngine;
|
|
395
396
|
/** Defaults to a fresh memory adapter. */
|
|
396
397
|
readonly adapter?: DatabaseAdapter;
|
|
397
398
|
/** The plugin's clock; `setNow` changes it later. */
|
|
398
399
|
readonly now?: () => number;
|
|
399
400
|
}
|
|
400
401
|
/**
|
|
401
|
-
* Runs one plugin the
|
|
402
|
-
*
|
|
402
|
+
* Runs one plugin on `createEngine`, the storage engine `createHotUpdater`
|
|
403
|
+
* runs its plugins on, so tests can call its API and measure what each call
|
|
404
|
+
* reads. Every adapter call is checked against the adapter contract
|
|
405
|
+
* (`verifyAdapter`), and `ctx.core` is empty: core's reads need a server.
|
|
406
|
+
* The engine gets the adapter without `prune`, as a store that expires rows
|
|
407
|
+
* itself would give it, so no retention pass adds to what a call reads and
|
|
408
|
+
* writes; rows stay past their retention.
|
|
403
409
|
*/
|
|
404
|
-
declare const createPluginTestHarness: <TPlugin extends HarnessPlugin>(plugin: TPlugin, options
|
|
410
|
+
declare const createPluginTestHarness: <TPlugin extends HarnessPlugin>(plugin: TPlugin, options?: PluginTestHarnessOptions) => Promise<{
|
|
405
411
|
api: ReturnType<TPlugin["init"]>["api"];
|
|
406
412
|
instance: ReturnType<TPlugin["init"]>;
|
|
407
|
-
/** The plugin's own database handle
|
|
408
|
-
db:
|
|
413
|
+
/** The plugin's own database handle. */
|
|
414
|
+
db: HotUpdaterDatabase<TPlugin["schema"]>;
|
|
409
415
|
adapter: DatabaseAdapter;
|
|
410
|
-
tables
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
416
|
+
/** The plugin's physical tables. */
|
|
417
|
+
tables: import("@hot-updater/plugin-core").PhysicalTable[];
|
|
418
|
+
/** Reads at the adapter and at the engine while `read` runs. */
|
|
419
|
+
measureReads: <T>(read: () => Promise<T>) => Promise<import("@hot-updater/plugin-core").ReadMeasurement<T>>;
|
|
420
|
+
setNow: (next: () => number) => void;
|
|
421
|
+
}>;
|
|
422
|
+
//#endregion
|
|
423
|
+
//#region src/databaseTestFixtures.d.ts
|
|
424
|
+
declare const createChannelRowFixture: (name?: string) => ChannelRow;
|
|
425
|
+
declare const createBundleRowFixture: (suffix: string, _channel?: string) => BundleRow;
|
|
426
|
+
declare const createBundlePatchRowFixture: (suffix: string, bundleId: string, baseBundleId: string, orderIndex?: number) => BundlePatchRow;
|
|
427
|
+
declare const createReleaseRowFixture: (suffix: string, bundle: BundleRow, channel: ChannelRow) => ReleaseRow;
|
|
428
|
+
declare const createBundleFixture: (suffix: string, _channel?: string) => Bundle;
|
|
429
|
+
//#endregion
|
|
430
|
+
//#region src/storeBundles.d.ts
|
|
431
|
+
/**
|
|
432
|
+
* Stores bundles and their patches with no release, as a database holds an
|
|
433
|
+
* artifact that no release uses. `deploy` always adds a release, and
|
|
434
|
+
* deleting a bundle's last release deletes the bundle, so tests store such
|
|
435
|
+
* bundles through the engine. A patch's base comes before it in `bundles`.
|
|
436
|
+
*/
|
|
437
|
+
declare const storeBundles: (database: EngineDatabase, bundles: readonly Bundle[]) => Promise<void>;
|
|
438
|
+
//#endregion
|
|
439
|
+
//#region src/insights/expectInsightsIndex.d.ts
|
|
440
|
+
/** Allow committed secondary indexes to catch up; query failures still fail. */
|
|
441
|
+
declare const expectInsightsIndex: <T>(query: () => Promise<T>, expected: T) => Promise<void>;
|
|
442
|
+
//#endregion
|
|
443
|
+
//#region src/insights/fixtures.d.ts
|
|
444
|
+
/** An applied update from install `suffix`, received at `receivedAtMs`. */
|
|
445
|
+
declare const createBundleEventRowFixture: (suffix: string, receivedAtMs: number) => Extract<BundleEventRow, {
|
|
446
|
+
type: "UPDATE_APPLIED" | "RECOVERED";
|
|
421
447
|
}>;
|
|
422
448
|
//#endregion
|
|
449
|
+
//#region src/insights/setupInsightsHttpTestSuite.d.ts
|
|
450
|
+
/**
|
|
451
|
+
* The Insights plugin's routes on a database: a client reports an event, and
|
|
452
|
+
* the admin routes read it back. The server must run `insights()`.
|
|
453
|
+
*/
|
|
454
|
+
declare const setupInsightsHttpTestSuite: (options: {
|
|
455
|
+
readonly getClient: () => HttpTestClient;
|
|
456
|
+
}) => void;
|
|
457
|
+
//#endregion
|
|
458
|
+
//#region src/insights/setupInsightsModelTestSuite.d.ts
|
|
459
|
+
/**
|
|
460
|
+
* The Insights report contract on one database: pass the Insights plugin's
|
|
461
|
+
* model over it, `createInsightsModel(createHotUpdater({ database, plugins:
|
|
462
|
+
* [insights()], clientAccess: "public" }).api.insights)`, as `createDatabase`.
|
|
463
|
+
*/
|
|
464
|
+
declare const setupInsightsModelTestSuite: (lifecycle: DatabaseTestLifecycle<InsightsModel>) => void;
|
|
465
|
+
//#endregion
|
|
466
|
+
//#region src/insights/index.d.ts
|
|
467
|
+
/**
|
|
468
|
+
* The Insights plugin's tests, for `setupDatabaseTestSuite`'s `plugins`: its
|
|
469
|
+
* HTTP routes on the provider's server, which must run `insights()`, and
|
|
470
|
+
* with `createModel` its report contract on the provider's database, such as
|
|
471
|
+
* `(database) => createInsightsModel(createHotUpdater({ database, plugins:
|
|
472
|
+
* [insights()], clientAccess: "public" }).api.insights)`.
|
|
473
|
+
*/
|
|
474
|
+
declare const insightsTestSuite: <TDatabase = EngineDatabase>(options?: {
|
|
475
|
+
readonly createModel?: (database: TDatabase) => InsightsModel;
|
|
476
|
+
}) => {
|
|
477
|
+
name: string;
|
|
478
|
+
register: (context: {
|
|
479
|
+
readonly getClient: () => HttpTestClient;
|
|
480
|
+
readonly getDatabase: () => TDatabase;
|
|
481
|
+
}) => void;
|
|
482
|
+
};
|
|
483
|
+
//#endregion
|
|
484
|
+
//#region src/kvTestStore.d.ts
|
|
485
|
+
interface MemoryKeyValueStoreOptions {
|
|
486
|
+
/** Test-only: the most items one `query` page returns. */
|
|
487
|
+
readonly nativePageSize?: number;
|
|
488
|
+
readonly limits?: KeyValueStore["limits"];
|
|
489
|
+
}
|
|
490
|
+
/**
|
|
491
|
+
* An in-memory key-value store that behaves like DynamoDB where the adapter
|
|
492
|
+
* can tell: a write checks every condition against the state before it,
|
|
493
|
+
* refuses two ops on one item, and applies all ops or none.
|
|
494
|
+
*/
|
|
495
|
+
declare const createMemoryKeyValueStore: (options?: MemoryKeyValueStoreOptions) => KeyValueStore & {
|
|
496
|
+
readonly items: () => number;
|
|
497
|
+
/** Each item's expiry, as a store's native TTL would hold it, by `[pk, sk]` JSON. */
|
|
498
|
+
readonly expiries: () => ReadonlyMap<string, number>;
|
|
499
|
+
};
|
|
500
|
+
//#endregion
|
|
423
501
|
//#region src/setupReleaseCatalogTestSuite.d.ts
|
|
424
502
|
/**
|
|
425
503
|
* Server conformance through HTTP only, on admin API protocol 2. The caller
|
|
@@ -447,6 +525,6 @@ declare const releaseCatalogArtifact: (bundle: BundleRow) => ArtifactInfo;
|
|
|
447
525
|
/** Install these storage objects in the test server, independently of its DB. */
|
|
448
526
|
declare const RELEASE_CATALOG_STORAGE_FIXTURES: Readonly<Record<string, string>>;
|
|
449
527
|
/** Storage boundary shared by provider and separately spawned server tests. */
|
|
450
|
-
declare const createReleaseCatalogTestStorage: () =>
|
|
528
|
+
declare const createReleaseCatalogTestStorage: () => StorageAdapter;
|
|
451
529
|
//#endregion
|
|
452
|
-
export { AdminApiTestClient, ContentionOptions, ContentionReport, DatabaseAdapterConformanceContext, DatabaseAdapterConformanceOptions, type DatabaseTestLifecycle, DatabaseTestSuiteOptions, HttpTestClient, HttpTestHandlers, HttpTestRequest, HttpTestRequestInit, HttpTestServer,
|
|
530
|
+
export { AdminApiTestClient, AggregateBatchingSuiteOptions, ContentionOptions, ContentionReport, DatabaseAdapterConformanceContext, DatabaseAdapterConformanceOptions, DatabasePluginTestSuite, type DatabaseTestLifecycle, DatabaseTestSuiteOptions, HttpTestClient, HttpTestHandlers, HttpTestRequest, HttpTestRequestInit, HttpTestServer, MeasuredDatabase, MeasuredDatabaseOptions, MeasuredDatabaseStorage, MemoryKeyValueStoreOptions, PluginTestHarnessOptions, RELEASE_CATALOG_DOWNLOAD_HASH, RELEASE_CATALOG_MANIFEST_URIS, RELEASE_CATALOG_STORAGE_FIXTURES, ReadBudgetAdapterContext, ReadBudgetSuiteOptions, RowsExamined, SqlExecutorLike, SqlQuery, SqlStatementLike, StorageAdapterTestSuiteOptions, conformanceCounters, conformanceExpiring, conformanceItems, createAdminApiTestClient, createBundleEventRowFixture, createBundleFixture, createBundlePatchRowFixture, createBundleRowFixture, createChannelRowFixture, createHandlerHttpTestClient, createHttpTestClient, createMeasuredDatabase, createMemoryKeyValueStore, createPluginTestHarness, createReleaseCatalogTestStorage, createReleaseRowFixture, expectInsightsIndex, insightsTestSuite, jsonRequest, mysqlRowsExamined, postgresRowsExamined, releaseCatalogArtifact, releaseCatalogDownloadUrl, runContentionHarness, setupAggregateBatchingTestSuite, setupBundleMethodsTestSuite, setupCoreAdminTestSuite, setupDatabaseAdapterConformanceSuite, setupDatabaseTestSuite, setupInsightsHttpTestSuite, setupInsightsModelTestSuite, setupReadBudgetTestSuite, setupReleaseCatalogTestSuite, setupSemverSatisfiesTestSuite, setupStorageAdapterTestSuite, startHttpTestServer, storeBundles, toDeployBundle, withAdapterLatency };
|