@api-client/core 0.14.10 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/RELEASE.md +163 -0
  2. package/RELEASE_SETUP.md +235 -0
  3. package/build/src/events/authorization/AuthorizationEvents.d.ts +1 -1
  4. package/build/src/events/authorization/AuthorizationEvents.d.ts.map +1 -1
  5. package/build/src/events/authorization/AuthorizationEvents.js +1 -1
  6. package/build/src/events/authorization/AuthorizationEvents.js.map +1 -1
  7. package/build/src/events/cookies/CookieEvents.d.ts +1 -1
  8. package/build/src/events/cookies/CookieEvents.d.ts.map +1 -1
  9. package/build/src/events/cookies/CookieEvents.js +1 -1
  10. package/build/src/events/cookies/CookieEvents.js.map +1 -1
  11. package/build/src/modeling/DomainImpactAnalysis.d.ts +22 -119
  12. package/build/src/modeling/DomainImpactAnalysis.d.ts.map +1 -1
  13. package/build/src/modeling/DomainImpactAnalysis.js +49 -155
  14. package/build/src/modeling/DomainImpactAnalysis.js.map +1 -1
  15. package/build/src/modeling/DomainValidation.d.ts +8 -0
  16. package/build/src/modeling/DomainValidation.d.ts.map +1 -0
  17. package/build/src/modeling/DomainValidation.js +99 -0
  18. package/build/src/modeling/DomainValidation.js.map +1 -0
  19. package/build/src/modeling/types.d.ts +70 -0
  20. package/build/src/modeling/types.d.ts.map +1 -1
  21. package/build/src/modeling/types.js.map +1 -1
  22. package/build/src/modeling/validation/rules.d.ts +2 -3
  23. package/build/src/modeling/validation/rules.d.ts.map +1 -1
  24. package/build/src/modeling/validation/rules.js.map +1 -1
  25. package/build/src/modeling/validation/semantic_validation.d.ts +31 -0
  26. package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -0
  27. package/build/src/modeling/validation/semantic_validation.js +126 -0
  28. package/build/src/modeling/validation/semantic_validation.js.map +1 -0
  29. package/build/tsconfig.tsbuildinfo +1 -1
  30. package/noop.ts +3 -0
  31. package/package.json +9 -4
  32. package/src/events/authorization/AuthorizationEvents.ts +1 -1
  33. package/src/events/cookies/CookieEvents.ts +1 -1
  34. package/src/modeling/DomainImpactAnalysis.ts +54 -239
  35. package/src/modeling/DomainValidation.ts +105 -0
  36. package/src/modeling/types.ts +86 -0
  37. package/src/modeling/validation/rules.ts +2 -4
  38. package/src/modeling/validation/semantic_validation.ts +145 -0
  39. package/tests/unit/events/EventsTestHelpers.ts +16 -0
  40. package/tests/unit/events/amf.spec.ts +151 -0
  41. package/tests/unit/events/authorization.spec.ts +150 -0
  42. package/tests/unit/events/cookie.spec.ts +274 -0
  43. package/tests/unit/events/encryption.spec.ts +108 -0
  44. package/tests/unit/events/events_polyfills.ts +77 -0
  45. package/tests/unit/events/process.spec.ts +120 -0
  46. package/tests/unit/events/reporting.spec.ts +82 -0
  47. package/tests/unit/events/telemetry.spec.ts +224 -0
  48. package/tests/unit/events/transport.spec.ts +139 -0
  49. package/tests/unit/modeling/domain_impact_analysis.spec.ts +0 -110
  50. package/tests/unit/modeling/domain_validation.spec.ts +94 -0
  51. package/tests/unit/modeling/validation/semantic_validation.spec.ts +91 -0
  52. package/tests/unit/models/environment.spec.ts +574 -0
  53. package/tests/unit/models/error_response.spec.ts +183 -0
  54. package/tests/unit/models/headers_array.spec.ts +86 -0
  55. package/tests/unit/models/http-actions/assertion/equal_assertion.spec.ts +103 -0
  56. package/tests/unit/models/http-actions/assertion/greater_than_assertion.spec.ts +91 -0
  57. package/tests/unit/models/http-actions/assertion/includes_assertion.spec.ts +71 -0
  58. package/tests/unit/models/http-actions/assertion/less_than_assertion.spec.ts +91 -0
  59. package/tests/unit/models/http-actions/assertion/matches_assertion.spec.ts +71 -0
  60. package/tests/unit/models/http-actions/assertion/matches_schema_assertion.spec.ts +117 -0
  61. package/tests/unit/models/http-actions/assertion/not_equal_assertion.spec.ts +103 -0
  62. package/tests/unit/models/http-actions/assertion/not_includes_assertion.spec.ts +71 -0
  63. package/tests/unit/models/http-actions/assertion/not_ok_assertion.spec.ts +47 -0
  64. package/tests/unit/models/http-actions/assertion/not_to_be_assertion.spec.ts +72 -0
  65. package/tests/unit/models/http-actions/assertion/ok_assertion.spec.ts +44 -0
  66. package/tests/unit/models/http-actions/assertion/to_be_assertion.spec.ts +71 -0
  67. package/tests/unit/models/http-actions/transformation/as_lower_case_step.spec.ts +47 -0
  68. package/tests/unit/models/http-actions/transformation/as_number_step.spec.ts +47 -0
  69. package/tests/unit/models/http-actions/transformation/as_upper_case_step.spec.ts +47 -0
  70. package/tests/unit/models/http-actions/transformation/round_step.spec.ts +69 -0
  71. package/tests/unit/models/http-actions/transformation/substring_step.spec.ts +85 -0
  72. package/tests/unit/models/http-actions/transformation/trim_step.spec.ts +44 -0
  73. package/tests/unit/models/http_cookie.spec.ts +516 -0
  74. package/tests/unit/models/http_history.spec.ts +443 -0
  75. package/tests/unit/models/project_folder.spec.ts +926 -0
  76. package/tests/unit/models/project_item.spec.ts +137 -0
  77. package/tests/unit/models/project_request.spec.ts +1047 -0
  78. package/tests/unit/models/project_schema.spec.ts +236 -0
  79. package/tests/unit/models/property.spec.ts +625 -0
  80. package/tests/unit/models/provider.spec.ts +102 -0
  81. package/tests/unit/models/request.spec.ts +1206 -0
  82. package/tests/unit/models/request_log.spec.ts +308 -0
  83. package/tests/unit/models/request_time.spec.ts +138 -0
  84. package/tests/unit/models/response_redirect.spec.ts +303 -0
  85. package/tests/unit/models/sent_request.spec.ts +206 -0
  86. package/tests/unit/models/server.spec.ts +195 -0
  87. package/tests/unit/models/thing.spec.ts +154 -0
  88. package/build/oauth-popup.html +0 -33
  89. /package/tests/unit/models/{Certificate.spec.ts → certificate.spec.ts} +0 -0
  90. /package/tests/unit/models/{HostRule.spec.ts → host_rule.spec.ts} +0 -0
  91. /package/tests/unit/models/{HttpProject.spec.ts → http_project.spec.ts} +0 -0
  92. /package/tests/unit/models/{HttpRequest.spec.ts → http_request.spec.ts} +0 -0
  93. /package/tests/unit/models/{HttpResponse.spec.ts → http_response.spec.ts} +0 -0
  94. /package/tests/unit/models/{License.spec.ts → license.spec.ts} +0 -0
  95. /package/tests/unit/models/{Response.spec.ts → response.spec.ts} +0 -0
