@likec4/core 1.10.1 → 1.12.0

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.
@@ -1,202 +0,0 @@
1
- import { prop } from 'remeda'
2
- import { describe, expect, it } from 'vitest'
3
- import type { Element, Fqn } from '../types'
4
- import {
5
- ancestorsFqn,
6
- commonAncestor,
7
- compareFqnHierarchically,
8
- isAncestor,
9
- isDescendantOf,
10
- notDescendantOf,
11
- parentFqn,
12
- sortByFqnHierarchically,
13
- sortNaturalByFqn
14
- } from './fqn'
15
-
16
- const el = (id: string): Element => ({ id }) as unknown as Element
17
-
18
- describe('parentFqn', () => {
19
- it('should return null if no parent', () => {
20
- expect(parentFqn('a' as Fqn)).toBeNull()
21
- })
22
- it('should return parent', () => {
23
- expect(parentFqn('a.b' as Fqn)).toBe('a')
24
- expect(parentFqn('a.b.c' as Fqn)).toBe('a.b')
25
- })
26
- })
27
-
28
- describe('ancestorsFqn', () => {
29
- it('should return empty array if no parent', () => {
30
- expect(ancestorsFqn('a' as Fqn)).toEqual([])
31
- })
32
- it('should return ancestors', () => {
33
- expect(ancestorsFqn('a.b.c.d.e' as Fqn)).toEqual(['a.b.c.d', 'a.b.c', 'a.b', 'a'])
34
- })
35
- })
36
-
37
- describe('commonAncestor', () => {
38
- it('should return null if no common ancestor', () => {
39
- expect(commonAncestor('a' as Fqn, 'b' as Fqn)).toBeNull()
40
- expect(commonAncestor('a.b' as Fqn, 'c.d' as Fqn)).toBeNull()
41
- })
42
-
43
- it('should return common ancestor', () => {
44
- expect(commonAncestor('a.b' as Fqn, 'a.c' as Fqn)).toBe('a')
45
- expect(commonAncestor('a.b.c' as Fqn, 'a.b.e' as Fqn)).toBe('a.b')
46
- expect(commonAncestor('a.b.c.d.e' as Fqn, 'a.b.c.d' as Fqn)).toBe('a.b.c')
47
- })
48
- })
49
-
50
- describe('isAncestor', () => {
51
- it('should return true if ancestor', () => {
52
- expect(isAncestor('a' as Fqn, 'a.b' as Fqn)).toBe(true)
53
- expect(isAncestor('a.b' as Fqn, 'a.b.c' as Fqn)).toBe(true)
54
- })
55
- it('should return false if not ancestor', () => {
56
- expect(isAncestor('a' as Fqn, 'b' as Fqn)).toBe(false)
57
- expect(isAncestor('a.b' as Fqn, 'a' as Fqn)).toBe(false)
58
- expect(isAncestor('a.b' as Fqn, 'b.a' as Fqn)).toBe(false)
59
- })
60
- })
61
-
62
- describe('isDescendantOf', () => {
63
- const predicate = isDescendantOf(['a', 'b', 'a.b', 'a.b.c'].map(el))
64
-
65
- it('should return true if isDescendantOf', () => {
66
- expect(predicate(el('a'))).toBe(true)
67
- expect(predicate(el('b.c'))).toBe(true)
68
- expect(predicate(el('a.b.c.d.e'))).toBe(true)
69
- })
70
- it('should return false if not descendantOf', () => {
71
- expect(predicate(el('c'))).toBe(false)
72
- expect(predicate(el('ac'))).toBe(false)
73
- expect(predicate(el('d.a.c'))).toBe(false)
74
- })
75
- })
76
-
77
- describe('notDescendantOf', () => {
78
- const predicate = notDescendantOf(['a', 'b', 'a.b', 'a.b.c'].map(el))
79
-
80
- it('should return true if notDescendantOf', () => {
81
- expect(predicate(el('c'))).toBe(true)
82
- expect(predicate(el('ac'))).toBe(true)
83
- expect(predicate(el('d.a.c'))).toBe(true)
84
- })
85
- it('should return false if descendantOf', () => {
86
- expect(predicate(el('a'))).toBe(false)
87
- expect(predicate(el('b.c'))).toBe(false)
88
- expect(predicate(el('a.b.c.d.e'))).toBe(false)
89
- })
90
- })
91
-
92
- describe('compareFqnHierarchically', () => {
93
- it('should compare hierarchically', () => {
94
- expect([
95
- 'a',
96
- 'b',
97
- 'a.b',
98
- 'a.b.c',
99
- 'a.c.c'
100
- ].sort(compareFqnHierarchically)).toEqual([
101
- 'a',
102
- 'b',
103
- 'a.b',
104
- 'a.b.c',
105
- 'a.c.c'
106
- ])
107
- })
108
-
109
- it('should preserve initial order', () => {
110
- expect(
111
- ['aaa', 'aa', 'a', 'aaa.c', 'aa.b', 'a.c', 'a.b'].sort(compareFqnHierarchically)
112
- ).toEqual(['aaa', 'aa', 'a', 'aaa.c', 'aa.b', 'a.c', 'a.b'])
113
- })
114
- })
115
-
116
- describe('sortNaturalByFqn', () => {
117
- it('should sort hierarchically', () => {
118
- expect(
119
- sortNaturalByFqn([
120
- el('a.b'),
121
- el('a'),
122
- el('a.c.a.b'),
123
- el('a.c.c'),
124
- el('b'),
125
- el('a.b.c')
126
- ]).map(prop('id'))
127
- ).toEqual([
128
- 'a',
129
- 'b',
130
- 'a.b',
131
- 'a.b.c',
132
- 'a.c.c',
133
- 'a.c.a.b'
134
- ])
135
- })
136
-
137
- it('should sort natural', () => {
138
- expect(
139
- sortNaturalByFqn([
140
- el('b'),
141
- el('a.c.c'),
142
- el('a'),
143
- el('a.b1'),
144
- el('a.b2'),
145
- el('a.b10'),
146
- el('a.b2.c')
147
- ]).map(prop('id'))
148
- ).toEqual([
149
- 'a',
150
- 'b',
151
- 'a.b1',
152
- 'a.b2',
153
- 'a.b10',
154
- 'a.b2.c',
155
- 'a.c.c'
156
- ])
157
- })
158
- })
159
-
160
- describe('sortByFqnHierarchically', () => {
161
- it('should sort hierarchically', () => {
162
- expect(
163
- sortByFqnHierarchically([
164
- el('a.b'),
165
- el('a'),
166
- el('a.b.c'),
167
- el('a.c.a.b'),
168
- el('a.c.c'),
169
- el('b')
170
- ]).map(prop('id'))
171
- ).toEqual([
172
- 'a',
173
- 'b',
174
- 'a.b',
175
- 'a.b.c',
176
- 'a.c.c',
177
- 'a.c.a.b'
178
- ])
179
- })
180
-
181
- it('should preserve initial order', () => {
182
- expect(
183
- sortByFqnHierarchically([
184
- el('b'),
185
- el('a.c.c'),
186
- el('a'),
187
- el('a.b10'),
188
- el('a.b2.c'),
189
- el('a.b1'),
190
- el('a.b2')
191
- ]).map(prop('id'))
192
- ).toEqual([
193
- 'b',
194
- 'a',
195
- 'a.b10',
196
- 'a.b1',
197
- 'a.b2',
198
- 'a.c.c',
199
- 'a.b2.c'
200
- ])
201
- })
202
- })
@@ -1,266 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import type { Fqn, Relation, RelationID } from '../types'
3
- import { compareRelations, isAnyBetween, isAnyInOut, isBetween, isIncoming, isInside, isOutgoing } from './relations'
4
-
5
- const relations = [
6
- {
7
- id: 'customer:cloud.frontend.dashboard' as RelationID,
8
- source: 'customer' as Fqn,
9
- target: 'cloud.frontend.dashboard' as Fqn,
10
- title: ''
11
- },
12
- {
13
- id: 'support:cloud.frontend.adminPanel' as RelationID,
14
- source: 'support' as Fqn,
15
- target: 'cloud.frontend.adminPanel' as Fqn,
16
- title: ''
17
- },
18
- {
19
- id: 'cloud.backend.storage:amazon.s3' as RelationID,
20
- source: 'cloud.backend.storage' as Fqn,
21
- target: 'amazon.s3' as Fqn,
22
- title: ''
23
- },
24
- {
25
- id: 'amazon.api:cloud.backend.graphql' as RelationID,
26
- source: 'amazon.api' as Fqn,
27
- target: 'cloud.backend.graphql' as Fqn,
28
- title: ''
29
- },
30
- {
31
- id: 'cloud.backend.graphql:cloud.backend.storage' as RelationID,
32
- source: 'cloud.backend.graphql' as Fqn,
33
- target: 'cloud.backend.storage' as Fqn,
34
- title: ''
35
- },
36
- {
37
- id: 'cloud.frontend.dashboard:cloud.backend.graphql' as RelationID,
38
- source: 'cloud.frontend.dashboard' as Fqn,
39
- target: 'cloud.backend.graphql' as Fqn,
40
- title: ''
41
- },
42
- {
43
- id: 'cloud.frontend.adminPanel:cloud.backend.graphql' as RelationID,
44
- source: 'cloud.frontend.adminPanel' as Fqn,
45
- target: 'cloud.backend.graphql' as Fqn,
46
- title: ''
47
- }
48
- ] satisfies Relation[]
49
-
50
- describe('relation predicates', () => {
51
- const expectRelations = (predicate: (relation: Relation) => boolean) =>
52
- expect(relations.filter(predicate).map(r => r.id))
53
-
54
- const customer = 'customer' as Fqn
55
- const cloud = 'cloud' as Fqn
56
- const frontend = 'cloud.frontend' as Fqn
57
- const backend = 'cloud.backend' as Fqn
58
-
59
- it('isBetween: cloud.frontend -> cloud.backend', () => {
60
- expectRelations(isBetween(frontend, backend)).toEqual([
61
- 'cloud.frontend.dashboard:cloud.backend.graphql',
62
- 'cloud.frontend.adminPanel:cloud.backend.graphql'
63
- ])
64
- })
65
-
66
- it('isBetween: cloud.frontend.dashboard -> cloud.backend', () => {
67
- expectRelations(isBetween('cloud.frontend.dashboard' as Fqn, backend)).toEqual([
68
- 'cloud.frontend.dashboard:cloud.backend.graphql'
69
- ])
70
- })
71
-
72
- it('isBetween: cloud.backend -> cloud.frontend', () => {
73
- expectRelations(isBetween(backend, frontend)).toEqual([])
74
- })
75
-
76
- it('isBetween: cloud.backend -> cloud.backend', () => {
77
- expectRelations(isBetween(backend, backend)).toEqual([
78
- 'cloud.backend.graphql:cloud.backend.storage'
79
- ])
80
- })
81
-
82
- it('isBetween: customer -> cloud', () => {
83
- expectRelations(isBetween(customer, cloud)).toEqual(['customer:cloud.frontend.dashboard'])
84
- })
85
-
86
- it('isIncoming: customer', () => {
87
- expectRelations(isIncoming(customer)).toEqual([])
88
- })
89
-
90
- it('isIncoming: cloud', () => {
91
- expectRelations(isIncoming(cloud)).toEqual([
92
- 'customer:cloud.frontend.dashboard',
93
- 'support:cloud.frontend.adminPanel',
94
- 'amazon.api:cloud.backend.graphql'
95
- ])
96
- })
97
-
98
- it('isIncoming: cloud.frontend', () => {
99
- expectRelations(isIncoming(frontend)).toEqual([
100
- 'customer:cloud.frontend.dashboard',
101
- 'support:cloud.frontend.adminPanel'
102
- ])
103
- })
104
-
105
- it('isIncoming: cloud.frontend.dashboard', () => {
106
- expectRelations(isIncoming('cloud.frontend.dashboard' as Fqn)).toEqual([
107
- 'customer:cloud.frontend.dashboard'
108
- ])
109
- })
110
-
111
- it('isOutgoing: cloud', () => {
112
- expectRelations(isOutgoing(cloud)).toEqual(['cloud.backend.storage:amazon.s3'])
113
- })
114
-
115
- it('isOutgoing: cloud.frontend', () => {
116
- expectRelations(isOutgoing(frontend)).toEqual([
117
- 'cloud.frontend.dashboard:cloud.backend.graphql',
118
- 'cloud.frontend.adminPanel:cloud.backend.graphql'
119
- ])
120
- })
121
-
122
- it('isOutgoing: cloud.backend.storage', () => {
123
- expectRelations(isOutgoing('cloud.backend.storage' as Fqn)).toEqual([
124
- 'cloud.backend.storage:amazon.s3'
125
- ])
126
- })
127
-
128
- it('isInside: cloud', () => {
129
- expectRelations(isInside(cloud)).toEqual([
130
- 'cloud.backend.graphql:cloud.backend.storage',
131
- 'cloud.frontend.dashboard:cloud.backend.graphql',
132
- 'cloud.frontend.adminPanel:cloud.backend.graphql'
133
- ])
134
- })
135
-
136
- it('isInside: cloud.frontend', () => {
137
- expectRelations(isInside(frontend)).toEqual([])
138
- })
139
-
140
- it('isInside: cloud.backend', () => {
141
- expectRelations(isInside(backend)).toEqual(['cloud.backend.graphql:cloud.backend.storage'])
142
- })
143
-
144
- it('isAnyInOut: customer', () => {
145
- expectRelations(isAnyInOut(customer)).toEqual(['customer:cloud.frontend.dashboard'])
146
- })
147
-
148
- it('isAnyInOut: cloud', () => {
149
- expectRelations(isAnyInOut(cloud)).toEqual([
150
- 'customer:cloud.frontend.dashboard',
151
- 'support:cloud.frontend.adminPanel',
152
- 'cloud.backend.storage:amazon.s3',
153
- 'amazon.api:cloud.backend.graphql'
154
- ])
155
- })
156
-
157
- it('isAnyBetween: cloud and amazon', () => {
158
- expectRelations(isAnyBetween(cloud, 'amazon' as Fqn)).toEqual([
159
- 'cloud.backend.storage:amazon.s3',
160
- 'amazon.api:cloud.backend.graphql'
161
- ])
162
- })
163
- })
164
-
165
- describe('compareRelations', () => {
166
- function rel(source: string, target: string) {
167
- return {
168
- source,
169
- target
170
- }
171
- }
172
- function sorted(...relations: Array<{ source: string; target: string }>) {
173
- return relations.toSorted(compareRelations).map(r => r.source + ' -> ' + r.target)
174
- }
175
-
176
- it('should sort by source and target', () => {
177
- expect(sorted(rel('customer', 'cloud.ui'), rel('customer', 'cloud'))).toEqual([
178
- 'customer -> cloud',
179
- 'customer -> cloud.ui'
180
- ])
181
-
182
- expect(
183
- sorted(
184
- rel('1', '2.1'),
185
- rel('1', '2'),
186
- rel('1.1.1', '2'),
187
- rel('1.1.1', '2.1'),
188
- rel('1.1', '2')
189
- )
190
- ).toEqual(['1 -> 2', '1 -> 2.1', '1.1 -> 2', '1.1.1 -> 2', '1.1.1 -> 2.1'])
191
- })
192
-
193
- it('should sort by parent', () => {
194
- expect(
195
- sorted(
196
- // same parent
197
- rel('cloud.1.1', 'cloud.2.1'),
198
- rel('cloud.1.1', 'cloud.2'),
199
- rel('cloud.1', 'cloud.2.1'),
200
- rel('cloud.1', 'cloud.2'),
201
- // no same parent
202
- rel('cloud.1.1', 'aws.1'),
203
- rel('cloud.1', 'aws.1'),
204
- rel('cloud', 'aws')
205
- )
206
- ).toEqual([
207
- 'cloud -> aws',
208
- 'cloud.1 -> aws.1',
209
- 'cloud.1.1 -> aws.1',
210
- 'cloud.1 -> cloud.2',
211
- 'cloud.1 -> cloud.2.1',
212
- 'cloud.1.1 -> cloud.2',
213
- 'cloud.1.1 -> cloud.2.1'
214
- ])
215
- })
216
-
217
- it('should sort by ancestors', () => {
218
- expect(
219
- sorted(
220
- // same parent 2nd level
221
- rel('cloud.api.1', 'cloud.api.2'),
222
- rel('cloud.ui.1', 'cloud.ui.2'),
223
- rel('cloud.ui.2', 'cloud.ui.1'),
224
- // same ancestor cloud
225
- rel('cloud.ui.1', 'cloud.api.1'),
226
- rel('cloud.ui.2', 'cloud.api.2'),
227
- rel('cloud.ui', 'cloud.api.1'),
228
- // same parent 1st level
229
- rel('cloud.ui', 'cloud.api'),
230
- // no same parent
231
- rel('cloud.api.1', 'aws.1'),
232
- rel('cloud.api', 'aws.1'),
233
- rel('cloud.api', 'aws'),
234
- rel('cloud.ui.1', 'aws.1'),
235
- rel('cloud.ui', 'aws'),
236
- rel('cloud', 'aws')
237
- )
238
- ).toEqual([
239
- 'cloud -> aws',
240
- 'cloud.api -> aws',
241
- 'cloud.ui -> aws',
242
- 'cloud.api -> aws.1',
243
- 'cloud.api.1 -> aws.1',
244
- 'cloud.ui.1 -> aws.1',
245
- 'cloud.ui -> cloud.api',
246
- 'cloud.ui -> cloud.api.1',
247
- 'cloud.ui.1 -> cloud.api.1',
248
- 'cloud.ui.2 -> cloud.api.2',
249
- 'cloud.api.1 -> cloud.api.2',
250
- 'cloud.ui.1 -> cloud.ui.2',
251
- 'cloud.ui.2 -> cloud.ui.1'
252
- ])
253
- })
254
-
255
- it('should sort relations from example', () => {
256
- expect(sorted(...relations)).toEqual([
257
- 'customer -> cloud.frontend.dashboard',
258
- 'support -> cloud.frontend.adminPanel',
259
- 'amazon.api -> cloud.backend.graphql',
260
- 'cloud.backend.storage -> amazon.s3',
261
- 'cloud.frontend.dashboard -> cloud.backend.graphql',
262
- 'cloud.frontend.adminPanel -> cloud.backend.graphql',
263
- 'cloud.backend.graphql -> cloud.backend.storage'
264
- ])
265
- })
266
- })