lutaml-jsonschema 0.1.16 → 0.1.19

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4a225cce2d635b5a3d8b1376087e61b6f20e319ed84b34bc543812279bb7c6f2
4
- data.tar.gz: 394d28b298c43654eb7d6f6614b310ef31311a0e51d6a537f41b70e947010fa6
3
+ metadata.gz: 7bedeb19c89b4d8d95067a18bb2b5fa17532e2730f6fbfe1da8584bfc70ae05a
4
+ data.tar.gz: 9221e7c2a1e0324aa9a1325831b36bf664f121466f02de6f07b940e0063830de
5
5
  SHA512:
6
- metadata.gz: 6f1f62960e78d9e07ac390fc37ab7c8e0853cbaeaefdaa12843bc41ceb17aecfdaa2193dcf26e382034008b4377fcedfad4094648ce858a611b431cfe78ead7c
7
- data.tar.gz: 9f1e1c38bd3335511264282a1e5d49cb74b4bb850a43ee58355fb64b422efb68415b778eda4540a79fbaf933bc265a583b0089b85b8bb5843da0709b9ddafd4e
6
+ metadata.gz: 60122241ce61b4ee49095aed3def11f74cac1f8bccecabb8189cc944a8979d904b54a505024cd9518f0943ea6f48e9a8690452e2681d1db8df654a43a3107965
7
+ data.tar.gz: f20ae2131c15ed7819318d97e9ca13d38307473900a9946ca8a39412798b5d6ebca25d32dfc3c9eef170a810c84a36b7e09455a1797cffc86fd3a8f3fa552c99
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-14 10:12:34 UTC using RuboCop version 1.86.1.
3
+ # on 2026-06-08 13:01:45 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -11,33 +11,146 @@ Gemspec/RequiredRubyVersion:
11
11
  Exclude:
12
12
  - 'lutaml-jsonschema.gemspec'
13
13
 
14
- # Offense count: 1
14
+ # Offense count: 39
15
+ # This cop supports safe autocorrection (--autocorrect).
16
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
17
+ # SupportedStyles: with_first_argument, with_fixed_indentation
18
+ Layout/ArgumentAlignment:
19
+ Exclude:
20
+ - 'lib/lutaml/jsonschema/cli.rb'
21
+ - 'lib/lutaml/jsonschema/schema_store.rb'
22
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
23
+ - 'spec/e2e/spa_e2e_spec.rb'
24
+ - 'spec/lutaml/configuration_spec.rb'
25
+ - 'spec/lutaml/jsonschema/schema_store_spec.rb'
26
+ - 'spec/lutaml/spa/spa_definition_spec.rb'
27
+
28
+ # Offense count: 2
29
+ # This cop supports safe autocorrection (--autocorrect).
30
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
31
+ # SupportedStyles: with_first_element, with_fixed_indentation
32
+ Layout/ArrayAlignment:
33
+ Exclude:
34
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
35
+ - 'spec/e2e/spa_e2e_spec.rb'
36
+
37
+ # Offense count: 34
15
38
  # This cop supports safe autocorrection (--autocorrect).
16
39
  # Configuration parameters: EnforcedStyleAlignWith.
17
40
  # SupportedStylesAlignWith: either, start_of_block, start_of_line
18
41
  Layout/BlockAlignment:
19
42
  Exclude:
43
+ - 'lib/lutaml/jsonschema/schema.rb'
20
44
  - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
