@toa.io/norm 1.0.0-alpha.284 → 1.0.0-alpha.286
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 +5 -5
- package/src/.component/.expand/bridge.js +1 -1
- package/src/.component/.expand/entity.js +1 -1
- package/src/.component/.expand/events.js +1 -1
- package/src/.component/.expand/extensions.js +1 -1
- package/src/.component/.expand/operations.js +1 -1
- package/src/.component/.expand/properties.js +1 -1
- package/src/.component/.expand/receivers.js +3 -2
- package/src/.component/.expand/version.js +11 -13
- package/src/.component/.normalize/entity.js +7 -0
- package/src/.component/.normalize/events.js +4 -1
- package/src/.component/.normalize/index.js +1 -0
- package/src/.component/.normalize/operations.js +9 -1
- package/src/.component/.normalize/receivers.js +4 -1
- package/src/.component/collapse.js +14 -6
- package/src/.component/defaults.js +3 -4
- package/src/.component/dependencies.js +7 -4
- package/src/.component/dereference.js +14 -13
- package/src/.component/expand.js +11 -2
- package/src/.component/extensions.js +2 -1
- package/src/.component/index.js +1 -0
- package/src/.component/merge.js +3 -5
- package/src/.component/migrations.js +57 -0
- package/src/.component/normalize.js +9 -1
- package/src/.component/schema.yaml +29 -39
- package/src/.component/validate.js +23 -3
- package/src/.context/.dependencies/connectors.js +3 -3
- package/src/.context/.dependencies/describe.js +4 -1
- package/src/.context/.dependencies/extensions.js +18 -13
- package/src/.context/.dependencies/load.js +6 -4
- package/src/.context/.dependencies/resolve.js +1 -1
- package/src/.context/dereference.js +3 -1
- package/src/.context/normalize.js +5 -0
- package/src/.context/schema.yaml +6 -4
- package/src/component.js +14 -5
- package/src/context.js +9 -5
- package/src/entity.js +15 -0
- package/src/index.js +1 -0
- package/src/shortcuts.js +0 -1
- package/test/component/collapse.fixtures.js +28 -34
- package/test/component/collapse.test.js +21 -0
- package/test/component/dependencies.test.js +6 -6
- package/test/component/dereference.fixtures.js +13 -17
- package/test/component/dereference.test.js +12 -3
- package/test/component/dummies/migrations/conflicting/migrations/0001-one.yaml +2 -0
- package/test/component/dummies/migrations/conflicting/migrations/0001-one.yml +2 -0
- package/test/component/dummies/migrations/malformed/migrations/0001-one.yaml +1 -0
- package/test/component/dummies/migrations/ordered/migrations/0001-first.yml +4 -0
- package/test/component/dummies/migrations/ordered/migrations/0002-second.yaml +4 -0
- package/test/component/dummies/migrations/ordered/migrations/0010-third.json +1 -0
- package/test/component/dummies/migrations/stateless/migrations/0001-one.yaml +2 -0
- package/test/component/migrations.test.js +62 -0
- package/test/component/normalize.test.js +51 -13
- package/test/component/validate.fixtures.js +14 -8
- package/test/component/validate.test.js +113 -59
- package/test/context/complete.test.js +5 -1
- package/test/context/dereference.test.js +3 -2
- package/test/context/expand.test.js +4 -2
- package/test/context/normalize.test.js +14 -1
- package/test/context/validate.test.js +40 -20
- package/test/shortcuts.test.js +2 -1
- package/types/component.d.ts +14 -3
- package/types/context/declaration.d.ts +1 -1
- package/types/context.d.ts +10 -9
- package/types/entity.d.ts +7 -0
- package/types/index.d.ts +1 -0
- package/CHANGELOG.md +0 -203
|
@@ -18,6 +18,53 @@ describe('operations', () => {
|
|
|
18
18
|
|
|
19
19
|
assert.deepStrictEqual(manifest.operations.add.bindings, manifest.bindings)
|
|
20
20
|
})
|
|
21
|
+
|
|
22
|
+
it('should state an empty output where none is declared', async () => {
|
|
23
|
+
await normalize(manifest)
|
|
24
|
+
|
|
25
|
+
assert.deepStrictEqual(manifest.operations.add.output, {})
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should scope a computation to none', async () => {
|
|
29
|
+
manifest.operations.compute = { type: 'computation' }
|
|
30
|
+
|
|
31
|
+
await normalize(manifest)
|
|
32
|
+
|
|
33
|
+
assert.deepStrictEqual(manifest.operations.compute.scope, 'none')
|
|
34
|
+
assert.deepStrictEqual(manifest.operations.compute.query, false)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should scope an effect to none unless it says otherwise', async () => {
|
|
38
|
+
manifest.operations.affect = { type: 'effect' }
|
|
39
|
+
manifest.operations.reach = { type: 'effect', scope: 'objects' }
|
|
40
|
+
|
|
41
|
+
await normalize(manifest)
|
|
42
|
+
|
|
43
|
+
assert.deepStrictEqual(manifest.operations.affect.scope, 'none')
|
|
44
|
+
assert.deepStrictEqual(manifest.operations.reach.scope, 'objects')
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('namespace', () => {
|
|
49
|
+
it('should default to `default`', async () => {
|
|
50
|
+
delete manifest.namespace
|
|
51
|
+
|
|
52
|
+
await normalize(manifest)
|
|
53
|
+
|
|
54
|
+
assert.deepStrictEqual(manifest.namespace, 'default')
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
describe('entity', () => {
|
|
59
|
+
it('should default storage and flags', async () => {
|
|
60
|
+
manifest.entity = { properties: {} }
|
|
61
|
+
|
|
62
|
+
await normalize(manifest)
|
|
63
|
+
|
|
64
|
+
assert.deepStrictEqual(manifest.entity.storage, '@toa.io/storages.mongodb')
|
|
65
|
+
assert.deepStrictEqual(manifest.entity.associated, false)
|
|
66
|
+
assert.deepStrictEqual(manifest.entity.custom, false)
|
|
67
|
+
})
|
|
21
68
|
})
|
|
22
69
|
|
|
23
70
|
describe('extensions', () => {
|
|
@@ -44,30 +91,21 @@ describe('extensions', () => {
|
|
|
44
91
|
describe('receivers', () => {
|
|
45
92
|
it('should substitute default namespace', async () => {
|
|
46
93
|
manifest.receivers = {
|
|
47
|
-
'messages.created': 'add'
|
|
94
|
+
'messages.created': { operation: 'add' }
|
|
48
95
|
}
|
|
49
96
|
|
|
50
97
|
await normalize(manifest)
|
|
51
98
|
|
|
52
|
-
assert.deepStrictEqual(manifest.receivers,
|
|
53
|
-
'default.messages.created': 'add'
|
|
54
|
-
})
|
|
99
|
+
assert.deepStrictEqual(Object.keys(manifest.receivers), ['default.messages.created'])
|
|
55
100
|
})
|
|
56
101
|
|
|
57
102
|
it('should not substitute default namespace for foreign events', async () => {
|
|
58
|
-
const receiver = {
|
|
59
|
-
transition: 'add',
|
|
60
|
-
source: 'test'
|
|
61
|
-
}
|
|
62
|
-
|
|
63
103
|
manifest.receivers = {
|
|
64
|
-
'messages.created':
|
|
104
|
+
'messages.created': { transition: 'add', source: 'test' }
|
|
65
105
|
}
|
|
66
106
|
|
|
67
107
|
await normalize(manifest)
|
|
68
108
|
|
|
69
|
-
assert.deepStrictEqual(manifest.receivers,
|
|
70
|
-
'messages.created': receiver
|
|
71
|
-
})
|
|
109
|
+
assert.deepStrictEqual(Object.keys(manifest.receivers), ['messages.created'])
|
|
72
110
|
})
|
|
73
111
|
})
|
|
@@ -3,13 +3,13 @@ export const ok = {
|
|
|
3
3
|
name: 'bar',
|
|
4
4
|
entity: {
|
|
5
5
|
storage: 'whatever',
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
associated: false,
|
|
7
|
+
custom: false,
|
|
8
|
+
properties: {
|
|
9
|
+
name: {
|
|
10
|
+
type: 'string',
|
|
11
|
+
minLength: 1,
|
|
12
|
+
maxLength: 128
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
},
|
|
@@ -35,11 +35,15 @@ export const ok = {
|
|
|
35
35
|
},
|
|
36
36
|
compute: {
|
|
37
37
|
type: 'computation',
|
|
38
|
+
scope: 'none',
|
|
39
|
+
query: false,
|
|
38
40
|
bridge: 'whatever',
|
|
39
41
|
bindings: ['@toa.io/bindings.amqp']
|
|
40
42
|
},
|
|
41
43
|
affect: {
|
|
42
44
|
type: 'effect',
|
|
45
|
+
scope: 'none',
|
|
46
|
+
query: false,
|
|
43
47
|
bridge: 'whatever',
|
|
44
48
|
bindings: ['@toa.io/bindings.amqp']
|
|
45
49
|
}
|
|
@@ -58,7 +62,9 @@ export const ok = {
|
|
|
58
62
|
operation: 'add',
|
|
59
63
|
bridge: 'whatever',
|
|
60
64
|
binding: 'amqp',
|
|
61
|
-
path: '/somewhere'
|
|
65
|
+
path: '/somewhere',
|
|
66
|
+
conditioned: false,
|
|
67
|
+
adaptive: false
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
}
|
|
@@ -31,34 +31,39 @@ it('should not have additional properties', async () => {
|
|
|
31
31
|
describe('namespace', () => {
|
|
32
32
|
it('should match token pattern', async () => {
|
|
33
33
|
manifest.namespace = '1'
|
|
34
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
34
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
35
|
+
/must match pattern/.test(error.message)
|
|
36
|
+
)
|
|
35
37
|
|
|
36
38
|
manifest.namespace = 'foo_'
|
|
37
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
39
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
40
|
+
/must match pattern/.test(error.message)
|
|
41
|
+
)
|
|
38
42
|
|
|
39
43
|
manifest.namespace = 'foo_bar'
|
|
40
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
44
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
45
|
+
/must match pattern/.test(error.message)
|
|
46
|
+
)
|
|
41
47
|
|
|
42
48
|
manifest.namespace = 'foo-'
|
|
43
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
49
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
50
|
+
/must match pattern/.test(error.message)
|
|
51
|
+
)
|
|
44
52
|
|
|
45
53
|
manifest.namespace = 'foo-bar'
|
|
46
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
54
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
55
|
+
/must match pattern/.test(error.message)
|
|
56
|
+
)
|
|
47
57
|
|
|
48
58
|
manifest.namespace = 'FooBar12'
|
|
49
59
|
await assert.doesNotReject(validate(manifest))
|
|
50
60
|
})
|
|
51
61
|
|
|
52
|
-
it(
|
|
62
|
+
it("should forbid 'system' namespace", async () => {
|
|
53
63
|
manifest.namespace = 'system'
|
|
54
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
it('should set `default` namespace', async () => {
|
|
58
|
-
delete manifest.namespace
|
|
59
|
-
|
|
60
|
-
await assert.doesNotReject(validate(manifest))
|
|
61
|
-
assert.deepStrictEqual(manifest.namespace, 'default')
|
|
64
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
65
|
+
/must NOT be valid/.test(error.message)
|
|
66
|
+
)
|
|
62
67
|
})
|
|
63
68
|
})
|
|
64
69
|
|
|
@@ -77,7 +82,9 @@ describe('entity', () => {
|
|
|
77
82
|
|
|
78
83
|
it('should be object', async () => {
|
|
79
84
|
manifest.entity = 'foo'
|
|
80
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
85
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
86
|
+
/must be object/.test(error.message)
|
|
87
|
+
)
|
|
81
88
|
})
|
|
82
89
|
|
|
83
90
|
it('should not have additional properties', async () => {
|
|
@@ -85,33 +92,24 @@ describe('entity', () => {
|
|
|
85
92
|
await assert.rejects(validate(manifest))
|
|
86
93
|
})
|
|
87
94
|
|
|
88
|
-
describe('
|
|
95
|
+
describe('properties', () => {
|
|
89
96
|
it('should be required', async () => {
|
|
90
|
-
delete manifest.entity.
|
|
97
|
+
delete manifest.entity.properties
|
|
91
98
|
await assert.rejects(validate(manifest))
|
|
92
99
|
})
|
|
93
100
|
|
|
94
|
-
it('should be JSON
|
|
95
|
-
manifest.entity.
|
|
101
|
+
it('should be JSON schemas', async () => {
|
|
102
|
+
manifest.entity.properties.foo = 'bar'
|
|
96
103
|
await assert.rejects(validate(manifest))
|
|
97
104
|
})
|
|
98
105
|
|
|
99
|
-
it('should
|
|
100
|
-
manifest.entity.
|
|
101
|
-
await assert.rejects(validate(manifest), (error) => /must be equal to constant/.test(error.message))
|
|
102
|
-
|
|
103
|
-
manifest.entity.schema = {}
|
|
104
|
-
validate(manifest)
|
|
105
|
-
assert.strictEqual(manifest.entity.schema.type, 'object')
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
it('should have property names matching token pattern', async () => {
|
|
109
|
-
manifest.entity.schema.properties._foo = { type: 'string' }
|
|
106
|
+
it('should have names matching token pattern', async () => {
|
|
107
|
+
manifest.entity.properties._foo = { type: 'string' }
|
|
110
108
|
await assert.rejects(validate(manifest), (error) => /pattern/.test(error.message))
|
|
111
109
|
})
|
|
112
110
|
|
|
113
|
-
it('should allow
|
|
114
|
-
manifest.entity.
|
|
111
|
+
it('should allow own id', async () => {
|
|
112
|
+
manifest.entity.properties.id = {
|
|
115
113
|
type: 'string',
|
|
116
114
|
pattern: '^[a-fA-F0-9]+$'
|
|
117
115
|
}
|
|
@@ -119,10 +117,34 @@ describe('entity', () => {
|
|
|
119
117
|
})
|
|
120
118
|
})
|
|
121
119
|
|
|
122
|
-
describe('
|
|
123
|
-
it('should
|
|
120
|
+
describe('required', () => {
|
|
121
|
+
it('should name declared properties', async () => {
|
|
122
|
+
manifest.entity.required = ['name']
|
|
124
123
|
await assert.doesNotReject(validate(manifest))
|
|
125
|
-
|
|
124
|
+
|
|
125
|
+
manifest.entity.required = ['nope']
|
|
126
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
127
|
+
/requires property 'nope'/.test(error.message)
|
|
128
|
+
)
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('blank', () => {
|
|
133
|
+
it('should name declared properties', async () => {
|
|
134
|
+
manifest.entity.blank = { name: 'whatever' }
|
|
135
|
+
await assert.doesNotReject(validate(manifest))
|
|
136
|
+
|
|
137
|
+
manifest.entity.blank = { nope: 1 }
|
|
138
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
139
|
+
/names property 'nope'/.test(error.message)
|
|
140
|
+
)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
it('should not name a system property', async () => {
|
|
144
|
+
manifest.entity.blank = { VERSION: 1 }
|
|
145
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
146
|
+
/must NOT be valid/.test(error.message)
|
|
147
|
+
)
|
|
126
148
|
})
|
|
127
149
|
})
|
|
128
150
|
})
|
|
@@ -130,25 +152,35 @@ describe('entity', () => {
|
|
|
130
152
|
describe('bindings', () => {
|
|
131
153
|
it('should be array of unique strings', async () => {
|
|
132
154
|
manifest.bindings = 'oops'
|
|
133
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
155
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
156
|
+
/must be array/.test(error.message)
|
|
157
|
+
)
|
|
134
158
|
|
|
135
159
|
manifest.bindings = ['oops', 'oops']
|
|
136
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
160
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
161
|
+
/duplicate items/.test(error.message)
|
|
162
|
+
)
|
|
137
163
|
|
|
138
164
|
manifest.bindings = ['oops', {}]
|
|
139
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
165
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
166
|
+
/must be string/.test(error.message)
|
|
167
|
+
)
|
|
140
168
|
})
|
|
141
169
|
|
|
142
170
|
it('should forbid explicit loop', async () => {
|
|
143
171
|
manifest.bindings = ['@toa.io/bindings.loop']
|
|
144
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
172
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
173
|
+
/must NOT be valid/.test(error.message)
|
|
174
|
+
)
|
|
145
175
|
})
|
|
146
176
|
})
|
|
147
177
|
|
|
148
178
|
describe('operations', () => {
|
|
149
179
|
it('should be object', async () => {
|
|
150
180
|
manifest.operations.get = 'bar'
|
|
151
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
181
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
182
|
+
/must be object/.test(error.message)
|
|
183
|
+
)
|
|
152
184
|
})
|
|
153
185
|
|
|
154
186
|
it('should not have additional properties', async () => {
|
|
@@ -161,45 +193,57 @@ describe('operations', () => {
|
|
|
161
193
|
await assert.rejects(validate(manifest))
|
|
162
194
|
|
|
163
195
|
manifest.operations.get.type = 'foo'
|
|
164
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
196
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
197
|
+
/one of the allowed values/.test(error.message)
|
|
198
|
+
)
|
|
165
199
|
})
|
|
166
200
|
|
|
167
201
|
it('should forbid explicit loop', async () => {
|
|
168
202
|
manifest.operations.get.bindings = ['@toa.io/bindings.loop']
|
|
169
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
203
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
204
|
+
/must NOT be valid/.test(error.message)
|
|
205
|
+
)
|
|
170
206
|
})
|
|
171
207
|
|
|
172
208
|
it('should forbid query: false for observations', async () => {
|
|
173
209
|
manifest.operations.get.query = false
|
|
174
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
210
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
211
|
+
/must NOT be valid/.test(error.message)
|
|
212
|
+
)
|
|
175
213
|
})
|
|
176
214
|
|
|
177
|
-
for (const [_, operation] of [
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
it(`should set query: false for ${_}`, async () => {
|
|
181
|
-
validate(manifest)
|
|
215
|
+
for (const [_, operation] of [['computation', 'compute']])
|
|
216
|
+
it(`should set query: false for ${_}`, async () => {
|
|
217
|
+
validate(manifest)
|
|
182
218
|
|
|
183
|
-
|
|
184
|
-
|
|
219
|
+
assert.strictEqual(manifest.operations[operation].query, false)
|
|
220
|
+
})
|
|
185
221
|
|
|
186
222
|
describe('scope', () => {
|
|
187
223
|
it('should have scope', async () => {
|
|
188
224
|
delete manifest.operations.get.scope
|
|
189
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
225
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
226
|
+
/required property/.test(error.message)
|
|
227
|
+
)
|
|
190
228
|
})
|
|
191
229
|
|
|
192
230
|
it('should allow only entity or set for observations', async () => {
|
|
193
231
|
manifest.operations.get.scope = 'changeset'
|
|
194
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
232
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
233
|
+
/allowed values/.test(error.message)
|
|
234
|
+
)
|
|
195
235
|
})
|
|
196
236
|
|
|
197
237
|
it('should allow only entity for transitions', async () => {
|
|
198
238
|
manifest.operations.add.scope = 'changeset'
|
|
199
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
239
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
240
|
+
/allowed values/.test(error.message)
|
|
241
|
+
)
|
|
200
242
|
|
|
201
243
|
manifest.operations.add.scope = 'set'
|
|
202
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
244
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
245
|
+
/allowed values/.test(error.message)
|
|
246
|
+
)
|
|
203
247
|
})
|
|
204
248
|
|
|
205
249
|
it('should allow only changeset for assignments', async () => {
|
|
@@ -207,14 +251,18 @@ describe('operations', () => {
|
|
|
207
251
|
await assert.doesNotReject(validate(manifest))
|
|
208
252
|
|
|
209
253
|
manifest.operations.set.scope = 'set'
|
|
210
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
254
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
255
|
+
/allowed values/.test(error.message)
|
|
256
|
+
)
|
|
211
257
|
})
|
|
212
258
|
})
|
|
213
259
|
|
|
214
260
|
describe('concurrency', () => {
|
|
215
261
|
it('should be required for transitions', async () => {
|
|
216
262
|
delete manifest.operations.add.concurrency
|
|
217
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
263
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
264
|
+
/required property/.test(error.message)
|
|
265
|
+
)
|
|
218
266
|
})
|
|
219
267
|
|
|
220
268
|
it('should throw for observations, assignments', async () => {
|
|
@@ -243,18 +291,24 @@ describe('receivers', () => {
|
|
|
243
291
|
it('should throw if transition points to undefined operation', async () => {
|
|
244
292
|
manifest.receivers['foo.bar.happened'].operation = 'notExists'
|
|
245
293
|
|
|
246
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
294
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
295
|
+
/refers to undefined operation/.test(error.message)
|
|
296
|
+
)
|
|
247
297
|
})
|
|
248
298
|
|
|
249
299
|
it('should throw if transition points to observation', async () => {
|
|
250
300
|
manifest.receivers['foo.bar.happened'].operation = 'get'
|
|
251
301
|
|
|
252
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
302
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
303
|
+
/of the allowed types/.test(error.message)
|
|
304
|
+
)
|
|
253
305
|
})
|
|
254
306
|
|
|
255
307
|
it('should throw if source has a name `context`', async () => {
|
|
256
308
|
manifest.receivers['foo.bar.happened'].source = 'context'
|
|
257
309
|
|
|
258
|
-
await assert.rejects(validate(manifest), (error) =>
|
|
310
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
311
|
+
/must NOT be valid/.test(error.message)
|
|
312
|
+
)
|
|
259
313
|
})
|
|
260
314
|
})
|
|
@@ -17,7 +17,11 @@ beforeEach(() => {
|
|
|
17
17
|
|
|
18
18
|
it('should complete compositions', () => {
|
|
19
19
|
assert.deepStrictEqual(context.compositions.length, fixtures.compositions.length)
|
|
20
|
-
assert.ok(
|
|
20
|
+
assert.ok(
|
|
21
|
+
fixtures.compositions.every((item) =>
|
|
22
|
+
context.compositions.some((candidate) => isDeepStrictEqual(candidate, item))
|
|
23
|
+
)
|
|
24
|
+
)
|
|
21
25
|
})
|
|
22
26
|
|
|
23
27
|
it('should create if compositions are not set', () => {
|
|
@@ -29,6 +29,7 @@ it('should throw on an unknown component', () => {
|
|
|
29
29
|
|
|
30
30
|
broken.compositions[1].components.push('nope.here')
|
|
31
31
|
|
|
32
|
-
assert.throws(() => dereference(broken),
|
|
33
|
-
|
|
32
|
+
assert.throws(() => dereference(broken), {
|
|
33
|
+
message: "Composition 'bar' lists an unknown component 'nope.here'."
|
|
34
|
+
})
|
|
34
35
|
})
|
|
@@ -47,8 +47,10 @@ describe('composition services', () => {
|
|
|
47
47
|
|
|
48
48
|
expand(context)
|
|
49
49
|
|
|
50
|
-
assert.deepStrictEqual(context.compositions[0].services,
|
|
51
|
-
|
|
50
|
+
assert.deepStrictEqual(context.compositions[0].services, [
|
|
51
|
+
'@toa.io/extensions.exposition',
|
|
52
|
+
'@toa.io/extensions.configuration'
|
|
53
|
+
])
|
|
52
54
|
})
|
|
53
55
|
|
|
54
56
|
it('should leave a package reference alone', () => {
|
|
@@ -31,6 +31,19 @@ it('should expand registry', () => {
|
|
|
31
31
|
normalize(context)
|
|
32
32
|
|
|
33
33
|
assert.deepStrictEqual(context.registry, {
|
|
34
|
-
base
|
|
34
|
+
base,
|
|
35
|
+
platforms: ['linux/amd64', 'linux/arm/v7', 'linux/arm64']
|
|
35
36
|
})
|
|
36
37
|
})
|
|
38
|
+
|
|
39
|
+
it('should set default platforms', () => {
|
|
40
|
+
context.registry = { base: generate() }
|
|
41
|
+
|
|
42
|
+
normalize(context)
|
|
43
|
+
|
|
44
|
+
assert.deepStrictEqual(context.registry.platforms, [
|
|
45
|
+
'linux/amd64',
|
|
46
|
+
'linux/arm/v7',
|
|
47
|
+
'linux/arm64'
|
|
48
|
+
])
|
|
49
|
+
})
|
|
@@ -19,12 +19,18 @@ beforeEach(() => {
|
|
|
19
19
|
describe('runtime', () => {
|
|
20
20
|
it('should require', () => {
|
|
21
21
|
delete context.runtime
|
|
22
|
-
assert.throws(
|
|
22
|
+
assert.throws(
|
|
23
|
+
() => validate(context),
|
|
24
|
+
(error) => /required/.test(error.message)
|
|
25
|
+
)
|
|
23
26
|
})
|
|
24
27
|
|
|
25
28
|
it('should require registry to match uri format', () => {
|
|
26
29
|
context.runtime.registry = 'not-a-uri'
|
|
27
|
-
assert.throws(
|
|
30
|
+
assert.throws(
|
|
31
|
+
() => validate(context),
|
|
32
|
+
(error) => /must match format/.test(error.message)
|
|
33
|
+
)
|
|
28
34
|
|
|
29
35
|
context.runtime.registry = 'http://localhost'
|
|
30
36
|
assert.doesNotThrow(() => validate(context))
|
|
@@ -32,7 +38,10 @@ describe('runtime', () => {
|
|
|
32
38
|
|
|
33
39
|
it('should require proxy to match uri format', () => {
|
|
34
40
|
context.runtime.proxy = 'not-a-uri'
|
|
35
|
-
assert.throws(
|
|
41
|
+
assert.throws(
|
|
42
|
+
() => validate(context),
|
|
43
|
+
(error) => /must match format/.test(error.message)
|
|
44
|
+
)
|
|
36
45
|
|
|
37
46
|
context.runtime.proxy = 'http://localhost'
|
|
38
47
|
assert.doesNotThrow(() => validate(context))
|
|
@@ -43,25 +52,25 @@ describe('registry', () => {
|
|
|
43
52
|
it('should require', () => {
|
|
44
53
|
delete context.registry
|
|
45
54
|
|
|
46
|
-
assert.throws(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
delete context.registry.platforms
|
|
51
|
-
|
|
52
|
-
validate(context)
|
|
53
|
-
|
|
54
|
-
assert.ok(context.registry.platforms instanceof Array)
|
|
55
|
-
assert.deepStrictEqual(context.registry.platforms, ['linux/amd64', 'linux/arm/v7', 'linux/arm64'])
|
|
55
|
+
assert.throws(
|
|
56
|
+
() => validate(context),
|
|
57
|
+
(error) => /required property 'registry'/.test(error.message)
|
|
58
|
+
)
|
|
56
59
|
})
|
|
57
60
|
})
|
|
58
61
|
|
|
59
62
|
it('should require name as label', () => {
|
|
60
63
|
delete context.name
|
|
61
|
-
assert.throws(
|
|
64
|
+
assert.throws(
|
|
65
|
+
() => validate(context),
|
|
66
|
+
(error) => /required/.test(error.message)
|
|
67
|
+
)
|
|
62
68
|
|
|
63
69
|
context.name = 'foo bar'
|
|
64
|
-
assert.throws(
|
|
70
|
+
assert.throws(
|
|
71
|
+
() => validate(context),
|
|
72
|
+
(error) => /pattern/.test(error.message)
|
|
73
|
+
)
|
|
65
74
|
|
|
66
75
|
context.name = 'foo-bar'
|
|
67
76
|
assert.doesNotThrow(() => validate(context))
|
|
@@ -69,7 +78,10 @@ it('should require name as label', () => {
|
|
|
69
78
|
|
|
70
79
|
it('should require registry url', () => {
|
|
71
80
|
delete context.registry
|
|
72
|
-
assert.throws(
|
|
81
|
+
assert.throws(
|
|
82
|
+
() => validate(context),
|
|
83
|
+
(error) => /required/.test(error.message)
|
|
84
|
+
)
|
|
73
85
|
})
|
|
74
86
|
|
|
75
87
|
it('should allow mono replicas and resources', () => {
|
|
@@ -93,16 +105,24 @@ describe('compositions', () => {
|
|
|
93
105
|
|
|
94
106
|
it('should require services to be a non-empty array of strings', () => {
|
|
95
107
|
context.compositions[0].services = []
|
|
96
|
-
assert.throws(
|
|
108
|
+
assert.throws(
|
|
109
|
+
() => validate(context),
|
|
110
|
+
(error) => /fewer than 1 items/.test(error.message)
|
|
111
|
+
)
|
|
97
112
|
|
|
98
113
|
context.compositions[0].services = 'exposition'
|
|
99
|
-
assert.throws(
|
|
114
|
+
assert.throws(
|
|
115
|
+
() => validate(context),
|
|
116
|
+
(error) => /must be array/.test(error.message)
|
|
117
|
+
)
|
|
100
118
|
})
|
|
101
119
|
|
|
102
120
|
it('should reject an unknown property', () => {
|
|
103
121
|
context.compositions[0].compoments = ['a.b']
|
|
104
122
|
|
|
105
|
-
assert.throws(
|
|
106
|
-
(
|
|
123
|
+
assert.throws(
|
|
124
|
+
() => validate(context),
|
|
125
|
+
(error) => /Property compoments is not expected to be here/.test(error.message)
|
|
126
|
+
)
|
|
107
127
|
})
|
|
108
128
|
})
|
package/test/shortcuts.test.js
CHANGED
|
@@ -55,7 +55,8 @@ describe('recognize', () => {
|
|
|
55
55
|
|
|
56
56
|
assert.deepStrictEqual(object[group], known)
|
|
57
57
|
|
|
58
|
-
for (const alias of Object.keys(fixtures.SHORTCUTS))
|
|
58
|
+
for (const alias of Object.keys(fixtures.SHORTCUTS))
|
|
59
|
+
assert.strictEqual(object[alias], undefined)
|
|
59
60
|
})
|
|
60
61
|
|
|
61
62
|
it('should not overwrite group', () => {
|
package/types/component.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { Locator
|
|
1
|
+
import type { Locator } from '@toa.io/core'
|
|
2
|
+
import type { operations } from '@toa.io/core/types'
|
|
2
3
|
|
|
3
4
|
type Map = {
|
|
4
5
|
[id: string]: Component
|
|
@@ -35,10 +36,20 @@ type Receiver = {
|
|
|
35
36
|
source?: string
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
type
|
|
39
|
-
|
|
39
|
+
type Migration = {
|
|
40
|
+
id: string
|
|
41
|
+
steps: unknown[]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type Entity = {
|
|
45
|
+
properties: Record<string, Object>
|
|
46
|
+
required?: string[]
|
|
47
|
+
/** what a record holds before anything is written to it */
|
|
48
|
+
blank?: Record<string, unknown>
|
|
40
49
|
storage?: string
|
|
41
50
|
associated?: boolean
|
|
51
|
+
custom?: boolean
|
|
52
|
+
migrations?: Migration[]
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
type Declaration = {
|