11import { describe , expect , it } from 'vitest'
2- import { diffSluggedResources , type SluggedResource } from './diffing'
2+ import { FeatureType , UsageMeterAggregationType } from '@/types'
3+ import {
4+ diffFeatures ,
5+ diffSluggedResources ,
6+ diffUsageMeters ,
7+ type FeatureDiffInput ,
8+ type SluggedResource ,
9+ type UsageMeterDiffInput ,
10+ } from './diffing'
311
412type SlugAndName = SluggedResource < { name : string } >
513
@@ -181,3 +189,241 @@ describe('diffSluggedResources', () => {
181189 expect ( result . toUpdate [ 0 ] . existing . slug ) . toBe ( 'minimal' )
182190 } )
183191} )
192+
193+ describe ( 'diffFeatures' , ( ) => {
194+ it ( 'should use diffSluggedResources to compute diff' , ( ) => {
195+ // Setup: existing has feature, proposed is empty
196+ const existing : FeatureDiffInput [ ] = [
197+ {
198+ slug : 'foo' ,
199+ name : 'Foo Feature' ,
200+ description : 'A test feature' ,
201+ type : FeatureType . Toggle ,
202+ active : true ,
203+ } ,
204+ ]
205+ const proposed : FeatureDiffInput [ ] = [ ]
206+
207+ const result = diffFeatures ( existing , proposed )
208+
209+ // Expectation: toRemove contains the feature
210+ expect ( result . toRemove ) . toHaveLength ( 1 )
211+ expect ( result . toRemove [ 0 ] . slug ) . toBe ( 'foo' )
212+ expect ( result . toCreate ) . toEqual ( [ ] )
213+ expect ( result . toUpdate ) . toEqual ( [ ] )
214+ } )
215+
216+ it ( 'should handle feature updates' , ( ) => {
217+ // Setup: existing and proposed both have same slug but different name
218+ const existing : FeatureDiffInput [ ] = [
219+ {
220+ slug : 'foo' ,
221+ name : 'Old Name' ,
222+ description : 'A test feature' ,
223+ type : FeatureType . Toggle ,
224+ active : true ,
225+ } ,
226+ ]
227+ const proposed : FeatureDiffInput [ ] = [
228+ {
229+ slug : 'foo' ,
230+ name : 'New Name' ,
231+ description : 'A test feature' ,
232+ type : FeatureType . Toggle ,
233+ active : true ,
234+ } ,
235+ ]
236+
237+ const result = diffFeatures ( existing , proposed )
238+
239+ // Expectation: toUpdate contains the feature with name change
240+ expect ( result . toRemove ) . toEqual ( [ ] )
241+ expect ( result . toCreate ) . toEqual ( [ ] )
242+ expect ( result . toUpdate ) . toHaveLength ( 1 )
243+ expect ( result . toUpdate [ 0 ] . existing . name ) . toBe ( 'Old Name' )
244+ expect ( result . toUpdate [ 0 ] . proposed . name ) . toBe ( 'New Name' )
245+ } )
246+
247+ it ( 'should handle feature creation' , ( ) => {
248+ // Setup: proposed has new feature not in existing
249+ const existing : FeatureDiffInput [ ] = [ ]
250+ const proposed : FeatureDiffInput [ ] = [
251+ {
252+ slug : 'new-feature' ,
253+ name : 'New Feature' ,
254+ description : 'A new feature' ,
255+ type : FeatureType . Toggle ,
256+ active : true ,
257+ } ,
258+ ]
259+
260+ const result = diffFeatures ( existing , proposed )
261+
262+ // Expectation: toCreate contains the new feature
263+ expect ( result . toRemove ) . toEqual ( [ ] )
264+ expect ( result . toCreate ) . toHaveLength ( 1 )
265+ expect ( result . toCreate [ 0 ] . slug ) . toBe ( 'new-feature' )
266+ expect ( result . toUpdate ) . toEqual ( [ ] )
267+ } )
268+
269+ it ( 'should handle mixed changes for features' , ( ) => {
270+ // Setup: remove one, update one, create one
271+ const existing : FeatureDiffInput [ ] = [
272+ {
273+ slug : 'remove-me' ,
274+ name : 'Remove' ,
275+ description : 'Will be removed' ,
276+ type : FeatureType . Toggle ,
277+ active : true ,
278+ } ,
279+ {
280+ slug : 'update-me' ,
281+ name : 'Old' ,
282+ description : 'Will be updated' ,
283+ type : FeatureType . Toggle ,
284+ active : true ,
285+ } ,
286+ ]
287+ const proposed : FeatureDiffInput [ ] = [
288+ {
289+ slug : 'update-me' ,
290+ name : 'New' ,
291+ description : 'Will be updated' ,
292+ type : FeatureType . Toggle ,
293+ active : false ,
294+ } ,
295+ {
296+ slug : 'create-me' ,
297+ name : 'Create' ,
298+ description : 'Will be created' ,
299+ type : FeatureType . Toggle ,
300+ active : true ,
301+ } ,
302+ ]
303+
304+ const result = diffFeatures ( existing , proposed )
305+
306+ // Expectations
307+ expect ( result . toRemove ) . toHaveLength ( 1 )
308+ expect ( result . toRemove [ 0 ] . slug ) . toBe ( 'remove-me' )
309+ expect ( result . toCreate ) . toHaveLength ( 1 )
310+ expect ( result . toCreate [ 0 ] . slug ) . toBe ( 'create-me' )
311+ expect ( result . toUpdate ) . toHaveLength ( 1 )
312+ expect ( result . toUpdate [ 0 ] . existing . name ) . toBe ( 'Old' )
313+ expect ( result . toUpdate [ 0 ] . proposed . name ) . toBe ( 'New' )
314+ } )
315+
316+ // TODO: after validation is implemented, add tests for permitted feature update fields
317+ } )
318+
319+ describe ( 'diffUsageMeters' , ( ) => {
320+ it ( 'should use diffSluggedResources to compute diff' , ( ) => {
321+ // Setup: existing has usage meter, proposed is empty
322+ const existing : UsageMeterDiffInput [ ] = [
323+ {
324+ slug : 'foo' ,
325+ name : 'Foo Meter' ,
326+ aggregationType : UsageMeterAggregationType . Sum ,
327+ } ,
328+ ]
329+ const proposed : UsageMeterDiffInput [ ] = [ ]
330+
331+ const result = diffUsageMeters ( existing , proposed )
332+
333+ // Expectation: toRemove contains the usage meter
334+ expect ( result . toRemove ) . toHaveLength ( 1 )
335+ expect ( result . toRemove [ 0 ] . slug ) . toBe ( 'foo' )
336+ expect ( result . toCreate ) . toEqual ( [ ] )
337+ expect ( result . toUpdate ) . toEqual ( [ ] )
338+ } )
339+
340+ it ( 'should handle usage meter updates' , ( ) => {
341+ // Setup: existing and proposed both have same slug but different name
342+ const existing : UsageMeterDiffInput [ ] = [
343+ {
344+ slug : 'foo' ,
345+ name : 'Old Name' ,
346+ aggregationType : UsageMeterAggregationType . Sum ,
347+ } ,
348+ ]
349+ const proposed : UsageMeterDiffInput [ ] = [
350+ {
351+ slug : 'foo' ,
352+ name : 'New Name' ,
353+ aggregationType : UsageMeterAggregationType . Sum ,
354+ } ,
355+ ]
356+
357+ const result = diffUsageMeters ( existing , proposed )
358+
359+ // Expectation: toUpdate contains the usage meter with name change
360+ expect ( result . toRemove ) . toEqual ( [ ] )
361+ expect ( result . toCreate ) . toEqual ( [ ] )
362+ expect ( result . toUpdate ) . toHaveLength ( 1 )
363+ expect ( result . toUpdate [ 0 ] . existing . name ) . toBe ( 'Old Name' )
364+ expect ( result . toUpdate [ 0 ] . proposed . name ) . toBe ( 'New Name' )
365+ } )
366+
367+ it ( 'should handle usage meter creation' , ( ) => {
368+ // Setup: proposed has new usage meter not in existing
369+ const existing : UsageMeterDiffInput [ ] = [ ]
370+ const proposed : UsageMeterDiffInput [ ] = [
371+ {
372+ slug : 'new-meter' ,
373+ name : 'New Meter' ,
374+ aggregationType :
375+ UsageMeterAggregationType . CountDistinctProperties ,
376+ } ,
377+ ]
378+
379+ const result = diffUsageMeters ( existing , proposed )
380+
381+ // Expectation: toCreate contains the new usage meter
382+ expect ( result . toRemove ) . toEqual ( [ ] )
383+ expect ( result . toCreate ) . toHaveLength ( 1 )
384+ expect ( result . toCreate [ 0 ] . slug ) . toBe ( 'new-meter' )
385+ expect ( result . toUpdate ) . toEqual ( [ ] )
386+ } )
387+
388+ it ( 'should handle mixed changes for usage meters' , ( ) => {
389+ // Setup: remove one, update one, create one
390+ const existing : UsageMeterDiffInput [ ] = [
391+ {
392+ slug : 'remove-me' ,
393+ name : 'Remove' ,
394+ aggregationType : UsageMeterAggregationType . Sum ,
395+ } ,
396+ {
397+ slug : 'update-me' ,
398+ name : 'Old' ,
399+ aggregationType : UsageMeterAggregationType . Sum ,
400+ } ,
401+ ]
402+ const proposed : UsageMeterDiffInput [ ] = [
403+ {
404+ slug : 'update-me' ,
405+ name : 'New' ,
406+ aggregationType :
407+ UsageMeterAggregationType . CountDistinctProperties ,
408+ } ,
409+ {
410+ slug : 'create-me' ,
411+ name : 'Create' ,
412+ aggregationType : UsageMeterAggregationType . Sum ,
413+ } ,
414+ ]
415+
416+ const result = diffUsageMeters ( existing , proposed )
417+
418+ // Expectations
419+ expect ( result . toRemove ) . toHaveLength ( 1 )
420+ expect ( result . toRemove [ 0 ] . slug ) . toBe ( 'remove-me' )
421+ expect ( result . toCreate ) . toHaveLength ( 1 )
422+ expect ( result . toCreate [ 0 ] . slug ) . toBe ( 'create-me' )
423+ expect ( result . toUpdate ) . toHaveLength ( 1 )
424+ expect ( result . toUpdate [ 0 ] . existing . name ) . toBe ( 'Old' )
425+ expect ( result . toUpdate [ 0 ] . proposed . name ) . toBe ( 'New' )
426+ } )
427+
428+ // TODO: after validation is implemented, add tests for permitted usage meter update fields
429+ } )
0 commit comments