@ditojs/server 3.0.0 → 3.1.1
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 +9 -7
- package/src/models/Model.test.js +0 -180
- package/src/schema/relations.test.js +0 -177
- package/src/schema/schema.test.js +0 -720
- package/src/storage/Storage.test.js +0 -58
- package/src/utils/function.test.js +0 -77
- package/src/utils/object.test.js +0 -86
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/main/packages/server",
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
"types": "./types/index.d.ts",
|
|
11
11
|
"files": [
|
|
12
12
|
"src/",
|
|
13
|
-
"types/"
|
|
13
|
+
"types/",
|
|
14
|
+
"!src/**/*.test.js",
|
|
15
|
+
"!src/**/__snapshots__/"
|
|
14
16
|
],
|
|
15
17
|
"bin": {
|
|
16
18
|
"dito": "./src/cli/index.js"
|
|
@@ -22,10 +24,10 @@
|
|
|
22
24
|
"node >= 18"
|
|
23
25
|
],
|
|
24
26
|
"dependencies": {
|
|
25
|
-
"@ditojs/admin": "^3.
|
|
26
|
-
"@ditojs/build": "^3.
|
|
27
|
-
"@ditojs/router": "^3.
|
|
28
|
-
"@ditojs/utils": "^3.
|
|
27
|
+
"@ditojs/admin": "^3.1.1",
|
|
28
|
+
"@ditojs/build": "^3.1.1",
|
|
29
|
+
"@ditojs/router": "^3.1.1",
|
|
30
|
+
"@ditojs/utils": "^3.1.1",
|
|
29
31
|
"@koa/cors": "^5.0.0",
|
|
30
32
|
"@koa/etag": "^5.0.2",
|
|
31
33
|
"@koa/multer": "^4.0.0",
|
|
@@ -87,5 +89,5 @@
|
|
|
87
89
|
"objection": "^3.1.5",
|
|
88
90
|
"typescript": "^6.0.3"
|
|
89
91
|
},
|
|
90
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "acce85bb38d817aab18f9d5d03b3aa9dff00c3f9"
|
|
91
93
|
}
|
package/src/models/Model.test.js
DELETED
|
@@ -1,180 +0,0 @@
|
|
|
1
|
-
import { Model } from './Model.js'
|
|
2
|
-
|
|
3
|
-
function makeFakeModel(assets) {
|
|
4
|
-
const storage = {
|
|
5
|
-
signAssetFile(file) {
|
|
6
|
-
file.signature = `sig-of-${file.key}`
|
|
7
|
-
}
|
|
8
|
-
}
|
|
9
|
-
return {
|
|
10
|
-
app: {
|
|
11
|
-
getStorage(name) {
|
|
12
|
-
return name === 'test' ? storage : null
|
|
13
|
-
}
|
|
14
|
-
},
|
|
15
|
-
definition: { assets }
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function signCallback(file, storage) {
|
|
20
|
-
const signed = { ...file }
|
|
21
|
-
storage.signAssetFile(signed)
|
|
22
|
-
return signed
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
describe('Model._mapAssetFiles: asset signing across data paths', () => {
|
|
26
|
-
it('writes the signature back to a literal data path', () => {
|
|
27
|
-
const model = makeFakeModel({ 'image.file': { storage: 'test' } })
|
|
28
|
-
const json = { image: { file: { key: 'foo.png' } } }
|
|
29
|
-
Model._mapAssetFiles.call(model, json, signCallback)
|
|
30
|
-
expect(json.image.file.signature).toBe('sig-of-foo.png')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('writes the signature back to every match of a deep wildcard', () => {
|
|
34
|
-
const model = makeFakeModel({ '**.file': { storage: 'test' } })
|
|
35
|
-
const json = {
|
|
36
|
-
content: {
|
|
37
|
-
file: { key: 'a.png' },
|
|
38
|
-
sections: [
|
|
39
|
-
{ file: { key: 'b.png' } },
|
|
40
|
-
{ file: { key: 'c.png' } }
|
|
41
|
-
]
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
Model._mapAssetFiles.call(model, json, signCallback)
|
|
45
|
-
expect(json.content.file.signature).toBe('sig-of-a.png')
|
|
46
|
-
expect(json.content.sections[0].file.signature).toBe('sig-of-b.png')
|
|
47
|
-
expect(json.content.sections[1].file.signature).toBe('sig-of-c.png')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('writes the signature back into wildcard array values', () => {
|
|
51
|
-
const model = makeFakeModel({ '**.files': { storage: 'test' } })
|
|
52
|
-
const json = {
|
|
53
|
-
content: {
|
|
54
|
-
files: [{ key: 'x.png' }, { key: 'y.png' }]
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
Model._mapAssetFiles.call(model, json, signCallback)
|
|
58
|
-
expect(json.content.files[0].signature).toBe('sig-of-x.png')
|
|
59
|
-
expect(json.content.files[1].signature).toBe('sig-of-y.png')
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
it('is a no-op when the wildcard matches nothing', () => {
|
|
63
|
-
const model = makeFakeModel({ '**.file': { storage: 'test' } })
|
|
64
|
-
const json = { name: 'no-files-here' }
|
|
65
|
-
Model._mapAssetFiles.call(model, json, signCallback)
|
|
66
|
-
expect(json).toEqual({ name: 'no-files-here' })
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
describe('Model.getProperty: type inference for $ref/discriminator schemas', () => {
|
|
71
|
-
function getProperty({ name, properties, definitions = {} }) {
|
|
72
|
-
return Model.getProperty.call(
|
|
73
|
-
{ definition: { properties }, jsonSchema: { definitions } },
|
|
74
|
-
name
|
|
75
|
-
)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
it('returns the property unchanged when it has an explicit type', () => {
|
|
79
|
-
expect(
|
|
80
|
-
getProperty({
|
|
81
|
-
name: 'name',
|
|
82
|
-
properties: { name: { type: 'string' } }
|
|
83
|
-
})
|
|
84
|
-
).toEqual({ type: 'string' })
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('infers type: object from a discriminator on a $ref schema', () => {
|
|
88
|
-
const property = getProperty({
|
|
89
|
-
name: 'content',
|
|
90
|
-
properties: { content: { $ref: '#StoryContent' } },
|
|
91
|
-
definitions: {
|
|
92
|
-
'#StoryContent': {
|
|
93
|
-
discriminator: { propertyName: 'type' },
|
|
94
|
-
oneOf: [
|
|
95
|
-
{ type: 'object', properties: { type: { const: 'a' } } },
|
|
96
|
-
{ type: 'object', properties: { type: { const: 'b' } } }
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
})
|
|
101
|
-
expect(property.type).toBe('object')
|
|
102
|
-
expect(property.discriminator).toEqual({ propertyName: 'type' })
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it('infers a shared type from a homogeneous oneOf', () => {
|
|
106
|
-
const property = getProperty({
|
|
107
|
-
name: 'content',
|
|
108
|
-
properties: {
|
|
109
|
-
content: { oneOf: [{ type: 'object' }, { type: 'object' }] }
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
expect(property.type).toBe('object')
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('infers a shared type from a homogeneous anyOf', () => {
|
|
116
|
-
const property = getProperty({
|
|
117
|
-
name: 'content',
|
|
118
|
-
properties: {
|
|
119
|
-
content: { anyOf: [{ type: 'array' }, { type: 'array' }] }
|
|
120
|
-
}
|
|
121
|
-
})
|
|
122
|
-
expect(property.type).toBe('array')
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it('leaves type undefined when oneOf branches disagree', () => {
|
|
126
|
-
const property = getProperty({
|
|
127
|
-
name: 'content',
|
|
128
|
-
properties: {
|
|
129
|
-
content: { oneOf: [{ type: 'object' }, { type: 'string' }] }
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
expect(property.type).toBeUndefined()
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('returns null for an unknown property', () => {
|
|
136
|
-
expect(getProperty({ name: 'missing', properties: {} })).toBeNull()
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
describe('Model.getPropertyOrRelationAtDataPath: traversal through inferred types', () => {
|
|
141
|
-
function lookup({ dataPath, properties, definitions = {} }) {
|
|
142
|
-
const host = {
|
|
143
|
-
definition: { properties },
|
|
144
|
-
jsonSchema: { definitions },
|
|
145
|
-
getRelations: () => ({}),
|
|
146
|
-
getProperty(name) {
|
|
147
|
-
return Model.getProperty.call(this, name)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
return Model.getPropertyOrRelationAtDataPath.call(host, dataPath)
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
it('exposes object type for a discriminator-rooted nested wildcard path', () => {
|
|
154
|
-
// Reproduces the original loadDataPath() crash on
|
|
155
|
-
// `content.article.items[**].file`: before the fix, `content` had
|
|
156
|
-
// no resolved `type`, so loadDataPath rejected the nested path.
|
|
157
|
-
const result = lookup({
|
|
158
|
-
dataPath: 'content.article.items[**].file',
|
|
159
|
-
properties: { content: { $ref: '#StoryContent' } },
|
|
160
|
-
definitions: {
|
|
161
|
-
'#StoryContent': {
|
|
162
|
-
discriminator: { propertyName: 'type' },
|
|
163
|
-
oneOf: [{ type: 'object' }]
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
expect(result.property.type).toBe('object')
|
|
168
|
-
expect(result.nestedDataPath).toBe('article/items/**/file')
|
|
169
|
-
expect(result.dataPath).toBe('content')
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
it('returns no nestedDataPath for a leaf property lookup', () => {
|
|
173
|
-
const result = lookup({
|
|
174
|
-
dataPath: 'name',
|
|
175
|
-
properties: { name: { type: 'string' } }
|
|
176
|
-
})
|
|
177
|
-
expect(result.property).toEqual({ type: 'string' })
|
|
178
|
-
expect(result.nestedDataPath).toBe('')
|
|
179
|
-
})
|
|
180
|
-
})
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BelongsToOneRelation,
|
|
3
|
-
HasOneRelation,
|
|
4
|
-
HasOneThroughRelation,
|
|
5
|
-
HasManyRelation,
|
|
6
|
-
ManyToManyRelation
|
|
7
|
-
} from 'objection'
|
|
8
|
-
import { Model } from '../models/index.js'
|
|
9
|
-
import {
|
|
10
|
-
getRelationClass,
|
|
11
|
-
convertRelation,
|
|
12
|
-
addRelationSchemas
|
|
13
|
-
} from './relations.js'
|
|
14
|
-
|
|
15
|
-
describe('getRelationClass()', () => {
|
|
16
|
-
it('returns the corresponding relation classes', () => {
|
|
17
|
-
expect(getRelationClass('belongs-to')).toBe(BelongsToOneRelation)
|
|
18
|
-
expect(getRelationClass('has-one')).toBe(HasOneRelation)
|
|
19
|
-
expect(getRelationClass('has-one-through')).toBe(HasOneThroughRelation)
|
|
20
|
-
expect(getRelationClass('has-many')).toBe(HasManyRelation)
|
|
21
|
-
expect(getRelationClass('many-to-many')).toBe(ManyToManyRelation)
|
|
22
|
-
|
|
23
|
-
expect(getRelationClass('belongsTo')).toBe(BelongsToOneRelation)
|
|
24
|
-
expect(getRelationClass('hasOne')).toBe(HasOneRelation)
|
|
25
|
-
expect(getRelationClass('hasOneThrough')).toBe(HasOneThroughRelation)
|
|
26
|
-
expect(getRelationClass('hasMany')).toBe(HasManyRelation)
|
|
27
|
-
expect(getRelationClass('manyToMany')).toBe(ManyToManyRelation)
|
|
28
|
-
|
|
29
|
-
expect(getRelationClass('BelongsToOneRelation')).toBe(BelongsToOneRelation)
|
|
30
|
-
expect(getRelationClass('HasOneRelation')).toBe(HasOneRelation)
|
|
31
|
-
expect(getRelationClass('HasOneThroughRelation'))
|
|
32
|
-
.toBe(HasOneThroughRelation)
|
|
33
|
-
expect(getRelationClass('HasManyRelation')).toBe(HasManyRelation)
|
|
34
|
-
expect(getRelationClass('ManyToManyRelation')).toBe(ManyToManyRelation)
|
|
35
|
-
})
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
describe('convertRelation()', () => {
|
|
39
|
-
class ModelOne extends Model {}
|
|
40
|
-
class ModelTwo extends Model {}
|
|
41
|
-
|
|
42
|
-
const models = { ModelOne, ModelTwo }
|
|
43
|
-
|
|
44
|
-
it('converts a belongs-to relation to Objection.js format', () => {
|
|
45
|
-
expect(
|
|
46
|
-
convertRelation(
|
|
47
|
-
{
|
|
48
|
-
relation: 'belongs-to',
|
|
49
|
-
from: 'ModelOne.modelTwoId',
|
|
50
|
-
to: 'ModelTwo.id',
|
|
51
|
-
modify: 'myScope'
|
|
52
|
-
},
|
|
53
|
-
models
|
|
54
|
-
)
|
|
55
|
-
).toEqual({
|
|
56
|
-
relation: BelongsToOneRelation,
|
|
57
|
-
modelClass: ModelTwo,
|
|
58
|
-
join: {
|
|
59
|
-
from: 'ModelOne.modelTwoId',
|
|
60
|
-
to: 'ModelTwo.id'
|
|
61
|
-
},
|
|
62
|
-
modify: 'myScope'
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('converts a many-to-many relation to Objection.js format', () => {
|
|
67
|
-
expect(
|
|
68
|
-
convertRelation(
|
|
69
|
-
{
|
|
70
|
-
relation: 'many-to-many',
|
|
71
|
-
from: 'ModelOne.id',
|
|
72
|
-
to: 'ModelTwo.id',
|
|
73
|
-
inverse: false
|
|
74
|
-
},
|
|
75
|
-
models
|
|
76
|
-
)
|
|
77
|
-
).toEqual({
|
|
78
|
-
relation: ManyToManyRelation,
|
|
79
|
-
modelClass: ModelTwo,
|
|
80
|
-
join: {
|
|
81
|
-
from: 'ModelOne.id',
|
|
82
|
-
through: {
|
|
83
|
-
from: 'ModelOneModelTwo.modelOneId',
|
|
84
|
-
to: 'ModelOneModelTwo.modelTwoId'
|
|
85
|
-
},
|
|
86
|
-
to: 'ModelTwo.id'
|
|
87
|
-
}
|
|
88
|
-
})
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
it('converts an inverse many-to-many relation to Objection.js format', () => {
|
|
92
|
-
expect(
|
|
93
|
-
convertRelation(
|
|
94
|
-
{
|
|
95
|
-
relation: 'many-to-many',
|
|
96
|
-
from: 'ModelTwo.id',
|
|
97
|
-
to: 'ModelOne.id',
|
|
98
|
-
inverse: true
|
|
99
|
-
},
|
|
100
|
-
models
|
|
101
|
-
)
|
|
102
|
-
).toEqual({
|
|
103
|
-
relation: ManyToManyRelation,
|
|
104
|
-
modelClass: ModelOne,
|
|
105
|
-
join: {
|
|
106
|
-
from: 'ModelTwo.id',
|
|
107
|
-
through: {
|
|
108
|
-
from: 'ModelOneModelTwo.modelTwoId',
|
|
109
|
-
to: 'ModelOneModelTwo.modelOneId'
|
|
110
|
-
},
|
|
111
|
-
to: 'ModelOne.id'
|
|
112
|
-
}
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('preserves Objection.js relation format', () => {
|
|
117
|
-
const relation = {
|
|
118
|
-
relation: BelongsToOneRelation,
|
|
119
|
-
modelClass: ModelTwo,
|
|
120
|
-
join: {
|
|
121
|
-
from: 'ModelOne.modelTwoId',
|
|
122
|
-
to: 'ModelTwo.id'
|
|
123
|
-
},
|
|
124
|
-
modify: 'myScope'
|
|
125
|
-
}
|
|
126
|
-
expect(convertRelation(relation, models)).toEqual(relation)
|
|
127
|
-
})
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
describe('addRelationSchemas()', () => {
|
|
131
|
-
class ModelOne extends Model {
|
|
132
|
-
static relations = {
|
|
133
|
-
modelTwo: {
|
|
134
|
-
relation: 'belongs-to',
|
|
135
|
-
from: 'ModelOne.modelTwoId',
|
|
136
|
-
to: 'ModelTwo.id'
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
class ModelTwo extends Model {
|
|
142
|
-
static relations = {
|
|
143
|
-
modelOnes: {
|
|
144
|
-
relation: 'has-many',
|
|
145
|
-
from: 'ModelTwo.id',
|
|
146
|
-
to: 'ModelOne.modelTwoId',
|
|
147
|
-
owner: true
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Add a mock app to both, to expose models:
|
|
153
|
-
ModelOne.app = ModelTwo.app = {
|
|
154
|
-
models: { ModelOne, ModelTwo }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
it('adds correct property schema for a belongs-to relation', () => {
|
|
158
|
-
expect(addRelationSchemas(ModelOne, {})).toEqual({
|
|
159
|
-
modelTwo: {
|
|
160
|
-
anyOf: [
|
|
161
|
-
{ type: 'null' },
|
|
162
|
-
{ relate: 'ModelTwo' },
|
|
163
|
-
{ $ref: 'ModelTwo' }
|
|
164
|
-
]
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
it('adds correct property schema for a has-many owner relation', () => {
|
|
170
|
-
expect(addRelationSchemas(ModelTwo, {})).toEqual({
|
|
171
|
-
modelOnes: {
|
|
172
|
-
type: 'array',
|
|
173
|
-
items: { $ref: 'ModelOne' }
|
|
174
|
-
}
|
|
175
|
-
})
|
|
176
|
-
})
|
|
177
|
-
})
|
|
@@ -1,720 +0,0 @@
|
|
|
1
|
-
import { convertSchema } from './schema.js'
|
|
2
|
-
|
|
3
|
-
describe('convertSchema()', () => {
|
|
4
|
-
it('expands objects with properties to full JSON schemas', () => {
|
|
5
|
-
const properties = {
|
|
6
|
-
myString: {
|
|
7
|
-
type: 'string'
|
|
8
|
-
},
|
|
9
|
-
myNumber: {
|
|
10
|
-
type: 'number'
|
|
11
|
-
},
|
|
12
|
-
myInteger: {
|
|
13
|
-
type: 'integer'
|
|
14
|
-
},
|
|
15
|
-
myBoolean: {
|
|
16
|
-
type: 'boolean'
|
|
17
|
-
},
|
|
18
|
-
myObject: {
|
|
19
|
-
type: 'object'
|
|
20
|
-
},
|
|
21
|
-
myArray: {
|
|
22
|
-
type: 'array'
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
expect(
|
|
26
|
-
convertSchema({
|
|
27
|
-
type: 'object',
|
|
28
|
-
properties
|
|
29
|
-
})
|
|
30
|
-
).toEqual({
|
|
31
|
-
type: 'object',
|
|
32
|
-
properties,
|
|
33
|
-
unevaluatedProperties: false
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('supports falsy default values', () => {
|
|
38
|
-
const properties = {
|
|
39
|
-
myString: {
|
|
40
|
-
type: 'string',
|
|
41
|
-
default: ''
|
|
42
|
-
},
|
|
43
|
-
myNumber: {
|
|
44
|
-
type: 'number',
|
|
45
|
-
default: 0
|
|
46
|
-
},
|
|
47
|
-
myInteger: {
|
|
48
|
-
type: 'integer',
|
|
49
|
-
default: 0
|
|
50
|
-
},
|
|
51
|
-
myBoolean: {
|
|
52
|
-
type: 'boolean',
|
|
53
|
-
default: false
|
|
54
|
-
},
|
|
55
|
-
myObject: {
|
|
56
|
-
type: 'object',
|
|
57
|
-
default: null
|
|
58
|
-
},
|
|
59
|
-
myArray: {
|
|
60
|
-
type: 'array',
|
|
61
|
-
default: null
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
expect(
|
|
65
|
-
convertSchema({
|
|
66
|
-
type: 'object',
|
|
67
|
-
properties
|
|
68
|
-
})
|
|
69
|
-
).toEqual({
|
|
70
|
-
type: 'object',
|
|
71
|
-
properties,
|
|
72
|
-
unevaluatedProperties: false
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
it(`expands 'text' types to 'string' JSON schema types`, () => {
|
|
77
|
-
expect(
|
|
78
|
-
convertSchema({
|
|
79
|
-
type: 'object',
|
|
80
|
-
properties: {
|
|
81
|
-
myText: {
|
|
82
|
-
type: 'text'
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
})
|
|
86
|
-
).toEqual({
|
|
87
|
-
type: 'object',
|
|
88
|
-
properties: {
|
|
89
|
-
myText: {
|
|
90
|
-
type: 'string'
|
|
91
|
-
}
|
|
92
|
-
},
|
|
93
|
-
unevaluatedProperties: false
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
it('adds `required` arrays and formats for required properties', () => {
|
|
98
|
-
expect(
|
|
99
|
-
convertSchema({
|
|
100
|
-
type: 'object',
|
|
101
|
-
properties: {
|
|
102
|
-
myString: {
|
|
103
|
-
type: 'string',
|
|
104
|
-
required: true
|
|
105
|
-
},
|
|
106
|
-
myNumber: {
|
|
107
|
-
type: 'number',
|
|
108
|
-
required: true
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
).toEqual({
|
|
113
|
-
type: 'object',
|
|
114
|
-
properties: {
|
|
115
|
-
myString: {
|
|
116
|
-
type: 'string',
|
|
117
|
-
format: 'required'
|
|
118
|
-
},
|
|
119
|
-
myNumber: {
|
|
120
|
-
type: 'number',
|
|
121
|
-
format: 'required'
|
|
122
|
-
}
|
|
123
|
-
},
|
|
124
|
-
unevaluatedProperties: false,
|
|
125
|
-
required: ['myString', 'myNumber']
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
it('preserves JSON schema-style `required` arrays', () => {
|
|
130
|
-
expect(
|
|
131
|
-
convertSchema({
|
|
132
|
-
type: 'object',
|
|
133
|
-
required: ['myString', 'myNumber'],
|
|
134
|
-
properties: {
|
|
135
|
-
myString: { type: 'string' },
|
|
136
|
-
myNumber: { type: 'number' }
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
).toEqual({
|
|
140
|
-
type: 'object',
|
|
141
|
-
properties: {
|
|
142
|
-
myString: { type: 'string' },
|
|
143
|
-
myNumber: { type: 'number' }
|
|
144
|
-
},
|
|
145
|
-
unevaluatedProperties: false,
|
|
146
|
-
required: ['myString', 'myNumber']
|
|
147
|
-
})
|
|
148
|
-
})
|
|
149
|
-
|
|
150
|
-
it(`expands 'object' schemas with properties to JSON schemas allowing no unevaluated properties`, () => {
|
|
151
|
-
expect(
|
|
152
|
-
convertSchema({
|
|
153
|
-
type: 'object',
|
|
154
|
-
properties: {
|
|
155
|
-
myText: {
|
|
156
|
-
type: 'object',
|
|
157
|
-
properties: {}
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
})
|
|
161
|
-
).toEqual({
|
|
162
|
-
type: 'object',
|
|
163
|
-
properties: {
|
|
164
|
-
myText: {
|
|
165
|
-
type: 'object',
|
|
166
|
-
unevaluatedProperties: false,
|
|
167
|
-
properties: {}
|
|
168
|
-
}
|
|
169
|
-
},
|
|
170
|
-
unevaluatedProperties: false
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('preserves pre-existing settings for no unevaluated properties', () => {
|
|
175
|
-
expect(
|
|
176
|
-
convertSchema({
|
|
177
|
-
type: 'object',
|
|
178
|
-
properties: {
|
|
179
|
-
myText: {
|
|
180
|
-
type: 'object',
|
|
181
|
-
unevaluatedProperties: true,
|
|
182
|
-
properties: {}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
})
|
|
186
|
-
).toEqual({
|
|
187
|
-
type: 'object',
|
|
188
|
-
properties: {
|
|
189
|
-
myText: {
|
|
190
|
-
type: 'object',
|
|
191
|
-
unevaluatedProperties: true,
|
|
192
|
-
properties: {}
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
unevaluatedProperties: false
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
it('expands nested object schemas with required properties', () => {
|
|
200
|
-
expect(
|
|
201
|
-
convertSchema({
|
|
202
|
-
type: 'object',
|
|
203
|
-
properties: {
|
|
204
|
-
myText: {
|
|
205
|
-
type: 'object',
|
|
206
|
-
properties: {
|
|
207
|
-
myProperty: {
|
|
208
|
-
type: 'text',
|
|
209
|
-
required: true
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
})
|
|
215
|
-
).toEqual({
|
|
216
|
-
type: 'object',
|
|
217
|
-
properties: {
|
|
218
|
-
myText: {
|
|
219
|
-
type: 'object',
|
|
220
|
-
properties: {
|
|
221
|
-
myProperty: {
|
|
222
|
-
type: 'string',
|
|
223
|
-
format: 'required'
|
|
224
|
-
}
|
|
225
|
-
},
|
|
226
|
-
unevaluatedProperties: false,
|
|
227
|
-
required: ['myProperty']
|
|
228
|
-
}
|
|
229
|
-
},
|
|
230
|
-
unevaluatedProperties: false
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
|
-
|
|
234
|
-
it(`expands 'object' schemas with patternProperties`, () => {
|
|
235
|
-
expect(
|
|
236
|
-
convertSchema({
|
|
237
|
-
type: 'object',
|
|
238
|
-
properties: {
|
|
239
|
-
myText: {
|
|
240
|
-
type: 'object',
|
|
241
|
-
patternProperties: {
|
|
242
|
-
'^.*$': {
|
|
243
|
-
type: 'text'
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
})
|
|
249
|
-
).toEqual({
|
|
250
|
-
type: 'object',
|
|
251
|
-
properties: {
|
|
252
|
-
myText: {
|
|
253
|
-
type: 'object',
|
|
254
|
-
patternProperties: {
|
|
255
|
-
'^.*$': {
|
|
256
|
-
type: 'string'
|
|
257
|
-
}
|
|
258
|
-
},
|
|
259
|
-
unevaluatedProperties: false
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
unevaluatedProperties: false
|
|
263
|
-
})
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
it('expands datetime types to their JSON schema representation', () => {
|
|
267
|
-
expect(
|
|
268
|
-
convertSchema({
|
|
269
|
-
type: 'object',
|
|
270
|
-
properties: {
|
|
271
|
-
myDate: {
|
|
272
|
-
type: 'date'
|
|
273
|
-
},
|
|
274
|
-
myDateTime: {
|
|
275
|
-
type: 'datetime'
|
|
276
|
-
},
|
|
277
|
-
myTimeStamp: {
|
|
278
|
-
type: 'timestamp'
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
})
|
|
282
|
-
).toEqual({
|
|
283
|
-
type: 'object',
|
|
284
|
-
properties: {
|
|
285
|
-
myDate: {
|
|
286
|
-
type: ['string', 'object'],
|
|
287
|
-
format: 'date-time'
|
|
288
|
-
},
|
|
289
|
-
myDateTime: {
|
|
290
|
-
type: ['string', 'object'],
|
|
291
|
-
format: 'date-time'
|
|
292
|
-
},
|
|
293
|
-
myTimeStamp: {
|
|
294
|
-
type: ['string', 'object'],
|
|
295
|
-
format: 'date-time'
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
unevaluatedProperties: false
|
|
299
|
-
})
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
it('expands unrecognized types to `$ref` references', () => {
|
|
303
|
-
expect(
|
|
304
|
-
convertSchema({
|
|
305
|
-
type: 'object',
|
|
306
|
-
properties: {
|
|
307
|
-
myModel: {
|
|
308
|
-
type: 'MyModel'
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
})
|
|
312
|
-
).toEqual({
|
|
313
|
-
type: 'object',
|
|
314
|
-
properties: {
|
|
315
|
-
myModel: {
|
|
316
|
-
$ref: 'MyModel'
|
|
317
|
-
}
|
|
318
|
-
},
|
|
319
|
-
unevaluatedProperties: false
|
|
320
|
-
})
|
|
321
|
-
})
|
|
322
|
-
|
|
323
|
-
it(`expands unrecognized types to \`instanceof\` keywords when the \`useInstanceOf\` option is provided`, () => {
|
|
324
|
-
expect(
|
|
325
|
-
convertSchema(
|
|
326
|
-
{
|
|
327
|
-
type: 'object',
|
|
328
|
-
properties: {
|
|
329
|
-
myModel: {
|
|
330
|
-
type: 'MyModel'
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
useInstanceOf: true
|
|
336
|
-
}
|
|
337
|
-
)
|
|
338
|
-
).toEqual({
|
|
339
|
-
type: 'object',
|
|
340
|
-
properties: {
|
|
341
|
-
myModel: {
|
|
342
|
-
type: 'object',
|
|
343
|
-
instanceof: 'MyModel'
|
|
344
|
-
}
|
|
345
|
-
},
|
|
346
|
-
unevaluatedProperties: false
|
|
347
|
-
})
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
it('handles `nullable: true` correctly (now natively supported)', () => {
|
|
351
|
-
expect(
|
|
352
|
-
convertSchema({
|
|
353
|
-
type: 'object',
|
|
354
|
-
properties: {
|
|
355
|
-
myString: {
|
|
356
|
-
type: 'string',
|
|
357
|
-
nullable: true
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
})
|
|
361
|
-
).toEqual({
|
|
362
|
-
type: 'object',
|
|
363
|
-
properties: {
|
|
364
|
-
myString: {
|
|
365
|
-
type: 'string',
|
|
366
|
-
nullable: true
|
|
367
|
-
}
|
|
368
|
-
},
|
|
369
|
-
unevaluatedProperties: false
|
|
370
|
-
})
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
it(`handles \`nullable: true\` references correctly`, () => {
|
|
374
|
-
expect(
|
|
375
|
-
convertSchema({
|
|
376
|
-
type: 'object',
|
|
377
|
-
properties: {
|
|
378
|
-
myModel: {
|
|
379
|
-
type: 'MyModel',
|
|
380
|
-
nullable: true
|
|
381
|
-
}
|
|
382
|
-
}
|
|
383
|
-
})
|
|
384
|
-
).toEqual({
|
|
385
|
-
type: 'object',
|
|
386
|
-
properties: {
|
|
387
|
-
myModel: {
|
|
388
|
-
oneOf: [
|
|
389
|
-
{ type: 'null' },
|
|
390
|
-
{ $ref: 'MyModel' }
|
|
391
|
-
]
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
unevaluatedProperties: false
|
|
395
|
-
})
|
|
396
|
-
})
|
|
397
|
-
|
|
398
|
-
it(`handles \`nullable: true\` dates correctly (now natively supported)`, () => {
|
|
399
|
-
expect(
|
|
400
|
-
convertSchema({
|
|
401
|
-
type: 'object',
|
|
402
|
-
properties: {
|
|
403
|
-
myDate: {
|
|
404
|
-
type: 'date',
|
|
405
|
-
nullable: true
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
})
|
|
409
|
-
).toEqual({
|
|
410
|
-
type: 'object',
|
|
411
|
-
properties: {
|
|
412
|
-
myDate: {
|
|
413
|
-
type: ['string', 'object'],
|
|
414
|
-
format: 'date-time',
|
|
415
|
-
nullable: true
|
|
416
|
-
}
|
|
417
|
-
},
|
|
418
|
-
unevaluatedProperties: false
|
|
419
|
-
})
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
it(`handles \`nullable: true\` enums correctly`, () => {
|
|
423
|
-
expect(
|
|
424
|
-
convertSchema({
|
|
425
|
-
type: 'object',
|
|
426
|
-
properties: {
|
|
427
|
-
myEnum: {
|
|
428
|
-
type: 'string',
|
|
429
|
-
enum: ['one', 'two', 'three'],
|
|
430
|
-
nullable: true
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
})
|
|
434
|
-
).toEqual({
|
|
435
|
-
type: 'object',
|
|
436
|
-
properties: {
|
|
437
|
-
myEnum: {
|
|
438
|
-
type: 'string',
|
|
439
|
-
enum: ['one', 'two', 'three', null],
|
|
440
|
-
nullable: true
|
|
441
|
-
}
|
|
442
|
-
},
|
|
443
|
-
unevaluatedProperties: false
|
|
444
|
-
})
|
|
445
|
-
})
|
|
446
|
-
|
|
447
|
-
it('converts schemas within oneOf properties', () => {
|
|
448
|
-
expect(
|
|
449
|
-
convertSchema({
|
|
450
|
-
type: 'object',
|
|
451
|
-
properties: {
|
|
452
|
-
myList: {
|
|
453
|
-
type: 'array',
|
|
454
|
-
items: {
|
|
455
|
-
oneOf: [
|
|
456
|
-
{
|
|
457
|
-
type: 'object',
|
|
458
|
-
properties: {
|
|
459
|
-
prop1: {
|
|
460
|
-
type: 'string',
|
|
461
|
-
required: true
|
|
462
|
-
},
|
|
463
|
-
prop2: {
|
|
464
|
-
type: 'number',
|
|
465
|
-
required: true
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
},
|
|
469
|
-
{
|
|
470
|
-
type: 'object',
|
|
471
|
-
properties: {
|
|
472
|
-
prop3: {
|
|
473
|
-
type: 'string',
|
|
474
|
-
required: true
|
|
475
|
-
},
|
|
476
|
-
prop4: {
|
|
477
|
-
type: 'number',
|
|
478
|
-
required: true
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
]
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
})
|
|
487
|
-
).toEqual({
|
|
488
|
-
type: 'object',
|
|
489
|
-
properties: {
|
|
490
|
-
myList: {
|
|
491
|
-
type: 'array',
|
|
492
|
-
items: {
|
|
493
|
-
oneOf: [
|
|
494
|
-
{
|
|
495
|
-
type: 'object',
|
|
496
|
-
properties: {
|
|
497
|
-
prop1: {
|
|
498
|
-
type: 'string',
|
|
499
|
-
format: 'required'
|
|
500
|
-
},
|
|
501
|
-
prop2: {
|
|
502
|
-
type: 'number',
|
|
503
|
-
format: 'required'
|
|
504
|
-
}
|
|
505
|
-
},
|
|
506
|
-
required: ['prop1', 'prop2'],
|
|
507
|
-
unevaluatedProperties: false
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
type: 'object',
|
|
511
|
-
properties: {
|
|
512
|
-
prop3: {
|
|
513
|
-
type: 'string',
|
|
514
|
-
format: 'required'
|
|
515
|
-
},
|
|
516
|
-
prop4: {
|
|
517
|
-
type: 'number',
|
|
518
|
-
format: 'required'
|
|
519
|
-
}
|
|
520
|
-
},
|
|
521
|
-
required: ['prop3', 'prop4'],
|
|
522
|
-
unevaluatedProperties: false
|
|
523
|
-
}
|
|
524
|
-
]
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
},
|
|
528
|
-
unevaluatedProperties: false
|
|
529
|
-
})
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
it('supports `required: true` on object', () => {
|
|
533
|
-
expect(
|
|
534
|
-
convertSchema({
|
|
535
|
-
type: 'object',
|
|
536
|
-
properties: {
|
|
537
|
-
myObject: {
|
|
538
|
-
type: 'object',
|
|
539
|
-
required: true,
|
|
540
|
-
properties: {
|
|
541
|
-
prop1: {
|
|
542
|
-
type: 'string',
|
|
543
|
-
required: true
|
|
544
|
-
},
|
|
545
|
-
prop2: {
|
|
546
|
-
type: 'number',
|
|
547
|
-
required: true
|
|
548
|
-
}
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
})
|
|
553
|
-
).toEqual({
|
|
554
|
-
type: 'object',
|
|
555
|
-
properties: {
|
|
556
|
-
myObject: {
|
|
557
|
-
type: 'object',
|
|
558
|
-
format: 'required',
|
|
559
|
-
properties: {
|
|
560
|
-
prop1: {
|
|
561
|
-
format: 'required',
|
|
562
|
-
type: 'string'
|
|
563
|
-
},
|
|
564
|
-
prop2: {
|
|
565
|
-
format: 'required',
|
|
566
|
-
type: 'number'
|
|
567
|
-
}
|
|
568
|
-
},
|
|
569
|
-
unevaluatedProperties: false,
|
|
570
|
-
required: ['prop1', 'prop2']
|
|
571
|
-
}
|
|
572
|
-
},
|
|
573
|
-
unevaluatedProperties: false,
|
|
574
|
-
required: ['myObject']
|
|
575
|
-
})
|
|
576
|
-
})
|
|
577
|
-
|
|
578
|
-
it('processes discriminator schemas correctly', () => {
|
|
579
|
-
expect(
|
|
580
|
-
convertSchema({
|
|
581
|
-
type: 'object',
|
|
582
|
-
discriminator: { propertyName: 'foo' },
|
|
583
|
-
required: ['foo'],
|
|
584
|
-
oneOf: [
|
|
585
|
-
{
|
|
586
|
-
properties: {
|
|
587
|
-
foo: { const: 'x' },
|
|
588
|
-
a: {
|
|
589
|
-
type: 'string',
|
|
590
|
-
required: true
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
},
|
|
594
|
-
{
|
|
595
|
-
properties: {
|
|
596
|
-
foo: { enum: ['y', 'z'] },
|
|
597
|
-
b: {
|
|
598
|
-
type: 'string',
|
|
599
|
-
required: true
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
]
|
|
604
|
-
})
|
|
605
|
-
).toEqual({
|
|
606
|
-
type: 'object',
|
|
607
|
-
discriminator: { propertyName: 'foo' },
|
|
608
|
-
unevaluatedProperties: false,
|
|
609
|
-
required: ['foo'],
|
|
610
|
-
oneOf: [
|
|
611
|
-
{
|
|
612
|
-
properties: {
|
|
613
|
-
foo: { const: 'x' },
|
|
614
|
-
a: {
|
|
615
|
-
type: 'string',
|
|
616
|
-
format: 'required'
|
|
617
|
-
}
|
|
618
|
-
},
|
|
619
|
-
required: ['a']
|
|
620
|
-
},
|
|
621
|
-
{
|
|
622
|
-
properties: {
|
|
623
|
-
foo: { enum: ['y', 'z'] },
|
|
624
|
-
b: {
|
|
625
|
-
type: 'string',
|
|
626
|
-
format: 'required'
|
|
627
|
-
}
|
|
628
|
-
},
|
|
629
|
-
required: ['b']
|
|
630
|
-
}
|
|
631
|
-
]
|
|
632
|
-
})
|
|
633
|
-
})
|
|
634
|
-
|
|
635
|
-
it('supports nested Dito.js definitions', () => {
|
|
636
|
-
expect(
|
|
637
|
-
convertSchema(
|
|
638
|
-
{
|
|
639
|
-
type: 'object',
|
|
640
|
-
properties: {
|
|
641
|
-
prop1: {
|
|
642
|
-
$ref: '#type1'
|
|
643
|
-
},
|
|
644
|
-
prop2: {
|
|
645
|
-
type: 'object',
|
|
646
|
-
properties: {
|
|
647
|
-
prop3: {
|
|
648
|
-
$ref: '#type2'
|
|
649
|
-
}
|
|
650
|
-
},
|
|
651
|
-
definitions: {
|
|
652
|
-
'#type2': {
|
|
653
|
-
type: 'object',
|
|
654
|
-
properties: {
|
|
655
|
-
prop4: {
|
|
656
|
-
type: 'string'
|
|
657
|
-
},
|
|
658
|
-
|
|
659
|
-
prop5: {
|
|
660
|
-
$ref: '#type3'
|
|
661
|
-
}
|
|
662
|
-
},
|
|
663
|
-
|
|
664
|
-
definitions: {
|
|
665
|
-
'#type3': {
|
|
666
|
-
type: 'boolean'
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
},
|
|
673
|
-
|
|
674
|
-
definitions: {
|
|
675
|
-
'#type1': {
|
|
676
|
-
type: 'integer'
|
|
677
|
-
}
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
)
|
|
681
|
-
).toEqual({
|
|
682
|
-
type: 'object',
|
|
683
|
-
unevaluatedProperties: false,
|
|
684
|
-
properties: {
|
|
685
|
-
prop1: {
|
|
686
|
-
$ref: '#/definitions/#type1'
|
|
687
|
-
},
|
|
688
|
-
prop2: {
|
|
689
|
-
type: 'object',
|
|
690
|
-
unevaluatedProperties: false,
|
|
691
|
-
properties: {
|
|
692
|
-
prop3: {
|
|
693
|
-
$ref: '#/definitions/#type2'
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
},
|
|
698
|
-
definitions: {
|
|
699
|
-
'#type1': {
|
|
700
|
-
type: 'integer'
|
|
701
|
-
},
|
|
702
|
-
'#type2': {
|
|
703
|
-
type: 'object',
|
|
704
|
-
unevaluatedProperties: false,
|
|
705
|
-
properties: {
|
|
706
|
-
prop4: {
|
|
707
|
-
type: 'string'
|
|
708
|
-
},
|
|
709
|
-
prop5: {
|
|
710
|
-
$ref: '#/definitions/#type3'
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
},
|
|
714
|
-
'#type3': {
|
|
715
|
-
type: 'boolean'
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
})
|
|
719
|
-
})
|
|
720
|
-
})
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Storage } from './Storage.js'
|
|
2
|
-
|
|
3
|
-
function createStorage(keys = ['test-secret']) {
|
|
4
|
-
const app = { keys }
|
|
5
|
-
return new Storage(app, { name: 'test' })
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
function simulateUpload(storage, key, originalname) {
|
|
9
|
-
return storage.convertStorageFile({
|
|
10
|
-
key,
|
|
11
|
-
originalname,
|
|
12
|
-
mimetype: 'image/png',
|
|
13
|
-
size: 2048
|
|
14
|
-
})
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
describe('Storage: asset key signature verification', () => {
|
|
18
|
-
it('preserves the key through a full upload-then-save cycle', () => {
|
|
19
|
-
const storage = createStorage()
|
|
20
|
-
const uploaded = simulateUpload(storage, 'real-key.png', 'photo.png')
|
|
21
|
-
const fromClient = { ...uploaded }
|
|
22
|
-
storage.convertAssetFile(fromClient, { trusted: false })
|
|
23
|
-
expect(fromClient.key).toBe('real-key.png')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('throws when a forged key is submitted', () => {
|
|
27
|
-
const storage = createStorage()
|
|
28
|
-
const file = { key: 'victim-key.png', name: 'photo.png' }
|
|
29
|
-
expect(() => {
|
|
30
|
-
storage.convertAssetFile(file, { trusted: false })
|
|
31
|
-
}).toThrow('Invalid asset signature')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('throws when a valid signature is paired with a swapped key', () => {
|
|
35
|
-
const storage = createStorage()
|
|
36
|
-
const uploaded = simulateUpload(storage, 'legit.png', 'legit.png')
|
|
37
|
-
const forged = { ...uploaded, key: 'victim-file.png' }
|
|
38
|
-
expect(() => {
|
|
39
|
-
storage.convertAssetFile(forged, { trusted: false })
|
|
40
|
-
}).toThrow('Invalid asset signature')
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('does not throw when addFile() converts a server-created file', async () => {
|
|
44
|
-
const storage = createStorage()
|
|
45
|
-
const file = { key: 'imported.png', name: 'import.png' }
|
|
46
|
-
const data = Buffer.from('fake-image-data')
|
|
47
|
-
await storage.addFile(file, data)
|
|
48
|
-
expect(file.key).toBe('imported.png')
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('strips signature after conversion', () => {
|
|
52
|
-
const storage = createStorage()
|
|
53
|
-
const uploaded = simulateUpload(storage, 'upload.png', 'photo.png')
|
|
54
|
-
const fromClient = { ...uploaded }
|
|
55
|
-
storage.convertAssetFile(fromClient, { trusted: false })
|
|
56
|
-
expect('signature' in fromClient).toBe(false)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { describeFunction } from './function.js'
|
|
2
|
-
|
|
3
|
-
describe('describeFunction()', () => {
|
|
4
|
-
it('describes normal functions', () => {
|
|
5
|
-
expect(
|
|
6
|
-
describeFunction(function (a, b, c) {
|
|
7
|
-
return a + b + c
|
|
8
|
-
})
|
|
9
|
-
)
|
|
10
|
-
.toBe('function (a, b, c) { ... }')
|
|
11
|
-
})
|
|
12
|
-
|
|
13
|
-
it('describes lambdas with one param and a body', () => {
|
|
14
|
-
expect(
|
|
15
|
-
describeFunction(a => {
|
|
16
|
-
return a
|
|
17
|
-
})
|
|
18
|
-
)
|
|
19
|
-
.toBe('a => { ... }')
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('describes lambdas with one param and no body', () => {
|
|
23
|
-
expect(describeFunction(a => a))
|
|
24
|
-
.toBe('a => ...')
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('describes lambdas with multiple params and a body', () => {
|
|
28
|
-
expect(
|
|
29
|
-
describeFunction((a, b, c) => {
|
|
30
|
-
return a + b + c
|
|
31
|
-
})
|
|
32
|
-
)
|
|
33
|
-
.toBe('(a, b, c) => { ... }')
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
it('describes lambdas with multiple params and no body', () => {
|
|
37
|
-
expect(describeFunction((a, b, c) => a + b + c))
|
|
38
|
-
.toBe('(a, b, c) => ...')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('describes async functions', () => {
|
|
42
|
-
expect(
|
|
43
|
-
describeFunction(async function (a, b, c) {
|
|
44
|
-
return a + b + c
|
|
45
|
-
})
|
|
46
|
-
)
|
|
47
|
-
.toBe('async function (a, b, c) { ... }')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('describes async lambdas with one param and a body', () => {
|
|
51
|
-
expect(
|
|
52
|
-
describeFunction(async a => {
|
|
53
|
-
return a
|
|
54
|
-
})
|
|
55
|
-
)
|
|
56
|
-
.toBe('async a => { ... }')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('describes async lambdas with one param and no body', () => {
|
|
60
|
-
expect(describeFunction(async a => a))
|
|
61
|
-
.toBe('async a => ...')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('describes async lambdas with multiple params and a body', () => {
|
|
65
|
-
expect(
|
|
66
|
-
describeFunction(async (a, b, c) => {
|
|
67
|
-
return a + b + c
|
|
68
|
-
})
|
|
69
|
-
)
|
|
70
|
-
.toBe('async (a, b, c) => { ... }')
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('describes async lambdas with multiple params and no body', () => {
|
|
74
|
-
expect(describeFunction(async (a, b, c) => a + b + c))
|
|
75
|
-
.toBe('async (a, b, c) => ...')
|
|
76
|
-
})
|
|
77
|
-
})
|
package/src/utils/object.test.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getAllKeys,
|
|
3
|
-
getOwnProperty,
|
|
4
|
-
createLookup,
|
|
5
|
-
mergeReversed,
|
|
6
|
-
mergeAsReversedArrays
|
|
7
|
-
} from './object.js'
|
|
8
|
-
|
|
9
|
-
const object = Object.create({ a: 1 })
|
|
10
|
-
object.b = 2
|
|
11
|
-
|
|
12
|
-
describe('getAllKeys()', () => {
|
|
13
|
-
it('returns all keys, not just own keys', () => {
|
|
14
|
-
expect(getAllKeys(object)).toEqual(['b', 'a'])
|
|
15
|
-
})
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
describe('getOwnProperty()', () => {
|
|
19
|
-
it('only returns own properties', () => {
|
|
20
|
-
expect(getOwnProperty(object, 'a')).toBeUndefined()
|
|
21
|
-
expect(getOwnProperty(object, 'b')).toBe(2)
|
|
22
|
-
})
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
describe('createLookup()', () => {
|
|
26
|
-
it('creates lookup objects out of arrays of strings', () => {
|
|
27
|
-
expect(createLookup(['a', 'b'])).toEqual({
|
|
28
|
-
a: true,
|
|
29
|
-
b: true
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
describe('mergeReversed()', () => {
|
|
35
|
-
it('merges property values in reversed order', () => {
|
|
36
|
-
expect(
|
|
37
|
-
mergeReversed([
|
|
38
|
-
{
|
|
39
|
-
a: 3
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
a: 2,
|
|
43
|
-
b: 2
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
a: 1,
|
|
47
|
-
b: 1,
|
|
48
|
-
c: 1
|
|
49
|
-
}
|
|
50
|
-
])
|
|
51
|
-
).toEqual({
|
|
52
|
-
a: 3,
|
|
53
|
-
b: 2,
|
|
54
|
-
c: 1
|
|
55
|
-
})
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
describe('mergeAsReversedArrays()', () => {
|
|
60
|
-
it('merges property values into arrays', () => {
|
|
61
|
-
expect(
|
|
62
|
-
mergeAsReversedArrays([
|
|
63
|
-
{
|
|
64
|
-
a: 1,
|
|
65
|
-
b: 1,
|
|
66
|
-
c: 1
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
a: 2,
|
|
70
|
-
b: 2
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
a: 3
|
|
74
|
-
}
|
|
75
|
-
])
|
|
76
|
-
).toEqual({
|
|
77
|
-
a: [3, 2, 1],
|
|
78
|
-
b: [2, 1],
|
|
79
|
-
c: [1]
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe.skip('setupPropertyInheritance()', () => {
|
|
85
|
-
// TODO: Add unit tests for this!
|
|
86
|
-
})
|