@api-client/core 0.15.1 → 0.16.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 (125) hide show
  1. package/TESTING_READY.md +114 -0
  2. package/TESTING_SETUP.md +198 -0
  3. package/build/src/modeling/Semantics.d.ts +126 -2
  4. package/build/src/modeling/Semantics.d.ts.map +1 -1
  5. package/build/src/modeling/Semantics.js +281 -13
  6. package/build/src/modeling/Semantics.js.map +1 -1
  7. package/build/src/modeling/definitions/Calculated.d.ts +54 -0
  8. package/build/src/modeling/definitions/Calculated.d.ts.map +1 -0
  9. package/build/src/modeling/definitions/Calculated.js +31 -0
  10. package/build/src/modeling/definitions/Calculated.js.map +1 -0
  11. package/build/src/modeling/definitions/Categories.d.ts +60 -0
  12. package/build/src/modeling/definitions/Categories.d.ts.map +1 -0
  13. package/build/src/modeling/definitions/Categories.js +33 -0
  14. package/build/src/modeling/definitions/Categories.js.map +1 -0
  15. package/build/src/modeling/definitions/Derived.d.ts +54 -0
  16. package/build/src/modeling/definitions/Derived.d.ts.map +1 -0
  17. package/build/src/modeling/definitions/Derived.js +31 -0
  18. package/build/src/modeling/definitions/Derived.js.map +1 -0
  19. package/build/src/modeling/definitions/Description.d.ts +36 -0
  20. package/build/src/modeling/definitions/Description.d.ts.map +1 -0
  21. package/build/src/modeling/definitions/Description.js +28 -0
  22. package/build/src/modeling/definitions/Description.js.map +1 -0
  23. package/build/src/modeling/definitions/Email.d.ts +66 -0
  24. package/build/src/modeling/definitions/Email.d.ts.map +1 -0
  25. package/build/src/modeling/definitions/Email.js +33 -0
  26. package/build/src/modeling/definitions/Email.js.map +1 -0
  27. package/build/src/modeling/definitions/GeospatialCoordinates.d.ts +212 -0
  28. package/build/src/modeling/definitions/GeospatialCoordinates.d.ts.map +1 -0
  29. package/build/src/modeling/definitions/GeospatialCoordinates.js +129 -0
  30. package/build/src/modeling/definitions/GeospatialCoordinates.js.map +1 -0
  31. package/build/src/modeling/definitions/HTML.d.ts +88 -0
  32. package/build/src/modeling/definitions/HTML.d.ts.map +1 -0
  33. package/build/src/modeling/definitions/HTML.js +42 -0
  34. package/build/src/modeling/definitions/HTML.js.map +1 -0
  35. package/build/src/modeling/definitions/Markdown.d.ts +84 -0
  36. package/build/src/modeling/definitions/Markdown.d.ts.map +1 -0
  37. package/build/src/modeling/definitions/Markdown.js +41 -0
  38. package/build/src/modeling/definitions/Markdown.js.map +1 -0
  39. package/build/src/modeling/definitions/Password.d.ts +112 -0
  40. package/build/src/modeling/definitions/Password.d.ts.map +1 -0
  41. package/build/src/modeling/definitions/Password.js +57 -0
  42. package/build/src/modeling/definitions/Password.js.map +1 -0
  43. package/build/src/modeling/definitions/Phone.d.ts +83 -0
  44. package/build/src/modeling/definitions/Phone.d.ts.map +1 -0
  45. package/build/src/modeling/definitions/Phone.js +39 -0
  46. package/build/src/modeling/definitions/Phone.js.map +1 -0
  47. package/build/src/modeling/definitions/Price.d.ts +102 -0
  48. package/build/src/modeling/definitions/Price.d.ts.map +1 -0
  49. package/build/src/modeling/definitions/Price.js +99 -0
  50. package/build/src/modeling/definitions/Price.js.map +1 -0
  51. package/build/src/modeling/definitions/PublicUniqueName.d.ts +69 -0
  52. package/build/src/modeling/definitions/PublicUniqueName.d.ts.map +1 -0
  53. package/build/src/modeling/definitions/PublicUniqueName.js +34 -0
  54. package/build/src/modeling/definitions/PublicUniqueName.js.map +1 -0
  55. package/build/src/modeling/definitions/SKU.d.ts +127 -0
  56. package/build/src/modeling/definitions/SKU.d.ts.map +1 -0
  57. package/build/src/modeling/definitions/SKU.js +142 -0
  58. package/build/src/modeling/definitions/SKU.js.map +1 -0
  59. package/build/src/modeling/definitions/Status.d.ts +150 -0
  60. package/build/src/modeling/definitions/Status.d.ts.map +1 -0
  61. package/build/src/modeling/definitions/Status.js +60 -0
  62. package/build/src/modeling/definitions/Status.js.map +1 -0
  63. package/build/src/modeling/definitions/Summary.d.ts +53 -0
  64. package/build/src/modeling/definitions/Summary.d.ts.map +1 -0
  65. package/build/src/modeling/definitions/Summary.js +50 -0
  66. package/build/src/modeling/definitions/Summary.js.map +1 -0
  67. package/build/src/modeling/definitions/Tags.d.ts +52 -0
  68. package/build/src/modeling/definitions/Tags.d.ts.map +1 -0
  69. package/build/src/modeling/definitions/Tags.js +32 -0
  70. package/build/src/modeling/definitions/Tags.js.map +1 -0
  71. package/build/src/modeling/definitions/URL.d.ts +68 -0
  72. package/build/src/modeling/definitions/URL.d.ts.map +1 -0
  73. package/build/src/modeling/definitions/URL.js +37 -0
  74. package/build/src/modeling/definitions/URL.js.map +1 -0
  75. package/build/src/modeling/validation/semantic_validation.d.ts +4 -0
  76. package/build/src/modeling/validation/semantic_validation.d.ts.map +1 -1
  77. package/build/src/modeling/validation/semantic_validation.js +32 -1
  78. package/build/src/modeling/validation/semantic_validation.js.map +1 -1
  79. package/build/tsconfig.tsbuildinfo +1 -1
  80. package/package.json +1 -1
  81. package/src/modeling/Semantics.ts +297 -14
  82. package/src/modeling/definitions/Calculated.ts +76 -0
  83. package/src/modeling/definitions/Categories.ts +84 -0
  84. package/src/modeling/definitions/Derived.ts +76 -0
  85. package/src/modeling/definitions/Description.ts +55 -0
  86. package/src/modeling/definitions/Email.ts +90 -0
  87. package/src/modeling/definitions/GeospatialCoordinates.ts +274 -0
  88. package/src/modeling/definitions/HTML.ts +121 -0
  89. package/src/modeling/definitions/Markdown.ts +116 -0
  90. package/src/modeling/definitions/Password.ts +156 -0
  91. package/src/modeling/definitions/Phone.ts +116 -0
  92. package/src/modeling/definitions/Price.examples.md +158 -0
  93. package/src/modeling/definitions/Price.ts +180 -0
  94. package/src/modeling/definitions/PublicUniqueName.ts +98 -0
  95. package/src/modeling/definitions/SKU.examples.md +230 -0
  96. package/src/modeling/definitions/SKU.ts +254 -0
  97. package/src/modeling/definitions/Status.ts +227 -0
  98. package/src/modeling/definitions/Summary.ts +73 -0
  99. package/src/modeling/definitions/Tags.ts +75 -0
  100. package/src/modeling/definitions/URL.ts +96 -0
  101. package/src/modeling/validation/semantic_validation.ts +35 -1
  102. package/tests/example-test-setup.ts +133 -0
  103. package/tests/template-node.spec.ts +75 -0
  104. package/tests/test-utils.ts +293 -0
  105. package/tests/unit/modeling/definitions/calculated.spec.ts +33 -0
  106. package/tests/unit/modeling/definitions/categories.spec.ts +38 -0
  107. package/tests/unit/modeling/definitions/derived.spec.ts +34 -0
  108. package/tests/unit/modeling/definitions/description.spec.ts +38 -0
  109. package/tests/unit/modeling/definitions/email.spec.ts +38 -0
  110. package/tests/unit/modeling/definitions/geospatial-coordinates.spec.ts +41 -0
  111. package/tests/unit/modeling/definitions/html.spec.ts +38 -0
  112. package/tests/unit/modeling/definitions/markdown.spec.ts +38 -0
  113. package/tests/unit/modeling/definitions/password.spec.ts +347 -0
  114. package/tests/unit/modeling/definitions/phone.spec.ts +38 -0
  115. package/tests/unit/modeling/definitions/price.spec.ts +465 -0
  116. package/tests/unit/modeling/definitions/public-unique-name.spec.ts +38 -0
  117. package/tests/unit/modeling/definitions/sku.spec.ts +240 -0
  118. package/tests/unit/modeling/definitions/status.spec.ts +37 -0
  119. package/tests/unit/modeling/definitions/summary.spec.ts +36 -0
  120. package/tests/unit/modeling/definitions/tags.spec.ts +38 -0
  121. package/tests/unit/modeling/definitions/url.spec.ts +38 -0
  122. package/tests/unit/modeling/domain_property.spec.ts +106 -0
  123. package/tests/unit/modeling/domain_validation.spec.ts +5 -5
  124. package/tests/unit/modeling/semantic-configs.spec.ts +569 -0
  125. package/tests/unit/modeling/semantics.spec.ts +52 -0
