@@ -3,6 +3,7 @@ import {shallowRef} from 'vue';
33import { ManagedData } from '@/data/managedData' ;
44import { SessionMode } from '@/store/sessionMode' ;
55import {
6+ doesIdenticalSchemaDefinitionExist ,
67 extractAllInlinedSchemaElements ,
78 extractInlinedSchemaElement ,
89} from '@/schema/schemaManipulationUtils' ;
@@ -162,6 +163,129 @@ describe('schemaManipulationUtils', () => {
162163 } ) ;
163164 } ) ;
164165
166+ it ( 'reuses an existing matching definition under a different name when duplicate reuse is enabled' , ( ) => {
167+ // existing def is named "vector" but has the same content as the inlined a2 sub-schema
168+ schemaData . setDataAt ( [ '$defs' , 'vector' ] , {
169+ type : 'object' ,
170+ properties : {
171+ a2a : {
172+ type : 'string' ,
173+ } ,
174+ } ,
175+ } ) ;
176+
177+ const extractedPath = extractInlinedSchemaElement (
178+ [ 'properties' , 'a' , 'properties' , 'a2' ] ,
179+ schemaData ,
180+ // candidate name is different from the existing one; the dedupe should still find vector by content
181+ 'a2' ,
182+ true
183+ ) ;
184+
185+ expect ( extractedPath ) . toEqual ( [ '$defs' , 'vector' ] ) ;
186+ expect ( schemaData . dataAt ( [ '$defs' , 'a2' ] ) ) . toBeUndefined ( ) ;
187+ expect ( schemaData . dataAt ( [ 'properties' , 'a' , 'properties' , 'a2' ] ) ) . toEqual ( {
188+ $ref : '#/$defs/vector' ,
189+ } ) ;
190+ expect ( schemaData . dataAt ( [ 'properties' , 'a' , 'properties' , 'a3' ] ) ) . toEqual ( {
191+ type : 'object' ,
192+ $ref : '#/$defs/vector' ,
193+ } ) ;
194+ } ) ;
195+
196+ it ( 'collapses identical sub-schemas into a single $defs entry during bulk extraction' , ( ) => {
197+ schemaData = new ManagedData (
198+ shallowRef ( {
199+ type : 'object' ,
200+ properties : {
201+ point1 : {
202+ type : 'object' ,
203+ properties : {
204+ x : { type : 'number' } ,
205+ y : { type : 'number' } ,
206+ } ,
207+ } ,
208+ point2 : {
209+ type : 'object' ,
210+ properties : {
211+ x : { type : 'number' } ,
212+ y : { type : 'number' } ,
213+ } ,
214+ } ,
215+ point3 : {
216+ // key order differs from point1/point2; deep equality should still consider it equal
217+ type : 'object' ,
218+ properties : {
219+ y : { type : 'number' } ,
220+ x : { type : 'number' } ,
221+ } ,
222+ } ,
223+ } ,
224+ } ) ,
225+ SessionMode . SchemaEditor
226+ ) ;
227+
228+ const extractedCount = extractAllInlinedSchemaElements ( schemaData , false , false ) ;
229+
230+ // root is excluded by extractRootElement=false. The 3 inlined point objects should all
231+ // collapse into a single $defs entry — so only one definition is actually created.
232+ expect ( Object . keys ( schemaData . data . value . $defs ) ) . toHaveLength ( 1 ) ;
233+ const defName = Object . keys ( schemaData . data . value . $defs ) [ 0 ] ! ;
234+ const expectedRef = { $ref : `#/$defs/${ defName } ` } ;
235+ expect ( schemaData . data . value . properties . point1 ) . toEqual ( expectedRef ) ;
236+ expect ( schemaData . data . value . properties . point2 ) . toEqual ( expectedRef ) ;
237+ expect ( schemaData . data . value . properties . point3 ) . toEqual ( expectedRef ) ;
238+ // each of the three properties was processed
239+ expect ( extractedCount ) . toBe ( 3 ) ;
240+ } ) ;
241+
242+ describe ( 'doesIdenticalSchemaDefinitionExist' , ( ) => {
243+ it ( 'returns the path of an existing definition with deeply equal content' , ( ) => {
244+ schemaData . setDataAt ( [ '$defs' , 'foo' ] , {
245+ type : 'object' ,
246+ properties : { x : { type : 'number' } } ,
247+ } ) ;
248+
249+ const match = doesIdenticalSchemaDefinitionExist ( schemaData , {
250+ // keys in different order, but structurally identical
251+ properties : { x : { type : 'number' } } ,
252+ type : 'object' ,
253+ } ) ;
254+
255+ expect ( match ) . toEqual ( [ '$defs' , 'foo' ] ) ;
256+ } ) ;
257+
258+ it ( 'returns undefined when no equivalent definition exists' , ( ) => {
259+ schemaData . setDataAt ( [ '$defs' , 'foo' ] , { type : 'string' } ) ;
260+
261+ const match = doesIdenticalSchemaDefinitionExist ( schemaData , { type : 'number' } ) ;
262+
263+ expect ( match ) . toBeUndefined ( ) ;
264+ } ) ;
265+
266+ it ( 'returns undefined when there is no $defs section' , ( ) => {
267+ const match = doesIdenticalSchemaDefinitionExist ( schemaData , { type : 'string' } ) ;
268+ expect ( match ) . toBeUndefined ( ) ;
269+ } ) ;
270+
271+ it ( 'also searches the legacy "definitions" section' , ( ) => {
272+ schemaData . setDataAt ( [ 'definitions' , 'legacyFoo' ] , { type : 'object' } ) ;
273+
274+ const match = doesIdenticalSchemaDefinitionExist ( schemaData , { type : 'object' } ) ;
275+
276+ expect ( match ) . toEqual ( [ 'definitions' , 'legacyFoo' ] ) ;
277+ } ) ;
278+
279+ it ( 'prefers a $defs match over a definitions match when both exist' , ( ) => {
280+ schemaData . setDataAt ( [ 'definitions' , 'legacyFoo' ] , { type : 'string' } ) ;
281+ schemaData . setDataAt ( [ '$defs' , 'modernFoo' ] , { type : 'string' } ) ;
282+
283+ const match = doesIdenticalSchemaDefinitionExist ( schemaData , { type : 'string' } ) ;
284+
285+ expect ( match ) . toEqual ( [ '$defs' , 'modernFoo' ] ) ;
286+ } ) ;
287+ } ) ;
288+
165289 it ( 'extracts inlined subschemas that are nested inside an existing $defs entry' , ( ) => {
166290 schemaData = new ManagedData (
167291 shallowRef ( {
0 commit comments