@hot-updater/test-utils 1.0.0-rc.42 → 1.0.0-rc.44

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 CHANGED
@@ -37,12 +37,10 @@ Connect your database's lifecycle and the real server:
37
37
 
38
38
  ```ts
39
39
  import { createHotUpdater } from "@hot-updater/server";
40
- import {
41
- createInsightsModel,
42
- insights,
43
- } from "@hot-updater/server/plugins/insights";
40
+ import { createInsightsModel, insights } from "@hot-updater/server/plugins";
44
41
 
45
42
  import {
43
+ createReleaseCatalogTestStorage,
46
44
  insightsTestSuite,
47
45
  setupDatabaseTestSuite,
48
46
  startHttpTestServer,
@@ -77,6 +75,7 @@ setupDatabaseTestSuite({
77
75
  createInsightsModel(
78
76
  createHotUpdater({
79
77
  database,
78
+ storage: createReleaseCatalogTestStorage(),
80
79
  plugins: [insights()],
81
80
  clientAccess: "public",
82
81
  }).api.insights,
package/dist/index.d.mts CHANGED
@@ -128,7 +128,7 @@ type DatabaseTestSuiteOptions<TDatabase extends EngineDatabase = EngineDatabase>
128
128
  */
129
129
  readonly createHttpClient: (options: {
130
130
  readonly database: TDatabase;
131
- readonly storage: readonly StorageAdapter[];
131
+ readonly storage: StorageAdapter;
132
132
  }) => HttpTestServer | Promise<HttpTestServer>;
133
133
  /** The suites of the server plugins the provider opts in to test. */
134
134
  readonly plugins?: readonly DatabasePluginTestSuite<TDatabase>[];
@@ -464,8 +464,9 @@ declare const setupInsightsHttpTestSuite: (options: {
464
464
  //#region src/insights/setupInsightsModelTestSuite.d.ts
465
465
  /**
466
466
  * The Insights report contract on one database: pass the Insights plugin's
467
- * model over it, `createInsightsModel(createHotUpdater({ database, plugins:
468
- * [insights()], clientAccess: "public" }).api.insights)`, as `createDatabase`.
467
+ * model over it, `createInsightsModel(createHotUpdater({ database, storage,
468
+ * plugins: [insights()], clientAccess: "public" }).api.insights)`, as
469
+ * `createDatabase`.
469
470
  */
470
471
  declare const setupInsightsModelTestSuite: (lifecycle: DatabaseTestLifecycle<InsightsModel>) => void;
471
472
  //#endregion
@@ -474,8 +475,8 @@ declare const setupInsightsModelTestSuite: (lifecycle: DatabaseTestLifecycle<Ins
474
475
  * The Insights plugin's tests, for `setupDatabaseTestSuite`'s `plugins`: its
475
476
  * HTTP routes on the provider's server, which must run `insights()`, and
476
477
  * with `createModel` its report contract on the provider's database, such as
477
- * `(database) => createInsightsModel(createHotUpdater({ database, plugins:
478
- * [insights()], clientAccess: "public" }).api.insights)`.
478
+ * `(database) => createInsightsModel(createHotUpdater({ database, storage,
479
+ * plugins: [insights()], clientAccess: "public" }).api.insights)`.
479
480
  */
480
481
  declare const insightsTestSuite: <TDatabase = EngineDatabase>(options?: {
481
482
  readonly createModel?: (database: TDatabase) => InsightsModel;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
2
2
  import { NIL_UUID, createReleaseCatalogScopeKey, encodeChannelKey, selectDesiredRelease } from "@hot-updater/protocol";
3
- import { DATABASE_VERSION_COLUMN, DatabaseAdapterContractError, SETTINGS_TABLE, addDistinct, aggregateBatchingTables, assertStorageOperations, bundleToPatchRows, bundleToRow, compareUtf8, coreSchema, countDistinct, createBundleStorageKey, createEngine, createMemoryAdapter, createStorageKeyBuilder, createStorageRootUriWithPath, createStorageUri, createStorageUriWithRelativePath, findPhysicalIndex, getBundleArchiveStorageUri, getContentAddressedAssetStoragePath, indexOrderColumns, meterReads, parseStorageDownloadPath, parseStorageUri, targetBaseCandidateKey, toolingTargetOf, verifyAdapter } from "@hot-updater/plugin-core";
3
+ import { DATABASE_VERSION_COLUMN, DatabaseAdapterContractError, SETTINGS_TABLE, addDistinct, aggregateBatchingTables, assertStorageOperations, bundleToPatchRows, bundleToRow, compareUtf8, coreSchema, countDistinct, createBundleStorageKey, createEngine, createMemoryAdapter, createStorageKeyBuilder, createStorageRootUriWithPath, createStorageUri, createStorageUriWithRelativePath, findPhysicalIndex, getBundleArchiveStorageUri, getContentAddressedAssetStoragePath, indexOrderColumns, meterReads, parseStorageUri, targetBaseCandidateKey, toolingTargetOf, verifyAdapter } from "@hot-updater/plugin-core";
4
4
  import { createServer } from "node:http";
5
5
  //#region src/adminApiTestClient.ts
6
6
  const jsonRequest = (method, body) => ({
@@ -883,6 +883,7 @@ const setupReleaseCatalogTestSuite = (options) => {
883
883
  expect(response.status).toBe(200);
884
884
  expect(response.headers.get("content-type")).toBe("application/vnd.hot-updater.release-catalog+json; version=1");
885
885
  expect(response.headers.get("cache-control")).toBe("public, max-age=0, s-maxage=5");
886
+ expect(response.headers.get("cdn-cache-control")).toBe("public, max-age=5, stale-while-revalidate=5, stale-if-error=0");
886
887
  const catalog = await response.json();
887
888
  expect(catalog).toMatchObject({
888
889
  schemaVersion: 1,
@@ -1034,6 +1035,7 @@ const setupReleaseCatalogTestSuite = (options) => {
1034
1035
  const response = await request(catalogPath());
1035
1036
  expect(response.status).toBe(404);
1036
1037
  expect(response.headers.get("cache-control")).toBe("public, max-age=0, s-maxage=5");
1038
+ expect(response.headers.get("cdn-cache-control")).toBe("public, max-age=5, stale-while-revalidate=5, stale-if-error=0");
1037
1039
  expect(response.headers.get("x-hot-updater-catalog")).toBe("none");
1038
1040
  await publish("121");
1039
1041
  expect((await readCatalog()).releases).toHaveLength(1);
@@ -1355,7 +1357,7 @@ const setupDatabaseTestSuite = (options) => {
1355
1357
  beforeEach(async () => {
1356
1358
  client = await options.createHttpClient({
1357
1359
  database: getDatabase(),
1358
- storage: [createReleaseCatalogTestStorage()]
1360
+ storage: createReleaseCatalogTestStorage()
1359
1361
  });
1360
1362
  });
1361
1363
  afterEach(async () => {
@@ -2467,7 +2469,7 @@ const storageCase = (title, operations, run) => ({
2467
2469
  /**
2468
2470
  * The storage adapter contract, from what its consumers rely on: deploy and
2469
2471
  * patch (`put`, `get`, `exists`, `delete`), the Console, the server's update
2470
- * responses and signed downloads (`get`, `getDownloadUrl`), and
2472
+ * responses and the URLs devices download from (`get`, `getDownloadUrl`), and
2471
2473
  * `storage prune` (`listObjects`, `deleteObjects`).
2472
2474
  */
2473
2475
  const storageAdapterTestCases = [
@@ -2637,17 +2639,13 @@ const storageAdapterTestCases = [
2637
2639
  const { url } = await storage.getDownloadUrl({ storageUri });
2638
2640
  const label = `getDownloadUrl("${storageUri}") returned "${url}"`;
2639
2641
  expect(typeof url, label).toBe("string");
2640
- if (/^[a-z][a-z\d+.-]*:/i.test(url)) {
2641
- expect(["http:", "https:"], `${label}; a URL must use http(s)`).toContain(new URL(url).protocol);
2642
- if (fetchDownloadUrls) {
2643
- const response = await fetch(url);
2644
- expect(response.status, `${label}, which answered`).toBe(200);
2645
- expectBytes(new Uint8Array(await response.arrayBuffer()), bytes, `${label}, whose download`);
2646
- }
2647
- continue;
2642
+ expect(/^https?:\/\//i.test(url), `${label}; a download URL must be an absolute http(s) URL`).toBe(true);
2643
+ expect(() => new URL(url), label).not.toThrow();
2644
+ if (fetchDownloadUrls) {
2645
+ const response = await fetch(url);
2646
+ expect(response.status, `${label}, which answered`).toBe(200);
2647
+ expectBytes(new Uint8Array(await response.arrayBuffer()), bytes, `${label}, whose download`);
2648
2648
  }
2649
- expect(parseStorageDownloadPath(url)?.storageUri, `${label}; a path must be the /storage/ path of the same URI`).toBe(storageUri);
2650
- expect((await storage.getDownloadUrl({ storageUri })).url, `${label}, then another path`).toBe(url);
2651
2649
  }
2652
2650
  }),
2653
2651
  storageCase("lists every object with its key relative to the base path", ["listObjects"], async ({ storage, basePath }) => {
@@ -2784,7 +2782,10 @@ const createMeasuredDatabase = async (adapter, plugins, options = {}) => {
2784
2782
  });
2785
2783
  const hotUpdater = createHotUpdater({
2786
2784
  database,
2787
- storage: options.storage === void 0 ? [] : [storageOf(options.storage)],
2785
+ storage: options.storage === void 0 ? {
2786
+ name: "measuredStorage",
2787
+ protocol: "none"
2788
+ } : storageOf(options.storage),
2788
2789
  plugins: observed,
2789
2790
  ...plugins.some((plugin) => plugin.provides?.clientAuth === true) ? {} : { clientAccess: "public" }
2790
2791
  });
@@ -3479,11 +3480,7 @@ const setupReadBudgetTestSuite = (options) => {
3479
3480
  let seeded;
3480
3481
  let writes = 0;
3481
3482
  beforeAll(async () => {
3482
- const [{ insights }, { apiKeys }, { remoteConfig }] = await Promise.all([
3483
- import("@hot-updater/server/plugins/insights"),
3484
- import("@hot-updater/server/plugins/api-keys"),
3485
- import("@hot-updater/server/plugins/remote-config")
3486
- ]);
3483
+ const { insights, apiKeys, remoteConfig } = await import("@hot-updater/server/plugins");
3487
3484
  const plugins = [
3488
3485
  insights(),
3489
3486
  apiKeys(),
@@ -5204,8 +5201,9 @@ const setupInsightsHttpTestSuite = (options) => {
5204
5201
  //#region src/insights/setupInsightsModelTestSuite.ts
5205
5202
  /**
5206
5203
  * The Insights report contract on one database: pass the Insights plugin's
5207
- * model over it, `createInsightsModel(createHotUpdater({ database, plugins:
5208
- * [insights()], clientAccess: "public" }).api.insights)`, as `createDatabase`.
5204
+ * model over it, `createInsightsModel(createHotUpdater({ database, storage,
5205
+ * plugins: [insights()], clientAccess: "public" }).api.insights)`, as
5206
+ * `createDatabase`.
5209
5207
  */
5210
5208
  const setupInsightsModelTestSuite = (lifecycle) => {
5211
5209
  describe(lifecycle.name, () => {
@@ -5234,8 +5232,8 @@ const setupInsightsModelTestSuite = (lifecycle) => {
5234
5232
  * The Insights plugin's tests, for `setupDatabaseTestSuite`'s `plugins`: its
5235
5233
  * HTTP routes on the provider's server, which must run `insights()`, and
5236
5234
  * with `createModel` its report contract on the provider's database, such as
5237
- * `(database) => createInsightsModel(createHotUpdater({ database, plugins:
5238
- * [insights()], clientAccess: "public" }).api.insights)`.
5235
+ * `(database) => createInsightsModel(createHotUpdater({ database, storage,
5236
+ * plugins: [insights()], clientAccess: "public" }).api.insights)`.
5239
5237
  */
5240
5238
  const insightsTestSuite = (options = {}) => ({
5241
5239
  name: "Insights plugin",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hot-updater/test-utils",
3
- "version": "1.0.0-rc.42",
3
+ "version": "1.0.0-rc.44",
4
4
  "type": "module",
5
5
  "description": "Test utilities for Hot Updater packages",
6
6
  "sideEffects": false,
@@ -50,10 +50,10 @@
50
50
  "mysql2": "*",
51
51
  "pg": "*",
52
52
  "vitest": "^4.0.0",
53
- "@hot-updater/plugin-insights": "^1.0.0-rc.42",
54
- "@hot-updater/protocol": "^1.0.0-rc.42",
55
- "@hot-updater/plugin-core": "^1.0.0-rc.42",
56
- "@hot-updater/server": "^1.0.0-rc.42"
53
+ "@hot-updater/plugin-core": "^1.0.0-rc.44",
54
+ "@hot-updater/protocol": "^1.0.0-rc.44",
55
+ "@hot-updater/server": "^1.0.0-rc.44",
56
+ "@hot-updater/plugin-insights": "^1.0.0-rc.44"
57
57
  },
58
58
  "peerDependenciesMeta": {
59
59
  "@electric-sql/pglite": {
@@ -83,11 +83,11 @@
83
83
  "typescript": "npm:@typescript/typescript6@6.0.2",
84
84
  "vitest": "4.1.4",
85
85
  "workspace-tools": "^0.41.7",
86
- "@hot-updater/plugin-insights": "1.0.0-rc.42",
87
- "@hot-updater/protocol": "1.0.0-rc.42",
88
- "@hot-updater/js": "1.0.0-rc.42",
89
- "@hot-updater/server": "1.0.0-rc.42",
90
- "@hot-updater/plugin-core": "1.0.0-rc.42"
86
+ "@hot-updater/plugin-insights": "1.0.0-rc.44",
87
+ "@hot-updater/protocol": "1.0.0-rc.44",
88
+ "@hot-updater/js": "1.0.0-rc.44",
89
+ "@hot-updater/server": "1.0.0-rc.44",
90
+ "@hot-updater/plugin-core": "1.0.0-rc.44"
91
91
  },
92
92
  "inlinedDependencies": {
93
93
  "@nodelib/fs.scandir": "2.1.5",