@@ -0,0 +1,240 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createSKUSemantic,
4
+ isSKUSemantic,
5
+ type SKUConfig,
6
+ DEFAULT_SKU_CONFIG,
7
+ SKU_PRESETS,
8
+ validateSKUConfig,
9
+ validateSKUValue,
10
+ } from '../../../../src/modeling/definitions/SKU.js'
11
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
12
+
13
+ test.group('SKU Semantic Configuration', () => {
14
+ test('should create semantic with default config', ({ assert }) => {
15
+ const semantic = createSKUSemantic()
16
+ assert.equal(semantic.id, SemanticType.SKU)
17
+ assert.deepEqual(semantic.config, DEFAULT_SKU_CONFIG)
18
+ })
19
+
20
+ test('should create semantic with custom config', ({ assert }) => {
21
+ const config: SKUConfig = {
22
+ validationMode: 'lenient',
23
+ caseMode: 'lowercase',
24
+ prefix: 'PROD-',
25
+ enforceUniqueness: false,
26
+ }
27
+ const semantic = createSKUSemantic(config)
28
+ assert.equal(semantic.id, SemanticType.SKU)
29
+ assert.deepEqual(semantic.config, { ...DEFAULT_SKU_CONFIG, ...config })
30
+ })
31
+
32
+ test('should merge metadata separately', ({ assert }) => {
33
+ const config: SKUConfig = {
34
+ metadata: { source: 'legacy-system' },
35
+ }
36
+ const semantic = createSKUSemantic(config)
37
+ assert.deepEqual(semantic.config?.metadata, { source: 'legacy-system' })
38
+ })
39
+
40
+ test('should identify SKU semantic', ({ assert }) => {
41
+ const semantic = createSKUSemantic()
42
+ assert.isTrue(isSKUSemantic(semantic))
43
+ })
44
+
45
+ test('should not identify non-SKU semantic', ({ assert }) => {
46
+ const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
47
+ assert.isFalse(isSKUSemantic(fakeSemantic))
48
+ })
49
+
50
+ test('presets should be valid SKU semantics', ({ assert }) => {
51
+ Object.values(SKU_PRESETS).forEach((preset) => {
52
+ assert.isTrue(isSKUSemantic(preset))
53
+ assert.equal(preset.id, SemanticType.SKU)
54
+ })
55
+ })
56
+
57
+ test('PRODUCT_STANDARD preset should have correct configuration', ({ assert }) => {
58
+ const preset = SKU_PRESETS.PRODUCT_STANDARD
59
+ assert.equal(preset.config?.validationMode, 'strict')
60
+ assert.equal(preset.config?.caseMode, 'uppercase')
61
+ assert.equal(preset.config?.prefix, 'PROD-')
62
+ assert.equal(preset.config?.enforceUniqueness, true)
63
+ })
64
+
65
+ test('SIMPLE preset should have correct configuration', ({ assert }) => {
66
+ const preset = SKU_PRESETS.SIMPLE
67
+ assert.equal(preset.config?.validationMode, 'lenient')
68
+ assert.equal(preset.config?.caseMode, 'preserve')
69
+ assert.equal(preset.config?.enforceUniqueness, true)
70
+ })
71
+
72
+ test('AUTO_GENERATE preset should have correct configuration', ({ assert }) => {
73
+ const preset = SKU_PRESETS.AUTO_GENERATE
74
+ assert.equal(preset.config?.validationMode, 'strict')
75
+ assert.equal(preset.config?.caseMode, 'uppercase')
76
+ assert.equal(preset.config?.autoGenerate, true)
77
+ assert.equal(preset.config?.autoGenerateSource, 'name')
78
+ assert.equal(preset.config?.enforceUniqueness, true)
79
+ })
80
+
81
+ test('FLEXIBLE preset should have correct configuration', ({ assert }) => {
82
+ const preset = SKU_PRESETS.FLEXIBLE
83
+ assert.equal(preset.config?.validationMode, 'lenient')
84
+ assert.equal(preset.config?.caseMode, 'preserve')
85
+ assert.equal(preset.config?.enforceUniqueness, true)
86
+ assert.equal(preset.config?.validateReservedWords, false)
87
+ })
88
+ })
89
+
90
+ test.group('SKU Configuration Validation', () => {
91
+ test('should validate valid configuration', ({ assert }) => {
92
+ const config: SKUConfig = {
93
+ validationMode: 'strict',
94
+ caseMode: 'uppercase',
95
+ }
96
+ const errors = validateSKUConfig(config)
97
+ assert.lengthOf(errors, 0)
98
+ })
99
+
100
+ test('should require customPattern when validationMode is custom', ({ assert }) => {
101
+ const config: SKUConfig = { validationMode: 'custom' }
102
+ const errors = validateSKUConfig(config)
103
+ assert.include(errors, 'customPattern is required when validationMode is custom')
104
+ })
105
+
106
+ test('should validate custom pattern regex', ({ assert }) => {
107
+ const config: SKUConfig = { validationMode: 'custom', customPattern: '[invalid regex' }
108
+ const errors = validateSKUConfig(config)
109
+ assert.include(errors, 'customPattern must be a valid regular expression')
110
+ })
111
+
112
+ test('should require autoGenerate when autoGenerateSource is specified', ({ assert }) => {
113
+ const config: SKUConfig = { autoGenerateSource: 'name', autoGenerate: false }
114
+ const errors = validateSKUConfig(config)
115
+ assert.include(errors, 'autoGenerate must be true when autoGenerateSource is specified')
116
+ })
117
+
118
+ test('should accept valid custom pattern', ({ assert }) => {
119
+ const config: SKUConfig = { validationMode: 'custom', customPattern: '^[A-Z]{3}-\\d{3}$' }
120
+ const errors = validateSKUConfig(config)
121
+ assert.lengthOf(errors, 0)
122
+ })
123
+ })
124
+
125
+ test.group('SKU Value Validation', () => {
126
+ test('should validate valid SKU with default config', ({ assert }) => {
127
+ const errors = validateSKUValue('PRODUCT123')
128
+ assert.lengthOf(errors, 0)
129
+ })
130
+
131
+ test('should reject empty or null values', ({ assert }) => {
132
+ assert.include(validateSKUValue(''), 'SKU value must be a non-empty string')
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ assert.include(validateSKUValue(null as any), 'SKU value must be a non-empty string')
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ assert.include(validateSKUValue(undefined as any), 'SKU value must be a non-empty string')
137
+ })
138
+
139
+ test('should validate strict mode', ({ assert }) => {
140
+ const config: SKUConfig = { validationMode: 'strict' }
141
+
142
+ assert.lengthOf(validateSKUValue('ABC123', config), 0)
143
+ assert.lengthOf(validateSKUValue('ABC-123', config), 0)
144
+ assert.lengthOf(validateSKUValue('ABC_123', config), 0)
145
+ assert.include(
146
+ validateSKUValue('ABC.123', config),
147
+ 'SKU can only contain alphanumeric characters, hyphens, and underscores'
148
+ )
149
+ assert.include(
150
+ validateSKUValue('ABC@123', config),
151
+ 'SKU can only contain alphanumeric characters, hyphens, and underscores'
152
+ )
153
+ })
154
+
155
+ test('should validate lenient mode', ({ assert }) => {
156
+ const config: SKUConfig = { validationMode: 'lenient' }
157
+
158
+ assert.lengthOf(validateSKUValue('ABC123', config), 0)
159
+ assert.lengthOf(validateSKUValue('ABC-123', config), 0)
160
+ assert.lengthOf(validateSKUValue('ABC_123', config), 0)
161
+ assert.lengthOf(validateSKUValue('ABC.123', config), 0)
162
+ assert.include(validateSKUValue('ABC@123', config), 'SKU contains invalid characters')
163
+ assert.include(validateSKUValue('ABC#123', config), 'SKU contains invalid characters')
164
+ })
165
+
166
+ test('should validate custom pattern mode', ({ assert }) => {
167
+ const config: SKUConfig = {
168
+ validationMode: 'custom',
169
+ customPattern: '^[A-Z]{3}-\\d{3}$',
170
+ }
171
+
172
+ assert.lengthOf(validateSKUValue('ABC-123', config), 0)
173
+ assert.include(validateSKUValue('abc-123', config), 'SKU does not match the required pattern')
174
+ assert.include(validateSKUValue('ABCD-123', config), 'SKU does not match the required pattern')
175
+ assert.include(validateSKUValue('ABC-12', config), 'SKU does not match the required pattern')
176
+ })
177
+
178
+ test('should validate reserved words', ({ assert }) => {
179
+ const config: SKUConfig = {
180
+ validateReservedWords: true,
181
+ reservedValues: ['ADMIN', 'TEST', 'NULL'],
182
+ }
183
+
184
+ assert.include(validateSKUValue('ADMIN', config), 'SKU cannot use reserved value: ADMIN')
185
+ assert.include(validateSKUValue('test', config), 'SKU cannot use reserved value: test')
186
+ assert.include(validateSKUValue('null', config), 'SKU cannot use reserved value: null')
187
+ assert.lengthOf(validateSKUValue('PRODUCT', config), 0)
188
+ })
189
+
190
+ test('should skip reserved word validation when disabled', ({ assert }) => {
191
+ const config: SKUConfig = {
192
+ validateReservedWords: false,
193
+ reservedValues: ['ADMIN', 'TEST'],
194
+ }
195
+
196
+ assert.lengthOf(validateSKUValue('ADMIN', config), 0)
197
+ assert.lengthOf(validateSKUValue('TEST', config), 0)
198
+ })
199
+
200
+ test('should handle invalid custom pattern gracefully', ({ assert }) => {
201
+ const config: SKUConfig = {
202
+ validationMode: 'custom',
203
+ customPattern: '[invalid',
204
+ }
205
+
206
+ assert.include(validateSKUValue('ANYTHING', config), 'Invalid custom pattern configuration')
207
+ })
208
+
209
+ test('should validate multiple errors', ({ assert }) => {
210
+ const config: SKUConfig = {
211
+ validationMode: 'strict',
212
+ validateReservedWords: true,
213
+ reservedValues: ['TEST'],
214
+ }
215
+
216
+ const errors = validateSKUValue('TE$T', config)
217
+ assert.include(errors, 'SKU can only contain alphanumeric characters, hyphens, and underscores')
218
+ })
219
+ })
220
+
221
+ test.group('SKU Default Configuration', () => {
222
+ test('should have sensible defaults', ({ assert }) => {
223
+ assert.equal(DEFAULT_SKU_CONFIG.validationMode, 'strict')
224
+ assert.equal(DEFAULT_SKU_CONFIG.caseMode, 'uppercase')
225
+ assert.equal(DEFAULT_SKU_CONFIG.enforceUniqueness, true)
226
+ assert.equal(DEFAULT_SKU_CONFIG.autoGenerate, false)
227
+ assert.equal(DEFAULT_SKU_CONFIG.validateReservedWords, true)
228
+ })
229
+
230
+ test('should include common reserved values', ({ assert }) => {
231
+ const reservedValues = DEFAULT_SKU_CONFIG.reservedValues || []
232
+ assert.include(reservedValues, 'ADMIN')
233
+ assert.include(reservedValues, 'TEST')
234
+ assert.include(reservedValues, 'NULL')
235
+ assert.include(reservedValues, 'DEFAULT')
236
+ assert.include(reservedValues, 'UNDEFINED')
237
+ assert.include(reservedValues, 'SAMPLE')
238
+ assert.include(reservedValues, 'DEMO')
239
+ })
240
+ })
@@ -0,0 +1,37 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createStatusSemantic,
4
+ isStatusSemantic,
5
+ DEFAULT_STATUS_CONFIG,
6
+ type StatusConfig,
7
+ } from '../../../../src/modeling/definitions/Status.js'
8
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
9
+
10
+ test.group('Status Semantic Configuration', () => {
11
+ test('should create semantic with default config', ({ assert }) => {
12
+ const semantic = createStatusSemantic()
13
+ assert.equal(semantic.id, SemanticType.Status)
14
+ assert.deepEqual(semantic.config, DEFAULT_STATUS_CONFIG)
15
+ })
16
+
17
+ test('should create semantic with custom config', ({ assert }) => {
18
+ const config: StatusConfig = {
19
+ allowedStates: ['active', 'inactive'],
20
+ defaultState: 'active',
21
+ }
22
+ const semantic = createStatusSemantic(config)
23
+ assert.equal(semantic.id, SemanticType.Status)
24
+ assert.deepEqual(semantic.config, { ...DEFAULT_STATUS_CONFIG, ...config })
25
+ })
26
+
27
+ test('should identify status semantic', ({ assert }) => {
28
+ const semantic = createStatusSemantic()
29
+ assert.isTrue(isStatusSemantic(semantic))
30
+ })
31
+
32
+ test('should not identify non-status semantic', ({ assert }) => {
33
+ // Simulate a different semantic
34
+ const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
35
+ assert.isFalse(isStatusSemantic(fakeSemantic))
36
+ })
37
+ })
@@ -0,0 +1,36 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createSummarySemantic,
4
+ isSummarySemantic,
5
+ DEFAULT_SUMMARY_CONFIG,
6
+ type SummaryConfig,
7
+ } from '../../../../src/modeling/definitions/Summary.js'
8
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
9
+
10
+ test.group('Summary Semantic Configuration', () => {
11
+ test('should create semantic with default config', ({ assert }) => {
12
+ const semantic = createSummarySemantic()
13
+ assert.equal(semantic.id, SemanticType.Summary)
14
+ assert.deepEqual(semantic.config, DEFAULT_SUMMARY_CONFIG)
15
+ })
16
+
17
+ test('should create semantic with custom config', ({ assert }) => {
18
+ const config: SummaryConfig = {
19
+ metadata: { customField: 'value' },
20
+ }
21
+ const semantic = createSummarySemantic(config)
22
+ assert.equal(semantic.id, SemanticType.Summary)
23
+ assert.deepEqual(semantic.config, { ...DEFAULT_SUMMARY_CONFIG, ...config })
24
+ })
25
+
26
+ test('should identify summary semantic', ({ assert }) => {
27
+ const semantic = createSummarySemantic()
28
+ assert.isTrue(isSummarySemantic(semantic))
29
+ })
30
+
31
+ test('should not identify non-summary semantic', ({ assert }) => {
32
+ // Simulate a different semantic
33
+ const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
34
+ assert.isFalse(isSummarySemantic(fakeSemantic))
35
+ })
36
+ })
@@ -0,0 +1,38 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createTagsSemantic,
4
+ isTagsSemantic,
5
+ DEFAULT_TAGS_CONFIG,
6
+ type TagsConfig,
7
+ } from '../../../../src/modeling/definitions/Tags.js'
8
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
9
+
10
+ test.group('Tags Semantic Configuration', () => {
11
+ test('should create semantic with default config', ({ assert }) => {
12
+ const semantic = createTagsSemantic()
13
+ assert.equal(semantic.id, SemanticType.Tags)
14
+ assert.deepEqual(semantic.config, DEFAULT_TAGS_CONFIG)
15
+ })
16
+
17
+ test('should create semantic with custom config', ({ assert }) => {
18
+ const config: TagsConfig = {
19
+ maxTags: 10,
20
+ allowCustomTags: true,
21
+ predefinedTags: ['urgent', 'important'],
22
+ }
23
+ const semantic = createTagsSemantic(config)
24
+ assert.equal(semantic.id, SemanticType.Tags)
25
+ assert.deepEqual(semantic.config, { ...DEFAULT_TAGS_CONFIG, ...config })
26
+ })
27
+
28
+ test('should identify tags semantic', ({ assert }) => {
29
+ const semantic = createTagsSemantic()
30
+ assert.isTrue(isTagsSemantic(semantic))
31
+ })
32
+
33
+ test('should not identify non-tags semantic', ({ assert }) => {
34
+ // Simulate a different semantic
35
+ const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
36
+ assert.isFalse(isTagsSemantic(fakeSemantic))
37
+ })
38
+ })
@@ -0,0 +1,38 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createURLSemantic,
4
+ isURLSemantic,
5
+ DEFAULT_URL_CONFIG,
6
+ type URLConfig,
7
+ } from '../../../../src/modeling/definitions/URL.js'
8
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
9
+
10
+ test.group('URL Semantic Configuration', () => {
11
+ test('should create semantic with default config', ({ assert }) => {
12
+ const semantic = createURLSemantic()
13
+ assert.equal(semantic.id, SemanticType.URL)
14
+ assert.deepEqual(semantic.config, DEFAULT_URL_CONFIG)
15
+ })
16
+
17
+ test('should create semantic with custom config', ({ assert }) => {
18
+ const config: URLConfig = {
19
+ allowedProtocols: ['https'],
20
+ allowedDomains: ['example.com'],
21
+ requireHTTPS: true,
22
+ }
23
+ const semantic = createURLSemantic(config)
24
+ assert.equal(semantic.id, SemanticType.URL)
25
+ assert.deepEqual(semantic.config, { ...DEFAULT_URL_CONFIG, ...config })
26
+ })
27
+
28
+ test('should identify URL semantic', ({ assert }) => {
29
+ const semantic = createURLSemantic()
30
+ assert.isTrue(isURLSemantic(semantic))
31
+ })
32
+
33
+ test('should not identify non-URL semantic', ({ assert }) => {
34
+ // Simulate a different semantic
35
+ const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
36
+ assert.isFalse(isURLSemantic(fakeSemantic))
37
+ })
38
+ })
@@ -936,3 +936,109 @@ test.group('DomainProperty.hasSemantic()', () => {
936
936
  assert.isFalse(property.hasSemantic(SemanticType.CreatedTimestamp))
937
937
  })