45
+ - 'spec/e2e/spa_e2e_spec.rb'
46
+ - 'spec/lutaml/generator_spec.rb'
47
+
48
+ # Offense count: 29
49
+ # This cop supports safe autocorrection (--autocorrect).
50
+ Layout/BlockEndNewline:
51
+ Exclude:
52
+ - 'lib/lutaml/jsonschema/schema.rb'
53
+ - 'spec/e2e/spa_e2e_spec.rb'
54
+ - 'spec/lutaml/generator_spec.rb'
55
+
56
+ # Offense count: 7
57
+ # This cop supports safe autocorrection (--autocorrect).
58
+ Layout/ElseAlignment:
59
+ Exclude:
60
+ - 'lib/lutaml/jsonschema/schema_store.rb'
61
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
62
+
63
+ # Offense count: 1
64
+ # This cop supports safe autocorrection (--autocorrect).
65
+ Layout/EmptyLineAfterGuardClause:
66
+ Exclude:
67
+ - 'lib/lutaml/jsonschema/schema_store.rb'
68
+
69
+ # Offense count: 8
70
+ # This cop supports safe autocorrection (--autocorrect).
71
+ # Configuration parameters: EnforcedStyleAlignWith.
72
+ # SupportedStylesAlignWith: keyword, variable, start_of_line
73
+ Layout/EndAlignment:
74
+ Exclude:
75
+ - 'lib/lutaml/jsonschema/schema_store.rb'
76
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
77
+
78
+ # Offense count: 36
79
+ # This cop supports safe autocorrection (--autocorrect).
80
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
81
+ # SupportedStyles: special_inside_parentheses, consistent, align_braces
82
+ Layout/FirstHashElementIndentation:
83
+ Exclude:
84
+ - 'spec/lutaml/jsonschema_spec.rb'
85
+ - 'spec/lutaml/schema/each_child_spec.rb'
21
86
 
22
87
  # Offense count: 28
23
88
  # This cop supports safe autocorrection (--autocorrect).
89
+ # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
90
+ # SupportedHashRocketStyles: key, separator, table
91
+ # SupportedColonStyles: key, separator, table
92
+ # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit
93
+ Layout/HashAlignment:
94
+ Exclude:
95
+ - 'lib/lutaml/jsonschema/cli.rb'
96
+ - 'lib/lutaml/jsonschema/schema_store.rb'
97
+ - 'lib/lutaml/jsonschema/spa/spa_definition.rb'
98
+ - 'lib/lutaml/jsonschema/spa/spa_property.rb'
99
+ - 'lib/lutaml/jsonschema/spa/spa_schema.rb'
100
+ - 'spec/lutaml/configuration_spec.rb'
101
+ - 'spec/lutaml/jsonschema/schema_store_spec.rb'
102
+ - 'spec/lutaml/jsonschema_spec.rb'
103
+ - 'spec/lutaml/schema/each_child_spec.rb'
104
+ - 'spec/lutaml/spa/spa_definition_spec.rb'
105
+
106
+ # Offense count: 67
107
+ # This cop supports safe autocorrection (--autocorrect).
108
+ # Configuration parameters: Width, EnforcedStyleAlignWith, AllowedPatterns.
109
+ # SupportedStylesAlignWith: start_of_line, relative_to_receiver
110
+ Layout/IndentationWidth:
111
+ Exclude:
112
+ - 'lib/lutaml/jsonschema/schema.rb'
113
+ - 'lib/lutaml/jsonschema/schema_store.rb'
114
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
115
+ - 'spec/e2e/spa_e2e_spec.rb'
116
+ - 'spec/lutaml/generator_spec.rb'
117
+
118
+ # Offense count: 116
119
+ # This cop supports safe autocorrection (--autocorrect).
24
120
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
25
121
  # URISchemes: http, https
26
122
  Layout/LineLength:
123
+ Enabled: false
124
+
125
+ # Offense count: 4
126
+ # This cop supports safe autocorrection (--autocorrect).
127
+ Layout/MultilineBlockLayout:
128
+ Exclude:
129
+ - 'spec/e2e/spa_e2e_spec.rb'
130
+
131
+ # Offense count: 2
132
+ # This cop supports safe autocorrection (--autocorrect).
133
+ # Configuration parameters: EnforcedStyle.
134
+ # SupportedStyles: symmetrical, new_line, same_line
135
+ Layout/MultilineHashBraceLayout:
136
+ Exclude:
137
+ - 'spec/lutaml/jsonschema_spec.rb'
138
+ - 'spec/lutaml/schema/each_child_spec.rb'
139
+
140
+ # Offense count: 35
141
+ # This cop supports safe autocorrection (--autocorrect).
142
+ # Configuration parameters: AllowInHeredoc.
143
+ Layout/TrailingWhitespace:
27
144
  Exclude:
