@toa.io/norm 1.0.0-alpha.32 → 1.0.0-alpha.321
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 +305 -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 +7 -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 +69 -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 -73
- package/src/.component/validate.js +63 -17
- package/src/.context/.dependencies/connectors.js +5 -9
- package/src/.context/.dependencies/describe.js +4 -5
- package/src/.context/.dependencies/extensions.js +93 -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 +43 -6
- package/src/.context/dereference.js +11 -6
- package/src/.context/evict.js +38 -0
- package/src/.context/expand.js +11 -6
- 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 +44 -15
- package/src/definition.js +63 -0
- package/src/entity.js +15 -0
- package/src/index.js +7 -9
- package/src/manifest.js +154 -0
- package/src/map.js +24 -0
- package/src/shortcuts.js +11 -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 +289 -109
- 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 +126 -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 +31 -4
- 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
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import { converge } from '../../src/.context/index.js'
|
|
5
|
+
|
|
6
|
+
const REFERENCE = '@toa.io/extensions.convergence'
|
|
7
|
+
|
|
8
|
+
/** @type {object} */
|
|
9
|
+
let context
|
|
10
|
+
|
|
11
|
+
/** @type {object[]} */
|
|
12
|
+
let components
|
|
13
|
+
|
|
14
|
+
const component = (id, properties = {}) => ({ id, entity: {}, ...properties })
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
context = { annotations: { [REFERENCE]: { priority: 0 } } }
|
|
18
|
+
|
|
19
|
+
components = [
|
|
20
|
+
component('store.orders'),
|
|
21
|
+
// what an extension ships reaches a deployment the same way and converges the same way:
|
|
22
|
+
// an application whose identity does not converge has no users in its second region
|
|
23
|
+
component('identity.basic'),
|
|
24
|
+
component('exposition.octets', { entity: undefined })
|
|
25
|
+
]
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('should give the extension to every component that stores anything', () => {
|
|
29
|
+
converge(context, components)
|
|
30
|
+
|
|
31
|
+
assert.deepEqual(components[0].extensions, { [REFERENCE]: null })
|
|
32
|
+
assert.deepEqual(components[1].extensions, { [REFERENCE]: null })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should leave a component that stores nothing alone', () => {
|
|
36
|
+
converge(context, components)
|
|
37
|
+
|
|
38
|
+
assert.equal(components[2].extensions, undefined)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('should do nothing where the context is not a region', () => {
|
|
42
|
+
delete context.annotations
|
|
43
|
+
|
|
44
|
+
converge(context, components)
|
|
45
|
+
|
|
46
|
+
for (const one of components) assert.equal(one.extensions, undefined)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should leave what a manifest declared as it was', () => {
|
|
50
|
+
const declaration = { some: 'declaration' }
|
|
51
|
+
|
|
52
|
+
components[0].extensions = { [REFERENCE]: declaration }
|
|
53
|
+
|
|
54
|
+
converge(context, components)
|
|
55
|
+
|
|
56
|
+
assert.equal(components[0].extensions[REFERENCE], declaration)
|
|
57
|
+
})
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const clone = require('clone-deep')
|
|
1
|
+
import clone from 'clone-deep'
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* @param id {string}
|
|
@@ -22,12 +20,11 @@ const component = (id) => {
|
|
|
22
20
|
}
|
|
23
21
|
}
|
|
24
22
|
|
|
25
|
-
const context = {
|
|
23
|
+
export const context = {
|
|
26
24
|
name: 'test',
|
|
27
25
|
description: 'context fixture',
|
|
28
26
|
version: '0.0.0',
|
|
29
27
|
runtime: '0.0.0',
|
|
30
|
-
packages: 'namespaces/**/*',
|
|
31
28
|
registry: 'localhost:5000',
|
|
32
29
|
components: [component('a.b'), component('b.a'), component('d.c')],
|
|
33
30
|
compositions: [
|
|
@@ -42,7 +39,7 @@ const context = {
|
|
|
42
39
|
]
|
|
43
40
|
}
|
|
44
41
|
|
|
45
|
-
const expected = clone(context)
|
|
42
|
+
export const expected = clone(context)
|
|
46
43
|
|
|
47
44
|
expected.compositions = [
|
|
48
45
|
{
|
|
@@ -54,6 +51,3 @@ expected.compositions = [
|
|
|
54
51
|
components: [component('d.c'), component('a.b')]
|
|
55
52
|
}
|
|
56
53
|
]
|
|
57
|
-
|
|
58
|
-
exports.context = context
|
|
59
|
-
exports.expected = expected
|
|
@@ -1,24 +1,35 @@
|
|
|
1
|
-
|
|
1
|
+
import { it, before } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import clone from 'clone-deep'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { dereference } from '../../src/.context/index.js'
|
|
7
|
+
import * as fixtures from './dereference.fixtures.js'
|
|
7
8
|
|
|
8
9
|
/** @type {toa.norm.Context} */
|
|
9
10
|
let context
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
before(() => {
|
|
12
13
|
context = clone(fixtures.context)
|
|
13
14
|
dereference(context)
|
|
14
15
|
})
|
|
15
16
|
|
|
16
17
|
it('should dereference', () => {
|
|
17
|
-
|
|
18
|
+
assert.partialDeepStrictEqual(context, fixtures.expected)
|
|
18
19
|
})
|
|
19
20
|
|
|
20
21
|
it('should not throw on empty compositions', () => {
|
|
21
22
|
context.compositions = undefined
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
assert.doesNotThrow(() => dereference(context))
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
it('should throw on an unknown component', () => {
|
|
28
|
+
const broken = clone(fixtures.context)
|
|
29
|
+
|
|
30
|
+
broken.compositions[1].components.push('nope.here')
|
|
31
|
+
|
|
32
|
+
assert.throws(() => dereference(broken), {
|
|
33
|
+
message: "Composition 'bar' lists an unknown component 'nope.here'."
|
|
34
|
+
})
|
|
24
35
|
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @param id {string}
|
|
3
|
+
* @return {toa.norm.component.Declaration}
|
|
4
|
+
*/
|
|
5
|
+
const component = (id) => {
|
|
6
|
+
const [namespace, name] = id.split('.')
|
|
7
|
+
|
|
8
|
+
return {
|
|
9
|
+
namespace,
|
|
10
|
+
name,
|
|
11
|
+
version: '0.0.0',
|
|
12
|
+
locator: {
|
|
13
|
+
namespace,
|
|
14
|
+
name,
|
|
15
|
+
id,
|
|
16
|
+
label: `${namespace}-${name}`
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const context = {
|
|
22
|
+
name: 'test',
|
|
23
|
+
description: 'context fixture',
|
|
24
|
+
version: '0.0.0',
|
|
25
|
+
runtime: '0.0.0',
|
|
26
|
+
registry: 'localhost:5000',
|
|
27
|
+
components: [component('a.b'), component('b.a'), component('d.c')],
|
|
28
|
+
compositions: [
|
|
29
|
+
{
|
|
30
|
+
name: 'foo',
|
|
31
|
+
components: ['a.b', 'b.a'],
|
|
32
|
+
services: ['@toa.io/extensions.exposition', '@toa.io/extensions.cadence']
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'bar',
|
|
36
|
+
components: ['d.c']
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
|
|
4
|
+
import clone from 'clone-deep'
|
|
5
|
+
|
|
6
|
+
import { evict } from '../../src/.context/index.js'
|
|
7
|
+
import * as fixtures from './evict.fixtures.js'
|
|
8
|
+
|
|
9
|
+
/** @type {toa.norm.Context} */
|
|
10
|
+
let context
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
context = clone(fixtures.context)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('should keep a context that evicts nothing', () => {
|
|
17
|
+
evict(context)
|
|
18
|
+
|
|
19
|
+
assert.deepStrictEqual(context, fixtures.context)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should mark an evicted component and keep it', () => {
|
|
23
|
+
context.evicted = { components: ['a.b'] }
|
|
24
|
+
|
|
25
|
+
evict(context)
|
|
26
|
+
|
|
27
|
+
assert.deepStrictEqual(
|
|
28
|
+
context.components.map((component) => [
|
|
29
|
+
component.locator.id,
|
|
30
|
+
component.evicted === true
|
|
31
|
+
]),
|
|
32
|
+
[
|
|
33
|
+
['a.b', true],
|
|
34
|
+
['b.a', false],
|
|
35
|
+
['d.c', false]
|
|
36
|
+
]
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
it('should keep an evicted component in a composition that lists it', () => {
|
|
41
|
+
context.evicted = { components: ['a.b', 'd.c'] }
|
|
42
|
+
|
|
43
|
+
evict(context)
|
|
44
|
+
|
|
45
|
+
assert.deepStrictEqual(context.compositions[0].components, ['a.b', 'b.a'])
|
|
46
|
+
assert.deepStrictEqual(context.compositions[1].components, ['d.c'])
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('should refuse an unknown component', () => {
|
|
50
|
+
context.evicted = { components: ['d.a'] }
|
|
51
|
+
|
|
52
|
+
assert.throws(() => evict(context), {
|
|
53
|
+
message: "'evicted' names an unknown component 'd.a'."
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
it('should drop an evicted service from a composition that runs it', () => {
|
|
58
|
+
context.evicted = { services: ['@toa.io/extensions.exposition'] }
|
|
59
|
+
|
|
60
|
+
evict(context)
|
|
61
|
+
|
|
62
|
+
assert.deepStrictEqual(context.compositions[0].services, ['@toa.io/extensions.cadence'])
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should leave no services where every one is evicted', () => {
|
|
66
|
+
context.evicted = {
|
|
67
|
+
services: ['@toa.io/extensions.exposition', '@toa.io/extensions.cadence']
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
evict(context)
|
|
71
|
+
|
|
72
|
+
assert.strictEqual('services' in context.compositions[0], false)
|
|
73
|
+
})
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
4
|
+
import clone from 'clone-deep'
|
|
5
|
+
import { generate } from 'randomstring'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
import { expand } from '../../src/.context/index.js'
|
|
8
|
+
import * as fixtures from './expand.fixtures.js'
|
|
8
9
|
|
|
9
10
|
/** @type {toa.norm.context.Declaration | object} */
|
|
10
11
|
let context
|
|
@@ -17,7 +18,7 @@ describe('annotations', () => {
|
|
|
17
18
|
it('should not throw without annotations', () => {
|
|
18
19
|
delete context.annotations
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
assert.doesNotThrow(() => expand(context))
|
|
21
22
|
})
|
|
22
23
|
|
|
23
24
|
it('should expand known annotations', () => {
|
|
@@ -28,7 +29,7 @@ describe('annotations', () => {
|
|
|
28
29
|
|
|
29
30
|
expand(context)
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
assert.deepStrictEqual(context.annotations, fixtures.context.annotations)
|
|
32
33
|
})
|
|
33
34
|
|
|
34
35
|
it('should recognize annotations', () => {
|
|
@@ -36,6 +37,33 @@ describe('annotations', () => {
|
|
|
36
37
|
|
|
37
38
|
expand(context)
|
|
38
39
|
|
|
39
|
-
|
|
40
|
+
assert.notStrictEqual(context.annotations['@toa.io/storages.mongodb'], undefined)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
describe('composition services', () => {
|
|
45
|
+
it('should resolve shortcuts', () => {
|
|
46
|
+
context.compositions[0].services = ['exposition', 'configuration']
|
|
47
|
+
|
|
48
|
+
expand(context)
|
|
49
|
+
|
|
50
|
+
assert.deepStrictEqual(context.compositions[0].services, [
|
|
51
|
+
'@toa.io/extensions.exposition',
|
|
52
|
+
'@toa.io/extensions.configuration'
|
|
53
|
+
])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('should leave a package reference alone', () => {
|
|
57
|
+
context.compositions[0].services = ['@acme/extension']
|
|
58
|
+
|
|
59
|
+
expand(context)
|
|
60
|
+
|
|
61
|
+
assert.deepStrictEqual(context.compositions[0].services, ['@acme/extension'])
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
it('should not throw without compositions', () => {
|
|
65
|
+
delete context.compositions
|
|
66
|
+
|
|
67
|
+
assert.doesNotThrow(() => expand(context))
|
|
40
68
|
})
|
|
41
69
|
})
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import { generate } from 'randomstring'
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const context = {
|
|
3
|
+
export const context = {
|
|
6
4
|
runtime: {
|
|
7
5
|
version: '0.0.0'
|
|
8
6
|
},
|
|
9
7
|
name: 'test',
|
|
10
8
|
description: 'context fixture',
|
|
11
9
|
version: '0.0.0',
|
|
12
|
-
packages: 'namespaces/**/*',
|
|
13
10
|
registry: 'localhost:5000',
|
|
14
11
|
compositions: [
|
|
15
12
|
{
|
|
@@ -28,5 +25,3 @@ const context = {
|
|
|
28
25
|
}
|
|
29
26
|
}
|
|
30
27
|
}
|
|
31
|
-
|
|
32
|
-
exports.context = context
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
import * as definitions from '@toa.io/definitions'
|
|
2
|
+
import { it, beforeEach } from 'node:test'
|
|
3
|
+
import assert from 'node:assert/strict'
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
import clone from 'clone-deep'
|
|
6
|
+
import { generate } from 'randomstring'
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
import * as fixtures from './normalize.fixtures.js'
|
|
9
|
+
import { normalize } from '../../src/.context/index.js'
|
|
8
10
|
|
|
9
11
|
let context
|
|
10
12
|
|
|
@@ -17,8 +19,8 @@ it('should resolve local version', () => {
|
|
|
17
19
|
|
|
18
20
|
normalize(context)
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
assert.notDeepStrictEqual(context.runtime, '.')
|
|
23
|
+
assert.deepStrictEqual(context.runtime.version, definitions.version)
|
|
22
24
|
})
|
|
23
25
|
|
|
24
26
|
it('should expand registry', () => {
|
|
@@ -28,7 +30,29 @@ it('should expand registry', () => {
|
|
|
28
30
|
|
|
29
31
|
normalize(context)
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
base
|
|
33
|
+
assert.deepStrictEqual(context.registry, {
|
|
34
|
+
base,
|
|
35
|
+
platforms: ['linux/amd64', 'linux/arm/v7', 'linux/arm64']
|
|
33
36
|
})
|
|
34
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
|
+
})
|
|
50
|
+
|
|
51
|
+
it('should keep platforms declared as none', () => {
|
|
52
|
+
// `platforms: ~` in the context: the runner's own platform, not the default list
|
|
53
|
+
context.registry = { base: generate(), platforms: null }
|
|
54
|
+
|
|
55
|
+
normalize(context)
|
|
56
|
+
|
|
57
|
+
assert.strictEqual(context.registry.platforms, null)
|
|
58
|
+
})
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const context = {
|
|
1
|
+
export const context = {
|
|
4
2
|
runtime: {
|
|
5
3
|
version: '0.0.0'
|
|
6
4
|
},
|
|
7
5
|
name: 'test',
|
|
8
6
|
description: 'context fixture',
|
|
9
7
|
version: '0.0.0',
|
|
10
|
-
packages: 'namespaces/**/*',
|
|
11
8
|
build: {
|
|
12
9
|
command: 'echo test'
|
|
13
10
|
},
|
|
@@ -37,5 +34,3 @@ const context = {
|
|
|
37
34
|
}
|
|
38
35
|
}
|
|
39
36
|
}
|
|
40
|
-
|
|
41
|
-
exports.context = context
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
|
|
1
|
+
import { describe, it, before, beforeEach } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
import clone from 'clone-deep'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { validate } from '../../src/.context/index.js'
|
|
7
|
+
import * as fixtures from './validate.fixtures.js'
|
|
7
8
|
|
|
8
9
|
let context
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
before(() => {
|
|
12
|
+
assert.doesNotThrow(() => validate(fixtures.context))
|
|
12
13
|
})
|
|
13
14
|
|
|
14
15
|
beforeEach(() => {
|
|
@@ -18,23 +19,32 @@ beforeEach(() => {
|
|
|
18
19
|
describe('runtime', () => {
|
|
19
20
|
it('should require', () => {
|
|
20
21
|
delete context.runtime
|
|
21
|
-
|
|
22
|
+
assert.throws(
|
|
23
|
+
() => validate(context),
|
|
24
|
+
(error) => /required/.test(error.message)
|
|
25
|
+
)
|
|
22
26
|
})
|
|
23
27
|
|
|
24
28
|
it('should require registry to match uri format', () => {
|
|
25
29
|
context.runtime.registry = 'not-a-uri'
|
|
26
|
-
|
|
30
|
+
assert.throws(
|
|
31
|
+
() => validate(context),
|
|
32
|
+
(error) => /must match format/.test(error.message)
|
|
33
|
+
)
|
|
27
34
|
|
|
28
35
|
context.runtime.registry = 'http://localhost'
|
|
29
|
-
|
|
36
|
+
assert.doesNotThrow(() => validate(context))
|
|
30
37
|
})
|
|
31
38
|
|
|
32
39
|
it('should require proxy to match uri format', () => {
|
|
33
40
|
context.runtime.proxy = 'not-a-uri'
|
|
34
|
-
|
|
41
|
+
assert.throws(
|
|
42
|
+
() => validate(context),
|
|
43
|
+
(error) => /must match format/.test(error.message)
|
|
44
|
+
)
|
|
35
45
|
|
|
36
46
|
context.runtime.proxy = 'http://localhost'
|
|
37
|
-
|
|
47
|
+
assert.doesNotThrow(() => validate(context))
|
|
38
48
|
})
|
|
39
49
|
})
|
|
40
50
|
|
|
@@ -42,43 +52,171 @@ describe('registry', () => {
|
|
|
42
52
|
it('should require', () => {
|
|
43
53
|
delete context.registry
|
|
44
54
|
|
|
45
|
-
|
|
55
|
+
assert.throws(
|
|
56
|
+
() => validate(context),
|
|
57
|
+
(error) => /required property 'registry'/.test(error.message)
|
|
58
|
+
)
|
|
46
59
|
})
|
|
60
|
+
})
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
it('should require name as label', () => {
|
|
63
|
+
delete context.name
|
|
64
|
+
assert.throws(
|
|
65
|
+
() => validate(context),
|
|
66
|
+
(error) => /required/.test(error.message)
|
|
67
|
+
)
|
|
50
68
|
|
|
51
|
-
|
|
52
|
-
|
|
69
|
+
context.name = 'foo bar'
|
|
70
|
+
assert.throws(
|
|
71
|
+
() => validate(context),
|
|
72
|
+
(error) => /pattern/.test(error.message)
|
|
73
|
+
)
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
|
|
75
|
+
context.name = 'foo-bar'
|
|
76
|
+
assert.doesNotThrow(() => validate(context))
|
|
77
|
+
})
|
|
56
78
|
|
|
57
|
-
|
|
79
|
+
it('should require registry url', () => {
|
|
80
|
+
delete context.registry
|
|
81
|
+
assert.throws(
|
|
82
|
+
() => validate(context),
|
|
83
|
+
(error) => /required/.test(error.message)
|
|
84
|
+
)
|
|
85
|
+
})
|
|
58
86
|
|
|
59
|
-
|
|
60
|
-
|
|
87
|
+
describe('inbox', () => {
|
|
88
|
+
it('should allow a retention of ten minutes or more', () => {
|
|
89
|
+
context.inbox = { retention: 600 }
|
|
90
|
+
assert.doesNotThrow(() => validate(context))
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should refuse a retention shorter than ten minutes', () => {
|
|
94
|
+
context.inbox = { retention: 599 }
|
|
95
|
+
assert.throws(
|
|
96
|
+
() => validate(context),
|
|
97
|
+
(error) => /must be >= 600/.test(error.message)
|
|
98
|
+
)
|
|
61
99
|
})
|
|
62
100
|
})
|
|
63
101
|
|
|
64
|
-
it('should
|
|
65
|
-
|
|
66
|
-
|
|
102
|
+
it('should allow mono replicas and resources', () => {
|
|
103
|
+
context.mono = {
|
|
104
|
+
replicas: 2,
|
|
105
|
+
resources: {
|
|
106
|
+
cpu: ['200m', '2'],
|
|
107
|
+
memory: ['256Mi', '2Gi']
|
|
108
|
+
}
|
|
109
|
+
}
|
|
67
110
|
|
|
68
|
-
|
|
69
|
-
|
|
111
|
+
assert.doesNotThrow(() => validate(context))
|
|
112
|
+
})
|
|
70
113
|
|
|
71
|
-
|
|
72
|
-
|
|
114
|
+
describe('compositions', () => {
|
|
115
|
+
it('should allow services', () => {
|
|
116
|
+
context.compositions[0].services = ['@toa.io/extensions.exposition']
|
|
117
|
+
|
|
118
|
+
assert.doesNotThrow(() => validate(context))
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should require services to be a non-empty array of strings', () => {
|
|
122
|
+
context.compositions[0].services = []
|
|
123
|
+
assert.throws(
|
|
124
|
+
() => validate(context),
|
|
125
|
+
(error) => /fewer than 1 items/.test(error.message)
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
context.compositions[0].services = 'exposition'
|
|
129
|
+
assert.throws(
|
|
130
|
+
() => validate(context),
|
|
131
|
+
(error) => /must be array/.test(error.message)
|
|
132
|
+
)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('should reject an unknown property', () => {
|
|
136
|
+
context.compositions[0].compoments = ['a.b']
|
|
137
|
+
|
|
138
|
+
assert.throws(
|
|
139
|
+
() => validate(context),
|
|
140
|
+
(error) => /must NOT have additional properties/.test(error.message)
|
|
141
|
+
)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('should allow replicas of one', () => {
|
|
145
|
+
context.compositions[0].replicas = 1
|
|
146
|
+
|
|
147
|
+
assert.doesNotThrow(() => validate(context))
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('should refuse replicas below one', () => {
|
|
151
|
+
context.compositions[0].replicas = 0
|
|
152
|
+
|
|
153
|
+
assert.throws(
|
|
154
|
+
() => validate(context),
|
|
155
|
+
(error) => /must be >= 1/.test(error.message)
|
|
156
|
+
)
|
|
157
|
+
})
|
|
73
158
|
})
|
|
74
159
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
160
|
+
describe('evicted', () => {
|
|
161
|
+
it('should allow components and services', () => {
|
|
162
|
+
context.evicted = {
|
|
163
|
+
components: ['a.b'],
|
|
164
|
+
services: ['@toa.io/extensions.exposition']
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
assert.doesNotThrow(() => validate(context))
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
it('should require a component to be named as one', () => {
|
|
171
|
+
context.evicted = { components: ['a'] }
|
|
172
|
+
|
|
173
|
+
assert.throws(
|
|
174
|
+
() => validate(context),
|
|
175
|
+
(error) => /must match pattern/.test(error.message)
|
|
176
|
+
)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
it('should reject an empty declaration', () => {
|
|
180
|
+
context.evicted = {}
|
|
181
|
+
|
|
182
|
+
assert.throws(
|
|
183
|
+
() => validate(context),
|
|
184
|
+
(error) => /fewer than 1 properties/.test(error.message)
|
|
185
|
+
)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should reject an unknown property', () => {
|
|
189
|
+
context.evicted = { compoments: ['a.b'] }
|
|
190
|
+
|
|
191
|
+
assert.throws(
|
|
192
|
+
() => validate(context),
|
|
193
|
+
(error) => /must NOT have additional properties/.test(error.message)
|
|
194
|
+
)
|
|
195
|
+
})
|
|
79
196
|
})
|
|
80
197
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
198
|
+
describe('addressed', () => {
|
|
199
|
+
it('should take the timeout an addressed call waits by default', () => {
|
|
200
|
+
context.addressed = { timeout: 5000 }
|
|
201
|
+
|
|
202
|
+
assert.doesNotThrow(() => validate(context))
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
it('should take a timeout of a millisecond at least', () => {
|
|
206
|
+
context.addressed = { timeout: 0 }
|
|
207
|
+
|
|
208
|
+
assert.throws(
|
|
209
|
+
() => validate(context),
|
|
210
|
+
(error) => /must be >= 1/.test(error.message)
|
|
211
|
+
)
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
it('should take nothing else', () => {
|
|
215
|
+
context.addressed = { interval: 5000 }
|
|
216
|
+
|
|
217
|
+
assert.throws(
|
|
218
|
+
() => validate(context),
|
|
219
|
+
(error) => /must NOT have additional properties/.test(error.message)
|
|
220
|
+
)
|
|
221
|
+
})
|
|
84
222
|
})
|