938
938
  })
939
+
940
+ test.group('DomainProperty with GeospatialCoordinates semantic', () => {
941
+ test('can add GeospatialCoordinates semantic to a string property', ({ assert }) => {
942
+ const dataDomain = new DataDomain()
943
+ const property = new DomainProperty(dataDomain, 'test-parent', { type: 'string' })
944
+ const semantic = { id: SemanticType.GeospatialCoordinates }
945
+ property.addSemantic(semantic)
946
+ assert.deepInclude(property.semantics, semantic)
947
+ assert.isTrue(property.hasSemantic(SemanticType.GeospatialCoordinates))
948
+ })
949
+
950
+ test('can add GeospatialCoordinates semantic with configuration', ({ assert }) => {
951
+ const dataDomain = new DataDomain()
952
+ const property = new DomainProperty(dataDomain, 'test-parent', { type: 'string' })
953
+ const semantic = {
954
+ id: SemanticType.GeospatialCoordinates,
955
+ config: { format: 'lat,lon' },
956
+ }
957
+ property.addSemantic(semantic)
958
+ assert.deepInclude(property.semantics, semantic)
959
+ assert.isTrue(property.hasSemantic(SemanticType.GeospatialCoordinates))
960
+ })
961
+
962
+ test('can remove GeospatialCoordinates semantic', ({ assert }) => {
963
+ const dataDomain = new DataDomain()
964
+ const property = new DomainProperty(dataDomain, 'test-parent', { type: 'string' })
965
+ const semantic = { id: SemanticType.GeospatialCoordinates }
966
+ property.addSemantic(semantic)
967
+ assert.isTrue(property.hasSemantic(SemanticType.GeospatialCoordinates))
968
+
969
+ property.removeSemantic(SemanticType.GeospatialCoordinates)
970
+ assert.isFalse(property.hasSemantic(SemanticType.GeospatialCoordinates))
971
+ assert.lengthOf(property.semantics, 0)
972
+ })
973
+
974
+ test('can update existing GeospatialCoordinates semantic', ({ assert }) => {
975
+ const dataDomain = new DataDomain()
976
+ const property = new DomainProperty(dataDomain, 'test-parent', { type: 'string' })
977
+ const semantic1 = {
978
+ id: SemanticType.GeospatialCoordinates,
979
+ config: { format: 'lat,lon' },
980
+ }
981
+ const semantic2 = {
982
+ id: SemanticType.GeospatialCoordinates,
983
+ config: { format: 'postgis' },
984
+ }
985
+
986
+ property.addSemantic(semantic1)
987
+ property.addSemantic(semantic2)
988
+
989
+ assert.lengthOf(property.semantics, 1)
990
+ assert.deepInclude(property.semantics, semantic2)
991
+ assert.isTrue(property.hasSemantic(SemanticType.GeospatialCoordinates))
992
+ })
993
+
994
+ test('serializes GeospatialCoordinates semantic correctly', ({ assert }) => {
995
+ const dataDomain = new DataDomain()
996
+ const property = new DomainProperty(dataDomain, 'test-parent', {
997
+ type: 'string',
998
+ semantics: [{ id: SemanticType.GeospatialCoordinates }],
999
+ })
1000
+
1001
+ const json = property.toJSON()
1002
+ assert.deepEqual(json.semantics, [{ id: SemanticType.GeospatialCoordinates }])
1003
+ })
1004
+
1005
+ test('creates schema with GeospatialCoordinates semantic', ({ assert }) => {
1006
+ const schema = DomainProperty.createSchema({
1007
+ type: 'string',
1008
+ semantics: [{ id: SemanticType.GeospatialCoordinates }],
1009
+ })
1010
+
1011
+ assert.deepEqual(schema.semantics, [{ id: SemanticType.GeospatialCoordinates }])
1012
+ assert.equal(schema.type, 'string')
1013
+ })
1014
+
1015
+ test('constructor accepts GeospatialCoordinates semantic', ({ assert }) => {
1016
+ const dataDomain = new DataDomain()
1017
+ const property = new DomainProperty(dataDomain, 'test-parent', {
1018
+ type: 'string',
1019
+ semantics: [{ id: SemanticType.GeospatialCoordinates }],
1020
+ })
1021
+
1022
+ assert.deepEqual(property.semantics, [{ id: SemanticType.GeospatialCoordinates }])
1023
+ assert.isTrue(property.hasSemantic(SemanticType.GeospatialCoordinates))
1024
+ })
1025
+
1026
+ test('notifies change when adding GeospatialCoordinates semantic', async ({ assert }) => {
1027
+ const dataDomain = new DataDomain()
1028
+ const property = new DomainProperty(dataDomain, 'test-parent', { type: 'string' })
1029
+ const semantic = { id: SemanticType.GeospatialCoordinates }
1030
+ property.addSemantic(semantic)
1031
+ await assert.dispatches(dataDomain, 'change', { timeout: 20 })
1032
+ })
1033
+
1034
+ test('notifies change when removing GeospatialCoordinates semantic', async ({ assert }) => {
1035
+ const dataDomain = new DataDomain()
1036
+ const property = new DomainProperty(dataDomain, 'test-parent', { type: 'string' })
1037
+ const semantic = { id: SemanticType.GeospatialCoordinates }
1038
+ property.addSemantic(semantic)
1039
+ // Clear the event queue before removal
1040
+ await new Promise((resolve) => setTimeout(resolve, 0))
1041
+ property.removeSemantic(SemanticType.GeospatialCoordinates)
1042
+ await assert.dispatches(dataDomain, 'change', { timeout: 20 })
1043
+ })
1044
+ })
@@ -70,13 +70,13 @@ test.group('DomainImpactAnalysis.validate()', (group) => {
70
70
  entity1.addProperty({ key: 'p1', type: 'string', info: { name: 'property1' } })
71
71
  entity1.addProperty({ key: 'p2', type: 'string', primary: true, info: { name: 'property2' } })
72
72
  const entity2 = model.addEntity({ key: 'entity2', info: { name: 'entity2' } })
73
- const p3e2 = entity2.addProperty({ key: 'p3', type: 'number', info: { name: 'property3' } })
73
+ const p3e2 = entity2.addProperty({ key: 'p3', type: 'datetime', info: { name: 'property3' } })
74
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' } })
75
+ const p5e2 = entity2.addProperty({ key: 'p5', type: 'datetime', info: { name: 'property5' } })
76
+ const p6e2 = entity2.addProperty({ key: 'p6', type: 'datetime', info: { name: 'property6' } })
77
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' } })
78
+ const p3e1 = entity1.addProperty({ type: 'datetime', info: { name: 'p3e1' } })
79
+ const p4e1 = entity1.addProperty({ type: 'datetime', info: { name: 'p4e1' } })
80
80
  const p5e1 = entity1.addProperty({ type: 'boolean', info: { name: 'p5e1' } })
81
81
 
82
82
  entity1.addSemantic({ id: SemanticType.User })