@@ -0,0 +1,224 @@
1
+ import { test } from '@japa/runner'
2
+ import sinon from 'sinon'
3
+ import { EventTypes } from '../../../src/events/EventTypes.js'
4
+ import { Events } from '../../../src/events/Events.js'
5
+ import { ensureUnique } from './EventsTestHelpers.js'
6
+ import './events_polyfills.js'
7
+
8
+ test.group('Events > Telemetry > EventTypes.Telemetry', () => {
9
+ test('has the namespace', ({ assert }) => {
10
+ assert.typeOf(EventTypes.Telemetry, 'object')
11
+ })
12
+
13
+ test('has "{prop}" property')
14
+ .with([
15
+ { prop: 'view', value: 'telemetryscreenview' },
16
+ { prop: 'event', value: 'telemetryevent' },
17
+ { prop: 'exception', value: 'telemetryexception' },
18
+ { prop: 'social', value: 'telemetrysocial' },
19
+ { prop: 'timing', value: 'telemetrytiming' },
20
+ ])
21
+ .run(({ assert }, { prop, value }) => {
22
+ // @ts-expect-error Used in testing
23
+ assert.equal(EventTypes.Telemetry[prop], value)
24
+ })
25
+
26
+ test('has unique events for the namespace', () => {
27
+ ensureUnique('EventTypes.Telemetry', EventTypes.Telemetry)
28
+ })
29
+ })
30
+
31
+ test.group('Events > Telemetry > Events.Telemetry > view()', () => {
32
+ const screenName = 'test-screen'
33
+
34
+ test('dispatches the event', async ({ assert }) => {
35
+ const spy = sinon.spy()
36
+ globalThis.addEventListener(EventTypes.Telemetry.view, spy)
37
+ Events.Telemetry.view(screenName)
38
+ globalThis.removeEventListener(EventTypes.Telemetry.view, spy)
39
+ assert.isTrue(spy.calledOnce)
40
+ })
41
+
42
+ test('the event has screen name on the detail', async ({ assert }) => {
43
+ const spy = sinon.spy()
44
+ globalThis.addEventListener(EventTypes.Telemetry.view, spy)
45
+ Events.Telemetry.view(screenName)
46
+ globalThis.removeEventListener(EventTypes.Telemetry.view, spy)
47
+ const e = spy.args[0][0] as CustomEvent
48
+ assert.equal(e.detail.screenName, screenName)
49
+ })
50
+
51
+ test('the event has the custom configuration', async ({ assert }) => {
52
+ const spy = sinon.spy()
53
+ const custom = {
54
+ customMetrics: [{ index: 1, value: 1 }],
55
+ customDimensions: [{ index: 1, value: 'test' }],
56
+ }
57
+ globalThis.addEventListener(EventTypes.Telemetry.view, spy)
58
+ Events.Telemetry.view(screenName, undefined, custom)
59
+ globalThis.removeEventListener(EventTypes.Telemetry.view, spy)
60
+ const e = spy.args[0][0] as CustomEvent
61
+ assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
62
+ assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
63
+ })
64
+ })
65
+
66
+ test.group('Events > Telemetry > Events.Telemetry > event()', () => {
67
+ const init = {
68
+ category: 'e-cat',
69
+ action: 'e-act',
70
+ label: 'e-label',
71
+ value: 1,
72
+ }
73
+
74
+ test('dispatches the event', async ({ assert }) => {
75
+ const spy = sinon.spy()
76
+ globalThis.addEventListener(EventTypes.Telemetry.event, spy)
77
+ Events.Telemetry.event(init)
78
+ globalThis.removeEventListener(EventTypes.Telemetry.event, spy)
79
+ assert.isTrue(spy.calledOnce)
80
+ })
81
+
82
+ test('the event has the detail object', async ({ assert }) => {
83
+ const spy = sinon.spy()
84
+ globalThis.addEventListener(EventTypes.Telemetry.event, spy)
85
+ Events.Telemetry.event(init)
86
+ globalThis.removeEventListener(EventTypes.Telemetry.event, spy)
87
+ const e = spy.args[0][0] as CustomEvent
88
+ assert.deepEqual(e.detail, init)
89
+ })
90
+ })
91
+
92
+ test.group('Events > Telemetry > Events.Telemetry > exception()', () => {
93
+ const description = 'event-exception'
94
+ const fatal = true
95
+
96
+ test('dispatches the event', async ({ assert }) => {
97
+ const spy = sinon.spy()
98
+ globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
99
+ Events.Telemetry.exception(description)
100
+ globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
101
+ assert.isTrue(spy.calledOnce)
102
+ })
103
+
104
+ test('the event has the description', async ({ assert }) => {
105
+ const spy = sinon.spy()
106
+ globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
107
+ Events.Telemetry.exception(description)
108
+ globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
109
+ const e = spy.args[0][0] as CustomEvent
110
+ assert.equal(e.detail.description, description)
111
+ })
112
+
113
+ test('the event has the fatal', async ({ assert }) => {
114
+ const spy = sinon.spy()
115
+ globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
116
+ Events.Telemetry.exception(description, fatal)
117
+ globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
118
+ const e = spy.args[0][0] as CustomEvent
119
+ assert.equal(e.detail.fatal, fatal)
120
+ })
121
+
122
+ test('the event has custom configuration', async ({ assert }) => {
123
+ const spy = sinon.spy()
124
+ const custom = {
125
+ customMetrics: [{ index: 1, value: 1 }],
126
+ customDimensions: [{ index: 1, value: 'test' }],
127
+ }
128
+ globalThis.addEventListener(EventTypes.Telemetry.exception, spy)
129
+ Events.Telemetry.exception(description, fatal, undefined, custom)
130
+ globalThis.removeEventListener(EventTypes.Telemetry.exception, spy)
131
+ const e = spy.args[0][0] as CustomEvent
132
+ assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
133
+ assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
134
+ })
135
+ })
136
+
137
+ test.group('Events > Telemetry > Events.Telemetry > social()', () => {
138
+ const init = {
139
+ network: 'e-network',
140
+ action: 'e-action',
141
+ target: 'e-target',
142
+ }
143
+
144
+ test('dispatches the event', async ({ assert }) => {
145
+ const spy = sinon.spy()
146
+ globalThis.addEventListener(EventTypes.Telemetry.social, spy)
147
+ Events.Telemetry.social(init.network, init.action, init.target)
148
+ globalThis.removeEventListener(EventTypes.Telemetry.social, spy)
149
+ assert.isTrue(spy.calledOnce)
150
+ })
151
+
152
+ test('the event has the detail object', async ({ assert }) => {
153
+ const spy = sinon.spy()
154
+ globalThis.addEventListener(EventTypes.Telemetry.social, spy)
155
+ Events.Telemetry.social(init.network, init.action, init.target)
156
+ globalThis.removeEventListener(EventTypes.Telemetry.social, spy)
157
+ const e = spy.args[0][0] as CustomEvent
158
+ assert.deepEqual(e.detail, init)
159
+ })
160
+
161
+ test('the event has custom configuration', async ({ assert }) => {
162
+ const spy = sinon.spy()
163
+ const custom = {
164
+ customMetrics: [{ index: 1, value: 1 }],
165
+ customDimensions: [{ index: 1, value: 'test' }],
166
+ }
167
+ globalThis.addEventListener(EventTypes.Telemetry.social, spy)
168
+ Events.Telemetry.social(init.network, init.action, init.target, undefined, custom)
169
+ globalThis.removeEventListener(EventTypes.Telemetry.social, spy)
170
+ const e = spy.args[0][0] as CustomEvent
171
+ assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
172
+ assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
173
+ })
174
+ })
175
+
176
+ test.group('Events > Telemetry > Events.Telemetry > timing()', () => {
177
+ const init = {
178
+ category: 'e-category',
179
+ variable: 'e-variable',
180
+ value: 100,
181
+ label: 'e-label',
182
+ }
183
+
184
+ test('dispatches the event', async ({ assert }) => {
185
+ const spy = sinon.spy()
186
+ globalThis.addEventListener(EventTypes.Telemetry.timing, spy)
187
+ Events.Telemetry.timing(init.category, init.variable, init.value, init.label)
188
+ globalThis.removeEventListener(EventTypes.Telemetry.timing, spy)
189
+ assert.isTrue(spy.calledOnce)
190
+ })
191
+
192
+ test('the event has the detail object', async ({ assert }) => {
193
+ const spy = sinon.spy()
194
+ globalThis.addEventListener(EventTypes.Telemetry.timing, spy)
195
+ Events.Telemetry.timing(init.category, init.variable, init.value, init.label)
196
+ globalThis.removeEventListener(EventTypes.Telemetry.timing, spy)
197
+ const e = spy.args[0][0] as CustomEvent
198
+ assert.deepEqual(e.detail, init)
199
+ })
200
+
201
+ test('the event has custom configuration', async ({ assert }) => {
202
+ const spy = sinon.spy()
203
+ const custom = {
204
+ customMetrics: [{ index: 1, value: 1 }],
205
+ customDimensions: [{ index: 1, value: 'test' }],
206
+ }
207
+ globalThis.addEventListener(EventTypes.Telemetry.timing, spy)
208
+ Events.Telemetry.timing(init.category, init.variable, init.value, init.label, undefined, custom)
209
+ globalThis.removeEventListener(EventTypes.Telemetry.timing, spy)
210
+ const e = spy.args[0][0] as CustomEvent
211
+ assert.deepEqual(e.detail.customMetrics, custom.customMetrics, 'has customMetrics')
212
+ assert.deepEqual(e.detail.customDimensions, custom.customDimensions, 'has customDimensions')
213
+ })
214
+ })
215
+
216
+ test.group('Events > Telemetry > Events.Telemetry > State', () => {
217
+ test('set()', async ({ assert }) => {
218
+ const spy = sinon.spy()
219
+ globalThis.addEventListener(EventTypes.Telemetry.State.set, spy)
220
+ Events.Telemetry.State.set()
221
+ globalThis.removeEventListener(EventTypes.Telemetry.State.set, spy)
222
+ assert.isTrue(spy.calledOnce)
223
+ })
224
+ })
@@ -0,0 +1,139 @@
1
+ import { test } from '@japa/runner'
2
+ import sinon from 'sinon'
3
+ import { EventTypes } from '../../../src/events/EventTypes.js'
4
+ import { Events } from '../../../src/events/Events.js'
5
+ import { ensureUnique } from './EventsTestHelpers.js'
6
+ import { IHttpRequest, HttpRequest, Kind as HttpRequestKind } from '../../../src/models/HttpRequest.js'
7
+ import { Kind as HttpProjectKind } from '../../../src/models/HttpProject.js'
8
+ import { IHttpRequestDetail } from '../../../src/events/transport/TransportEvents.js'
9
+ import { IRequestProxyInit } from '../../../src/proxy/RequestProxy.js'
10
+ import { ContextEventDetailWithResult } from '../../../src/events/BaseEvents.js'
11
+ import { IHttpProjectProxyInit, IHttpProjectStoreProxyInit } from '../../../src/proxy/HttpProjectProxy.js'
12
+ import './events_polyfills.js'
13
+
14
+ test.group('Events > Transport > EventTypes.Transport.Core', () => {
15
+ test('has "{prop}" property')
16
+ .with([
17
+ ['request', 'transportcorerequest'],
18
+ ['httpProject', 'transportcorehttpproject'],
19
+ ])
20
+ .run(({ assert }, [prop, value]) => {
21
+ // @ts-expect-error Used in testing
22
+ assert.equal(EventTypes.Transport.Core[prop], value)
23
+ })
24
+
25
+ test('has unique events for the namespace', () => {
26
+ ensureUnique('EventTypes.Transport.Core', EventTypes.Transport.Core)
27
+ })
28
+ })
29
+
30
+ test.group('Events > Transport > EventTypes.Transport.Http', () => {
31
+ test('has "{prop}" property')
32
+ .with([['send', 'httptransportsend']])
33
+ .run(({ assert }, [prop, value]) => {
34
+ // @ts-expect-error Used in testing
35
+ assert.equal(EventTypes.Transport.Http[prop], value)
36
+ })
37
+
38
+ test('has unique events for the namespace', () => {
39
+ ensureUnique('EventTypes.Transport.Http', EventTypes.Transport.Http)
40
+ })
41
+ })
42
+
43
+ test.group('Events > Transport > EventTypes.Transport.Ws', () => {
44
+ test('has "{prop}" property')
45
+ .with([
46
+ ['connect', 'wstransportconnect'],
47
+ ['disconnect', 'wstransportdisconnect'],
48
+ ['send', 'wstransportsend'],
49
+ ])
50
+ .run(({ assert }, [prop, value]) => {
51
+ // @ts-expect-error Used in testing
52
+ assert.equal(EventTypes.Transport.Ws[prop], value)
53
+ })
54
+
55
+ test('has unique events for the namespace', () => {
56
+ ensureUnique('EventTypes.Transport.Ws', EventTypes.Transport.Ws)
57
+ })
58
+ })
59
+
60
+ test.group('Events > Transport > Events.Transport.Core > request()', () => {
61
+ const init: IRequestProxyInit = {
62
+ kind: HttpRequestKind,
63
+ request: new HttpRequest().toJSON(),
64
+ }
65
+
66
+ test('dispatches the event', async ({ assert }) => {
67
+ const spy = sinon.spy()
68
+ globalThis.addEventListener(EventTypes.Transport.Core.request, spy)
69
+ await Events.Transport.Core.request(init)
70
+ globalThis.removeEventListener(EventTypes.Transport.Core.request, spy)
71
+ assert.isTrue(spy.calledOnce)
72
+ })
73
+
74
+ test('has the detail object', async ({ assert }) => {
75
+ const spy = sinon.spy()
76
+ globalThis.addEventListener(EventTypes.Transport.Core.request, spy)
77
+ await Events.Transport.Core.request(init)
78
+ globalThis.removeEventListener(EventTypes.Transport.Core.request, spy)
79
+ const e = spy.args[0][0] as CustomEvent<ContextEventDetailWithResult<IRequestProxyInit>>
80
+ assert.deepEqual(e.detail, { ...init, result: undefined })
81
+ })
82
+ })
83
+
84
+ test.group('Events > Transport > Events.Transport.Core > httpProject()', () => {
85
+ const init: IHttpProjectStoreProxyInit = {
86
+ kind: HttpProjectKind,
87
+ pid: 'test-id',
88
+ options: { parallel: true },
89
+ organization: 'test-organization',
90
+ }
91
+
92
+ test('dispatches the event', async ({ assert }) => {
93
+ const spy = sinon.spy()
94
+ globalThis.addEventListener(EventTypes.Transport.Core.httpProject, spy)
95
+ await Events.Transport.Core.httpProject(init)
96
+ globalThis.removeEventListener(EventTypes.Transport.Core.httpProject, spy)
97
+ assert.isTrue(spy.calledOnce)
98
+ })
99
+
100
+ test('has the detail object', async ({ assert }) => {
101
+ const spy = sinon.spy()
102
+ globalThis.addEventListener(EventTypes.Transport.Core.httpProject, spy)
103
+ await Events.Transport.Core.httpProject(init)
104
+ globalThis.removeEventListener(EventTypes.Transport.Core.httpProject, spy)
105
+ const e = spy.args[0][0] as CustomEvent<ContextEventDetailWithResult<IHttpProjectProxyInit>>
106
+ assert.deepEqual(e.detail, { ...init, result: undefined })
107
+ })
108
+ })
109
+
110
+ test.group('Events > Transport > Events.Transport.Http > send()', () => {
111
+ const request: IHttpRequest = new HttpRequest().toJSON()
112
+ const init: RequestInit = { method: 'POST' }
113
+
114
+ test('dispatches the event', async ({ assert }) => {
115
+ const spy = sinon.spy()
116
+ globalThis.addEventListener(EventTypes.Transport.Http.send, spy)
117
+ await Events.Transport.Http.send(request)
118
+ globalThis.removeEventListener(EventTypes.Transport.Http.send, spy)
119
+ assert.isTrue(spy.calledOnce)
120
+ })
121
+
122
+ test('has the request on the detail', async ({ assert }) => {
123
+ const spy = sinon.spy()
124
+ globalThis.addEventListener(EventTypes.Transport.Http.send, spy)
125
+ await Events.Transport.Http.send(request)
126
+ globalThis.removeEventListener(EventTypes.Transport.Http.send, spy)
127
+ const e = spy.args[0][0] as CustomEvent<IHttpRequestDetail>
128
+ assert.equal(e.detail.request, request)
129
+ })
130
+
131
+ test('has the optional init on the detail', async ({ assert }) => {
132
+ const spy = sinon.spy()
133
+ globalThis.addEventListener(EventTypes.Transport.Http.send, spy)
134
+ await Events.Transport.Http.send(request, init)
135
+ globalThis.removeEventListener(EventTypes.Transport.Http.send, spy)
136
+ const e = spy.args[0][0] as CustomEvent<IHttpRequestDetail>
137
+ assert.deepEqual(e.detail.init, init)
138
+ })
139
+ })
@@ -12,7 +12,6 @@ import {
12
12
  DomainEntity,
13
13
  DomainProperty,
14
14
  DomainAssociation,
15
- DataDomainKind,
16
15
  } from '../../../src/index.js'
