@api-client/core 0.15.1 → 0.16.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.
Files changed (126) 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/data/models/example-generator-api.json +11 -11
  81. package/package.json +1 -1
  82. package/src/modeling/Semantics.ts +297 -14
  83. package/src/modeling/definitions/Calculated.ts +76 -0
  84. package/src/modeling/definitions/Categories.ts +84 -0
  85. package/src/modeling/definitions/Derived.ts +76 -0
  86. package/src/modeling/definitions/Description.ts +55 -0
  87. package/src/modeling/definitions/Email.ts +90 -0
  88. package/src/modeling/definitions/GeospatialCoordinates.ts +274 -0
  89. package/src/modeling/definitions/HTML.ts +121 -0
  90. package/src/modeling/definitions/Markdown.ts +116 -0
  91. package/src/modeling/definitions/Password.ts +156 -0
  92. package/src/modeling/definitions/Phone.ts +116 -0
  93. package/src/modeling/definitions/Price.examples.md +158 -0
  94. package/src/modeling/definitions/Price.ts +180 -0
  95. package/src/modeling/definitions/PublicUniqueName.ts +98 -0
  96. package/src/modeling/definitions/SKU.examples.md +230 -0
  97. package/src/modeling/definitions/SKU.ts +254 -0
  98. package/src/modeling/definitions/Status.ts +227 -0
  99. package/src/modeling/definitions/Summary.ts +73 -0
  100. package/src/modeling/definitions/Tags.ts +75 -0
  101. package/src/modeling/definitions/URL.ts +96 -0
  102. package/src/modeling/validation/semantic_validation.ts +35 -1
  103. package/tests/example-test-setup.ts +133 -0
  104. package/tests/template-node.spec.ts +75 -0
  105. package/tests/test-utils.ts +293 -0
  106. package/tests/unit/modeling/definitions/calculated.spec.ts +33 -0
  107. package/tests/unit/modeling/definitions/categories.spec.ts +38 -0
  108. package/tests/unit/modeling/definitions/derived.spec.ts +34 -0
  109. package/tests/unit/modeling/definitions/description.spec.ts +38 -0
  110. package/tests/unit/modeling/definitions/email.spec.ts +38 -0
  111. package/tests/unit/modeling/definitions/geospatial-coordinates.spec.ts +41 -0
  112. package/tests/unit/modeling/definitions/html.spec.ts +38 -0
  113. package/tests/unit/modeling/definitions/markdown.spec.ts +38 -0
  114. package/tests/unit/modeling/definitions/password.spec.ts +347 -0
  115. package/tests/unit/modeling/definitions/phone.spec.ts +38 -0
  116. package/tests/unit/modeling/definitions/price.spec.ts +465 -0
  117. package/tests/unit/modeling/definitions/public-unique-name.spec.ts +38 -0
  118. package/tests/unit/modeling/definitions/sku.spec.ts +240 -0
  119. package/tests/unit/modeling/definitions/status.spec.ts +37 -0
  120. package/tests/unit/modeling/definitions/summary.spec.ts +36 -0
  121. package/tests/unit/modeling/definitions/tags.spec.ts +38 -0
  122. package/tests/unit/modeling/definitions/url.spec.ts +38 -0
  123. package/tests/unit/modeling/domain_property.spec.ts +106 -0
  124. package/tests/unit/modeling/domain_validation.spec.ts +5 -5
  125. package/tests/unit/modeling/semantic-configs.spec.ts +569 -0
  126. package/tests/unit/modeling/semantics.spec.ts +52 -0
