velocious 1.0.546 → 1.0.548
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 +24 -0
- package/build/cli/tenant-database-command-helper.js +38 -3
- package/build/environment-handlers/node/cli/commands/test.js +32 -0
- package/build/src/cli/tenant-database-command-helper.d.ts +7 -1
- package/build/src/cli/tenant-database-command-helper.d.ts.map +1 -1
- package/build/src/cli/tenant-database-command-helper.js +36 -4
- package/build/src/environment-handlers/node/cli/commands/test.d.ts +8 -0
- package/build/src/environment-handlers/node/cli/commands/test.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/test.js +27 -1
- package/build/src/tenants/tenant-iterator.d.ts.map +1 -1
- package/build/src/tenants/tenant-iterator.js +9 -7
- package/build/src/testing/test-runner.d.ts +18 -0
- package/build/src/testing/test-runner.d.ts.map +1 -1
- package/build/src/testing/test-runner.js +20 -1
- package/build/tenants/tenant-iterator.js +8 -3
- package/build/testing/test-runner.js +23 -0
- package/package.json +1 -1
- package/src/cli/tenant-database-command-helper.js +38 -3
- package/src/environment-handlers/node/cli/commands/test.js +32 -0
- package/src/tenants/tenant-iterator.js +8 -3
- package/src/testing/test-runner.js +23 -0
|
@@ -237,6 +237,8 @@ export default class TestRunner {
|
|
|
237
237
|
this._failedTestDetails = []
|
|
238
238
|
/** @type {{fullDescription: string, filePath: string, line: number} | null} */
|
|
239
239
|
this._lastTestContext = null
|
|
240
|
+
/** @type {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} */
|
|
241
|
+
this._testDurations = []
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
/**
|
|
@@ -696,6 +698,17 @@ export default class TestRunner {
|
|
|
696
698
|
return this._successfulTests + this._failedTests
|
|
697
699
|
}
|
|
698
700
|
|
|
701
|
+
/**
|
|
702
|
+
* Returns the tests recorded during the run, slowest first.
|
|
703
|
+
* @param {number} [limit] - Maximum number of tests to return (0 returns all).
|
|
704
|
+
* @returns {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} - Slowest tests, slowest first.
|
|
705
|
+
*/
|
|
706
|
+
getSlowestTests(limit = 10) {
|
|
707
|
+
const sorted = [...this._testDurations].sort((testA, testB) => testB.durationMs - testA.durationMs)
|
|
708
|
+
|
|
709
|
+
return limit > 0 ? sorted.slice(0, limit) : sorted
|
|
710
|
+
}
|
|
711
|
+
|
|
699
712
|
/**
|
|
700
713
|
* Runs prepare.
|
|
701
714
|
* @returns {Promise<void>} - Resolves when complete.
|
|
@@ -706,6 +719,7 @@ export default class TestRunner {
|
|
|
706
719
|
this._successfulTests = 0
|
|
707
720
|
this._testsCount = 0
|
|
708
721
|
this._failedTestDetails = []
|
|
722
|
+
this._testDurations = []
|
|
709
723
|
await this.importTestFiles()
|
|
710
724
|
await this.analyzeTests(tests)
|
|
711
725
|
this._onlyFocussed = this.anyTestsFocussed
|
|
@@ -940,6 +954,8 @@ export default class TestRunner {
|
|
|
940
954
|
|
|
941
955
|
console.log(`${leftPadding}it ${testDescription}`)
|
|
942
956
|
|
|
957
|
+
const testStartMs = Date.now()
|
|
958
|
+
|
|
943
959
|
while (true) {
|
|
944
960
|
let shouldRetry = false
|
|
945
961
|
/**
|
|
@@ -1180,6 +1196,13 @@ export default class TestRunner {
|
|
|
1180
1196
|
|
|
1181
1197
|
break
|
|
1182
1198
|
}
|
|
1199
|
+
|
|
1200
|
+
this._testDurations.push({
|
|
1201
|
+
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
1202
|
+
filePath: testData.filePath ?? "<unknown>",
|
|
1203
|
+
line: testData.line ?? 0,
|
|
1204
|
+
durationMs: Date.now() - testStartMs
|
|
1205
|
+
})
|
|
1183
1206
|
}
|
|
1184
1207
|
|
|
1185
1208
|
for (const subDescription in tests.subs) {
|
package/package.json
CHANGED
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
import TenantIterator from "../tenants/tenant-iterator.js"
|
|
4
4
|
|
|
5
|
+
const DEFAULT_HEARTBEAT_INTERVAL_MS = 30_000
|
|
6
|
+
|
|
5
7
|
export default class TenantDatabaseCommandHelper {
|
|
6
8
|
/**
|
|
7
9
|
* Runs constructor.
|
|
8
10
|
* @param {object} args - Options object.
|
|
9
11
|
* @param {import("./base-command.js").default} args.command - CLI command instance.
|
|
12
|
+
* @param {number} [args.heartbeatIntervalMs] - Interval between progress heartbeats.
|
|
10
13
|
* @param {string | undefined} args.identifier - Tenant database identifier.
|
|
14
|
+
* @param {(message: string) => void} [args.output] - Progress output handler.
|
|
11
15
|
*/
|
|
12
|
-
constructor({command, identifier}) {
|
|
16
|
+
constructor({command, heartbeatIntervalMs = DEFAULT_HEARTBEAT_INTERVAL_MS, identifier, output = console.log}) {
|
|
13
17
|
if (!identifier) throw new Error("Missing tenant database identifier argument")
|
|
14
18
|
|
|
15
19
|
this.command = command
|
|
16
20
|
this.configuration = command.getConfiguration()
|
|
21
|
+
this.heartbeatIntervalMs = heartbeatIntervalMs
|
|
17
22
|
this.identifier = identifier
|
|
23
|
+
this.output = output
|
|
18
24
|
this.provider = this.configuration.getTenantDatabaseProvider(identifier)
|
|
19
25
|
}
|
|
20
26
|
|
|
@@ -78,13 +84,42 @@ export default class TenantDatabaseCommandHelper {
|
|
|
78
84
|
*/
|
|
79
85
|
async eachTenant(callback) {
|
|
80
86
|
const tenants = await this.listTenants()
|
|
87
|
+
const commandName = this.command.processArgs?.[0] || "db:tenants"
|
|
88
|
+
const parallelCount = this.parallelCount()
|
|
89
|
+
const progressPrefix = `${commandName} ${this.identifier}`
|
|
90
|
+
let activeTenantCount = 0
|
|
91
|
+
let completedTenantCount = 0
|
|
81
92
|
const iterator = new TenantIterator({
|
|
82
93
|
configuration: this.configuration,
|
|
83
94
|
identifier: this.identifier,
|
|
84
|
-
parallelCount
|
|
95
|
+
parallelCount
|
|
85
96
|
})
|
|
97
|
+
const progressHeartbeat = setInterval(() => {
|
|
98
|
+
this.output(`${progressPrefix}: heartbeat: ${completedTenantCount}/${tenants.length} completed, ${activeTenantCount} active`)
|
|
99
|
+
}, this.heartbeatIntervalMs)
|
|
100
|
+
|
|
101
|
+
progressHeartbeat.unref()
|
|
102
|
+
this.output(`${progressPrefix}: processing ${tenants.length} tenant(s) with parallelism ${parallelCount}`)
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
const processedTenantCount = await iterator.run(tenants, async (args) => {
|
|
106
|
+
activeTenantCount++
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
await callback(args)
|
|
110
|
+
completedTenantCount++
|
|
111
|
+
this.output(`${progressPrefix}: completed ${TenantIterator.tenantLabel(args.tenant)} (${completedTenantCount}/${tenants.length})`)
|
|
112
|
+
} finally {
|
|
113
|
+
activeTenantCount--
|
|
114
|
+
}
|
|
115
|
+
})
|
|
86
116
|
|
|
87
|
-
|
|
117
|
+
this.output(`${progressPrefix}: finished ${processedTenantCount}/${tenants.length} tenant(s)`)
|
|
118
|
+
|
|
119
|
+
return processedTenantCount
|
|
120
|
+
} finally {
|
|
121
|
+
clearInterval(progressHeartbeat)
|
|
122
|
+
}
|
|
88
123
|
}
|
|
89
124
|
|
|
90
125
|
/**
|
|
@@ -86,6 +86,25 @@ export default class VelociousCliCommandsTest extends BaseCommand {
|
|
|
86
86
|
process.exit(1)
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
// Report the slowest tests so suite hotspots are visible every run. Defaults to
|
|
90
|
+
// the top 10; tune with VELOCIOUS_SLOW_TEST_COUNT (0 disables). Skipped for
|
|
91
|
+
// single-test runs where it would just be noise.
|
|
92
|
+
const slowTestCount = resolveSlowTestCount(process.env.VELOCIOUS_SLOW_TEST_COUNT)
|
|
93
|
+
|
|
94
|
+
if (slowTestCount > 0 && executedTests > 1) {
|
|
95
|
+
const slowestTests = testRunner.getSlowestTests(slowTestCount)
|
|
96
|
+
|
|
97
|
+
if (slowestTests.length > 0) {
|
|
98
|
+
console.log(picocolors.cyan(`\nSlowest ${slowestTests.length} tests:`))
|
|
99
|
+
|
|
100
|
+
for (const slowTest of slowestTests) {
|
|
101
|
+
const location = slowTest.filePath && slowTest.line ? ` (${slowTest.filePath}:${slowTest.line})` : ""
|
|
102
|
+
|
|
103
|
+
console.log(picocolors.cyan(` ${String(slowTest.durationMs).padStart(6)}ms ${slowTest.fullDescription}${location}`))
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
89
108
|
if (testRunner.isFailed()) {
|
|
90
109
|
await testRunner.persistFailedTestConsoleOutputsToAssets()
|
|
91
110
|
const failedTests = testRunner.getFailedTestDetails()
|
|
@@ -116,3 +135,16 @@ export default class VelociousCliCommandsTest extends BaseCommand {
|
|
|
116
135
|
}
|
|
117
136
|
}
|
|
118
137
|
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Resolves how many slowest tests to report from the `VELOCIOUS_SLOW_TEST_COUNT`
|
|
141
|
+
* env value: defaults to 10 when unset; 0 (or an unparseable value) disables the
|
|
142
|
+
* report; otherwise the floored, non-negative integer.
|
|
143
|
+
* @param {string | undefined} rawEnvValue - Raw env value.
|
|
144
|
+
* @returns {number} - Number of slowest tests to report (0 disables).
|
|
145
|
+
*/
|
|
146
|
+
export function resolveSlowTestCount(rawEnvValue) {
|
|
147
|
+
if (rawEnvValue === undefined) return 10
|
|
148
|
+
|
|
149
|
+
return Math.max(0, Math.floor(Number(rawEnvValue)) || 0)
|
|
150
|
+
}
|
|
@@ -101,9 +101,14 @@ export default class TenantIterator {
|
|
|
101
101
|
if (tenant && typeof tenant === "object") {
|
|
102
102
|
const tenantObject = /** @type {{id?: ?, name?: ?, slug?: ?}} */ (tenant)
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
for (const attributeName of /** @type {Array<"slug" | "name" | "id">} */ (["slug", "name", "id"])) {
|
|
105
|
+
const attributeOrAccessor = tenantObject[attributeName]
|
|
106
|
+
const attributeValue = typeof attributeOrAccessor === "function"
|
|
107
|
+
? attributeOrAccessor.call(tenant)
|
|
108
|
+
: attributeOrAccessor
|
|
109
|
+
|
|
110
|
+
if (attributeValue) return String(attributeValue)
|
|
111
|
+
}
|
|
107
112
|
}
|
|
108
113
|
|
|
109
114
|
return JSON.stringify(tenant)
|
|
@@ -237,6 +237,8 @@ export default class TestRunner {
|
|
|
237
237
|
this._failedTestDetails = []
|
|
238
238
|
/** @type {{fullDescription: string, filePath: string, line: number} | null} */
|
|
239
239
|
this._lastTestContext = null
|
|
240
|
+
/** @type {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} */
|
|
241
|
+
this._testDurations = []
|
|
240
242
|
}
|
|
241
243
|
|
|
242
244
|
/**
|
|
@@ -696,6 +698,17 @@ export default class TestRunner {
|
|
|
696
698
|
return this._successfulTests + this._failedTests
|
|
697
699
|
}
|
|
698
700
|
|
|
701
|
+
/**
|
|
702
|
+
* Returns the tests recorded during the run, slowest first.
|
|
703
|
+
* @param {number} [limit] - Maximum number of tests to return (0 returns all).
|
|
704
|
+
* @returns {Array<{fullDescription: string, filePath: string, line: number, durationMs: number}>} - Slowest tests, slowest first.
|
|
705
|
+
*/
|
|
706
|
+
getSlowestTests(limit = 10) {
|
|
707
|
+
const sorted = [...this._testDurations].sort((testA, testB) => testB.durationMs - testA.durationMs)
|
|
708
|
+
|
|
709
|
+
return limit > 0 ? sorted.slice(0, limit) : sorted
|
|
710
|
+
}
|
|
711
|
+
|
|
699
712
|
/**
|
|
700
713
|
* Runs prepare.
|
|
701
714
|
* @returns {Promise<void>} - Resolves when complete.
|
|
@@ -706,6 +719,7 @@ export default class TestRunner {
|
|
|
706
719
|
this._successfulTests = 0
|
|
707
720
|
this._testsCount = 0
|
|
708
721
|
this._failedTestDetails = []
|
|
722
|
+
this._testDurations = []
|
|
709
723
|
await this.importTestFiles()
|
|
710
724
|
await this.analyzeTests(tests)
|
|
711
725
|
this._onlyFocussed = this.anyTestsFocussed
|
|
@@ -940,6 +954,8 @@ export default class TestRunner {
|
|
|
940
954
|
|
|
941
955
|
console.log(`${leftPadding}it ${testDescription}`)
|
|
942
956
|
|
|
957
|
+
const testStartMs = Date.now()
|
|
958
|
+
|
|
943
959
|
while (true) {
|
|
944
960
|
let shouldRetry = false
|
|
945
961
|
/**
|
|
@@ -1180,6 +1196,13 @@ export default class TestRunner {
|
|
|
1180
1196
|
|
|
1181
1197
|
break
|
|
1182
1198
|
}
|
|
1199
|
+
|
|
1200
|
+
this._testDurations.push({
|
|
1201
|
+
fullDescription: this.buildFullDescription(descriptions, testDescription),
|
|
1202
|
+
filePath: testData.filePath ?? "<unknown>",
|
|
1203
|
+
line: testData.line ?? 0,
|
|
1204
|
+
durationMs: Date.now() - testStartMs
|
|
1205
|
+
})
|
|
1183
1206
|
}
|
|
1184
1207
|
|
|
1185
1208
|
for (const subDescription in tests.subs) {
|