28
- - 'lib/lutaml/jsonschema/base.rb'
29
145
  - 'lib/lutaml/jsonschema/cli.rb'
30
- - 'lib/lutaml/jsonschema/combiner.rb'
31
- - 'lib/lutaml/jsonschema/schema.rb'
32
- - 'lib/lutaml/jsonschema/schema_set.rb'
146
+ - 'lib/lutaml/jsonschema/schema_store.rb'
33
147
  - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
34
- - 'lib/lutaml/jsonschema/spa/vue_inlined_strategy.rb'
35
- - 'lutaml-jsonschema.gemspec'
36
- - 'spec/lutaml/integration_spec.rb'
148
+ - 'spec/e2e/spa_e2e_spec.rb'
149
+ - 'spec/lutaml/configuration_spec.rb'
150
+ - 'spec/lutaml/jsonschema/schema_store_spec.rb'
37
151
  - 'spec/lutaml/jsonschema_spec.rb'
38
- - 'spec/lutaml/schema_set_cross_ref_spec.rb'
39
- - 'spec/lutaml/schema_set_resolution_spec.rb'
40
- - 'spec/lutaml/spa_builder_spec.rb'
152
+ - 'spec/lutaml/schema/each_child_spec.rb'
153
+ - 'spec/lutaml/spa/spa_definition_spec.rb'
41
154
 
42
155
  # Offense count: 2
43
156
  # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
@@ -51,7 +164,15 @@ Lint/IneffectiveAccessModifier:
51
164
  Exclude:
52
165
  - 'lib/lutaml/jsonschema/schema_set.rb'
53
166
 
54
- # Offense count: 16
167
+ # Offense count: 3
168
+ # This cop supports safe autocorrection (--autocorrect).
169
+ # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
170
+ # NotImplementedExceptions: NotImplementedError
171
+ Lint/UnusedMethodArgument:
172
+ Exclude:
173
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
174
+
175
+ # Offense count: 21
55
176
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
56
177
  Metrics/AbcSize:
57
178
  Exclude:
@@ -59,44 +180,45 @@ Metrics/AbcSize:
59
180
  - 'lib/lutaml/jsonschema/combiner.rb'
60
181
  - 'lib/lutaml/jsonschema/configuration.rb'
61
182
  - 'lib/lutaml/jsonschema/reference_resolver.rb'
62
- - 'lib/lutaml/jsonschema/schema_set.rb'
183
+ - 'lib/lutaml/jsonschema/schema.rb'
63
184
  - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
64
185
 
65
- # Offense count: 3
186
+ # Offense count: 6
66
187
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
67
188
  # AllowedMethods: refine
68
189
  Metrics/BlockLength:
69
190
  Max: 54
70
191
 
71
- # Offense count: 8
192
+ # Offense count: 12
72
193
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
73
194
  Metrics/CyclomaticComplexity:
74
195
  Exclude:
75
196
  - 'lib/lutaml/jsonschema/cli.rb'
76
- - 'lib/lutaml/jsonschema/combiner.rb'
77
- - 'lib/lutaml/jsonschema/configuration.rb'
78
197
  - 'lib/lutaml/jsonschema/reference_resolver.rb'
198
+ - 'lib/lutaml/jsonschema/schema.rb'
79
199
  - 'lib/lutaml/jsonschema/schema_set.rb'
200
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
80
201
 