@@ -0,0 +1,465 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createPriceSemantic,
4
+ isPriceSemantic,
5
+ validatePriceConfig,
6
+ DEFAULT_PRICE_CONFIG,
7
+ PRICE_PRESETS,
8
+ type PriceConfig,
9
+ } from '../../../../src/modeling/definitions/Price.js'
10
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
11
+
12
+ test.group('Price Semantic Configuration', () => {
13
+ test('should create semantic with default config', ({ assert }) => {
14
+ const semantic = createPriceSemantic()
15
+ assert.equal(semantic.id, SemanticType.Price)
16
+ assert.deepEqual(semantic.config, DEFAULT_PRICE_CONFIG)
17
+ })
18
+
19
+ test('should create semantic with custom config', ({ assert }) => {
20
+ const config: PriceConfig = {
21
+ storageFormat: 'complex_object',
22
+ allowedCurrencies: ['USD', 'EUR'],
23
+ decimalPlaces: 4,
24
+ allowNegative: true,
25
+ validateCurrencyCode: false,
26
+ }
27
+ const semantic = createPriceSemantic(config)
28
+ assert.equal(semantic.id, SemanticType.Price)
29
+ assert.deepEqual(semantic.config, { ...DEFAULT_PRICE_CONFIG, ...config })
30
+ })
31
+
32
+ test('should merge metadata properly', ({ assert }) => {
33
+ const config: PriceConfig = {
34
+ metadata: {
35
+ customField: 'value',
36
+ anotherField: 123,
37
+ },
38
+ }
39
+ const semantic = createPriceSemantic(config)
40
+ assert.equal(semantic.id, SemanticType.Price)
41
+ assert.deepEqual(semantic.config?.metadata, config.metadata)
42
+ })
43
+
44
+ test('should identify price semantic', ({ assert }) => {
45
+ const semantic = createPriceSemantic()
46
+ assert.isTrue(isPriceSemantic(semantic))
47
+ })
48
+
49
+ test('should not identify non-price semantic', ({ assert }) => {
50
+ const fakeSemantic: AppliedDataSemantic = {
51
+ id: SemanticType.Email,
52
+ config: {},
53
+ }
54
+ assert.isFalse(isPriceSemantic(fakeSemantic))
55
+ })
56
+
57
+ test('DEFAULT_PRICE_CONFIG should have correct values', ({ assert }) => {
58
+ assert.equal(DEFAULT_PRICE_CONFIG.storageFormat, 'decimal')
59
+ assert.equal(DEFAULT_PRICE_CONFIG.defaultCurrency, 'USD')
60
+ assert.equal(DEFAULT_PRICE_CONFIG.decimalPlaces, 2)
61
+ assert.equal(DEFAULT_PRICE_CONFIG.allowNegative, false)
62
+ assert.equal(DEFAULT_PRICE_CONFIG.validateCurrencyCode, true)
63
+ })
64
+ })
65
+
66
+ test.group('Price Storage Formats', () => {
67
+ test('should support decimal storage format', ({ assert }) => {
68
+ const config: PriceConfig = {
69
+ storageFormat: 'decimal',
70
+ defaultCurrency: 'USD',
71
+ decimalPlaces: 2,
72
+ }
73
+ const semantic = createPriceSemantic(config)
74
+ assert.equal(semantic.config?.storageFormat, 'decimal')
75
+ assert.equal(semantic.config?.defaultCurrency, 'USD')
76
+ assert.equal(semantic.config?.decimalPlaces, 2)
77
+ })
78
+
79
+ test('should support integer_cents storage format', ({ assert }) => {
80
+ const config: PriceConfig = {
81
+ storageFormat: 'integer_cents',
82
+ defaultCurrency: 'EUR',
83
+ decimalPlaces: 2,
84
+ }
85
+ const semantic = createPriceSemantic(config)
86
+ assert.equal(semantic.config?.storageFormat, 'integer_cents')
87
+ assert.equal(semantic.config?.defaultCurrency, 'EUR')
88
+ })
89
+
90
+ test('should support complex_object storage format', ({ assert }) => {
91
+ const config: PriceConfig = {
92
+ storageFormat: 'complex_object',
93
+ allowedCurrencies: ['USD', 'EUR', 'GBP'],
94
+ decimalPlaces: 2,
95
+ }
96
+ const semantic = createPriceSemantic(config)
97
+ assert.equal(semantic.config?.storageFormat, 'complex_object')
98
+ assert.deepEqual(semantic.config?.allowedCurrencies, ['USD', 'EUR', 'GBP'])
99
+ })
100
+ })
101
+
102
+ test.group('Price Configuration Validation', () => {
103
+ test('should validate that defaultCurrency is required for decimal format', ({ assert }) => {
104
+ const config: PriceConfig = {
105
+ storageFormat: 'decimal',
106
+ // Missing defaultCurrency
107
+ decimalPlaces: 2,
108
+ }
109
+ const errors = validatePriceConfig(config)
110
+ assert.include(errors, 'defaultCurrency is required when storageFormat is not complex_object')
111
+ })
112
+
113
+ test('should validate that defaultCurrency is required for integer_cents format', ({ assert }) => {
114
+ const config: PriceConfig = {
115
+ storageFormat: 'integer_cents',
116
+ // Missing defaultCurrency
117
+ decimalPlaces: 2,
118
+ }
119
+ const errors = validatePriceConfig(config)
120
+ assert.include(errors, 'defaultCurrency is required when storageFormat is not complex_object')
121
+ })
122
+
123
+ test('should not require defaultCurrency for complex_object format', ({ assert }) => {
124
+ const config: PriceConfig = {
125
+ storageFormat: 'complex_object',
126
+ // No defaultCurrency needed
127
+ allowedCurrencies: ['USD', 'EUR'],
128
+ }
129
+ const errors = validatePriceConfig(config)
130
+ assert.notInclude(errors, 'defaultCurrency is required when storageFormat is not complex_object')
131
+ })
132
+
133
+ test('should validate that decimalPlaces is non-negative', ({ assert }) => {
134
+ const config: PriceConfig = {
135
+ storageFormat: 'decimal',
136
+ defaultCurrency: 'USD',
137
+ decimalPlaces: -1,
138
+ }
139
+ const errors = validatePriceConfig(config)
140
+ assert.include(errors, 'decimalPlaces must be non-negative')
141
+ })
142
+
143
+ test('should accept zero decimal places', ({ assert }) => {
144
+ const config: PriceConfig = {
145
+ storageFormat: 'decimal',
146
+ defaultCurrency: 'JPY',
147
+ decimalPlaces: 0,
148
+ }
149
+ const errors = validatePriceConfig(config)
150
+ assert.notInclude(errors, 'decimalPlaces must be non-negative')
151
+ })
152
+
153
+ test('should return empty array for valid config', ({ assert }) => {
154
+ const config: PriceConfig = {
155
+ storageFormat: 'decimal',
156
+ defaultCurrency: 'USD',
157
+ decimalPlaces: 2,
158
+ allowNegative: false,
159
+ }
160
+ const errors = validatePriceConfig(config)
161
+ assert.lengthOf(errors, 0)
162
+ })
163
+
164
+ test('should accumulate multiple validation errors', ({ assert }) => {
165
+ const config: PriceConfig = {
166
+ storageFormat: 'decimal',
167
+ // Missing defaultCurrency
168
+ decimalPlaces: -2, // Invalid decimal places
169
+ }
170
+ const errors = validatePriceConfig(config)
171
+ assert.lengthOf(errors, 2)
172
+ assert.include(errors, 'defaultCurrency is required when storageFormat is not complex_object')
173
+ assert.include(errors, 'decimalPlaces must be non-negative')
174
+ })
175
+ })
176
+
177
+ test.group('Price Presets', () => {
178
+ test('USD_DECIMAL preset should have correct configuration', ({ assert }) => {
179
+ const preset = PRICE_PRESETS.USD_DECIMAL
180
+ assert.equal(preset.id, SemanticType.Price)
181
+ assert.equal(preset.config?.storageFormat, 'decimal')
182
+ assert.equal(preset.config?.defaultCurrency, 'USD')
183
+ assert.equal(preset.config?.decimalPlaces, 2)
184
+ assert.equal(preset.config?.allowNegative, false)
185
+ })
186
+
187
+ test('USD_CENTS preset should have correct configuration', ({ assert }) => {
188
+ const preset = PRICE_PRESETS.USD_CENTS
189
+ assert.equal(preset.id, SemanticType.Price)
190
+ assert.equal(preset.config?.storageFormat, 'integer_cents')
191
+ assert.equal(preset.config?.defaultCurrency, 'USD')
192
+ assert.equal(preset.config?.decimalPlaces, 2)
193
+ assert.equal(preset.config?.allowNegative, false)
194
+ })
195
+
196
+ test('MULTI_CURRENCY preset should have correct configuration', ({ assert }) => {
197
+ const preset = PRICE_PRESETS.MULTI_CURRENCY
198
+ assert.equal(preset.id, SemanticType.Price)
199
+ assert.equal(preset.config?.storageFormat, 'complex_object')
200
+ assert.deepEqual(preset.config?.allowedCurrencies, ['USD', 'EUR', 'GBP', 'JPY', 'CAD'])
201
+ assert.equal(preset.config?.decimalPlaces, 2)
202
+ assert.equal(preset.config?.allowNegative, false)
203
+ })
204
+
205
+ test('WITH_NEGATIVES preset should have correct configuration', ({ assert }) => {
206
+ const preset = PRICE_PRESETS.WITH_NEGATIVES
207
+ assert.equal(preset.id, SemanticType.Price)
208
+ assert.equal(preset.config?.storageFormat, 'decimal')
209
+ assert.equal(preset.config?.defaultCurrency, 'USD')
210
+ assert.equal(preset.config?.decimalPlaces, 2)
211
+ assert.equal(preset.config?.allowNegative, true)
212
+ })
213
+
214
+ test('HIGH_PRECISION preset should have correct configuration', ({ assert }) => {
215
+ const preset = PRICE_PRESETS.HIGH_PRECISION
216
+ assert.equal(preset.id, SemanticType.Price)
217
+ assert.equal(preset.config?.storageFormat, 'decimal')
218
+ assert.equal(preset.config?.defaultCurrency, 'BTC')
219
+ assert.equal(preset.config?.decimalPlaces, 8)
220
+ assert.equal(preset.config?.allowNegative, false)
221
+ })
222
+
223
+ test('all presets should be valid price semantics', ({ assert }) => {
224
+ Object.values(PRICE_PRESETS).forEach((preset) => {
225
+ assert.isTrue(isPriceSemantic(preset))
226
+ // Validate each preset config
227
+ if (preset.config) {
228
+ const errors = validatePriceConfig(preset.config)
229
+ assert.lengthOf(errors, 0, `Preset ${preset.config.storageFormat} should have valid config`)
230
+ }
231
+ })
232
+ })
233
+ })
234
+
235
+ test.group('Price Currency Configuration', () => {
236
+ test('should support single currency with defaultCurrency', ({ assert }) => {
237
+ const config: PriceConfig = {
238
+ storageFormat: 'decimal',
239
+ defaultCurrency: 'EUR',
240
+ decimalPlaces: 2,
241
+ }
242
+ const semantic = createPriceSemantic(config)
243
+ assert.equal(semantic.config?.defaultCurrency, 'EUR')
244
+ assert.isUndefined(semantic.config?.allowedCurrencies)
245
+ })
246
+
247
+ test('should support multiple currencies with allowedCurrencies', ({ assert }) => {
248
+ const currencies = ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD']
249
+ const config: PriceConfig = {
250
+ storageFormat: 'complex_object',
251
+ allowedCurrencies: currencies,
252
+ decimalPlaces: 2,
253
+ }
254
+ const semantic = createPriceSemantic(config)
255
+ assert.deepEqual(semantic.config?.allowedCurrencies, currencies)
256
+ })
257
+
258
+ test('should support currency validation toggle', ({ assert }) => {
259
+ const configWithValidation: PriceConfig = {
260
+ storageFormat: 'decimal',
261
+ defaultCurrency: 'USD',
262
+ validateCurrencyCode: true,
263
+ }
264
+ const configWithoutValidation: PriceConfig = {
265
+ storageFormat: 'decimal',
266
+ defaultCurrency: 'USD',
267
+ validateCurrencyCode: false,
268
+ }
269
+
270
+ const semanticWithValidation = createPriceSemantic(configWithValidation)
271
+ const semanticWithoutValidation = createPriceSemantic(configWithoutValidation)
272
+
273
+ assert.isTrue(semanticWithValidation.config?.validateCurrencyCode)
274
+ assert.isFalse(semanticWithoutValidation.config?.validateCurrencyCode)
275
+ })
276
+ })
277
+
278
+ test.group('Price Precision Configuration', () => {
279
+ test('should support standard 2 decimal places (USD, EUR)', ({ assert }) => {
280
+ const config: PriceConfig = {
281
+ storageFormat: 'decimal',
282
+ defaultCurrency: 'USD',
283
+ decimalPlaces: 2,
284
+ }
285
+ const semantic = createPriceSemantic(config)
286
+ assert.equal(semantic.config?.decimalPlaces, 2)
287
+ })
288
+
289
+ test('should support zero decimal places (JPY, KRW)', ({ assert }) => {
290
+ const config: PriceConfig = {
291
+ storageFormat: 'decimal',
292
+ defaultCurrency: 'JPY',
293
+ decimalPlaces: 0,
294
+ }
295
+ const semantic = createPriceSemantic(config)
296
+ assert.equal(semantic.config?.decimalPlaces, 0)
297
+ })
298
+
299
+ test('should support high precision (3 decimal places for BHD, KWD)', ({ assert }) => {
300
+ const config: PriceConfig = {
301
+ storageFormat: 'decimal',
302
+ defaultCurrency: 'BHD',
303
+ decimalPlaces: 3,
304
+ }
305
+ const semantic = createPriceSemantic(config)
306
+ assert.equal(semantic.config?.decimalPlaces, 3)
307
+ })
308
+
309
+ test('should support very high precision (cryptocurrencies)', ({ assert }) => {
310
+ const config: PriceConfig = {
311
+ storageFormat: 'decimal',
312
+ defaultCurrency: 'BTC',
313
+ decimalPlaces: 8,
314
+ }
315
+ const semantic = createPriceSemantic(config)
316
+ assert.equal(semantic.config?.decimalPlaces, 8)
317
+ })
318
+ })
319
+
320
+ test.group('Price Negative Values Configuration', () => {
321
+ test('should support disallowing negative values (default)', ({ assert }) => {
322
+ const config: PriceConfig = {
323
+ storageFormat: 'decimal',
324
+ defaultCurrency: 'USD',
325
+ allowNegative: false,
326
+ }
327
+ const semantic = createPriceSemantic(config)
328
+ assert.isFalse(semantic.config?.allowNegative)
329
+ })
330
+
331
+ test('should support allowing negative values (refunds, discounts)', ({ assert }) => {
332
+ const config: PriceConfig = {
333
+ storageFormat: 'decimal',
334
+ defaultCurrency: 'USD',
335
+ allowNegative: true,
336
+ }
337
+ const semantic = createPriceSemantic(config)
338
+ assert.isTrue(semantic.config?.allowNegative)
339
+ })
340
+ })
341
+
342
+ test.group('Price Metadata Configuration', () => {
343
+ test('should support custom metadata', ({ assert }) => {
344
+ const metadata = {
345
+ displayFormat: 'currency',
346
+ showCurrencySymbol: true,
347
+ rounding: 'standard',
348
+ customField: 'value',
349
+ }
350
+ const config: PriceConfig = {
351
+ storageFormat: 'decimal',
352
+ defaultCurrency: 'USD',
353
+ metadata,
354
+ }
355
+ const semantic = createPriceSemantic(config)
356
+ assert.deepEqual(semantic.config?.metadata, metadata)
357
+ })
358
+
359
+ test('should merge metadata with other config', ({ assert }) => {
360
+ const config: PriceConfig = {
361
+ storageFormat: 'complex_object',
362
+ allowedCurrencies: ['USD', 'EUR'],
363
+ decimalPlaces: 2,
364
+ metadata: {
365
+ source: 'external_api',
366
+ lastUpdated: '2023-01-01',
367
+ },
368
+ }
369
+ const semantic = createPriceSemantic(config)
370
+ assert.equal(semantic.config?.storageFormat, 'complex_object')
371
+ assert.deepEqual(semantic.config?.allowedCurrencies, ['USD', 'EUR'])
372
+ assert.equal(semantic.config?.decimalPlaces, 2)
373
+ assert.deepEqual(semantic.config?.metadata, {
374
+ source: 'external_api',
375
+ lastUpdated: '2023-01-01',
376
+ })
377
+ })
378
+ })
379
+
380
+ test.group('Price Cross-semantic Type Guards', () => {
381
+ test('should correctly identify different semantic types', ({ assert }) => {
382
+ const priceSemantic = createPriceSemantic()
383
+
384
+ // Mock other semantics for comparison
385
+ const emailSemantic: AppliedDataSemantic = { id: SemanticType.Email }
386
+ const statusSemantic: AppliedDataSemantic = { id: SemanticType.Status }
387
+ const userSemantic: AppliedDataSemantic = { id: SemanticType.User }
388
+
389
+ // Price semantic should only be identified as Price
390
+ assert.isTrue(isPriceSemantic(priceSemantic))
391
+ assert.isFalse(isPriceSemantic(emailSemantic))
392
+ assert.isFalse(isPriceSemantic(statusSemantic))
393
+ assert.isFalse(isPriceSemantic(userSemantic))
394
+ })
395
+ })
396
+
397
+ test.group('Price Real-world Use Cases', () => {
398
+ test('should configure for simple e-commerce store', ({ assert }) => {
399
+ const config: PriceConfig = {
400
+ storageFormat: 'decimal',
401
+ defaultCurrency: 'USD',
402
+ decimalPlaces: 2,
403
+ allowNegative: false,
404
+ validateCurrencyCode: true,
405
+ }
406
+ const semantic = createPriceSemantic(config)
407
+ const errors = validatePriceConfig(config)
408
+
409
+ assert.lengthOf(errors, 0)
410
+ assert.equal(semantic.config?.storageFormat, 'decimal')
411
+ assert.equal(semantic.config?.defaultCurrency, 'USD')
412
+ assert.isFalse(semantic.config?.allowNegative)
413
+ })
414
+
415
+ test('should configure for international marketplace', ({ assert }) => {
416
+ const config: PriceConfig = {
417
+ storageFormat: 'complex_object',
418
+ allowedCurrencies: ['USD', 'EUR', 'GBP', 'JPY', 'CAD', 'AUD'],
419
+ decimalPlaces: 2,
420
+ allowNegative: false,
421
+ validateCurrencyCode: true,
422
+ }
423
+ const semantic = createPriceSemantic(config)
424
+ const errors = validatePriceConfig(config)
425
+
426
+ assert.lengthOf(errors, 0)
427
+ assert.equal(semantic.config?.storageFormat, 'complex_object')
428
+ assert.isArray(semantic.config?.allowedCurrencies)
429
+ assert.lengthOf(semantic.config?.allowedCurrencies || [], 6)
430
+ })
431
+
432
+ test('should configure for financial system with precision', ({ assert }) => {
433
+ const config: PriceConfig = {
434
+ storageFormat: 'integer_cents',
435
+ defaultCurrency: 'USD',
436
+ decimalPlaces: 4,
437
+ allowNegative: true,
438
+ validateCurrencyCode: true,
439
+ }
440
+ const semantic = createPriceSemantic(config)
441
+ const errors = validatePriceConfig(config)
442
+
443
+ assert.lengthOf(errors, 0)
444
+ assert.equal(semantic.config?.storageFormat, 'integer_cents')
445
+ assert.equal(semantic.config?.decimalPlaces, 4)
446
+ assert.isTrue(semantic.config?.allowNegative)
447
+ })
448
+
449
+ test('should configure for cryptocurrency trading', ({ assert }) => {
450
+ const config: PriceConfig = {
451
+ storageFormat: 'decimal',
452
+ defaultCurrency: 'BTC',
453
+ decimalPlaces: 8,
454
+ allowNegative: false,
455
+ validateCurrencyCode: false, // Custom crypto currency
456
+ }
457
+ const semantic = createPriceSemantic(config)
458
+ const errors = validatePriceConfig(config)
459
+
460
+ assert.lengthOf(errors, 0)
461
+ assert.equal(semantic.config?.defaultCurrency, 'BTC')
462
+ assert.equal(semantic.config?.decimalPlaces, 8)
463
+ assert.isFalse(semantic.config?.validateCurrencyCode)
464
+ })
465
+ })
@@ -0,0 +1,38 @@
1
+ import { test } from '@japa/runner'
2
+ import {
3
+ createPublicUniqueNameSemantic,
4
+ isPublicUniqueNameSemantic,
5
+ DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG,
6
+ type PublicUniqueNameConfig,
7
+ } from '../../../../src/modeling/definitions/PublicUniqueName.js'
8
+ import { SemanticType, type AppliedDataSemantic } from '../../../../src/modeling/Semantics.js'
9
+
10
+ test.group('PublicUniqueName Semantic Configuration', () => {
11
+ test('should create semantic with default config', ({ assert }) => {
12
+ const semantic = createPublicUniqueNameSemantic()
13
+ assert.equal(semantic.id, SemanticType.PublicUniqueName)
14
+ assert.deepEqual(semantic.config, DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG)
15
+ })
16
+
17
+ test('should create semantic with custom config', ({ assert }) => {
18
+ const config: PublicUniqueNameConfig = {
19
+ sourceField: 'title',
20
+ separator: '-',
21
+ maxLength: 50,
22
+ }
23
+ const semantic = createPublicUniqueNameSemantic(config)
24
+ assert.equal(semantic.id, SemanticType.PublicUniqueName)
25
+ assert.deepEqual(semantic.config, { ...DEFAULT_PUBLIC_UNIQUE_NAME_CONFIG, ...config })
26
+ })
27
+
28
+ test('should identify public unique name semantic', ({ assert }) => {
29
+ const semantic = createPublicUniqueNameSemantic()
30
+ assert.isTrue(isPublicUniqueNameSemantic(semantic))
31
+ })
32
+
33
+ test('should not identify non-public-unique-name semantic', ({ assert }) => {
34
+ // Simulate a different semantic
35
+ const fakeSemantic: AppliedDataSemantic = { id: SemanticType.Email, config: {} }
36
+ assert.isFalse(isPublicUniqueNameSemantic(fakeSemantic))
37
+ })
38
+ })