velocious 1.0.37 → 1.0.38
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/package.json +1 -1
- package/spec/database/record/create-spec.js +3 -1
- package/spec/database/record/validations-spec.js +4 -2
- package/spec/database/transactions-spec.js +3 -1
- package/spec/http-server/get-spec.js +1 -1
- package/spec/http-server/root-get-spec.js +1 -1
- package/src/configuration.js +5 -1
- package/src/controller.js +15 -1
- package/src/database/record/index.js +25 -1
- package/src/http-server/client/request-parser.js +3 -6
- package/src/routes/built-in/errors/not-found.ejs +1 -1
- package/src/testing/test-runner.js +30 -7
- package/src/testing/test.js +17 -1
- package/src/utils/with-tracked-stack.js +61 -0
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Dummy from "../../dummy/index.js"
|
|
2
2
|
import Project from "../../dummy/src/models/project.js"
|
|
3
3
|
import Task from "../../dummy/src/models/task.js"
|
|
4
|
+
import {ValidationError} from "../../../src/database/record/index.js"
|
|
4
5
|
|
|
5
6
|
describe("Record - create", () => {
|
|
6
7
|
it("creates a new simple record with relationships and translations", async () => {
|
|
@@ -49,7 +50,8 @@ describe("Record - create", () => {
|
|
|
49
50
|
|
|
50
51
|
throw new Error("Didnt expect to succeed")
|
|
51
52
|
} catch (error) {
|
|
52
|
-
expect(error
|
|
53
|
+
expect(error).toBeInstanceOf(ValidationError)
|
|
54
|
+
expect(error.message).toEqual("Name can't be blank")
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
const projectsCount = await Project.count()
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import Dummy from "../../dummy/index.js"
|
|
2
2
|
import Task from "../../dummy/src/models/task.js"
|
|
3
|
+
import {ValidationError} from "../../../src/database/record/index.js"
|
|
3
4
|
|
|
4
5
|
describe("Record - validations", () => {
|
|
5
6
|
it("raises validations if trying to create an invalid record because of a presence validation", async () => {
|
|
6
7
|
await Dummy.run(async () => {
|
|
7
8
|
const task = new Task({name: " "})
|
|
8
9
|
|
|
9
|
-
await expectAsync(task.save()).toBeRejectedWith(new
|
|
10
|
+
await expectAsync(task.save()).toBeRejectedWith(new ValidationError("Name can't be blank"))
|
|
10
11
|
})
|
|
11
12
|
})
|
|
12
13
|
|
|
@@ -21,7 +22,8 @@ describe("Record - validations", () => {
|
|
|
21
22
|
|
|
22
23
|
throw new Error("Task 2 save didn't fail")
|
|
23
24
|
} catch (error) {
|
|
24
|
-
expect(error
|
|
25
|
+
expect(error).toBeInstanceOf(ValidationError)
|
|
26
|
+
expect(error.message).toEqual("Name has already been taken")
|
|
25
27
|
}
|
|
26
28
|
})
|
|
27
29
|
})
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Dummy from "../dummy/index.js"
|
|
2
2
|
import dummyConfiguration from "../dummy/src/config/configuration.js"
|
|
3
3
|
import Task from "../dummy/src/models/task.js"
|
|
4
|
+
import {ValidationError} from "../../src/database/record/index.js"
|
|
4
5
|
|
|
5
6
|
describe("database - transactions", () => {
|
|
6
7
|
it("supports transactions and savepoints", async () => {
|
|
@@ -22,7 +23,8 @@ describe("database - transactions", () => {
|
|
|
22
23
|
throw new Error("Didn't expect to succeed")
|
|
23
24
|
})
|
|
24
25
|
} catch (error) {
|
|
25
|
-
expect(error
|
|
26
|
+
expect(error).toBeInstanceOf(ValidationError)
|
|
27
|
+
expect(error.message).toEqual("Name can't be blank")
|
|
26
28
|
}
|
|
27
29
|
})
|
|
28
30
|
})
|
package/src/configuration.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {digg} from "diggerize"
|
|
2
2
|
import restArgsError from "./utils/rest-args-error.js"
|
|
3
|
+
import {withTrackedStack} from "./utils/with-tracked-stack.js"
|
|
3
4
|
|
|
4
5
|
export default class VelociousConfiguration {
|
|
5
6
|
static current(throwError = true) {
|
|
@@ -153,8 +154,11 @@ export default class VelociousConfiguration {
|
|
|
153
154
|
|
|
154
155
|
async withConnections(callback) {
|
|
155
156
|
const dbs = {}
|
|
157
|
+
const stack = Error().stack
|
|
156
158
|
const actualCallback = async () => {
|
|
157
|
-
|
|
159
|
+
await withTrackedStack(stack, async () => {
|
|
160
|
+
return await callback(dbs)
|
|
161
|
+
})
|
|
158
162
|
}
|
|
159
163
|
|
|
160
164
|
let runRequest = actualCallback
|
package/src/controller.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {digs} from "diggerize"
|
|
2
2
|
import ejs from "ejs"
|
|
3
|
+
import {incorporate} from "incorporator"
|
|
3
4
|
import * as inflection from "inflection"
|
|
4
5
|
import {Logger} from "./logger.js"
|
|
5
6
|
import restArgsError from "./utils/rest-args-error.js"
|
|
@@ -31,6 +32,18 @@ export default class VelociousController {
|
|
|
31
32
|
this._viewPath = viewPath
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
getAction() {
|
|
36
|
+
return this._action
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getParams() {
|
|
40
|
+
return this._params
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getRequest() {
|
|
44
|
+
return this._request
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
async _runBeforeCallbacks() {
|
|
35
48
|
await this.logger.debug("_runBeforeCallbacks")
|
|
36
49
|
|
|
@@ -88,8 +101,9 @@ export default class VelociousController {
|
|
|
88
101
|
return new Promise((resolve, reject) => {
|
|
89
102
|
const viewPath = `${this._viewPath}/${inflection.dasherize(inflection.underscore(this._action))}.ejs`
|
|
90
103
|
const {viewParams} = digs(this, "viewParams")
|
|
104
|
+
const actualViewParams = incorporate({controller: this}, viewParams)
|
|
91
105
|
|
|
92
|
-
ejs.renderFile(viewPath,
|
|
106
|
+
ejs.renderFile(viewPath, actualViewParams, {}, (err, str) => {
|
|
93
107
|
if (err) {
|
|
94
108
|
reject(err)
|
|
95
109
|
} else {
|
|
@@ -10,6 +10,24 @@ import Query from "../query/index.js"
|
|
|
10
10
|
import ValidatorsPresence from "./validators/presence.js"
|
|
11
11
|
import ValidatorsUniqueness from "./validators/uniqueness.js"
|
|
12
12
|
|
|
13
|
+
class ValidationError extends Error {
|
|
14
|
+
getModel() {
|
|
15
|
+
return this._model
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
setModel(model) {
|
|
19
|
+
this._model = model
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
getValidationErrors() {
|
|
23
|
+
return this._validationErrors
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
setValidationErrors(validationErrors) {
|
|
27
|
+
this._validationErrors = validationErrors
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
class VelociousDatabaseRecord {
|
|
14
32
|
static validatorTypes() {
|
|
15
33
|
if (!this._validatorTypes) this._validatorTypes = {}
|
|
@@ -883,7 +901,12 @@ class VelociousDatabaseRecord {
|
|
|
883
901
|
}
|
|
884
902
|
|
|
885
903
|
if (Object.keys(this._validationErrors).length > 0) {
|
|
886
|
-
|
|
904
|
+
const validationError = new ValidationError(this.fullErrorMessages().join(". "))
|
|
905
|
+
|
|
906
|
+
validationError.setValidationErrors(this._validationErrors)
|
|
907
|
+
validationError.setModel(this)
|
|
908
|
+
|
|
909
|
+
throw validationError
|
|
887
910
|
}
|
|
888
911
|
}
|
|
889
912
|
|
|
@@ -913,4 +936,5 @@ class VelociousDatabaseRecord {
|
|
|
913
936
|
VelociousDatabaseRecord.registerValidatorType("presence", ValidatorsPresence)
|
|
914
937
|
VelociousDatabaseRecord.registerValidatorType("uniqueness", ValidatorsUniqueness)
|
|
915
938
|
|
|
939
|
+
export {ValidationError}
|
|
916
940
|
export default VelociousDatabaseRecord
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {digg} from "diggerize"
|
|
2
2
|
import {EventEmitter} from "events"
|
|
3
|
-
import
|
|
3
|
+
import {incorporate} from "incorporator"
|
|
4
4
|
import ParamsToObject from "./params-to-object.js"
|
|
5
5
|
import RequestBuffer from "./request-buffer/index.js"
|
|
6
6
|
|
|
@@ -33,9 +33,8 @@ export default class VelociousHttpServerClientRequestParser {
|
|
|
33
33
|
|
|
34
34
|
const paramsToObject = new ParamsToObject(unorderedParams)
|
|
35
35
|
const newParams = paramsToObject.toObject()
|
|
36
|
-
const incorporator = new Incorporator({objects: [this.params, newParams]})
|
|
37
36
|
|
|
38
|
-
|
|
37
|
+
incorporate(this.params, newParams)
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
feed = (data) => this.requestBuffer.feed(data)
|
|
@@ -82,9 +81,7 @@ export default class VelociousHttpServerClientRequestParser {
|
|
|
82
81
|
getProtocol = () => this._getHostMatch()?.protocol
|
|
83
82
|
|
|
84
83
|
requestDone = () => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
incorporator.merge()
|
|
84
|
+
incorporate(this.params, this.requestBuffer.params)
|
|
88
85
|
|
|
89
86
|
this.state = "done"
|
|
90
87
|
this.events.emit("done")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
Path not found: <%= controller.getRequest().path() %>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import {addTrackedStackToError} from "../utils/with-tracked-stack.js"
|
|
1
2
|
import Application from "../../src/application.js"
|
|
2
3
|
import RequestClient from "./request-client.js"
|
|
3
4
|
import {tests} from "./test.js"
|
|
@@ -39,7 +40,7 @@ export default class TestRunner {
|
|
|
39
40
|
|
|
40
41
|
async importTestFiles() {
|
|
41
42
|
for (const testFile of this.testFiles) {
|
|
42
|
-
|
|
43
|
+
await import(testFile)
|
|
43
44
|
}
|
|
44
45
|
}
|
|
45
46
|
|
|
@@ -50,18 +51,39 @@ export default class TestRunner {
|
|
|
50
51
|
async run() {
|
|
51
52
|
this.failedTests = 0
|
|
52
53
|
this.successfulTests = 0
|
|
53
|
-
|
|
54
54
|
await this.importTestFiles()
|
|
55
|
+
this.onlyFocussed = this.areAnyTestsFocussed(tests)
|
|
55
56
|
await this.runTests(tests, [], 0)
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
areAnyTestsFocussed(tests) {
|
|
60
|
+
for (const testDescription in tests.tests) {
|
|
61
|
+
const testData = tests.tests[testDescription]
|
|
62
|
+
const testArgs = Object.assign({}, testData.args)
|
|
63
|
+
|
|
64
|
+
if (testArgs.focus) {
|
|
65
|
+
return true
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
for (const subDescription in tests.subs) {
|
|
70
|
+
const subTest = tests.subs[subDescription]
|
|
71
|
+
const result = this.areAnyTestsFocussed(subTest)
|
|
72
|
+
|
|
73
|
+
if (result) return true
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return false
|
|
77
|
+
}
|
|
78
|
+
|
|
58
79
|
async runTests(tests, descriptions, indentLevel) {
|
|
59
80
|
const leftPadding = " ".repeat(indentLevel * 2)
|
|
60
81
|
|
|
61
82
|
for (const testDescription in tests.tests) {
|
|
62
83
|
const testData = tests.tests[testDescription]
|
|
63
84
|
const testArgs = Object.assign({}, testData.args)
|
|
64
|
-
|
|
85
|
+
|
|
86
|
+
if (this.onlyFocussed && !testArgs.focus) continue
|
|
65
87
|
|
|
66
88
|
if (testArgs.type == "request") {
|
|
67
89
|
testArgs.application = await this.application()
|
|
@@ -77,7 +99,7 @@ export default class TestRunner {
|
|
|
77
99
|
this.failedTests++
|
|
78
100
|
|
|
79
101
|
// console.error(`${leftPadding} Test failed: ${error.message}`)
|
|
80
|
-
console.error(error
|
|
102
|
+
console.error(addTrackedStackToError(error))
|
|
81
103
|
}
|
|
82
104
|
}
|
|
83
105
|
|
|
@@ -86,9 +108,10 @@ export default class TestRunner {
|
|
|
86
108
|
const subTest = tests.subs[subDescription]
|
|
87
109
|
const newDecriptions = descriptions.concat([subDescription])
|
|
88
110
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
111
|
+
if (!this.onlyFocussed || this.areAnyTestsFocussed(subTest)) {
|
|
112
|
+
console.log(`${leftPadding}${subDescription}`)
|
|
113
|
+
await this.runTests(subTest, newDecriptions, indentLevel + 1)
|
|
114
|
+
}
|
|
92
115
|
}
|
|
93
116
|
})
|
|
94
117
|
}
|
package/src/testing/test.js
CHANGED
|
@@ -153,4 +153,20 @@ function it(description, arg1, arg2) {
|
|
|
153
153
|
currentTest.tests[description] = {args: newTestArgs, function: testFunction}
|
|
154
154
|
}
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
function fit(description, arg1, arg2) {
|
|
157
|
+
let testArgs, testFunction
|
|
158
|
+
|
|
159
|
+
if (typeof arg1 == "function") {
|
|
160
|
+
testFunction = arg1
|
|
161
|
+
testArgs = {focus: true}
|
|
162
|
+
} else if (typeof arg2 == "function") {
|
|
163
|
+
testFunction = arg2
|
|
164
|
+
testArgs = Object.assign({focus: true}, arg1)
|
|
165
|
+
} else {
|
|
166
|
+
throw new Error(`Invalid arguments for it: ${description}, ${arg1}`)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return it(description, testArgs, testFunction)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export {describe, expect, fit, it, tests}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {AsyncLocalStorage} from "async_hooks"
|
|
2
|
+
|
|
3
|
+
let asyncLocalStorage
|
|
4
|
+
|
|
5
|
+
if (AsyncLocalStorage) {
|
|
6
|
+
asyncLocalStorage = new AsyncLocalStorage()
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function addTrackedStackToError(error) {
|
|
10
|
+
// Not supported
|
|
11
|
+
if (!asyncLocalStorage) return
|
|
12
|
+
|
|
13
|
+
const parentStacks = asyncLocalStorage.getStore() || []
|
|
14
|
+
const additionalStackLines = []
|
|
15
|
+
|
|
16
|
+
for (const parentStack of parentStacks) {
|
|
17
|
+
for (const parentStackLine of parentStack) {
|
|
18
|
+
additionalStackLines.push(parentStackLine)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Replace the error message on the first line with this string
|
|
23
|
+
error.stack += "\n" + additionalStackLines.join("\n")
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function withTrackedStack(arg1, arg2) {
|
|
27
|
+
let callback, stack
|
|
28
|
+
|
|
29
|
+
if (arg2) {
|
|
30
|
+
callback = arg2
|
|
31
|
+
stack = arg1
|
|
32
|
+
} else {
|
|
33
|
+
callback = arg1
|
|
34
|
+
stack = Error().stack
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Not supported
|
|
38
|
+
if (!asyncLocalStorage) return await callback()
|
|
39
|
+
|
|
40
|
+
const parentStacks = asyncLocalStorage.getStore() || []
|
|
41
|
+
const additionalStackLines = [" [WITH TRACKED STACK]"]
|
|
42
|
+
const currentStackLines = stack.split("\n")
|
|
43
|
+
|
|
44
|
+
for (let i = currentStackLines.length; i >= 0; i--) {
|
|
45
|
+
const stackLine = currentStackLines[i]
|
|
46
|
+
|
|
47
|
+
if (stackLine == " [WITH TRACKED STACK]") {
|
|
48
|
+
break
|
|
49
|
+
} else {
|
|
50
|
+
additionalStackLines.unshift(stackLine)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const newStacks = [additionalStackLines, ...parentStacks]
|
|
55
|
+
|
|
56
|
+
await asyncLocalStorage.run(newStacks, async () => {
|
|
57
|
+
return await callback()
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export {addTrackedStackToError, withTrackedStack}
|