81
- # Offense count: 17
202
+ # Offense count: 22
82
203
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
83
204
  Metrics/MethodLength:
84
- Max: 47
205
+ Max: 70
85
206
 
86
- # Offense count: 6
207
+ # Offense count: 9
87
208
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
88
209
  Metrics/PerceivedComplexity:
89
210
  Exclude:
90
- - 'lib/lutaml/jsonschema/combiner.rb'
91
- - 'lib/lutaml/jsonschema/configuration.rb'
92
211
  - 'lib/lutaml/jsonschema/reference_resolver.rb'
212
+ - 'lib/lutaml/jsonschema/schema.rb'
93
213
  - 'lib/lutaml/jsonschema/schema_set.rb'
214
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
94
215
 
95
- # Offense count: 1
216
+ # Offense count: 2
96
217
  # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
97
218
  # AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
98
219
  Naming/MethodParameterName:
99
220
  Exclude:
221
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
100
222
  - 'lib/lutaml/jsonschema/spa/vue_inlined_strategy.rb'
101
223
 
102
224
  # Offense count: 1
@@ -107,8 +229,71 @@ Naming/PredicateMethod:
107
229
  Exclude:
108
230
  - 'lib/lutaml/jsonschema/schema_set.rb'
109
231
 
232
+ # Offense count: 51
233
+ # This cop supports safe autocorrection (--autocorrect).
234
+ # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
235
+ # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
236
+ # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
237
+ # FunctionalMethods: let, let!, subject, watch
238
+ # AllowedMethods: lambda, proc, it
239
+ Style/BlockDelimiters:
240
+ Exclude:
241
+ - 'lib/lutaml/jsonschema/schema.rb'
242
+ - 'spec/e2e/spa_e2e_spec.rb'
243
+ - 'spec/lutaml/generator_spec.rb'
244
+
245
+ # Offense count: 1
246
+ # This cop supports safe autocorrection (--autocorrect).
247
+ # Configuration parameters: EnforcedStyle, AllowComments.
248
+ # SupportedStyles: empty, nil, both
249
+ Style/EmptyElse:
250
+ Exclude:
251
+ - 'lib/lutaml/jsonschema/schema_store.rb'
252
+
110
253
  # Offense count: 2
111
254
  # This cop supports unsafe autocorrection (--autocorrect-all).
112
255
  Style/IdenticalConditionalBranches:
113
256
  Exclude:
114
257
  - 'spec/lutaml/spa_builder_spec.rb'
258
+
259
+ # Offense count: 1
260
+ # This cop supports safe autocorrection (--autocorrect).
261
+ Style/MultilineIfModifier:
262
+ Exclude:
263
+ - 'lib/lutaml/jsonschema/schema_store.rb'
264
+
265
+ # Offense count: 7
266
+ # This cop supports safe autocorrection (--autocorrect).
267
+ Style/MultilineTernaryOperator:
268
+ Exclude:
269
+ - 'lib/lutaml/jsonschema/schema_store.rb'
270
+ - 'lib/lutaml/jsonschema/spa/spa_builder.rb'
271
+
272
+ # Offense count: 1
273
+ # This cop supports safe autocorrection (--autocorrect).
274
+ Style/RedundantStringEscape:
275
+ Exclude:
276
+ - 'spec/lutaml/schema/each_child_spec.rb'
277
+
278
+ # Offense count: 2
279
+ # This cop supports unsafe autocorrection (--autocorrect-all).
280
+ Style/SingleArgumentDig:
281
+ Exclude:
282
+ - 'spec/e2e/spa_e2e_spec.rb'
283
+
284
+ # Offense count: 2
285
+ # This cop supports safe autocorrection (--autocorrect).
286
+ # Configuration parameters: EnforcedStyleForMultiline.
287
+ # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
288
+ Style/TrailingCommaInArguments:
289
+ Exclude:
290
+ - 'lib/lutaml/jsonschema/ljr_package_definition.rb'
291
+ - 'spec/lutaml/spa/generator_spec.rb'
292
+
293
+ # Offense count: 4
294
+ # This cop supports safe autocorrection (--autocorrect).
295
+ # Configuration parameters: EnforcedStyleForMultiline.
296
+ # SupportedStylesForMultiline: comma, consistent_comma, diff_comma, no_comma
297
+ Style/TrailingCommaInArrayLiteral:
298
+ Exclude:
299
+ - 'spec/e2e/spa_e2e_spec.rb'
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
3
 
