velocious 1.0.643 → 1.0.645
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 +21 -10
- package/build/authorization/ability.js +36 -13
- package/build/background-jobs/adapter.js +9 -0
- package/build/background-jobs/main.js +35 -10
- package/build/background-jobs/store.js +217 -38
- package/build/background-jobs/types.js +15 -0
- package/build/configuration-types.js +1 -1
- package/build/configuration.js +45 -9
- package/build/database/drivers/base-column.js +8 -0
- package/build/database/drivers/mssql/sql/update.js +1 -3
- package/build/database/drivers/mysql/sql/update.js +1 -3
- package/build/database/drivers/pgsql/column.js +22 -0
- package/build/database/drivers/pgsql/sql/update.js +1 -3
- package/build/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/build/database/drivers/sqlite/sql/update.js +1 -3
- package/build/database/query/index.js +2 -2
- package/build/database/query/model-class-query.js +26 -10
- package/build/database/query/preloader/has-many.js +4 -1
- package/build/database/query/query-data.js +2 -1
- package/build/database/query/update-base.js +14 -0
- package/build/database/query/with-count.js +3 -1
- package/build/database/record/acts-as-list.js +37 -13
- package/build/database/record/attachments/store.js +336 -26
- package/build/database/record/auditing.js +6 -3
- package/build/database/record/index.js +197 -42
- package/build/database/record/instance-relationships/belongs-to.js +2 -1
- package/build/database/record/validators/uniqueness.js +8 -5
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/build/frontend-model-controller.js +136 -53
- package/build/frontend-model-resource/base-resource.js +87 -37
- package/build/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/build/frontend-models/base.js +307 -59
- package/build/frontend-models/preloader.js +6 -3
- package/build/frontend-models/query.js +15 -6
- package/build/frontend-models/resource-definition.js +21 -2
- package/build/frontend-models/use-destroyed-event.js +1 -1
- package/build/frontend-models/use-model-class-event.js +2 -2
- package/build/frontend-models/use-updated-event.js +1 -1
- package/build/frontend-models/websocket-channel.js +256 -93
- package/build/frontend-models/websocket-publishers.js +218 -18
- package/build/http-server/client/websocket-session.js +13 -1
- package/build/http-server/websocket-channel.js +15 -2
- package/build/src/authorization/ability.d.ts +9 -3
- package/build/src/authorization/ability.d.ts.map +1 -1
- package/build/src/authorization/ability.js +33 -14
- package/build/src/background-jobs/adapter.d.ts +6 -0
- package/build/src/background-jobs/adapter.d.ts.map +1 -1
- package/build/src/background-jobs/adapter.js +9 -1
- package/build/src/background-jobs/main.d.ts +6 -0
- package/build/src/background-jobs/main.d.ts.map +1 -1
- package/build/src/background-jobs/main.js +34 -11
- package/build/src/background-jobs/store.d.ts +66 -12
- package/build/src/background-jobs/store.d.ts.map +1 -1
- package/build/src/background-jobs/store.js +191 -35
- package/build/src/background-jobs/types.d.ts +55 -0
- package/build/src/background-jobs/types.d.ts.map +1 -1
- package/build/src/background-jobs/types.js +16 -1
- package/build/src/configuration-types.d.ts +3 -3
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +2 -2
- package/build/src/configuration.d.ts +18 -8
- package/build/src/configuration.d.ts.map +1 -1
- package/build/src/configuration.js +38 -7
- package/build/src/database/drivers/base-column.d.ts +5 -0
- package/build/src/database/drivers/base-column.d.ts.map +1 -1
- package/build/src/database/drivers/base-column.js +8 -1
- package/build/src/database/drivers/mssql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/sql/update.js +2 -4
- package/build/src/database/drivers/mysql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/sql/update.js +2 -4
- package/build/src/database/drivers/pgsql/column.d.ts +5 -0
- package/build/src/database/drivers/pgsql/column.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/column.js +19 -1
- package/build/src/database/drivers/pgsql/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/sql/update.js +2 -4
- package/build/src/database/drivers/sqlite/sql/alter-table.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/alter-table.js +6 -2
- package/build/src/database/drivers/sqlite/sql/update.d.ts.map +1 -1
- package/build/src/database/drivers/sqlite/sql/update.js +2 -4
- package/build/src/database/query/index.d.ts +4 -4
- package/build/src/database/query/index.d.ts.map +1 -1
- package/build/src/database/query/index.js +3 -3
- package/build/src/database/query/model-class-query.d.ts +8 -2
- package/build/src/database/query/model-class-query.d.ts.map +1 -1
- package/build/src/database/query/model-class-query.js +25 -11
- package/build/src/database/query/preloader/has-many.d.ts.map +1 -1
- package/build/src/database/query/preloader/has-many.js +4 -2
- package/build/src/database/query/query-data.d.ts.map +1 -1
- package/build/src/database/query/query-data.js +3 -2
- package/build/src/database/query/update-base.d.ts +7 -0
- package/build/src/database/query/update-base.d.ts.map +1 -1
- package/build/src/database/query/update-base.js +13 -1
- package/build/src/database/query/with-count.d.ts.map +1 -1
- package/build/src/database/query/with-count.js +3 -2
- package/build/src/database/record/acts-as-list.d.ts.map +1 -1
- package/build/src/database/record/acts-as-list.js +35 -14
- package/build/src/database/record/attachments/store.d.ts +67 -0
- package/build/src/database/record/attachments/store.d.ts.map +1 -1
- package/build/src/database/record/attachments/store.js +301 -25
- package/build/src/database/record/auditing.d.ts.map +1 -1
- package/build/src/database/record/auditing.js +7 -4
- package/build/src/database/record/index.d.ts +78 -26
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +171 -42
- package/build/src/database/record/instance-relationships/belongs-to.d.ts.map +1 -1
- package/build/src/database/record/instance-relationships/belongs-to.js +3 -2
- package/build/src/database/record/validators/uniqueness.d.ts.map +1 -1
- package/build/src/database/record/validators/uniqueness.js +8 -6
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +4 -4
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -12
- package/build/src/frontend-model-controller.d.ts +29 -8
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +122 -51
- package/build/src/frontend-model-resource/base-resource.d.ts +51 -24
- package/build/src/frontend-model-resource/base-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/base-resource.js +83 -37
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts +45 -26
- package/build/src/frontend-model-resource/velocious-attachment-resource.d.ts.map +1 -1
- package/build/src/frontend-model-resource/velocious-attachment-resource.js +89 -31
- package/build/src/frontend-models/base.d.ts +53 -20
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +275 -59
- package/build/src/frontend-models/preloader.d.ts.map +1 -1
- package/build/src/frontend-models/preloader.js +6 -4
- package/build/src/frontend-models/query.d.ts +2 -2
- package/build/src/frontend-models/query.d.ts.map +1 -1
- package/build/src/frontend-models/query.js +14 -7
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +20 -3
- package/build/src/frontend-models/use-destroyed-event.d.ts +1 -1
- package/build/src/frontend-models/use-destroyed-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-destroyed-event.js +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts +2 -2
- package/build/src/frontend-models/use-model-class-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-model-class-event.js +3 -3
- package/build/src/frontend-models/use-updated-event.d.ts +1 -1
- package/build/src/frontend-models/use-updated-event.d.ts.map +1 -1
- package/build/src/frontend-models/use-updated-event.js +2 -2
- package/build/src/frontend-models/websocket-channel.d.ts +60 -41
- package/build/src/frontend-models/websocket-channel.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-channel.js +225 -85
- package/build/src/frontend-models/websocket-publishers.d.ts +9 -0
- package/build/src/frontend-models/websocket-publishers.d.ts.map +1 -1
- package/build/src/frontend-models/websocket-publishers.js +190 -19
- package/build/src/http-server/client/websocket-session.d.ts.map +1 -1
- package/build/src/http-server/client/websocket-session.js +11 -2
- package/build/src/http-server/websocket-channel.d.ts +20 -8
- package/build/src/http-server/websocket-channel.d.ts.map +1 -1
- package/build/src/http-server/websocket-channel.js +15 -3
- package/build/src/sync/assets/cache.d.ts +288 -0
- package/build/src/sync/assets/cache.d.ts.map +1 -0
- package/build/src/sync/assets/cache.js +883 -0
- package/build/src/sync/assets/types.d.ts +199 -0
- package/build/src/sync/assets/types.d.ts.map +1 -0
- package/build/src/sync/assets/types.js +56 -0
- package/build/src/sync/sync-api-client.d.ts.map +1 -1
- package/build/src/sync/sync-api-client.js +4 -3
- package/build/src/sync/sync-client.d.ts.map +1 -1
- package/build/src/sync/sync-client.js +3 -2
- package/build/src/sync/sync-envelope-replay-service.d.ts.map +1 -1
- package/build/src/sync/sync-envelope-replay-service.js +6 -5
- package/build/src/sync/sync-publisher.d.ts.map +1 -1
- package/build/src/sync/sync-publisher.js +4 -3
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts +2 -2
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.d.ts.map +1 -1
- package/build/src/testing/browser-frontend-model-event-hook-scenarios.js +3 -3
- package/build/src/testing/test-profiler.d.ts +4 -4
- package/build/src/testing/test-profiler.d.ts.map +1 -1
- package/build/src/testing/test-profiler.js +3 -3
- package/build/src/testing/test-runner.d.ts +279 -117
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +503 -933
- package/build/src/testing/test.d.ts +11 -72
- package/build/src/testing/test.d.ts.map +1 -1
- package/build/src/testing/test.js +131 -284
- package/build/src/testing/velocious-attempt-executor.d.ts +54 -0
- package/build/src/testing/velocious-attempt-executor.d.ts.map +1 -0
- package/build/src/testing/velocious-attempt-executor.js +408 -0
- package/build/src/testing/velocious-runner-reporter.d.ts +67 -0
- package/build/src/testing/velocious-runner-reporter.d.ts.map +1 -0
- package/build/src/testing/velocious-runner-reporter.js +240 -0
- package/build/src/testing/velocious-suite-hook-executor.d.ts +19 -0
- package/build/src/testing/velocious-suite-hook-executor.d.ts.map +1 -0
- package/build/src/testing/velocious-suite-hook-executor.js +41 -0
- package/build/src/testing/velocious-test-arguments.d.ts +39 -0
- package/build/src/testing/velocious-test-arguments.d.ts.map +1 -0
- package/build/src/testing/velocious-test-arguments.js +55 -0
- package/build/src/utils/model-primary-key.d.ts +73 -0
- package/build/src/utils/model-primary-key.d.ts.map +1 -0
- package/build/src/utils/model-primary-key.js +144 -0
- package/build/src/utils/sha256-bytes-hex.d.ts +7 -0
- package/build/src/utils/sha256-bytes-hex.d.ts.map +1 -0
- package/build/src/utils/sha256-bytes-hex.js +117 -0
- package/build/src/utils/sha256-hex.d.ts +2 -4
- package/build/src/utils/sha256-hex.d.ts.map +1 -1
- package/build/src/utils/sha256-hex.js +6 -120
- package/build/sync/assets/cache.js +998 -0
- package/build/sync/assets/types.js +62 -0
- package/build/sync/sync-api-client.js +3 -2
- package/build/sync/sync-client.js +2 -1
- package/build/sync/sync-envelope-replay-service.js +5 -4
- package/build/sync/sync-publisher.js +3 -2
- package/build/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/build/testing/test-profiler.js +2 -2
- package/build/testing/test-runner.js +496 -990
- package/build/testing/test.js +164 -306
- package/build/testing/velocious-attempt-executor.js +441 -0
- package/build/testing/velocious-runner-reporter.js +264 -0
- package/build/testing/velocious-suite-hook-executor.js +43 -0
- package/build/testing/velocious-test-arguments.js +64 -0
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/utils/model-primary-key.js +171 -0
- package/build/utils/sha256-bytes-hex.js +132 -0
- package/build/utils/sha256-hex.js +7 -135
- package/package.json +5 -4
- package/src/authorization/ability.js +36 -13
- package/src/background-jobs/adapter.js +9 -0
- package/src/background-jobs/main.js +35 -10
- package/src/background-jobs/store.js +217 -38
- package/src/background-jobs/types.js +15 -0
- package/src/configuration-types.js +1 -1
- package/src/configuration.js +45 -9
- package/src/database/drivers/base-column.js +8 -0
- package/src/database/drivers/mssql/sql/update.js +1 -3
- package/src/database/drivers/mysql/sql/update.js +1 -3
- package/src/database/drivers/pgsql/column.js +22 -0
- package/src/database/drivers/pgsql/sql/update.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +11 -1
- package/src/database/drivers/sqlite/sql/update.js +1 -3
- package/src/database/query/index.js +2 -2
- package/src/database/query/model-class-query.js +26 -10
- package/src/database/query/preloader/has-many.js +4 -1
- package/src/database/query/query-data.js +2 -1
- package/src/database/query/update-base.js +14 -0
- package/src/database/query/with-count.js +3 -1
- package/src/database/record/acts-as-list.js +37 -13
- package/src/database/record/attachments/store.js +336 -26
- package/src/database/record/auditing.js +6 -3
- package/src/database/record/index.js +197 -42
- package/src/database/record/instance-relationships/belongs-to.js +2 -1
- package/src/database/record/validators/uniqueness.js +8 -5
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +24 -8
- package/src/frontend-model-controller.js +136 -53
- package/src/frontend-model-resource/base-resource.js +87 -37
- package/src/frontend-model-resource/velocious-attachment-resource.js +98 -31
- package/src/frontend-models/base.js +307 -59
- package/src/frontend-models/preloader.js +6 -3
- package/src/frontend-models/query.js +15 -6
- package/src/frontend-models/resource-definition.js +21 -2
- package/src/frontend-models/use-destroyed-event.js +1 -1
- package/src/frontend-models/use-model-class-event.js +2 -2
- package/src/frontend-models/use-updated-event.js +1 -1
- package/src/frontend-models/websocket-channel.js +256 -93
- package/src/frontend-models/websocket-publishers.js +218 -18
- package/src/http-server/client/websocket-session.js +13 -1
- package/src/http-server/websocket-channel.js +15 -2
- package/src/sync/assets/cache.js +998 -0
- package/src/sync/assets/types.js +62 -0
- package/src/sync/sync-api-client.js +3 -2
- package/src/sync/sync-client.js +2 -1
- package/src/sync/sync-envelope-replay-service.js +5 -4
- package/src/sync/sync-publisher.js +3 -2
- package/src/testing/browser-frontend-model-event-hook-scenarios.js +2 -2
- package/src/testing/test-profiler.js +2 -2
- package/src/testing/test-runner.js +496 -990
- package/src/testing/test.js +164 -306
- package/src/testing/velocious-attempt-executor.js +441 -0
- package/src/testing/velocious-runner-reporter.js +264 -0
- package/src/testing/velocious-suite-hook-executor.js +43 -0
- package/src/testing/velocious-test-arguments.js +64 -0
- package/src/utils/model-primary-key.js +171 -0
- package/src/utils/sha256-bytes-hex.js +132 -0
- package/src/utils/sha256-hex.js +7 -135
- package/build/src/testing/testing-package-adapter.d.ts +0 -9
- package/build/src/testing/testing-package-adapter.d.ts.map +0 -1
- package/build/src/testing/testing-package-adapter.js +0 -69
- package/build/testing/testing-package-adapter.js +0 -76
- package/src/testing/testing-package-adapter.js +0 -76
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
3
|
+
import { TestDatabaseAccessRevokedError } from "../environment-handlers/base.js"
|
|
4
|
+
import { clearDeliveries } from "../mailer.js"
|
|
5
|
+
import restArgsError from "../utils/rest-args-error.js"
|
|
6
|
+
import {clearTimeout as realClearTimeout, setTimeout as realSetTimeout} from "node:timers"
|
|
7
|
+
|
|
8
|
+
/** @typedef {import("@velocious/testing/runner").AttemptExecutorInput["beforeEach"][number]} PackageHookDeclaration */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Marks one whole-lifecycle timeout while its underlying promise keeps running.
|
|
12
|
+
* @typedef {Error & {velociousTestTimeout?: true}} TestTimeoutError
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Runs one promise with a lifecycle timeout.
|
|
17
|
+
* @param {Promise<ReturnType<typeof JSON.parse>> | ReturnType<typeof JSON.parse>} promise - Promise or value.
|
|
18
|
+
* @param {number} timeoutMs - Timeout in milliseconds.
|
|
19
|
+
* @param {string} testDescription - Test description.
|
|
20
|
+
* @returns {Promise<ReturnType<typeof JSON.parse>>} - Lifecycle result.
|
|
21
|
+
*/
|
|
22
|
+
function runWithTimeout(promise, timeoutMs, testDescription) {
|
|
23
|
+
const timeoutSeconds = (timeoutMs / 1000).toFixed(3).replace(/\.?0+$/, "")
|
|
24
|
+
/** @type {TestTimeoutError} */
|
|
25
|
+
const timeoutError = new Error(`Timed out after ${timeoutSeconds}s: ${testDescription}`)
|
|
26
|
+
timeoutError.velociousTestTimeout = true
|
|
27
|
+
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
const timeout = realSetTimeout(() => reject(timeoutError), timeoutMs)
|
|
30
|
+
|
|
31
|
+
Promise.resolve(promise).then((result) => {
|
|
32
|
+
realClearTimeout(timeout)
|
|
33
|
+
resolve(result)
|
|
34
|
+
}).catch((error) => {
|
|
35
|
+
realClearTimeout(timeout)
|
|
36
|
+
reject(error)
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Waits for detached lifecycle cleanup up to the timeout grace period.
|
|
43
|
+
* @param {Promise<ReturnType<typeof JSON.parse>>} lifecycle - Detached lifecycle promise.
|
|
44
|
+
* @param {number} graceMs - Maximum wait.
|
|
45
|
+
* @returns {Promise<{settled: false} | {settled: true, status: "fulfilled"} | {settled: true, status: "rejected", reason: ReturnType<typeof JSON.parse>}>} - Settlement outcome.
|
|
46
|
+
*/
|
|
47
|
+
function awaitSettledOrGrace(lifecycle, graceMs) {
|
|
48
|
+
return new Promise((resolve) => {
|
|
49
|
+
let settled = false
|
|
50
|
+
const graceTimer = realSetTimeout(() => {
|
|
51
|
+
if (settled) return
|
|
52
|
+
|
|
53
|
+
settled = true
|
|
54
|
+
resolve({settled: false})
|
|
55
|
+
}, graceMs)
|
|
56
|
+
|
|
57
|
+
Promise.resolve(lifecycle).then(
|
|
58
|
+
() => {
|
|
59
|
+
if (settled) return
|
|
60
|
+
|
|
61
|
+
settled = true
|
|
62
|
+
realClearTimeout(graceTimer)
|
|
63
|
+
resolve({settled: true, status: "fulfilled"})
|
|
64
|
+
},
|
|
65
|
+
(reason) => {
|
|
66
|
+
if (settled) return
|
|
67
|
+
|
|
68
|
+
settled = true
|
|
69
|
+
realClearTimeout(graceTimer)
|
|
70
|
+
resolve({settled: true, status: "rejected", reason})
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Checks whether a late lifecycle stopped only because its attempt access was revoked.
|
|
78
|
+
* @param {ReturnType<typeof JSON.parse>} error - Lifecycle rejection.
|
|
79
|
+
* @returns {boolean} - Whether every contained error is expected revocation.
|
|
80
|
+
*/
|
|
81
|
+
function isTestDatabaseAccessRevocation(error) {
|
|
82
|
+
if (error instanceof TestDatabaseAccessRevokedError) return true
|
|
83
|
+
if (error instanceof AggregateError) {
|
|
84
|
+
return error.errors.length > 0 && error.errors.every((nestedError) => isTestDatabaseAccessRevocation(nestedError))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export default class VelociousAttemptExecutor {
|
|
91
|
+
/**
|
|
92
|
+
* Creates an executor for framework-owned attempt lifecycle work.
|
|
93
|
+
* @param {object} args - Constructor arguments.
|
|
94
|
+
* @param {import("./test-runner.js").default} args.testRunner - Owning Velocious runner.
|
|
95
|
+
*/
|
|
96
|
+
constructor({testRunner, ...restArgs}) {
|
|
97
|
+
restArgsError(restArgs)
|
|
98
|
+
this.testRunner = testRunner
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Normalizes the legacy timeout contract at the framework adapter boundary.
|
|
103
|
+
* @param {number | undefined} timeoutMs - Declared package timeout.
|
|
104
|
+
* @returns {number | undefined} - Positive finite timeout, or no timeout.
|
|
105
|
+
*/
|
|
106
|
+
normalizeTimeoutMs(timeoutMs) {
|
|
107
|
+
return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : undefined
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Executes exactly one complete Velocious-owned test attempt.
|
|
112
|
+
* @param {import("@velocious/testing/runner").AttemptExecutorInput} input - Package attempt.
|
|
113
|
+
* @returns {Promise<void>} - Resolves after one complete framework attempt.
|
|
114
|
+
*/
|
|
115
|
+
async execute({afterEach, args, attemptNumber, beforeEach, context, defaultExecute, fullName, suite, test, timeoutMs, ...restArgs}) {
|
|
116
|
+
restArgsError(restArgs)
|
|
117
|
+
void context
|
|
118
|
+
void defaultExecute
|
|
119
|
+
void suite
|
|
120
|
+
const testRunner = this.testRunner
|
|
121
|
+
const effectiveTimeoutMs = this.normalizeTimeoutMs(timeoutMs)
|
|
122
|
+
const compatibility = await testRunner.testCompatibility(test)
|
|
123
|
+
const {testArgs, testData} = compatibility
|
|
124
|
+
const metadata = testRunner.testMetadata(test)
|
|
125
|
+
const {descriptions, testDescription} = metadata
|
|
126
|
+
/** @type {ReturnType<typeof JSON.parse>} */
|
|
127
|
+
let caughtError
|
|
128
|
+
let failed = false
|
|
129
|
+
/** @type {Promise<ReturnType<typeof JSON.parse>> | undefined} */
|
|
130
|
+
let testLifecycle
|
|
131
|
+
/** @type {{pool: import("../database/pool/base.js").default, registration: import("../database/pool/base.js").TestSharedConnectionRegistration}[]} */
|
|
132
|
+
let testSharedConnectionRegistrations = []
|
|
133
|
+
let testSharedConnectionsActive = false
|
|
134
|
+
/** @type {import("./test-runner.js").SharedTransactionBrokerRegistration | undefined} */
|
|
135
|
+
let sharedTransactionBrokerRegistration
|
|
136
|
+
/** @type {import("./test-runner.js").SharedTransactionBrokerRegistration | undefined} */
|
|
137
|
+
let sharedTransactionBrokerPreparation
|
|
138
|
+
/** @type {import("./test-runner.js").TransactionalTenantRegistration[]} */
|
|
139
|
+
const transactionalTenantRegistrations = []
|
|
140
|
+
/** @type {import("./test-runner.js").BrowserDummyConnectionRegistration[]} */
|
|
141
|
+
const browserDummyConnectionRegistrations = []
|
|
142
|
+
const testDatabaseAccessScope = {revoked: false}
|
|
143
|
+
/** @type {Set<Error>} */
|
|
144
|
+
const recordedTimeoutCleanupErrors = new Set()
|
|
145
|
+
let abortRemainingTests = false
|
|
146
|
+
let attemptTimedOut = false
|
|
147
|
+
testArgs.registerTransactionalTenant = async (args) => {
|
|
148
|
+
await testRunner.registerTransactionalTenant(args, transactionalTenantRegistrations)
|
|
149
|
+
}
|
|
150
|
+
const profiler = testRunner._profiler
|
|
151
|
+
const profileAttempt = profiler?.startAttempt({
|
|
152
|
+
descriptions,
|
|
153
|
+
attemptNumber,
|
|
154
|
+
testData,
|
|
155
|
+
testDescription
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
try {
|
|
159
|
+
const runLifecycleCallback = async () => await testRunner.runWithDummyIfNeeded(testArgs, async () => {
|
|
160
|
+
const useTransaction = testArgs.databaseCleaning?.transaction === true
|
|
161
|
+
const shouldTruncate = testArgs.databaseCleaning?.truncate ?? !useTransaction
|
|
162
|
+
const useSharedTestConnections = useTransaction || testArgs.type == "request"
|
|
163
|
+
const useTestConnections = useSharedTestConnections || shouldTruncate
|
|
164
|
+
const runTestAttempt = async () => {
|
|
165
|
+
if (useSharedTestConnections) {
|
|
166
|
+
testSharedConnectionRegistrations = testRunner.activateTestSharedConnections()
|
|
167
|
+
testSharedConnectionsActive = true
|
|
168
|
+
}
|
|
169
|
+
/** @type {ReturnType<typeof JSON.parse>[]} */
|
|
170
|
+
const lifecycleErrors = []
|
|
171
|
+
let runCleanupHooks = false
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
if (useSharedTestConnections) {
|
|
175
|
+
sharedTransactionBrokerPreparation = await testRunner.prepareSharedTransactionBroker()
|
|
176
|
+
}
|
|
177
|
+
runCleanupHooks = true
|
|
178
|
+
|
|
179
|
+
clearDeliveries()
|
|
180
|
+
await this.runBeforeEaches({beforeEaches: beforeEach, testArgs, testData})
|
|
181
|
+
|
|
182
|
+
if (useSharedTestConnections) {
|
|
183
|
+
const activeConnections = testRunner.sharedTransactionConnections({transactionsOnly: true})
|
|
184
|
+
if (sharedTransactionBrokerPreparation && !testRunner.sharedTransactionBrokerMatchesConnections(sharedTransactionBrokerPreparation, activeConnections)) {
|
|
185
|
+
testRunner.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
186
|
+
testSharedConnectionRegistrations = []
|
|
187
|
+
testSharedConnectionsActive = false
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
sharedTransactionBrokerRegistration = await testRunner.startSharedTransactionBroker(sharedTransactionBrokerPreparation, activeConnections)
|
|
191
|
+
sharedTransactionBrokerPreparation = undefined
|
|
192
|
+
if (sharedTransactionBrokerRegistration && !testSharedConnectionsActive) {
|
|
193
|
+
testSharedConnectionRegistrations = testRunner.activateTestSharedConnections()
|
|
194
|
+
testSharedConnectionsActive = true
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
testRunner._lastTestContext = {
|
|
199
|
+
fullDescription: fullName,
|
|
200
|
+
filePath: testData.filePath ?? "<unknown>",
|
|
201
|
+
line: testData.line ?? 0
|
|
202
|
+
}
|
|
203
|
+
await testRunner.runProfileSpan({phase: "test body", filePath: testData.ownerFilePath ?? testData.filePath}, async () => {
|
|
204
|
+
await test.callback(...args)
|
|
205
|
+
})
|
|
206
|
+
} catch (error) {
|
|
207
|
+
lifecycleErrors.push(error)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (runCleanupHooks) {
|
|
211
|
+
try {
|
|
212
|
+
await testRunner.getConfiguration().awaitPendingBroadcasts()
|
|
213
|
+
} catch (error) {
|
|
214
|
+
lifecycleErrors.push(error)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
if (testSharedConnectionsActive) {
|
|
219
|
+
testRunner.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
220
|
+
testSharedConnectionRegistrations = []
|
|
221
|
+
testSharedConnectionsActive = false
|
|
222
|
+
}
|
|
223
|
+
} catch (error) {
|
|
224
|
+
lifecycleErrors.push(error)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
await testRunner.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
|
|
229
|
+
sharedTransactionBrokerRegistration = undefined
|
|
230
|
+
sharedTransactionBrokerPreparation = undefined
|
|
231
|
+
} catch (error) {
|
|
232
|
+
lifecycleErrors.push(error)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
await this.runAfterEaches({afterEaches: [...afterEach].reverse(), testArgs, testData})
|
|
237
|
+
} catch (error) {
|
|
238
|
+
lifecycleErrors.push(error)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
await testRunner.cleanupTransactionalTenants(transactionalTenantRegistrations)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
lifecycleErrors.push(error)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (testSharedConnectionsActive) {
|
|
249
|
+
try {
|
|
250
|
+
testRunner.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
251
|
+
} catch (error) {
|
|
252
|
+
lifecycleErrors.push(error)
|
|
253
|
+
}
|
|
254
|
+
testSharedConnectionsActive = false
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (lifecycleErrors.length == 1) throw lifecycleErrors[0]
|
|
258
|
+
if (lifecycleErrors.length > 1) {
|
|
259
|
+
throw new AggregateError(lifecycleErrors, "Test lifecycle and cleanup failed", {cause: lifecycleErrors[0]})
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (useTestConnections) {
|
|
264
|
+
await testRunner.getConfiguration().ensureConnections({name: `Test: ${testDescription}`}, runTestAttempt)
|
|
265
|
+
} else {
|
|
266
|
+
await runTestAttempt()
|
|
267
|
+
}
|
|
268
|
+
}, browserDummyConnectionRegistrations)
|
|
269
|
+
const lifecycleCallback = async () => await testRunner.getConfiguration().runWithTestDatabaseAccessScope(testDatabaseAccessScope, runLifecycleCallback)
|
|
270
|
+
testLifecycle = profileAttempt && profiler
|
|
271
|
+
? profiler.runAttempt(profileAttempt, lifecycleCallback)
|
|
272
|
+
: lifecycleCallback()
|
|
273
|
+
|
|
274
|
+
if (effectiveTimeoutMs !== undefined) {
|
|
275
|
+
await runWithTimeout(testLifecycle, effectiveTimeoutMs, testDescription)
|
|
276
|
+
} else {
|
|
277
|
+
await testLifecycle
|
|
278
|
+
}
|
|
279
|
+
} catch (error) {
|
|
280
|
+
failed = true
|
|
281
|
+
caughtError = error
|
|
282
|
+
const timedOut = Boolean(/** @type {TestTimeoutError} */ (error)?.velociousTestTimeout)
|
|
283
|
+
attemptTimedOut = timedOut
|
|
284
|
+
|
|
285
|
+
if (timedOut && testLifecycle) {
|
|
286
|
+
const emergencyCleanupErrors = []
|
|
287
|
+
|
|
288
|
+
if (profileAttempt && profiler) profiler.finishAttempt(profileAttempt, "timed-out")
|
|
289
|
+
const lifecycleOutcome = await awaitSettledOrGrace(testLifecycle, effectiveTimeoutMs ?? 60000)
|
|
290
|
+
|
|
291
|
+
if (lifecycleOutcome.settled && lifecycleOutcome.status === "rejected") {
|
|
292
|
+
emergencyCleanupErrors.push(lifecycleOutcome.reason)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!lifecycleOutcome.settled) {
|
|
296
|
+
testDatabaseAccessScope.revoked = true
|
|
297
|
+
void testLifecycle.catch((cleanupError) => {
|
|
298
|
+
if (isTestDatabaseAccessRevocation(cleanupError)) return
|
|
299
|
+
testRunner.recordTimeoutCleanupFailure(cleanupError, "test lifecycle", recordedTimeoutCleanupErrors)
|
|
300
|
+
})
|
|
301
|
+
const quarantine = testRunner.quarantineBrowserDummyConnections(browserDummyConnectionRegistrations)
|
|
302
|
+
const quarantineOutcome = await awaitSettledOrGrace(quarantine, effectiveTimeoutMs ?? 60000)
|
|
303
|
+
const usesBrowserTransactions = testArgs.databaseCleaning?.transaction === true
|
|
304
|
+
const usesBrowserTruncation = testArgs.databaseCleaning?.truncate ?? !usesBrowserTransactions
|
|
305
|
+
|
|
306
|
+
abortRemainingTests = testRunner.isBrowserTestMode()
|
|
307
|
+
&& testRunner.hasTag(testArgs, "dummy")
|
|
308
|
+
&& (usesBrowserTransactions || usesBrowserTruncation)
|
|
309
|
+
|
|
310
|
+
if (quarantineOutcome.settled && quarantineOutcome.status === "rejected") {
|
|
311
|
+
emergencyCleanupErrors.push(quarantineOutcome.reason)
|
|
312
|
+
} else if (!quarantineOutcome.settled) {
|
|
313
|
+
void quarantine.catch((cleanupError) => {
|
|
314
|
+
testRunner.recordTimeoutCleanupFailure(cleanupError, "browser dummy connection quarantine", recordedTimeoutCleanupErrors)
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
if (testSharedConnectionsActive) {
|
|
321
|
+
testRunner.clearTestSharedConnections(testSharedConnectionRegistrations)
|
|
322
|
+
testSharedConnectionRegistrations = []
|
|
323
|
+
testSharedConnectionsActive = false
|
|
324
|
+
}
|
|
325
|
+
} catch (cleanupError) {
|
|
326
|
+
emergencyCleanupErrors.push(cleanupError)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const brokerCleanup = testRunner.stopSharedTransactionBroker(sharedTransactionBrokerRegistration || sharedTransactionBrokerPreparation)
|
|
330
|
+
const brokerCleanupOutcome = await awaitSettledOrGrace(brokerCleanup, effectiveTimeoutMs ?? 60000)
|
|
331
|
+
|
|
332
|
+
if (brokerCleanupOutcome.settled && brokerCleanupOutcome.status === "rejected") {
|
|
333
|
+
emergencyCleanupErrors.push(brokerCleanupOutcome.reason)
|
|
334
|
+
} else if (!brokerCleanupOutcome.settled) {
|
|
335
|
+
void brokerCleanup.catch((cleanupError) => {
|
|
336
|
+
testRunner.recordTimeoutCleanupFailure(cleanupError, "shared transaction broker", recordedTimeoutCleanupErrors)
|
|
337
|
+
})
|
|
338
|
+
}
|
|
339
|
+
sharedTransactionBrokerRegistration = undefined
|
|
340
|
+
sharedTransactionBrokerPreparation = undefined
|
|
341
|
+
const emergencyCleanup = testRunner.cleanupTransactionalTenants(transactionalTenantRegistrations, {discard: true})
|
|
342
|
+
const emergencyCleanupOutcome = await awaitSettledOrGrace(emergencyCleanup, effectiveTimeoutMs ?? 60000)
|
|
343
|
+
|
|
344
|
+
if (emergencyCleanupOutcome.settled && emergencyCleanupOutcome.status === "rejected") {
|
|
345
|
+
emergencyCleanupErrors.push(emergencyCleanupOutcome.reason)
|
|
346
|
+
} else if (!emergencyCleanupOutcome.settled) {
|
|
347
|
+
void emergencyCleanup.catch((cleanupError) => {
|
|
348
|
+
testRunner.recordTimeoutCleanupFailure(cleanupError, "transactional tenant", recordedTimeoutCleanupErrors)
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (emergencyCleanupErrors.length > 0) {
|
|
353
|
+
caughtError = new AggregateError(
|
|
354
|
+
[caughtError, ...emergencyCleanupErrors],
|
|
355
|
+
"Test timeout and emergency cleanup failed",
|
|
356
|
+
{cause: caughtError}
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (browserDummyConnectionRegistrations.some((registration) => registration.quarantined)) {
|
|
362
|
+
testDatabaseAccessScope.revoked = true
|
|
363
|
+
abortRemainingTests = true
|
|
364
|
+
}
|
|
365
|
+
} finally {
|
|
366
|
+
testDatabaseAccessScope.revoked = true
|
|
367
|
+
|
|
368
|
+
if (profileAttempt && profiler) {
|
|
369
|
+
profiler.finishAttempt(profileAttempt, failed
|
|
370
|
+
? (attemptTimedOut ? "timed-out" : "failed")
|
|
371
|
+
: "passed")
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
testRunner.recordAttemptOutcome(test, attemptNumber, {
|
|
376
|
+
abortRemainingTests,
|
|
377
|
+
error: caughtError,
|
|
378
|
+
failed
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
if (failed) throw caughtError
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Runs before-each hooks in inherited declaration order.
|
|
386
|
+
* @param {object} args - Hook arguments.
|
|
387
|
+
* @param {PackageHookDeclaration[]} args.beforeEaches - Setup hooks.
|
|
388
|
+
* @param {import("./velocious-test-arguments.js").TestArgs} args.testArgs - Stable test arguments.
|
|
389
|
+
* @param {import("./velocious-test-arguments.js").TestData} args.testData - Test registration.
|
|
390
|
+
* @returns {Promise<void>} - Resolves after all setup hooks complete.
|
|
391
|
+
*/
|
|
392
|
+
async runBeforeEaches({beforeEaches, testArgs, testData}) {
|
|
393
|
+
for (const hook of beforeEaches) {
|
|
394
|
+
const metadata = this.testRunner.hookMetadata(hook)
|
|
395
|
+
|
|
396
|
+
await this.testRunner.runProfileSpan({
|
|
397
|
+
phase: "beforeEach",
|
|
398
|
+
declarationIndex: metadata.declarationIndex,
|
|
399
|
+
declarationScopeId: metadata.declarationScopeId,
|
|
400
|
+
filePath: metadata.ownerFilePath
|
|
401
|
+
}, async () => {
|
|
402
|
+
await hook.callback({configuration: this.testRunner.getConfiguration(), testArgs, testData})
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Runs every after-each hook while preserving all failures.
|
|
409
|
+
* @param {object} args - Hook arguments.
|
|
410
|
+
* @param {PackageHookDeclaration[]} args.afterEaches - Cleanup hooks.
|
|
411
|
+
* @param {import("./velocious-test-arguments.js").TestArgs} args.testArgs - Stable test arguments.
|
|
412
|
+
* @param {import("./velocious-test-arguments.js").TestData} args.testData - Test registration.
|
|
413
|
+
* @returns {Promise<void>} - Resolves after every cleanup hook settles.
|
|
414
|
+
*/
|
|
415
|
+
async runAfterEaches({afterEaches, testArgs, testData}) {
|
|
416
|
+
/** @type {ReturnType<typeof JSON.parse>[]} */
|
|
417
|
+
const errors = []
|
|
418
|
+
|
|
419
|
+
for (const hook of afterEaches) {
|
|
420
|
+
const metadata = this.testRunner.hookMetadata(hook)
|
|
421
|
+
|
|
422
|
+
try {
|
|
423
|
+
await this.testRunner.runProfileSpan({
|
|
424
|
+
phase: "afterEach",
|
|
425
|
+
declarationIndex: metadata.declarationIndex,
|
|
426
|
+
declarationScopeId: metadata.declarationScopeId,
|
|
427
|
+
filePath: metadata.ownerFilePath
|
|
428
|
+
}, async () => {
|
|
429
|
+
await hook.callback({configuration: this.testRunner.getConfiguration(), testArgs, testData})
|
|
430
|
+
})
|
|
431
|
+
} catch (error) {
|
|
432
|
+
errors.push(error)
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
if (errors.length == 1) throw errors[0]
|
|
437
|
+
if (errors.length > 1) {
|
|
438
|
+
throw new AggregateError(errors, "Multiple afterEach hooks failed", {cause: errors[0]})
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|