@toa.io/norm 1.0.0-alpha.33 → 1.0.0-alpha.330
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/CHANGELOG.md +381 -0
- package/package.json +9 -7
- package/src/.component/.expand/bridge.js +2 -6
- package/src/.component/.expand/entity.js +2 -9
- package/src/.component/.expand/events.js +2 -6
- package/src/.component/.expand/extensions.js +8 -9
- package/src/.component/.expand/index.js +7 -19
- package/src/.component/.expand/operations.js +2 -9
- package/src/.component/.expand/receivers.js +4 -7
- package/src/.component/.expand/version.js +72 -28
- package/src/.component/.normalize/entity.js +7 -0
- package/src/.component/.normalize/events.js +16 -4
- package/src/.component/.normalize/index.js +4 -9
- package/src/.component/.normalize/operations.js +13 -6
- package/src/.component/.normalize/receivers.js +4 -5
- package/src/.component/collapse.js +60 -9
- package/src/.component/defaults.js +5 -11
- package/src/.component/dependencies.js +14 -10
- package/src/.component/dereference.js +5 -49
- package/src/.component/expand.js +3 -9
- package/src/.component/extensions.js +9 -10
- package/src/.component/index.js +10 -21
- package/src/.component/merge.js +25 -15
- package/src/.component/migrations.js +57 -0
- package/src/.component/normalize.js +10 -6
- package/src/.component/schema.yaml +165 -70
- package/src/.component/validate.js +67 -17
- package/src/.context/.dependencies/connectors.js +5 -9
- package/src/.context/.dependencies/describe.js +4 -5
- package/src/.context/.dependencies/extensions.js +98 -20
- package/src/.context/.dependencies/index.js +3 -9
- package/src/.context/.dependencies/resolve.js +44 -7
- package/src/.context/complete.js +1 -5
- package/src/.context/converge.js +31 -0
- package/src/.context/dependencies.js +44 -6
- package/src/.context/dereference.js +11 -6
- package/src/.context/evict.js +40 -0
- package/src/.context/expand.js +37 -5
- package/src/.context/index.js +8 -15
- package/src/.context/normalize.js +9 -7
- package/src/.context/schema.yaml +183 -11
- package/src/.context/validate.js +11 -14
- package/src/component.js +33 -14
- package/src/context.js +47 -15
- package/src/definition.js +63 -0
- package/src/entity.js +15 -0
- package/src/index.js +7 -9
- package/src/manifest.js +159 -0
- package/src/map.js +26 -0
- package/src/shortcuts.js +12 -15
- package/test/component/collapse.fixtures.js +30 -39
- package/test/component/collapse.test.js +90 -10
- package/test/component/dependencies.test.js +16 -14
- package/test/component/dereference.fixtures.js +10 -98
- package/test/component/dereference.test.js +10 -14
- package/test/component/dummies/extension/index.js +1 -5
- 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/expand.fixtures.js +13 -13
- package/test/component/expand.test.js +17 -13
- package/test/component/migrations.test.js +62 -0
- package/test/component/normalize.fixtures.js +3 -7
- package/test/component/normalize.test.js +94 -28
- package/test/component/validate.fixtures.js +17 -15
- package/test/component/validate.test.js +311 -108
- package/test/context/complete.fixtures.js +2 -8
- package/test/context/complete.test.js +14 -8
- package/test/context/converge.test.js +57 -0
- package/test/context/dereference.fixtures.js +3 -9
- package/test/context/dereference.test.js +18 -7
- package/test/context/evict.fixtures.js +39 -0
- package/test/context/evict.test.js +73 -0
- package/test/context/expand.fixtures.js +1 -5
- package/test/context/expand.test.js +36 -8
- package/test/context/normalize.fixtures.js +2 -7
- package/test/context/normalize.test.js +33 -9
- package/test/context/validate.fixtures.js +1 -6
- package/test/context/validate.test.js +173 -35
- package/test/definition.test.js +35 -0
- package/test/dummies/bare/index.js +1 -0
- package/test/dummies/defined/definition.js +1 -0
- package/test/dummies/defined/index.js +1 -0
- package/test/dummies/defined/package.json +1 -0
- package/test/manifest.test.js +137 -0
- package/test/shortcuts.fixtures.js +4 -9
- package/test/shortcuts.test.js +17 -15
- package/test/system.test.js +31 -0
- package/types/component.d.ts +32 -5
- package/types/context/declaration.d.ts +34 -3
- package/types/context.d.ts +27 -13
- package/types/entity.d.ts +7 -0
- package/types/index.d.ts +18 -3
- package/src/.component/.expand/properties.js +0 -14
- package/src/.context/.dependencies/load.js +0 -36
- package/test/context/dependencies.load.test.js +0 -24
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import clone from 'clone-deep'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
const { validate } = require('../../src/.component')
|
|
8
|
-
const fixtures = require('./validate.fixtures')
|
|
6
|
+
import { validate } from '../../src/.component/index.js'
|
|
7
|
+
import * as fixtures from './validate.fixtures.js'
|
|
9
8
|
|
|
10
9
|
let manifest
|
|
11
10
|
|
|
@@ -13,248 +12,452 @@ beforeEach(() => {
|
|
|
13
12
|
manifest = clone(fixtures.ok)
|
|
14
13
|
})
|
|
15
14
|
|
|
16
|
-
it('should be ok', () => {
|
|
17
|
-
|
|
15
|
+
it('should be ok', async () => {
|
|
16
|
+
await assert.doesNotReject(validate(manifest))
|
|
18
17
|
})
|
|
19
18
|
|
|
20
|
-
it('should provide error', () => {
|
|
19
|
+
it('should provide error', async () => {
|
|
21
20
|
manifest.foo = 'bar'
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
await assert.rejects(validate(manifest))
|
|
24
23
|
})
|
|
25
24
|
|
|
26
|
-
it('should not have additional properties', () => {
|
|
25
|
+
it('should not have additional properties', async () => {
|
|
27
26
|
manifest.foo = 'bar'
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
await assert.rejects(validate(manifest))
|
|
30
29
|
})
|
|
31
30
|
|
|
32
31
|
describe('namespace', () => {
|
|
33
|
-
it('should match token pattern', () => {
|
|
32
|
+
it('should match token pattern', async () => {
|
|
34
33
|
manifest.namespace = '1'
|
|
35
|
-
|
|
34
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
35
|
+
/must match pattern/.test(error.message)
|
|
36
|
+
)
|
|
36
37
|
|
|
37
38
|
manifest.namespace = 'foo_'
|
|
38
|
-
|
|
39
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
40
|
+
/must match pattern/.test(error.message)
|
|
41
|
+
)
|
|
39
42
|
|
|
40
43
|
manifest.namespace = 'foo_bar'
|
|
41
|
-
|
|
44
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
45
|
+
/must match pattern/.test(error.message)
|
|
46
|
+
)
|
|
42
47
|
|
|
43
48
|
manifest.namespace = 'foo-'
|
|
44
|
-
|
|
49
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
50
|
+
/must match pattern/.test(error.message)
|
|
51
|
+
)
|
|
45
52
|
|
|
46
53
|
manifest.namespace = 'foo-bar'
|
|
47
|
-
|
|
54
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
55
|
+
/must match pattern/.test(error.message)
|
|
56
|
+
)
|
|
48
57
|
|
|
49
58
|
manifest.namespace = 'FooBar12'
|
|
50
|
-
|
|
59
|
+
await assert.doesNotReject(validate(manifest))
|
|
51
60
|
})
|
|
52
61
|
|
|
53
|
-
it(
|
|
62
|
+
it("should forbid 'system' namespace", async () => {
|
|
54
63
|
manifest.namespace = 'system'
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
it('should set `default` namespace', async () => {
|
|
59
|
-
delete manifest.namespace
|
|
60
|
-
|
|
61
|
-
expect(() => validate(manifest)).not.toThrow()
|
|
62
|
-
expect(manifest.namespace).toStrictEqual('default')
|
|
64
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
65
|
+
/must NOT be valid/.test(error.message)
|
|
66
|
+
)
|
|
63
67
|
})
|
|
64
68
|
})
|
|
65
69
|
|
|
66
70
|
describe('name', () => {
|
|
67
|
-
it('should be optional', () => {
|
|
71
|
+
it('should be optional', async () => {
|
|
68
72
|
delete manifest.name
|
|
69
|
-
|
|
73
|
+
await assert.doesNotReject(validate(manifest))
|
|
70
74
|
})
|
|
71
75
|
})
|
|
72
76
|
|
|
73
77
|
describe('entity', () => {
|
|
74
|
-
it('should be optional', () => {
|
|
78
|
+
it('should be optional', async () => {
|
|
75
79
|
delete manifest.entity
|
|
76
|
-
|
|
80
|
+
await assert.doesNotReject(validate(manifest))
|
|
77
81
|
})
|
|
78
82
|
|
|
79
|
-
it('should be object', () => {
|
|
83
|
+
it('should be object', async () => {
|
|
80
84
|
manifest.entity = 'foo'
|
|
81
|
-
|
|
85
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
86
|
+
/must be object/.test(error.message)
|
|
87
|
+
)
|
|
82
88
|
})
|
|
83
89
|
|
|
84
|
-
it('should not have additional properties', () => {
|
|
90
|
+
it('should not have additional properties', async () => {
|
|
85
91
|
manifest.entity.foo = 'bar'
|
|
86
|
-
|
|
92
|
+
await assert.rejects(validate(manifest))
|
|
87
93
|
})
|
|
88
94
|
|
|
89
|
-
describe('
|
|
90
|
-
it('should be required', () => {
|
|
91
|
-
delete manifest.entity.
|
|
92
|
-
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
it('should be JSON schema object', () => {
|
|
96
|
-
manifest.entity.schema = { properties: { foo: 'bar' } }
|
|
97
|
-
expect(() => validate(manifest)).toThrow()
|
|
95
|
+
describe('properties', () => {
|
|
96
|
+
it('should be required', async () => {
|
|
97
|
+
delete manifest.entity.properties
|
|
98
|
+
await assert.rejects(validate(manifest))
|
|
98
99
|
})
|
|
99
100
|
|
|
100
|
-
it('should be JSON
|
|
101
|
-
manifest.entity.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
manifest.entity.schema = {}
|
|
105
|
-
validate(manifest)
|
|
106
|
-
expect(manifest.entity.schema.type).toBe('object')
|
|
101
|
+
it('should be JSON schemas', async () => {
|
|
102
|
+
manifest.entity.properties.foo = 'bar'
|
|
103
|
+
await assert.rejects(validate(manifest))
|
|
107
104
|
})
|
|
108
105
|
|
|
109
|
-
it('should have
|
|
110
|
-
manifest.entity.
|
|
111
|
-
|
|
106
|
+
it('should have names matching token pattern', async () => {
|
|
107
|
+
manifest.entity.properties._foo = { type: 'string' }
|
|
108
|
+
await assert.rejects(validate(manifest), (error) => /pattern/.test(error.message))
|
|
112
109
|
})
|
|
113
110
|
|
|
114
|
-
it('should allow
|
|
115
|
-
manifest.entity.
|
|
111
|
+
it('should allow own id', async () => {
|
|
112
|
+
manifest.entity.properties.id = {
|
|
116
113
|
type: 'string',
|
|
117
114
|
pattern: '^[a-fA-F0-9]+$'
|
|
118
115
|
}
|
|
119
|
-
|
|
116
|
+
await assert.doesNotReject(validate(manifest))
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should not declare TRAILERS', async () => {
|
|
120
|
+
manifest.entity.properties.TRAILERS = { type: 'object' }
|
|
121
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
122
|
+
/System property 'TRAILERS' cannot be declared/.test(error.message)
|
|
123
|
+
)
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
it('should not declare DISCARD', async () => {
|
|
127
|
+
manifest.entity.properties.DISCARD = { type: 'boolean' }
|
|
128
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
129
|
+
/System property 'DISCARD' cannot be declared/.test(error.message)
|
|
130
|
+
)
|
|
120
131
|
})
|
|
121
132
|
})
|
|
122
133
|
|
|
123
|
-
describe('
|
|
124
|
-
it('should
|
|
125
|
-
|
|
126
|
-
|
|
134
|
+
describe('required', () => {
|
|
135
|
+
it('should name declared properties', async () => {
|
|
136
|
+
manifest.entity.required = ['name']
|
|
137
|
+
await assert.doesNotReject(validate(manifest))
|
|
138
|
+
|
|
139
|
+
manifest.entity.required = ['nope']
|
|
140
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
141
|
+
/requires property 'nope'/.test(error.message)
|
|
142
|
+
)
|
|
143
|
+
})
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
describe('blank', () => {
|
|
147
|
+
it('should name declared properties', async () => {
|
|
148
|
+
manifest.entity.blank = { name: 'whatever' }
|
|
149
|
+
await assert.doesNotReject(validate(manifest))
|
|
150
|
+
|
|
151
|
+
manifest.entity.blank = { nope: 1 }
|
|
152
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
153
|
+
/names property 'nope'/.test(error.message)
|
|
154
|
+
)
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should not name a system property', async () => {
|
|
158
|
+
manifest.entity.blank = { VERSION: 1 }
|
|
159
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
160
|
+
/must NOT be valid/.test(error.message)
|
|
161
|
+
)
|
|
162
|
+
})
|
|
163
|
+
|
|
164
|
+
it('should not name TRAILERS', async () => {
|
|
165
|
+
manifest.entity.blank = { TRAILERS: {} }
|
|
166
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
167
|
+
/must NOT be valid/.test(error.message)
|
|
168
|
+
)
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
it('should not name DISCARD', async () => {
|
|
172
|
+
manifest.entity.blank = { DISCARD: true }
|
|
173
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
174
|
+
/must NOT be valid/.test(error.message)
|
|
175
|
+
)
|
|
127
176
|
})
|
|
128
177
|
})
|
|
129
178
|
})
|
|
130
179
|
|
|
131
180
|
describe('bindings', () => {
|
|
132
|
-
it('should be array of unique strings', () => {
|
|
181
|
+
it('should be array of unique strings', async () => {
|
|
133
182
|
manifest.bindings = 'oops'
|
|
134
|
-
|
|
183
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
184
|
+
/must be array/.test(error.message)
|
|
185
|
+
)
|
|
135
186
|
|
|
136
187
|
manifest.bindings = ['oops', 'oops']
|
|
137
|
-
|
|
188
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
189
|
+
/duplicate items/.test(error.message)
|
|
190
|
+
)
|
|
138
191
|
|
|
139
192
|
manifest.bindings = ['oops', {}]
|
|
140
|
-
|
|
193
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
194
|
+
/must be string/.test(error.message)
|
|
195
|
+
)
|
|
141
196
|
})
|
|
142
197
|
|
|
143
|
-
it('should forbid explicit loop', () => {
|
|
198
|
+
it('should forbid explicit loop', async () => {
|
|
144
199
|
manifest.bindings = ['@toa.io/bindings.loop']
|
|
145
|
-
|
|
200
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
201
|
+
/must NOT be valid/.test(error.message)
|
|
202
|
+
)
|
|
146
203
|
})
|
|
147
204
|
})
|
|
148
205
|
|
|
149
206
|
describe('operations', () => {
|
|
150
|
-
it('should be object', () => {
|
|
207
|
+
it('should be object', async () => {
|
|
151
208
|
manifest.operations.get = 'bar'
|
|
152
|
-
|
|
209
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
210
|
+
/must be object/.test(error.message)
|
|
211
|
+
)
|
|
153
212
|
})
|
|
154
213
|
|
|
155
|
-
it('should not have additional properties', () => {
|
|
214
|
+
it('should not have additional properties', async () => {
|
|
156
215
|
manifest.operations.get.foo = 'bar'
|
|
157
|
-
|
|
216
|
+
await assert.rejects(validate(manifest))
|
|
158
217
|
})
|
|
159
218
|
|
|
160
|
-
it('should have type (transition or observation)', () => {
|
|
219
|
+
it('should have type (transition or observation)', async () => {
|
|
161
220
|
delete manifest.operations.get.type
|
|
162
|
-
|
|
221
|
+
await assert.rejects(validate(manifest))
|
|
163
222
|
|
|
164
223
|
manifest.operations.get.type = 'foo'
|
|
165
|
-
|
|
224
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
225
|
+
/one of the allowed values/.test(error.message)
|
|
226
|
+
)
|
|
166
227
|
})
|
|
167
228
|
|
|
168
|
-
it('should forbid explicit loop', () => {
|
|
229
|
+
it('should forbid explicit loop', async () => {
|
|
169
230
|
manifest.operations.get.bindings = ['@toa.io/bindings.loop']
|
|
170
|
-
|
|
231
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
232
|
+
/must NOT be valid/.test(error.message)
|
|
233
|
+
)
|
|
171
234
|
})
|
|
172
235
|
|
|
173
|
-
it('should forbid query: false for observations', () => {
|
|
236
|
+
it('should forbid query: false for observations', async () => {
|
|
174
237
|
manifest.operations.get.query = false
|
|
175
|
-
|
|
238
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
239
|
+
/must NOT be valid/.test(error.message)
|
|
240
|
+
)
|
|
176
241
|
})
|
|
177
242
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
validate(manifest)
|
|
243
|
+
for (const [_, operation] of [['computation', 'compute']])
|
|
244
|
+
it(`should set query: false for ${_}`, async () => {
|
|
245
|
+
validate(manifest)
|
|
182
246
|
|
|
183
|
-
|
|
184
|
-
|
|
247
|
+
assert.strictEqual(manifest.operations[operation].query, false)
|
|
248
|
+
})
|
|
185
249
|
|
|
186
250
|
describe('scope', () => {
|
|
187
|
-
it('should have scope', () => {
|
|
251
|
+
it('should have scope', async () => {
|
|
188
252
|
delete manifest.operations.get.scope
|
|
189
|
-
|
|
253
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
254
|
+
/required property/.test(error.message)
|
|
255
|
+
)
|
|
190
256
|
})
|
|
191
257
|
|
|
192
|
-
it('should allow only entity or set for observations', () => {
|
|
258
|
+
it('should allow only entity or set for observations', async () => {
|
|
193
259
|
manifest.operations.get.scope = 'changeset'
|
|
194
|
-
|
|
260
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
261
|
+
/allowed values/.test(error.message)
|
|
262
|
+
)
|
|
195
263
|
})
|
|
196
264
|
|
|
197
|
-
it('should allow only entity for transitions', () => {
|
|
265
|
+
it('should allow only entity for transitions', async () => {
|
|
198
266
|
manifest.operations.add.scope = 'changeset'
|
|
199
|
-
|
|
267
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
268
|
+
/allowed values/.test(error.message)
|
|
269
|
+
)
|
|
200
270
|
|
|
201
271
|
manifest.operations.add.scope = 'set'
|
|
202
|
-
|
|
272
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
273
|
+
/allowed values/.test(error.message)
|
|
274
|
+
)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
it('should refuse the names the scopes had before `entry` and `entries`', async () => {
|
|
278
|
+
for (const scope of ['object', 'objects']) {
|
|
279
|
+
manifest.operations.get.scope = scope
|
|
280
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
281
|
+
/allowed values/.test(error.message)
|
|
282
|
+
)
|
|
283
|
+
}
|
|
203
284
|
})
|
|
204
285
|
|
|
205
|
-
it('should allow only changeset for assignments', () => {
|
|
286
|
+
it('should allow only changeset for assignments', async () => {
|
|
206
287
|
manifest.operations.set.scope = 'changeset'
|
|
207
|
-
|
|
288
|
+
await assert.doesNotReject(validate(manifest))
|
|
208
289
|
|
|
209
290
|
manifest.operations.set.scope = 'set'
|
|
210
|
-
|
|
291
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
292
|
+
/allowed values/.test(error.message)
|
|
293
|
+
)
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
describe('once', () => {
|
|
298
|
+
it('should be true, false or a number of seconds', async () => {
|
|
299
|
+
for (const once of [true, false, 600, 86400]) {
|
|
300
|
+
manifest.operations.add.once = once
|
|
301
|
+
await assert.doesNotReject(validate(manifest), `once: ${once}`)
|
|
302
|
+
}
|
|
303
|
+
})
|
|
304
|
+
|
|
305
|
+
it('should stay true rather than be coerced into a number', async () => {
|
|
306
|
+
manifest.operations.add.once = true
|
|
307
|
+
await validate(manifest)
|
|
308
|
+
|
|
309
|
+
assert.strictEqual(manifest.operations.add.once, true)
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
it('should not be a window shorter than ten minutes', async () => {
|
|
313
|
+
manifest.operations.add.once = 599
|
|
314
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
315
|
+
/must be >= 600/.test(error.message)
|
|
316
|
+
)
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
it('should not be a fraction of a second', async () => {
|
|
320
|
+
manifest.operations.add.once = 600.5
|
|
321
|
+
await assert.rejects(validate(manifest))
|
|
322
|
+
})
|
|
323
|
+
|
|
324
|
+
it('should throw for an operation that takes none', async () => {
|
|
325
|
+
manifest.operations.get.once = 600
|
|
326
|
+
await assert.rejects(validate(manifest))
|
|
211
327
|
})
|
|
212
328
|
})
|
|
213
329
|
|
|
214
330
|
describe('concurrency', () => {
|
|
215
|
-
it('should be required for transitions', () => {
|
|
331
|
+
it('should be required for transitions', async () => {
|
|
216
332
|
delete manifest.operations.add.concurrency
|
|
217
|
-
|
|
333
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
334
|
+
/required property/.test(error.message)
|
|
335
|
+
)
|
|
218
336
|
})
|
|
219
337
|
|
|
220
|
-
it('should throw for observations, assignments', () => {
|
|
338
|
+
it('should throw for observations, assignments', async () => {
|
|
221
339
|
manifest.operations.get.concurrency = 'none'
|
|
222
|
-
|
|
340
|
+
await assert.rejects(validate(manifest))
|
|
223
341
|
delete manifest.operations.get.concurrency
|
|
224
342
|
|
|
225
343
|
manifest.operations.set.concurrency = 'none'
|
|
226
|
-
|
|
344
|
+
await assert.rejects(validate(manifest))
|
|
227
345
|
})
|
|
228
346
|
})
|
|
229
347
|
|
|
230
348
|
describe('input, output', () => {
|
|
231
|
-
it('should be schema', () => {
|
|
349
|
+
it('should be schema', async () => {
|
|
232
350
|
manifest.operations.get.input = { properties: { foo: 'bar' } }
|
|
233
|
-
|
|
351
|
+
await assert.rejects(validate(manifest))
|
|
234
352
|
|
|
235
353
|
delete manifest.operations.get.input
|
|
236
354
|
manifest.operations.get.output = { properties: { foo: 'bar' } }
|
|
237
|
-
|
|
355
|
+
await assert.rejects(validate(manifest))
|
|
238
356
|
})
|
|
239
357
|
})
|
|
240
358
|
})
|
|
241
359
|
|
|
242
360
|
describe('receivers', () => {
|
|
243
|
-
it('should throw if transition points to undefined operation', () => {
|
|
361
|
+
it('should throw if transition points to undefined operation', async () => {
|
|
244
362
|
manifest.receivers['foo.bar.happened'].operation = 'notExists'
|
|
245
363
|
|
|
246
|
-
|
|
364
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
365
|
+
/refers to undefined operation/.test(error.message)
|
|
366
|
+
)
|
|
247
367
|
})
|
|
248
368
|
|
|
249
|
-
it('should throw if transition points to observation', () => {
|
|
369
|
+
it('should throw if transition points to observation', async () => {
|
|
250
370
|
manifest.receivers['foo.bar.happened'].operation = 'get'
|
|
251
371
|
|
|
252
|
-
|
|
372
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
373
|
+
/of the allowed types/.test(error.message)
|
|
374
|
+
)
|
|
253
375
|
})
|
|
254
376
|
|
|
255
377
|
it('should throw if source has a name `context`', async () => {
|
|
256
378
|
manifest.receivers['foo.bar.happened'].source = 'context'
|
|
257
379
|
|
|
258
|
-
|
|
380
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
381
|
+
/must NOT be valid/.test(error.message)
|
|
382
|
+
)
|
|
383
|
+
})
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
describe('stateful', () => {
|
|
387
|
+
it('should be allowed on an operation', async () => {
|
|
388
|
+
const [operation] = Object.values(manifest.operations)
|
|
389
|
+
|
|
390
|
+
operation.stateful = true
|
|
391
|
+
|
|
392
|
+
await assert.doesNotReject(validate(manifest))
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
it('should be a boolean', async () => {
|
|
396
|
+
const [operation] = Object.values(manifest.operations)
|
|
397
|
+
|
|
398
|
+
operation.stateful = 'yes'
|
|
399
|
+
|
|
400
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
401
|
+
/must be boolean/.test(error.message)
|
|
402
|
+
)
|
|
403
|
+
})
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
describe('unchained', () => {
|
|
407
|
+
it('should be allowed on an operation', async () => {
|
|
408
|
+
const [operation] = Object.values(manifest.operations)
|
|
409
|
+
|
|
410
|
+
operation.unchained = true
|
|
411
|
+
|
|
412
|
+
await assert.doesNotReject(validate(manifest))
|
|
413
|
+
})
|
|
414
|
+
|
|
415
|
+
it('should be a boolean', async () => {
|
|
416
|
+
const [operation] = Object.values(manifest.operations)
|
|
417
|
+
|
|
418
|
+
operation.unchained = 'yes'
|
|
419
|
+
|
|
420
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
421
|
+
/must be boolean/.test(error.message)
|
|
422
|
+
)
|
|
423
|
+
})
|
|
424
|
+
})
|
|
425
|
+
|
|
426
|
+
describe('stream', () => {
|
|
427
|
+
beforeEach(() => {
|
|
428
|
+
manifest.operations.read = {
|
|
429
|
+
type: 'computation',
|
|
430
|
+
scope: 'none',
|
|
431
|
+
query: false,
|
|
432
|
+
bridge: 'whatever',
|
|
433
|
+
bindings: ['@toa.io/bindings.amqp'],
|
|
434
|
+
stream: 'content'
|
|
435
|
+
}
|
|
436
|
+
})
|
|
437
|
+
|
|
438
|
+
it('should reject an operation whose bindings carry no stream', async () => {
|
|
439
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
440
|
+
/takes a stream, which none of its bindings carries/.test(error.message)
|
|
441
|
+
)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
it('should accept an operation served where it is composed', async () => {
|
|
445
|
+
manifest.operations.read.bindings = []
|
|
446
|
+
|
|
447
|
+
await assert.doesNotReject(validate(manifest))
|
|
448
|
+
})
|
|
449
|
+
|
|
450
|
+
it('should accept an operation bound to a binding that carries a stream', async () => {
|
|
451
|
+
manifest.operations.read.bindings = ['@toa.io/bindings.http']
|
|
452
|
+
|
|
453
|
+
await assert.doesNotReject(validate(manifest))
|
|
454
|
+
})
|
|
455
|
+
|
|
456
|
+
it('should reject a transition that takes a stream', async () => {
|
|
457
|
+
manifest.operations.add.stream = 'content'
|
|
458
|
+
|
|
459
|
+
await assert.rejects(validate(manifest), (error) =>
|
|
460
|
+
/must be equal to one of the allowed values/.test(error.message)
|
|
461
|
+
)
|
|
259
462
|
})
|
|
260
463
|
})
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const component = (id) => {
|
|
4
2
|
const [namespace, name] = id.split('.')
|
|
5
3
|
|
|
@@ -16,12 +14,11 @@ const component = (id) => {
|
|
|
16
14
|
}
|
|
17
15
|
}
|
|
18
16
|
|
|
19
|
-
const context = {
|
|
17
|
+
export const context = {
|
|
20
18
|
runtime: '0.0.0',
|
|
21
19
|
name: 'test',
|
|
22
20
|
description: 'context fixture',
|
|
23
21
|
version: '0.0.0',
|
|
24
|
-
packages: 'namespaces/**/*',
|
|
25
22
|
registry: 'localhost:5000',
|
|
26
23
|
components: [
|
|
27
24
|
component('a.b'),
|
|
@@ -43,7 +40,7 @@ const context = {
|
|
|
43
40
|
}
|
|
44
41
|
|
|
45
42
|
/** @type {Array<toa.norm.context.Composition>} */
|
|
46
|
-
const compositions = [
|
|
43
|
+
export const compositions = [
|
|
47
44
|
...context.compositions,
|
|
48
45
|
{
|
|
49
46
|
name: 'd-a',
|
|
@@ -54,6 +51,3 @@ const compositions = [
|
|
|
54
51
|
components: [component('d.b')]
|
|
55
52
|
}
|
|
56
53
|
]
|
|
57
|
-
|
|
58
|
-
exports.context = context
|
|
59
|
-
exports.compositions = compositions
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { isDeepStrictEqual } from 'node:util'
|
|
2
4
|
|
|
3
|
-
|
|
5
|
+
import clone from 'clone-deep'
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
import { complete } from '../../src/.context/index.js'
|
|
8
|
+
import * as fixtures from './complete.fixtures.js'
|
|
7
9
|
|
|
8
10
|
/** @type {toa.norm.Context} */
|
|
9
11
|
let context
|
|
@@ -14,8 +16,12 @@ beforeEach(() => {
|
|
|
14
16
|
})
|
|
15
17
|
|
|
16
18
|
it('should complete compositions', () => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
assert.deepStrictEqual(context.compositions.length, fixtures.compositions.length)
|
|
20
|
+
assert.ok(
|
|
21
|
+
fixtures.compositions.every((item) =>
|
|
22
|
+
context.compositions.some((candidate) => isDeepStrictEqual(candidate, item))
|
|
23
|
+
)
|
|
24
|
+
)
|
|
19
25
|
})
|
|
20
26
|
|
|
21
27
|
it('should create if compositions are not set', () => {
|
|
@@ -26,6 +32,6 @@ it('should create if compositions are not set', () => {
|
|
|
26
32
|
components: [component]
|
|
27
33
|
}))
|
|
28
34
|
|
|
29
|
-
|
|
30
|
-
|
|
35
|
+
assert.doesNotThrow(() => complete(context))
|
|
36
|
+
assert.deepStrictEqual(context.compositions, compositions)
|
|
31
37
|
})
|