17
16
 
18
17
  test.group('DomainImpactAnalysis', (group) => {
@@ -450,112 +449,3 @@ test.group('DomainImpactAnalysis', (group) => {
450
449
  assert.lengthOf(report.impact, 0, 'has all items')
451
450
  })
452
451
  })
453
-
454
- test.group('DomainImpactAnalysis.publishAnalysis()', (group) => {
455
- let domain: DataDomain
456
- let analysis: DomainImpactAnalysis
457
-
458
- group.each.setup(() => {
459
- domain = new DataDomain()
460
- analysis = new DomainImpactAnalysis(domain)
461
- })
462
-
463
- test('publishAnalysis() should return an empty report when the domain is empty', ({ assert }) => {
464
- const report = analysis.publishAnalysis()
465
- assert.deepEqual(report, {
466
- key: '',
467
- kind: DataDomainKind,
468
- impact: [],
469
- canProceed: true,
470
- })
471
- })
472
-
473
- test('publishAnalysis() should return validation errors for entities', ({ assert }) => {
474
- const model = domain.addModel({ key: 'model' })
475
- const entity = model.addEntity({ key: 'entity', info: { name: 'Invalid-Name' } }) // Invalid name
476
-
477
- const report = analysis.publishAnalysis()
478
- // no key
479
- // no properties
480
- // invalid name
481
- // name is not a snake case
482
- assert.lengthOf(report.impact, 4)
483
- assert.equal(report.impact[0].key, 'entity')
484
- assert.equal(report.impact[0].kind, entity.kind)
485
- assert.equal(report.impact[0].type, 'publish')
486
- assert.equal(report.impact[0].severity, 'error')
487
- assert.equal(report.canProceed, false)
488
- })
489
-
490
- test('publishAnalysis() should return validation errors for properties', ({ assert }) => {
491
- const model = domain.addModel({ key: 'model' })
492
- const entity = model.addEntity({ key: 'entity', info: { name: 'entity' } })
493
- const property = entity.addProperty({ key: 'invalid-property', type: 'string', info: { name: 'invalid-property' } })
494
-
495
- const report = analysis.publishAnalysis()
496
- // no key (entity)
497
- // invalid name (property)
498
- assert.lengthOf(report.impact, 2)
499
- assert.equal(report.impact[1].key, property.key)
500
- assert.equal(report.impact[1].kind, property.kind)
501
- assert.equal(report.impact[1].type, 'publish')
502
- assert.equal(report.impact[1].severity, 'error')
503
- assert.equal(report.canProceed, false)
504
- })
505
-
506
- test('publishAnalysis() should return validation errors for associations', ({ assert }) => {
507
- const model = domain.addModel({ key: 'model' })
508
- const entity1 = model.addEntity({ key: 'entity1', info: { name: 'entity1' } })
509
- const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
510
- const association = entity1.addAssociation({ key: entity2.key }, { info: { name: 'Invalid-Name' } })
511
-
512
- const report = analysis.publishAnalysis()
513
- // no key (entity1)
514
- // invalid name (association)
515
- // snake case (association)
516
- // no key (entity2)
517
- // no properties (entity2)
518
- assert.lengthOf(report.impact, 5)
519
- assert.equal(report.impact[1].key, association.key)
520
- assert.equal(report.impact[1].kind, association.kind)
521
- assert.equal(report.impact[1].type, 'publish')
522
- assert.equal(report.impact[1].severity, 'error')
523
- assert.equal(report.canProceed, false)
524
- })
525
-
526
- test('publishAnalysis() should return multiple validation errors', ({ assert }) => {
527
- const model = domain.addModel({ key: 'model' })
528
- const entity1 = model.addEntity({ key: 'invalid-entity', info: { name: 'Invalid-Entity' } })
529
- entity1.addProperty({ key: 'invalid-property', type: 'string', info: { name: 'Invalid-Property' } })
530
- const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
531
- entity1.addAssociation({ key: entity2.key }, { info: { name: 'Invalid-Name' } })
532
-
533
- const report = analysis.publishAnalysis()
534
- // entity1 - no key
535
- // entity1 - invalid name
536
- // entity1 - snake case
537
- // property - invalid name
538
- // property - snake case
539
- // association - invalid name
540
- // association - snake case
541
- // entity2 - no key
542
- // entity2 - no properties
543
- assert.lengthOf(report.impact, 9)
544
- assert.equal(report.canProceed, false)
545
- })
546
-
547
- test('publishAnalysis() should return no errors for a valid domain', ({ assert }) => {
548
- const model = domain.addModel({ key: 'model' })
549
- const entity1 = model.addEntity({ key: 'entity1', info: { name: 'entity1' } })
550
- entity1.addProperty({ key: 'p1', type: 'string', info: { name: 'property1' } })
551
- entity1.addProperty({ key: 'p2', type: 'string', primary: true, info: { name: 'property2' } })
552
- const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
553
- entity2.addProperty({ key: 'p3', type: 'number', info: { name: 'property3' } })
554
- entity2.addProperty({ key: 'p4', type: 'string', primary: true, info: { name: 'property4' } })
555
- entity1.addAssociation({ key: entity2.key }, { info: { name: 'name' } })
556
-
557
- const report = analysis.publishAnalysis()
558
- assert.lengthOf(report.impact, 0)
559
- assert.equal(report.canProceed, true)
560
- })
561
- })
@@ -0,0 +1,94 @@
1
+ import { test } from '@japa/runner'
2
+ import { DomainValidation } from '../../../src/modeling/DomainValidation.js'
3
+ import { DataDomain, DataDomainKind } from '../../../src/index.js'
4
+ import { SemanticType } from '../../../src/modeling/Semantics.js'
5
+
6
+ test.group('DomainImpactAnalysis.validate()', (group) => {
7
+ let domain: DataDomain
8
+ let analysis: DomainValidation
9
+
10
+ group.each.setup(() => {
11
+ domain = new DataDomain()
12
+ analysis = new DomainValidation(domain)
13
+ })
14
+
15
+ test('validate() should return an empty report when the domain is empty', ({ assert }) => {
16
+ const report = analysis.validate()
17
+ assert.deepEqual(report, {
18
+ key: '',
19
+ kind: DataDomainKind,
20
+ impact: [],
21
+ canProceed: true,
22
+ })
23
+ })
24
+
25
+ test('validate() should return validation errors for entities', ({ assert }) => {
26
+ const model = domain.addModel({ key: 'model' })
27
+ model.addEntity({ key: 'entity', info: { name: 'Invalid-Name' } }) // Invalid name
28
+
29
+ const report = analysis.validate()
30
+ assert.lengthOf(report.impact, 8)
31
+ // we test specific rules in the validation tests
32
+ })
33
+
34
+ test('validate() should return validation errors for properties', ({ assert }) => {
35
+ const model = domain.addModel({ key: 'model' })
36
+ const entity = model.addEntity({ key: 'entity', info: { name: 'entity' } })
37
+ entity.addProperty({ key: 'invalid-property', type: 'string', info: { name: 'invalid-property' } })
38
+
39
+ const report = analysis.validate()
40
+ assert.lengthOf(report.impact, 6)
41
+ // we test specific rules in the validation tests
42
+ })
43
+
44
+ test('validate() should return validation errors for associations', ({ assert }) => {
45
+ const model = domain.addModel({ key: 'model' })
46
+ const entity1 = model.addEntity({ key: 'entity1', info: { name: 'entity1' } })
47
+ const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
48
+ entity1.addAssociation({ key: entity2.key }, { info: { name: 'Invalid-Name' } })
49
+
50
+ const report = analysis.validate()
51
+ assert.lengthOf(report.impact, 12)
52
+ // we test specific rules in the validation tests
53
+ })
54
+
55
+ test('validate() should return multiple validation errors', ({ assert }) => {
56
+ const model = domain.addModel({ key: 'model' })
57
+ const entity1 = model.addEntity({ key: 'invalid-entity', info: { name: 'Invalid-Entity' } })
58
+ entity1.addProperty({ key: 'invalid-property', type: 'string', info: { name: 'Invalid-Property' } })
59
+ const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
60
+ entity1.addAssociation({ key: entity2.key }, { info: { name: 'Invalid-Name' } })
61
+
62
+ const report = analysis.validate()
63
+ assert.lengthOf(report.impact, 16)
64
+ // we test specific rules in the validation tests
65
+ })
66
+
67
+ test('validate() should return no errors for a valid domain', ({ assert }) => {
68
+ const model = domain.addModel({ key: 'model' })
69
+ const entity1 = model.addEntity({ key: 'entity1', info: { name: 'entity1' } })
70
+ entity1.addProperty({ key: 'p1', type: 'string', info: { name: 'property1' } })
71
+ entity1.addProperty({ key: 'p2', type: 'string', primary: true, info: { name: 'property2' } })
72
+ const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
73
+ const p3e2 = entity2.addProperty({ key: 'p3', type: 'number', info: { name: 'property3' } })
74
+ entity2.addProperty({ key: 'p4', type: 'string', primary: true, info: { name: 'property4' } })
75
+ const p5e2 = entity2.addProperty({ key: 'p5', type: 'number', info: { name: 'property5' } })
76
+ const p6e2 = entity2.addProperty({ key: 'p6', type: 'number', info: { name: 'property6' } })
77
+ entity1.addAssociation({ key: entity2.key }, { info: { name: 'name' } })
78
+ const p3e1 = entity1.addProperty({ type: 'number', info: { name: 'p3e1' } })
79
+ const p4e1 = entity1.addProperty({ type: 'number', info: { name: 'p4e1' } })
80
+ const p5e1 = entity1.addProperty({ type: 'boolean', info: { name: 'p5e1' } })
81
+
82
+ entity1.addSemantic({ id: SemanticType.User })
83
+ p3e2.addSemantic({ id: SemanticType.CreatedTimestamp })
84
+ p5e2.addSemantic({ id: SemanticType.UpdatedTimestamp })
85
+ p6e2.addSemantic({ id: SemanticType.DeletedTimestamp })
86
+ p3e1.addSemantic({ id: SemanticType.CreatedTimestamp })
87
+ p4e1.addSemantic({ id: SemanticType.UpdatedTimestamp })
88
+ p5e1.addSemantic({ id: SemanticType.DeletedFlag })
89
+
90
+ const report = analysis.validate()
91
+ assert.lengthOf(report.impact, 0)
92
+ assert.equal(report.canProceed, true)
93
+ })
94
+ })
@@ -0,0 +1,91 @@
1
+ import { test } from '@japa/runner'
2
+ import { DataDomain } from '../../../../src/modeling/DataDomain.js'
3
+ import { SemanticValidation } from '../../../../src/modeling/validation/semantic_validation.js'
4
+ import { SemanticType } from '../../../../src/modeling/Semantics.js'
5
+
6
+ test.group('SemanticValidation', (group) => {
7
+ let domain: DataDomain
8
+ let validation: SemanticValidation
9
+
10
+ group.each.setup(() => {
11
+ domain = new DataDomain()
12
+ validation = new SemanticValidation(domain)
13
+ })
14
+
15
+ test('validate() should return a warning when no User entity is found', ({ assert }) => {
16
+ const model = domain.addModel({ key: 'model' })
17
+ model.addEntity({ key: 'entity', info: { name: 'Entity' } })
18
+ const results = validation.validate()
19
+ assert.lengthOf(results, 4) // Warning for User + 2 info for timestamps + info for soft delete
20
+ assert.equal(results[0].rule, 'recommended')
21
+ assert.equal(results[0].severity, 'warning')
22
+ assert.equal(results[0].message, 'No entity with User taxonomy found in the domain.')
23
+ })
24
+
25
+ test('validate() should not return a warning when a User entity is found', ({ assert }) => {
26
+ const model = domain.addModel({ key: 'model' })
27
+ const entity = model.addEntity({ key: 'user', info: { name: 'User' } })
28
+ entity.addSemantic({ id: SemanticType.User })
29
+ const results = validation.validate()
30
+ assert.lengthOf(results, 3) // Only info for timestamps (2) and soft delete
31
+ assert.notEqual(results[0].message, 'No entity with User taxonomy found in the domain.')
32
+ })
33
+
34
+ test('validate() should return info when an entity has no timestamp taxonomy', ({ assert }) => {
35
+ const model = domain.addModel({ key: 'model' })
36
+ model.addEntity({ key: 'entity', info: { name: 'Entity' } })
37
+ const results = validation.validate()
38
+ assert.lengthOf(results, 4)
39
+ const timestampResults = results.filter(
40
+ (r) => r.message.includes('CreatedTimestamp') || r.message.includes('UpdatedTimestamp')
41
+ )
42
+ assert.lengthOf(timestampResults, 2)
43
+ assert.equal(timestampResults[0].severity, 'info')
44
+ assert.equal(timestampResults[1].severity, 'info')
45
+ })
46
+
47
+ test('validate() should not return info when an entity has timestamp taxonomy', ({ assert }) => {
48
+ const model = domain.addModel({ key: 'model' })
49
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
50
+ const createdProperty = entity.addProperty({
51
+ key: 'created_at',
52
+ type: 'datetime',
53
+ info: { name: 'created_at' },
54
+ })
55
+ const updatedProperty = entity.addProperty({
56
+ key: 'updated_at',
57
+ type: 'datetime',
58
+ info: { name: 'updated_at' },
59
+ })
60
+ createdProperty.addSemantic({ id: SemanticType.CreatedTimestamp })
61
+ updatedProperty.addSemantic({ id: SemanticType.UpdatedTimestamp })
62
+ const results = validation.validate()
63
+ const timestampResults = results.filter(
64
+ (r) => r.message.includes('CreatedTimestamp') || r.message.includes('UpdatedTimestamp')
65
+ )
66
+ assert.lengthOf(timestampResults, 0)
67
+ })
68
+
69
+ test('validate() should return info when an entity has no soft delete taxonomy', ({ assert }) => {
70
+ const model = domain.addModel({ key: 'model' })
71
+ model.addEntity({ key: 'entity', info: { name: 'Entity' } })
72
+ const results = validation.validate()
73
+ const softDeleteResults = results.filter((r) => r.message.includes('soft delete taxonomy'))
74
+ assert.lengthOf(softDeleteResults, 1)
75
+ assert.equal(softDeleteResults[0].severity, 'info')
76
+ })
77
+
78
+ test('validate() should not return info when an entity has soft delete taxonomy', ({ assert }) => {
79
+ const model = domain.addModel({ key: 'model' })
80
+ const entity = model.addEntity({ key: 'entity', info: { name: 'Entity' } })
81
+ const deletedProperty = entity.addProperty({
82
+ key: 'deleted_at',
83
+ type: 'datetime',
84
+ info: { name: 'deleted_at' },
85
+ })
86
+ deletedProperty.addSemantic({ id: SemanticType.DeletedTimestamp })
87
+ const results = validation.validate()
88
+ const softDeleteResults = results.filter((r) => r.message.includes('soft delete taxonomy'))
89
+ assert.lengthOf(softDeleteResults, 0)
90
+ })
91
+ })