4
- require "lutaml/jsonschema/cli"
4
+ require "lutaml/jsonschema"
5
5
 
6
6
  Lutaml::Jsonschema::Cli.start(ARGV)
@@ -0,0 +1,338 @@
1
+ /**
2
+ * @vitest-environment jsdom
3
+ */
4
+ import { describe, it, expect, beforeEach } from 'vitest'
5
+ import { createPinia, setActivePinia } from 'pinia'
6
+ import { useSchemaStore } from '../stores/schemaStore'
7
+ import {
8
+ primaryType,
9
+ displayType,
10
+ refLabel,
11
+ humanizeConstraints,
12
+ hasConstraints,
13
+ } from '../composables/useSchemaTypes'
14
+ import { resolveSchemaRef } from '../composables/useDefinitionResolver'
15
+ import { createField, buildDefaultJson } from '../composables/useBuilderField'
16
+ import type { SpaDocument, SpaProperty, SpaDefinition, SpaSchema } from '../types'
17
+
18
+ function prop(overrides: Partial<SpaProperty> = {}): SpaProperty {
19
+ return { name: 'test', ...overrides }
20
+ }
21
+
22
+ function definition(overrides: Partial<SpaDefinition> = {}): SpaDefinition {
23
+ return { name: 'Test', properties: [], required: [], ...overrides }
24
+ }
25
+
26
+ function schema(overrides: Partial<SpaSchema> = {}): SpaSchema {
27
+ return { name: 'TestSchema', properties: [], definitions: [], required: [], ...overrides }
28
+ }
29
+
30
+ /**
31
+ * Build a SpaDocument that mimics what the Ruby backend outputs
32
+ * for a complex schema like ISO 19115-4 mdj.json.
33
+ */
34
+ function buildComplexDoc(): SpaDocument {
35
+ const mdIdentifier: SpaDefinition = {
36
+ name: 'MD_Identifier',
37
+ title: 'Identifier',
38
+ type: 'object',
39
+ properties: [
40
+ prop({ name: 'code', type: 'string', required: true }),
41
+ prop({ name: 'codeSpace', type: 'string', format: 'uri' }),
42
+ ],
43
+ required: ['code'],
44
+ }
45
+
46
+ const topicCategory: SpaDefinition = {
47
+ name: 'MD_TopicCategoryCode',
48
+ title: 'Topic Category',
49
+ type: 'string',
50
+ enum: ['farming', 'biota', 'boundaries', 'climatology', 'economy', 'elevation'],
51
+ properties: [],
52
+ required: [],
53
+ }
54
+
55
+ const obligationCode: SpaDefinition = {
56
+ name: 'MD_ObligationCode',
57
+ type: 'string',
58
+ enum: ['mandatory', 'optional', 'conditional'],
59
+ properties: [],
60
+ required: [],
61
+ }
62
+
63
+ const ciTelephone: SpaDefinition = {
64
+ name: 'CI_Telephone',
65
+ type: 'object',
66
+ properties: [
67
+ prop({ name: 'number', type: 'string' }),
68
+ ],
69
+ required: [],
70
+ }
71
+
72
+ const durationType: SpaDefinition = {
73
+ name: 'DurationType',
74
+ type: 'string',
75
+ format: 'duration',
76
+ pattern: '^P.*$',
77
+ properties: [],
78
+ required: [],
79
+ }
80
+
81
+ const constraintUnion: SpaDefinition = {
82
+ name: 'Abstract_ConstraintUnion',
83
+ hasOneOf: true,
84
+ properties: [],
85
+ required: [],
86
+ }
87
+
88
+ const mdMetadata: SpaDefinition = {
89
+ name: 'MD_Metadata',
90
+ title: 'Metadata',
91
+ type: 'object',
92
+ description: 'Root metadata object',
93
+ properties: [
94
+ prop({ name: 'name', type: 'string', required: true }),
95
+ prop({
96
+ name: 'identifiers',
97
+ type: 'array',
98
+ itemsType: 'object',
99
+ itemsRef: '#/$defs/MD_Identifier',
100
+ }),
101
+ prop({
102
+ name: 'topicCategory',
103
+ ref: '#/$defs/MD_TopicCategoryCode',
104
+ type: 'string',
105
+ enum: ['farming', 'biota', 'boundaries', 'climatology', 'economy', 'elevation'],
106
+ }),
107
+ prop({
108
+ name: 'contacts',
109
+ type: 'array',
110
+ itemsType: 'object',
111
+ itemsRef: '#/$defs/CI_Contact',
112
+ minItems: 1,
113
+ }),
114
+ ],
115
+ required: ['name'],
116
+ }
117
+
118
+ const ciContact: SpaDefinition = {
119
+ name: 'CI_Contact',
120
+ type: 'object',
121
+ properties: [
122
+ prop({
123
+ name: 'phone',
124
+ type: 'array',
125
+ itemsType: 'object',
126
+ itemsRef: '#/$defs/CI_Telephone',
127
+ }),
128
+ ],
129
+ required: [],
130
+ }
131
+
132
+ return {
133
+ metadata: { title: 'ISO 19115-4 Metadata' },
134
+ schemas: [{
135
+ name: 'mdj',
136
+ title: 'ISO 19115-4',
137
+ type: 'object',
138
+ properties: [],
139
+ definitions: [
140
+ mdMetadata,
141
+ mdIdentifier,
142
+ topicCategory,
143
+ obligationCode,
144
+ ciContact,
145
+ ciTelephone,
146
+ durationType,
147
+ constraintUnion,
148
+ ],
149
+ required: [],
150
+ sourceJson: '{}',
151
+ }],
152
+ searchIndex: [],
153
+ }
154
+ }
155
+
156
+ describe('SPA data completeness for complex schemas', () => {
157
+ let store: ReturnType<typeof useSchemaStore>
158
+
159
+ beforeEach(() => {
160
+ setActivePinia(createPinia())
161
+ store = useSchemaStore()
162
+ })
163
+
164
+ describe('backend data integrity', () => {
165
+ const doc = buildComplexDoc()
166
+
167
+ it('array properties have itemsRef resolved from items.$ref', () => {
168
+ const mdMetadata = doc.schemas[0].definitions.find(d => d.name === 'MD_Metadata')!
169
+ const identifiers = mdMetadata.properties.find(p => p.name === 'identifiers')!
170
+ expect(identifiers.itemsRef).toBe('#/$defs/MD_Identifier')
171
+ expect(identifiers.itemsType).toBe('object')
172
+ })
173
+
174
+ it('array properties with itemsRef show correct display type', () => {
175
+ const mdMetadata = doc.schemas[0].definitions.find(d => d.name === 'MD_Metadata')!
176
+ const identifiers = mdMetadata.properties.find(p => p.name === 'identifiers')!
177
+ expect(displayType(identifiers)).toBe('array of MD_Identifier')
178
+ })
179
+
180
+ it('enum definitions carry their enum values', () => {
181
+ const topicCat = doc.schemas[0].definitions.find(d => d.name === 'MD_TopicCategoryCode')!
182
+ expect(topicCat.enum).toHaveLength(6)
183
+ expect(topicCat.enum).toContain('farming')
184
+ expect(topicCat.enum).toContain('elevation')
185
+ })
186
+
187
+ it('properties resolved from enum definitions carry enum values', () => {
188
+ const mdMetadata = doc.schemas[0].definitions.find(d => d.name === 'MD_Metadata')!
189
+ const topic = mdMetadata.properties.find(p => p.name === 'topicCategory')!
190
+ expect(topic.enum).toHaveLength(6)
191
+ expect(topic.ref).toBe('#/$defs/MD_TopicCategoryCode')
192
+ })
193
+
194
+ it('format and pattern on non-object definitions are preserved', () => {
195
+ const duration = doc.schemas[0].definitions.find(d => d.name === 'DurationType')!
196
+ expect(duration.format).toBe('duration')
197
+ expect(duration.pattern).toBe('^P.*$')
198
+ })
199
+
200
+ it('minItems on array properties is preserved', () => {
201
+ const mdMetadata = doc.schemas[0].definitions.find(d => d.name === 'MD_Metadata')!
202
+ const contacts = mdMetadata.properties.find(p => p.name === 'contacts')!
203
+ expect(contacts.minItems).toBe(1)
204
+ })
205
+
206
+ it('nested array itemsRef through definition chain', () => {
207
+ const ciContact = doc.schemas[0].definitions.find(d => d.name === 'CI_Contact')!
208
+ const phone = ciContact.properties.find(p => p.name === 'phone')!
209
+ expect(phone.itemsRef).toBe('#/$defs/CI_Telephone')
210
+ })
211
+
212
+ it('hasOneOf flag on union definitions', () => {
213
+ const constraint = doc.schemas[0].definitions.find(d => d.name === 'Abstract_ConstraintUnion')!
214
+ expect(constraint.hasOneOf).toBe(true)
215
+ })
216
+ })
217
+
218
+ describe('frontend rendering with store', () => {
219
+ it('store loads complex document and resolves definitions', () => {
220
+ window.SCHEMA_DATA = buildComplexDoc() as any
221
+ store.loadFromWindow()
222
+
223
+ const schemas = store.schemas
224
+ expect(schemas).toHaveLength(1)
225
+ expect(schemas[0].definitions).toHaveLength(8)
226
+ })
227
+
228
+ it('store normalizes $ref → ref on nested definition properties', () => {
229
+ const doc = buildComplexDoc()
230
+ // Simulate backend output where $ref is the JSON key
231
+ const topicProp = doc.schemas[0].definitions[0].properties.find(p => p.name === 'topicCategory')!
232
+ ;(topicProp as any).$ref = topicProp.ref
233
+ delete topicProp.ref
234
+
235
+ window.SCHEMA_DATA = doc as any
236
+ store.loadFromWindow()
237
+
238
+ const mdMetadata = store.schemas[0].definitions.find(d => d.name === 'MD_Metadata')!
239
+ const topic = mdMetadata.properties.find(p => p.name === 'topicCategory')!
240
+ expect(topic.ref).toBe('#/$defs/MD_TopicCategoryCode')
241
+ expect((topic as any).$ref).toBeUndefined()
242
+ })
243
+
244
+ it('definition resolver finds enum definitions', () => {
245
+ const s = schema({
246
+ definitions: [
247
+ definition({ name: 'MD_TopicCategoryCode', type: 'string', enum: ['farming', 'biota'] }),
248
+ ],
249
+ })
250
+ const resolved = resolveSchemaRef('#/$defs/MD_TopicCategoryCode', s)
251
+ expect(resolved).not.toBeNull()
252
+ expect(resolved!.enum).toEqual(['farming', 'biota'])
253
+ })
254
+ })
255
+
256
+ describe('type display for complex schema patterns', () => {
257
+ it('refLabel extracts definition name from $defs path', () => {
258
+ expect(refLabel('#/$defs/MD_Identifier')).toBe('MD_Identifier')
259
+ })
260
+
261
+ it('refLabel handles definitions path', () => {
262
+ expect(refLabel('#/definitions/address')).toBe('address')
263
+ })
264
+
265
+ it('displayType for array with itemsRef', () => {
266
+ expect(displayType(prop({ type: 'array', itemsRef: '#/$defs/MD_Identifier' }))).toBe('array of MD_Identifier')
267
+ })
268
+
269
+ it('prefers itemsRef label when itemsType is generic object', () => {
270
+ expect(displayType(prop({ type: 'array', itemsType: 'object', itemsRef: '#/$defs/Foo' }))).toBe('array of Foo')
271
+ })
272
+
273
+ it('uses itemsType when it is a specific primitive type', () => {
274
+ expect(displayType(prop({ type: 'array', itemsType: 'string', itemsRef: '#/$defs/Foo' }))).toBe('array of string')
275
+ })
276
+
277
+ it('displayType for array with itemsRef and constraints', () => {
278
+ const p = prop({ type: 'array', itemsRef: '#/$defs/MD_Identifier', minItems: 1, maxItems: 10 })
279
+ expect(displayType(p)).toBe('array of MD_Identifier [ 1 .. 10 ]')
280
+ })
281
+
282
+ it('displayType for nullable array with itemsRef', () => {
283
+ expect(displayType(prop({ type: 'array,null', itemsRef: '#/$defs/Item' }))).toBe('array of Item | null')
284
+ })
285
+ })
286
+
287
+ describe('builder field creation for complex properties', () => {
288
+ it('creates field for array property with itemsRef', () => {
289
+ const p = prop({ name: 'ids', type: 'array', itemsType: 'object', itemsRef: '#/$defs/MD_Identifier' })
290
+ const s = schema({
291
+ definitions: [definition({ name: 'MD_Identifier', type: 'object', properties: [prop({ name: 'code', type: 'string' })], required: [] })],
292
+ })
293
+ const field = createField(p, [], s)
294
+ expect(field.arrayItems).toHaveLength(1)
295
+ expect(field.resolvedDef).toBeNull()
296
+ })
297
+
298
+ it('creates field for property resolved from enum definition', () => {
299
+ const p = prop({
300
+ name: 'topic',
301
+ ref: '#/$defs/MD_TopicCategoryCode',
302
+ type: 'string',
303
+ enum: ['farming', 'biota', 'boundaries'],
304
+ })
305
+ const s = schema({
306
+ definitions: [definition({ name: 'MD_TopicCategoryCode', type: 'string', enum: ['farming', 'biota', 'boundaries'], properties: [], required: [] })],
307
+ })
308
+ const field = createField(p, [], s)
309
+ // Enum property should have first enum as initial value
310
+ expect(field.rawValue).toBe('farming')
311
+ })
312
+
313
+ it('buildDefaultJson handles array items with itemsRef', () => {
314
+ const props = [
315
+ prop({ name: 'tags', type: 'array', itemsType: 'string' }),
316
+ ]
317
+ const json = buildDefaultJson(props)
318
+ expect(json.tags).toEqual([''])
319
+ })
320
+ })
321
+
322
+ describe('constraint chips for complex definitions', () => {
323
+ it('enum definition has constraints detected via property resolver', () => {
324
+ const p = prop({
325
+ name: 'topic',
326
+ type: 'string',
327
+ enum: ['farming', 'biota'],
328
+ ref: '#/$defs/MD_TopicCategoryCode',
329
+ })
330
+ expect(hasConstraints(p)).toBe(true)
331
+ })
332
+
333
+ it('array with itemsRef and minItems has constraints', () => {
334
+ const p = prop({ name: 'items', type: 'array', itemsRef: '#/$defs/X', minItems: 1 })
335
+ expect(hasConstraints(p)).toBe(true)
336
+ })
337
+ })
338
+ })