velocious 1.0.97 → 1.0.98
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/eslint.config.js +1 -0
- package/package.json +2 -1
- package/spec/database/connection/drivers/mysql/query-parser-spec.js +4 -4
- package/spec/http-server/post-spec.js +2 -0
- package/src/application.js +27 -9
- package/src/configuration-resolver.js +29 -10
- package/src/configuration-types.js +44 -0
- package/src/configuration.js +63 -33
- package/src/database/drivers/base-column.js +6 -1
- package/src/database/drivers/base-columns-index.js +11 -1
- package/src/database/drivers/base-foreign-key.js +45 -0
- package/src/database/drivers/base-table.js +24 -2
- package/src/database/drivers/base.js +211 -39
- package/src/database/drivers/mssql/index.js +1 -3
- package/src/database/drivers/sqlite/sql/alter-table.js +4 -2
- package/src/database/handler.js +5 -0
- package/src/database/migration/index.js +77 -19
- package/src/database/migrator/files-finder.js +21 -22
- package/src/database/migrator/types.js +29 -0
- package/src/database/migrator.js +98 -59
- package/src/database/pool/async-tracked-multi-connection.js +42 -7
- package/src/database/pool/base-methods-forward.js +37 -0
- package/src/database/pool/base.js +79 -46
- package/src/database/pool/single-multi-use.js +18 -3
- package/src/database/query/alter-table-base.js +4 -4
- package/src/database/query/base.js +9 -2
- package/src/database/query/create-database-base.js +8 -0
- package/src/database/query/create-index-base.js +20 -5
- package/src/database/query/create-table-base.js +28 -9
- package/src/database/query/from-base.js +17 -0
- package/src/database/query/from-plain.js +8 -3
- package/src/database/query/from-table.js +8 -3
- package/src/database/query/index.js +43 -32
- package/src/database/query/join-base.js +28 -1
- package/src/database/query/join-object.js +67 -0
- package/src/database/query/join-plain.js +6 -1
- package/src/database/query/order-base.js +18 -0
- package/src/database/query/order-plain.js +8 -2
- package/src/database/query/select-base.js +15 -0
- package/src/database/query/select-plain.js +6 -1
- package/src/database/query/select-table-and-column.js +8 -2
- package/src/database/query/where-base.js +23 -1
- package/src/database/query/where-hash.js +15 -0
- package/src/database/query/where-plain.js +6 -0
- package/src/database/query-parser/base-query-parser.js +8 -2
- package/src/database/query-parser/from-parser.js +2 -0
- package/src/database/query-parser/joins-parser.js +10 -45
- package/src/database/query-parser/select-parser.js +2 -0
- package/src/database/record/index.js +1 -1
- package/src/database/table-data/index.js +39 -121
- package/src/database/table-data/table-column.js +54 -25
- package/src/database/table-data/table-foreign-key.js +5 -3
- package/src/database/table-data/table-index.js +12 -6
- package/src/database/table-data/table-reference.js +2 -0
- package/src/database/use-database.js +4 -2
- package/src/environment-handlers/base.js +41 -8
- package/src/environment-handlers/node/cli/commands/destroy/migration.js +3 -0
- package/src/environment-handlers/node/cli/commands/generate/migration.js +3 -0
- package/src/environment-handlers/node/cli/commands/generate/model.js +3 -0
- package/src/environment-handlers/node/cli/commands/init.js +3 -0
- package/src/environment-handlers/node.js +59 -28
- package/src/http-client/header.js +6 -0
- package/src/http-client/request.js +31 -5
- package/src/http-client/response.js +31 -7
- package/src/http-server/client/index.js +24 -4
- package/src/http-server/client/request-buffer/form-data-part.js +11 -0
- package/src/http-server/client/request-buffer/header.js +6 -0
- package/src/http-server/client/request-buffer/index.js +91 -13
- package/src/http-server/client/request-parser.js +26 -0
- package/src/http-server/client/request-runner.js +15 -3
- package/src/http-server/client/request.js +17 -0
- package/src/http-server/client/response.js +41 -1
- package/src/http-server/index.js +32 -4
- package/src/http-server/server-client.js +33 -2
- package/src/http-server/worker-handler/index.js +42 -9
- package/src/http-server/worker-handler/worker-script.js +2 -0
- package/src/http-server/worker-handler/worker-thread.js +34 -6
- package/src/logger.js +21 -15
- package/src/routes/app-routes.js +1 -1
- package/src/testing/test-files-finder.js +8 -4
- package/src/testing/test-runner.js +76 -24
- package/src/utils/backtrace-cleaner.js +6 -4
- package/src/utils/ensure-error.js +13 -0
- package/src/utils/file-exists.js +3 -1
- package/src/utils/rest-args-error.js +2 -0
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
2
3
|
import {Logger} from "../../logger.js"
|
|
3
4
|
import {Worker} from "worker_threads"
|
|
5
|
+
import ensureError from "../../utils/ensure-error.js"
|
|
4
6
|
|
|
5
7
|
export default class VelociousHttpServerWorker {
|
|
8
|
+
/**
|
|
9
|
+
* @param {object} args
|
|
10
|
+
* @param {import("../../configuration.js").default} args.configuration
|
|
11
|
+
* @param {number} args.workerCount
|
|
12
|
+
*/
|
|
6
13
|
constructor({configuration, workerCount}) {
|
|
7
14
|
this.configuration = configuration
|
|
15
|
+
|
|
16
|
+
/** @type {Record<number, import("../server-client.js").default>} */
|
|
8
17
|
this.clients = {}
|
|
18
|
+
|
|
9
19
|
this.logger = new Logger(this)
|
|
10
20
|
this.workerCount = workerCount
|
|
11
21
|
}
|
|
@@ -18,7 +28,7 @@ export default class VelociousHttpServerWorker {
|
|
|
18
28
|
}
|
|
19
29
|
|
|
20
30
|
async _spawnWorker() {
|
|
21
|
-
const
|
|
31
|
+
const debug = this.configuration.debug
|
|
22
32
|
const directory = this.configuration.getDirectory()
|
|
23
33
|
const velociousPath = await this.configuration.getEnvironmentHandler().getVelociousPath()
|
|
24
34
|
|
|
@@ -35,25 +45,38 @@ export default class VelociousHttpServerWorker {
|
|
|
35
45
|
this.worker.on("message", this.onWorkerMessage)
|
|
36
46
|
}
|
|
37
47
|
|
|
48
|
+
/**
|
|
49
|
+
* @param {import("../server-client.js").default} client
|
|
50
|
+
* @returns {void}
|
|
51
|
+
*/
|
|
38
52
|
addSocketConnection(client) {
|
|
39
|
-
const clientCount =
|
|
53
|
+
const clientCount = client.clientCount
|
|
40
54
|
|
|
41
55
|
client.socket.on("end", () => {
|
|
42
56
|
this.logger.debug(`Removing ${clientCount} from clients`)
|
|
43
57
|
delete this.clients[clientCount]
|
|
44
58
|
})
|
|
45
59
|
|
|
46
|
-
|
|
60
|
+
if (!this.worker) throw new Error("Worker not initialized")
|
|
61
|
+
|
|
62
|
+
client.setWorker(this.worker)
|
|
47
63
|
client.listen()
|
|
48
64
|
|
|
49
65
|
this.clients[clientCount] = client
|
|
50
66
|
this.worker.postMessage({command: "newClient", clientCount})
|
|
51
67
|
}
|
|
52
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @param {any} error
|
|
71
|
+
*/
|
|
53
72
|
onWorkerError = (error) => {
|
|
54
|
-
throw error // Throws original error with backtrace and everything into the console
|
|
73
|
+
throw ensureError(error) // Throws original error with backtrace and everything into the console
|
|
55
74
|
}
|
|
56
75
|
|
|
76
|
+
/**
|
|
77
|
+
* @param {number} code
|
|
78
|
+
* @returns {void}
|
|
79
|
+
*/
|
|
57
80
|
onWorkerExit = (code) => {
|
|
58
81
|
if (code !== 0) {
|
|
59
82
|
throw new Error(`Client worker stopped with exit code ${code}`)
|
|
@@ -62,22 +85,32 @@ export default class VelociousHttpServerWorker {
|
|
|
62
85
|
}
|
|
63
86
|
}
|
|
64
87
|
|
|
88
|
+
/**
|
|
89
|
+
* @param {object} data
|
|
90
|
+
* @param {string} data.command
|
|
91
|
+
* @param {number} data.clientCount
|
|
92
|
+
* @param {string} data.output
|
|
93
|
+
* @returns {void}
|
|
94
|
+
*/
|
|
65
95
|
onWorkerMessage = (data) => {
|
|
66
96
|
this.logger.debug(`Worker message`, data)
|
|
67
97
|
|
|
68
|
-
const {command} =
|
|
98
|
+
const {command} = data
|
|
69
99
|
|
|
70
100
|
if (command == "started") {
|
|
71
|
-
this.onStartCallback
|
|
101
|
+
if (this.onStartCallback) {
|
|
102
|
+
this.onStartCallback(null)
|
|
103
|
+
}
|
|
104
|
+
|
|
72
105
|
this.onStartCallback = null
|
|
73
106
|
} else if (command == "clientOutput") {
|
|
74
107
|
this.logger.debug("CLIENT OUTPUT", data)
|
|
75
108
|
|
|
76
|
-
const {clientCount, output} =
|
|
109
|
+
const {clientCount, output} = data
|
|
77
110
|
|
|
78
111
|
this.clients[clientCount]?.send(output)
|
|
79
112
|
} else if (command == "clientClose") {
|
|
80
|
-
const {clientCount} =
|
|
113
|
+
const {clientCount} = data
|
|
81
114
|
|
|
82
115
|
this.clients[clientCount]?.end()
|
|
83
116
|
} else {
|
|
@@ -1,14 +1,25 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import Application from "../../application.js"
|
|
2
4
|
import Client from "../client/index.js"
|
|
3
|
-
import {digg
|
|
5
|
+
import {digg} from "diggerize"
|
|
4
6
|
import errorLogger from "../../error-logger.js"
|
|
5
7
|
import {Logger} from "../../logger.js"
|
|
6
8
|
|
|
7
9
|
export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
10
|
+
/**
|
|
11
|
+
* @param {object} args
|
|
12
|
+
* @param {import("worker_threads").parentPort} args.parentPort
|
|
13
|
+
* @param {{directory: string, environment: string, workerCount: number}} args.workerData
|
|
14
|
+
*/
|
|
8
15
|
constructor({parentPort, workerData}) {
|
|
9
|
-
|
|
16
|
+
if (!parentPort) throw new Error("parentPort is required")
|
|
17
|
+
|
|
18
|
+
const {workerCount} = workerData
|
|
10
19
|
|
|
20
|
+
/** @type {Record<number, Client>} */
|
|
11
21
|
this.clients = {}
|
|
22
|
+
|
|
12
23
|
this.logger = new Logger(this, {debug: false})
|
|
13
24
|
this.parentPort = parentPort
|
|
14
25
|
this.workerData = workerData
|
|
@@ -17,6 +28,8 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
17
28
|
parentPort.on("message", errorLogger(this.onCommand))
|
|
18
29
|
|
|
19
30
|
this.initialize().then(() => {
|
|
31
|
+
if (!this.application) throw new Error("Application not initialized")
|
|
32
|
+
|
|
20
33
|
this.application.initialize().then(() => {
|
|
21
34
|
this.logger.debug(`Worker ${workerCount} started`)
|
|
22
35
|
parentPort.postMessage({command: "started"})
|
|
@@ -24,29 +37,44 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
24
37
|
})
|
|
25
38
|
}
|
|
26
39
|
|
|
40
|
+
/**
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
27
43
|
async initialize() {
|
|
28
|
-
const {
|
|
44
|
+
const {directory, environment} = this.workerData
|
|
29
45
|
const configurationPath = `${directory}/src/config/configuration.js`
|
|
30
46
|
const configurationImport = await import(configurationPath)
|
|
31
47
|
|
|
48
|
+
/** @type {import("../../configuration.js").default} */
|
|
32
49
|
this.configuration = configurationImport.default
|
|
50
|
+
|
|
51
|
+
if (!this.configuration) throw new Error(`Configuration couldn't be loaded from: ${configurationPath}`)
|
|
52
|
+
|
|
33
53
|
this.configuration.setEnvironment(environment)
|
|
34
54
|
this.configuration.setCurrent()
|
|
35
55
|
|
|
36
|
-
this.application = new Application({configuration: this.configuration,
|
|
56
|
+
this.application = new Application({configuration: this.configuration, type: "worker-handler"})
|
|
37
57
|
|
|
38
58
|
if (this.configuration.isInitialized()) {
|
|
39
59
|
await this.configuration.initialize({type: "worker-handler"})
|
|
40
60
|
}
|
|
41
61
|
}
|
|
42
62
|
|
|
63
|
+
/**
|
|
64
|
+
* @param {object} data
|
|
65
|
+
* @param {string} data.command
|
|
66
|
+
* @param {string} [data.chunk]
|
|
67
|
+
* @param {number} data.clientCount
|
|
68
|
+
*/
|
|
43
69
|
onCommand = async (data) => {
|
|
44
70
|
await this.logger.debug(() => [`Worker ${this.workerCount} received command`, data])
|
|
45
71
|
|
|
46
72
|
const command = data.command
|
|
47
73
|
|
|
48
74
|
if (command == "newClient") {
|
|
49
|
-
|
|
75
|
+
if (!this.configuration) throw new Error("Configuration not initialized")
|
|
76
|
+
|
|
77
|
+
const {clientCount} = data
|
|
50
78
|
const client = new Client({
|
|
51
79
|
clientCount,
|
|
52
80
|
configuration: this.configuration
|
|
@@ -65,7 +93,7 @@ export default class VelociousHttpServerWorkerHandlerWorkerThread {
|
|
|
65
93
|
} else if (command == "clientWrite") {
|
|
66
94
|
await this.logger.debug("Looking up client")
|
|
67
95
|
|
|
68
|
-
const {chunk, clientCount} =
|
|
96
|
+
const {chunk, clientCount} = data
|
|
69
97
|
const client = digg(this.clients, clientCount)
|
|
70
98
|
|
|
71
99
|
await this.logger.debug(`Sending to client ${clientCount}`)
|
package/src/logger.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import Configuration from "./configuration.js"
|
|
2
4
|
import restArgsError from "./utils/rest-args-error.js"
|
|
3
5
|
|
|
@@ -8,9 +10,10 @@ import restArgsError from "./utils/rest-args-error.js"
|
|
|
8
10
|
function consoleLog(message) {
|
|
9
11
|
return new Promise((resolve) => {
|
|
10
12
|
if (process.stdout) {
|
|
11
|
-
process.stdout.write(`${message}\n`, "utf8", resolve)
|
|
13
|
+
process.stdout.write(`${message}\n`, "utf8", () => resolve())
|
|
12
14
|
} else {
|
|
13
15
|
console.log(message)
|
|
16
|
+
resolve()
|
|
14
17
|
}
|
|
15
18
|
})
|
|
16
19
|
}
|
|
@@ -22,9 +25,10 @@ function consoleLog(message) {
|
|
|
22
25
|
function consoleError(message) {
|
|
23
26
|
return new Promise((resolve) => {
|
|
24
27
|
if (process.stderr) {
|
|
25
|
-
process.stderr.write(`${message}\n`, "utf8", resolve)
|
|
28
|
+
process.stderr.write(`${message}\n`, "utf8", () => resolve())
|
|
26
29
|
} else {
|
|
27
30
|
console.error(message)
|
|
31
|
+
resolve()
|
|
28
32
|
}
|
|
29
33
|
})
|
|
30
34
|
}
|
|
@@ -36,17 +40,19 @@ function consoleError(message) {
|
|
|
36
40
|
function consoleWarn(message) {
|
|
37
41
|
return new Promise((resolve) => {
|
|
38
42
|
if (process.stderr) {
|
|
39
|
-
process.stderr.write(`${message}\n`, "utf8", resolve)
|
|
43
|
+
process.stderr.write(`${message}\n`, "utf8", () => resolve())
|
|
40
44
|
} else {
|
|
41
45
|
console.warn(message)
|
|
46
|
+
resolve()
|
|
42
47
|
}
|
|
43
48
|
})
|
|
44
49
|
}
|
|
45
50
|
|
|
46
51
|
/**
|
|
47
|
-
* @param {Array} messages
|
|
52
|
+
* @param {...any|function() : Array<any>} messages
|
|
53
|
+
* @returns {Array<any>} - Either the function result or the messages
|
|
48
54
|
*/
|
|
49
|
-
function functionOrMessages(messages) {
|
|
55
|
+
function functionOrMessages(...messages) {
|
|
50
56
|
if (messages.length === 1 && typeof messages[0] == "function") {
|
|
51
57
|
messages = messages[0]()
|
|
52
58
|
}
|
|
@@ -65,7 +71,7 @@ function messagesToMessage(...messages) {
|
|
|
65
71
|
for (const messagePartIndex in messages) {
|
|
66
72
|
const messagePart = messages[messagePartIndex]
|
|
67
73
|
|
|
68
|
-
if (messagePartIndex > 0) {
|
|
74
|
+
if (typeof messagePartIndex == "number" && messagePartIndex > 0) {
|
|
69
75
|
message += " "
|
|
70
76
|
}
|
|
71
77
|
|
|
@@ -85,7 +91,7 @@ class Logger {
|
|
|
85
91
|
* @param {object} args
|
|
86
92
|
* @param {boolean} args.debug
|
|
87
93
|
*/
|
|
88
|
-
constructor(object, {debug, ...restArgs} = {}) {
|
|
94
|
+
constructor(object, {debug, ...restArgs} = {debug: false}) {
|
|
89
95
|
restArgsError(restArgs)
|
|
90
96
|
|
|
91
97
|
this._debug = debug
|
|
@@ -114,7 +120,7 @@ class Logger {
|
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
/**
|
|
117
|
-
* @
|
|
123
|
+
* @type {(...args: Parameters<typeof functionOrMessages>) => Promise<void>}
|
|
118
124
|
*/
|
|
119
125
|
async debug(...messages) {
|
|
120
126
|
if (this._debug || this.getConfiguration()?.debug) {
|
|
@@ -123,17 +129,17 @@ class Logger {
|
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
/**
|
|
126
|
-
* @
|
|
132
|
+
* @type {(...args: Parameters<typeof functionOrMessages>) => Promise<void>}
|
|
127
133
|
*/
|
|
128
134
|
async log(...messages) {
|
|
129
|
-
await consoleLog(messagesToMessage(this._subject, ...functionOrMessages(messages)))
|
|
135
|
+
await consoleLog(messagesToMessage(this._subject, ...functionOrMessages(...messages)))
|
|
130
136
|
}
|
|
131
137
|
|
|
132
138
|
/**
|
|
133
|
-
* @
|
|
139
|
+
* @type {(...args: Parameters<typeof functionOrMessages>) => Promise<void>}
|
|
134
140
|
*/
|
|
135
141
|
async error(...messages) {
|
|
136
|
-
await consoleError(messagesToMessage(this._subject, ...functionOrMessages(messages)))
|
|
142
|
+
await consoleError(messagesToMessage(this._subject, ...functionOrMessages(...messages)))
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
/**
|
|
@@ -145,10 +151,10 @@ class Logger {
|
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
/**
|
|
148
|
-
* @
|
|
154
|
+
* @type {(...args: Parameters<typeof functionOrMessages>) => Promise<void>}
|
|
149
155
|
*/
|
|
150
156
|
async warn(...messages) {
|
|
151
|
-
await consoleWarn(messagesToMessage(this._subject, ...functionOrMessages(messages)))
|
|
157
|
+
await consoleWarn(messagesToMessage(this._subject, ...functionOrMessages(...messages)))
|
|
152
158
|
}
|
|
153
159
|
}
|
|
154
160
|
|
|
@@ -163,6 +169,6 @@ export default async function logger(object, ...messages) {
|
|
|
163
169
|
const configuration = object.configuration || Configuration.current()
|
|
164
170
|
|
|
165
171
|
if (configuration.debug) {
|
|
166
|
-
await consoleLog(messagesToMessage(className, ...functionOrMessages(messages)))
|
|
172
|
+
await consoleLog(messagesToMessage(className, ...functionOrMessages(...messages)))
|
|
167
173
|
}
|
|
168
174
|
}
|
package/src/routes/app-routes.js
CHANGED
|
@@ -2,7 +2,7 @@ import {digg} from "diggerize"
|
|
|
2
2
|
|
|
3
3
|
export default class VelociousRoutesAppRoutes {
|
|
4
4
|
/**
|
|
5
|
-
* @param {import("../configuration.js")} configuration
|
|
5
|
+
* @param {import("../configuration.js").default} configuration
|
|
6
6
|
* @returns {import("./index.js").default}
|
|
7
7
|
*/
|
|
8
8
|
static async getRoutes(configuration) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import fs from "fs/promises"
|
|
4
4
|
|
|
5
5
|
import fileExists from "../utils/file-exists.js"
|
|
6
|
+
import {Logger} from "../logger.js"
|
|
6
7
|
import restArgsError from "../utils/rest-args-error.js"
|
|
7
8
|
|
|
8
9
|
// Incredibly complex class to find files in multiple simultanious running promises to do it as fast as possible.
|
|
@@ -19,6 +20,7 @@ export default class TestFilesFinder {
|
|
|
19
20
|
restArgsError(restArgs)
|
|
20
21
|
|
|
21
22
|
this.directory = directory
|
|
23
|
+
this.logger = new Logger(this)
|
|
22
24
|
|
|
23
25
|
if (directories) {
|
|
24
26
|
this.directories = directories
|
|
@@ -63,10 +65,7 @@ export default class TestFilesFinder {
|
|
|
63
65
|
async findTestFiles() {
|
|
64
66
|
await this.withFindingCount(async () => {
|
|
65
67
|
for (const directory of this.directories) {
|
|
66
|
-
console.log({directory})
|
|
67
|
-
|
|
68
68
|
if (await fileExists(directory)) {
|
|
69
|
-
console.log("Exists!")
|
|
70
69
|
await this.findTestFilesInDir(directory)
|
|
71
70
|
}
|
|
72
71
|
}
|
|
@@ -159,6 +158,7 @@ export default class TestFilesFinder {
|
|
|
159
158
|
if (this.directoryArgs.length > 0) {
|
|
160
159
|
for (const directoryArg of this.directoryArgs) {
|
|
161
160
|
if (localPath.startsWith(directoryArg) && this.looksLikeTestFile(file)) {
|
|
161
|
+
this.logger.debug("Found test file because matching dir and looks like this file:", file)
|
|
162
162
|
return true
|
|
163
163
|
}
|
|
164
164
|
}
|
|
@@ -167,10 +167,14 @@ export default class TestFilesFinder {
|
|
|
167
167
|
if (this.fileArgs.length > 0) {
|
|
168
168
|
for (const fileArg of this.fileArgs) {
|
|
169
169
|
if (fileArg == localPath) {
|
|
170
|
+
this.logger.debug("Found test file because matching file arg:", file)
|
|
170
171
|
return true
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
|
-
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (this.fileArgs.length == 0 && this.directoryArgs.length == 0 && this.looksLikeTestFile(file)) {
|
|
177
|
+
this.logger.debug("Found test file because looks like this file:", file)
|
|
174
178
|
return true
|
|
175
179
|
}
|
|
176
180
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
import {addTrackedStackToError} from "../utils/with-tracked-stack.js"
|
|
2
4
|
import Application from "../../src/application.js"
|
|
3
5
|
import BacktraceCleaner from "../utils/backtrace-cleaner.js"
|
|
@@ -5,6 +7,34 @@ import RequestClient from "./request-client.js"
|
|
|
5
7
|
import restArgsError from "../utils/rest-args-error.js"
|
|
6
8
|
import {tests} from "./test.js"
|
|
7
9
|
|
|
10
|
+
/**
|
|
11
|
+
* @typedef {object} TestArgs
|
|
12
|
+
* @property {Application} application
|
|
13
|
+
* @property {RequestClient} client
|
|
14
|
+
* @property {boolean} focus
|
|
15
|
+
* @property {string} type
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @typedef {object} TestData
|
|
20
|
+
* @property {TestArgs} args
|
|
21
|
+
* @property {function(TestArgs) : Promise<void>} function
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @typedef {object} AfterBeforeEachCallback
|
|
26
|
+
* @property {function({configuration: import("../configuration.js").default, testArgs: TestArgs, testData: TestData}) : Promise<void>} callback
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* @typedef {object} TestsArgument
|
|
31
|
+
* @property {boolean} [anyTestsFocussed]
|
|
32
|
+
* @property {AfterBeforeEachCallback[]} afterEaches
|
|
33
|
+
* @property {AfterBeforeEachCallback[]} beforeEaches
|
|
34
|
+
* @property {Record<string, TestData>} tests - A unique identifier for the node.
|
|
35
|
+
* @property {Record<string, TestsArgument>} subs - Optional child nodes. Each item is another `Node`, allowing recursion.
|
|
36
|
+
*/
|
|
37
|
+
|
|
8
38
|
export default class TestRunner {
|
|
9
39
|
/**
|
|
10
40
|
* @param {object} args
|
|
@@ -18,6 +48,10 @@ export default class TestRunner {
|
|
|
18
48
|
|
|
19
49
|
this._configuration = configuration
|
|
20
50
|
this._testFiles = testFiles
|
|
51
|
+
|
|
52
|
+
this._failedTests = 0
|
|
53
|
+
this._successfulTests = 0
|
|
54
|
+
this._testsCount = 0
|
|
21
55
|
}
|
|
22
56
|
|
|
23
57
|
/**
|
|
@@ -37,13 +71,6 @@ export default class TestRunner {
|
|
|
37
71
|
if (!this._application) {
|
|
38
72
|
this._application = new Application({
|
|
39
73
|
configuration: this.getConfiguration(),
|
|
40
|
-
databases: {
|
|
41
|
-
default: {
|
|
42
|
-
host: "mysql",
|
|
43
|
-
username: "user",
|
|
44
|
-
password: ""
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
74
|
httpServer: {port: 31006},
|
|
48
75
|
type: "test-runner"
|
|
49
76
|
})
|
|
@@ -56,7 +83,7 @@ export default class TestRunner {
|
|
|
56
83
|
}
|
|
57
84
|
|
|
58
85
|
/**
|
|
59
|
-
* @returns {RequestClient}
|
|
86
|
+
* @returns {Promise<RequestClient>}
|
|
60
87
|
*/
|
|
61
88
|
async requestClient() {
|
|
62
89
|
if (!this._requestClient) {
|
|
@@ -67,7 +94,7 @@ export default class TestRunner {
|
|
|
67
94
|
}
|
|
68
95
|
|
|
69
96
|
/**
|
|
70
|
-
* @returns {void}
|
|
97
|
+
* @returns {Promise<void>}
|
|
71
98
|
*/
|
|
72
99
|
async importTestFiles() {
|
|
73
100
|
await this.getConfiguration().getEnvironmentHandler().importTestFiles(this.getTestFiles())
|
|
@@ -76,25 +103,37 @@ export default class TestRunner {
|
|
|
76
103
|
/**
|
|
77
104
|
* @returns {boolean}
|
|
78
105
|
*/
|
|
79
|
-
isFailed() { return this._failedTests > 0 }
|
|
106
|
+
isFailed() { return this._failedTests !== undefined && this._failedTests > 0 }
|
|
80
107
|
|
|
81
108
|
/**
|
|
82
109
|
* @returns {number}
|
|
83
110
|
*/
|
|
84
|
-
getFailedTests() {
|
|
111
|
+
getFailedTests() {
|
|
112
|
+
if (this._failedTests === undefined) throw new Error("Tests hasn't been run yet")
|
|
113
|
+
|
|
114
|
+
return this._failedTests
|
|
115
|
+
}
|
|
85
116
|
|
|
86
117
|
/**
|
|
87
118
|
* @returns {number}
|
|
88
119
|
*/
|
|
89
|
-
getSuccessfulTests() {
|
|
120
|
+
getSuccessfulTests() {
|
|
121
|
+
if (this._successfulTests === undefined) throw new Error("Tests hasn't been run yet")
|
|
122
|
+
|
|
123
|
+
return this._successfulTests
|
|
124
|
+
}
|
|
90
125
|
|
|
91
126
|
/**
|
|
92
127
|
* @returns {number}
|
|
93
128
|
*/
|
|
94
|
-
getTestsCount() {
|
|
129
|
+
getTestsCount() {
|
|
130
|
+
if (this._testsCount === undefined) throw new Error("Tests hasn't been run yet")
|
|
131
|
+
|
|
132
|
+
return this._testsCount
|
|
133
|
+
}
|
|
95
134
|
|
|
96
135
|
/**
|
|
97
|
-
* @returns {void}
|
|
136
|
+
* @returns {Promise<void>}
|
|
98
137
|
*/
|
|
99
138
|
async prepare() {
|
|
100
139
|
this.anyTestsFocussed = false
|
|
@@ -124,7 +163,7 @@ export default class TestRunner {
|
|
|
124
163
|
}
|
|
125
164
|
|
|
126
165
|
/**
|
|
127
|
-
* @returns {void}
|
|
166
|
+
* @returns {Promise<void>}
|
|
128
167
|
*/
|
|
129
168
|
async run() {
|
|
130
169
|
await this.getConfiguration().ensureConnections(async () => {
|
|
@@ -139,7 +178,8 @@ export default class TestRunner {
|
|
|
139
178
|
}
|
|
140
179
|
|
|
141
180
|
/**
|
|
142
|
-
* @
|
|
181
|
+
* @param {TestsArgument} tests
|
|
182
|
+
* @returns {{anyTestsFocussed: boolean}}
|
|
143
183
|
*/
|
|
144
184
|
analyzeTests(tests) {
|
|
145
185
|
let anyTestsFocussedFound = false
|
|
@@ -171,6 +211,12 @@ export default class TestRunner {
|
|
|
171
211
|
}
|
|
172
212
|
|
|
173
213
|
/**
|
|
214
|
+
* @param {object} args
|
|
215
|
+
* @param {Array<AfterBeforeEachCallback>} args.afterEaches
|
|
216
|
+
* @param {Array<AfterBeforeEachCallback>} args.beforeEaches
|
|
217
|
+
* @param {TestsArgument} args.tests
|
|
218
|
+
* @param {string[]} args.descriptions
|
|
219
|
+
* @param {number} args.indentLevel
|
|
174
220
|
* @returns {Promise<void>}
|
|
175
221
|
*/
|
|
176
222
|
async runTests({afterEaches, beforeEaches, tests, descriptions, indentLevel}) {
|
|
@@ -180,7 +226,7 @@ export default class TestRunner {
|
|
|
180
226
|
|
|
181
227
|
for (const testDescription in tests.tests) {
|
|
182
228
|
const testData = tests.tests[testDescription]
|
|
183
|
-
const testArgs = Object.assign({}, testData.args)
|
|
229
|
+
const testArgs = /** @type {TestArgs} */ (Object.assign({}, testData.args))
|
|
184
230
|
|
|
185
231
|
if (this._onlyFocussed && !testArgs.focus) continue
|
|
186
232
|
|
|
@@ -204,15 +250,21 @@ export default class TestRunner {
|
|
|
204
250
|
} catch (error) {
|
|
205
251
|
this._failedTests++
|
|
206
252
|
|
|
207
|
-
|
|
208
|
-
|
|
253
|
+
if (error instanceof Error) {
|
|
254
|
+
console.error(`${leftPadding} Test failed:`, error.message)
|
|
255
|
+
addTrackedStackToError(error)
|
|
209
256
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
257
|
+
const backtraceCleaner = new BacktraceCleaner(error)
|
|
258
|
+
const cleanedStack = backtraceCleaner.getCleanedStack()
|
|
259
|
+
const stackLines = cleanedStack?.split("\n")
|
|
213
260
|
|
|
214
|
-
|
|
215
|
-
|
|
261
|
+
if (stackLines) {
|
|
262
|
+
for (const stackLine of stackLines) {
|
|
263
|
+
console.error(`${leftPadding} ${stackLine}`)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
} else {
|
|
267
|
+
console.error(`${leftPadding} Test failed with a ${typeof error}:`, error)
|
|
216
268
|
}
|
|
217
269
|
} finally {
|
|
218
270
|
for (const afterEachData of newAfterEaches) {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
|
|
1
3
|
export default class BacktraceCleaner {
|
|
2
4
|
/**
|
|
3
5
|
* @param {Error} error
|
|
4
|
-
* @returns {string}
|
|
6
|
+
* @returns {string | undefined}
|
|
5
7
|
*/
|
|
6
8
|
static getCleanedStack(error) {
|
|
7
9
|
return new BacktraceCleaner(error).getCleanedStack()
|
|
@@ -15,11 +17,11 @@ export default class BacktraceCleaner {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
|
-
* @returns {string}
|
|
20
|
+
* @returns {string | undefined}
|
|
19
21
|
*/
|
|
20
22
|
getCleanedStack() {
|
|
21
|
-
const backtrace = this.error
|
|
23
|
+
const backtrace = this.error?.stack?.split("\n")
|
|
22
24
|
|
|
23
|
-
return backtrace
|
|
25
|
+
return backtrace?.filter((line) => !line.includes("node_modules") && !line.includes("(node:internal/process/")).join("\n")
|
|
24
26
|
}
|
|
25
27
|
}
|
package/src/utils/file-exists.js
CHANGED