@@ -265,6 +265,108 @@ func (s *UsageBasedIntentOverrideAdapterSuite) TestDeleteChargeWithIntentOverrid
265265 s .Equal (deletedAt , * fetched .DeletedAt )
266266}
267267
268+ // newTestUnitConfig builds a deterministic unit config for round-trip assertions.
269+ func newTestUnitConfig (factor int64 , displayUnit string ) * productcatalog.UnitConfig {
270+ return & productcatalog.UnitConfig {
271+ Operation : productcatalog .UnitConfigOperationDivide ,
272+ ConversionFactor : alpacadecimal .NewFromInt (factor ),
273+ Rounding : productcatalog .UnitConfigRoundingModeCeiling ,
274+ Precision : 0 ,
275+ DisplayUnit : lo .ToPtr (displayUnit ),
276+ }
277+ }
278+
279+ // TestUnitConfigRoundTrip verifies unit_config persists through the charge write
280+ // sites. unit_config is a mutable field in IntentMutableFields (alongside price),
281+ // so it round-trips through both the base layer (create/update/clear) and the
282+ // override layer (create/update/clear).
283+ func (s * UsageBasedIntentOverrideAdapterSuite ) TestUnitConfigRoundTrip () {
284+ ctx := s .T ().Context ()
285+ namespace := "usagebased-unitconfig-adapter"
286+ charge := s .createCharge (namespace )
287+
288+ // base create→read: createCharge persisted a base unit_config (divide 1000)
289+ fetched , err := s .adapter .GetByID (ctx , usagebased.GetByIDInput {ChargeID : charge .GetChargeID ()})
290+ s .Require ().NoError (err )
291+ s .Require ().NotNil (fetched .Intent .GetBaseIntent ().UnitConfig )
292+ s .True (fetched .Intent .GetBaseIntent ().UnitConfig .Equal (newTestUnitConfig (1000 , "K" )))
293+
294+ // base update→read: the base unit_config is mutable (lives alongside price)
295+ s .Require ().NoError (fetched .Intent .Mutate (chargesmeta .ChangeTargetBase , func (f * usagebased.IntentMutableFields ) {
296+ f .UnitConfig = newTestUnitConfig (1000000 , "M" )
297+ }))
298+ updated , err := s .adapter .UpdateCharge (ctx , fetched .ChargeBase )
299+ s .Require ().NoError (err )
300+ s .True (updated .Intent .GetBaseIntent ().UnitConfig .Equal (newTestUnitConfig (1000000 , "M" )))
301+
302+ fetched , err = s .adapter .GetByID (ctx , usagebased.GetByIDInput {ChargeID : charge .GetChargeID ()})
303+ s .Require ().NoError (err )
304+ s .True (fetched .Intent .GetBaseIntent ().UnitConfig .Equal (newTestUnitConfig (1000000 , "M" )))
305+
306+ // base clear→read
307+ s .Require ().NoError (fetched .Intent .Mutate (chargesmeta .ChangeTargetBase , func (f * usagebased.IntentMutableFields ) {
308+ f .UnitConfig = nil
309+ }))
310+ updated , err = s .adapter .UpdateCharge (ctx , fetched .ChargeBase )
311+ s .Require ().NoError (err )
312+ s .Nil (updated .Intent .GetBaseIntent ().UnitConfig )
313+
314+ fetched , err = s .adapter .GetByID (ctx , usagebased.GetByIDInput {ChargeID : charge .GetChargeID ()})
315+ s .Require ().NoError (err )
316+ s .Nil (fetched .Intent .GetBaseIntent ().UnitConfig )
317+
318+ // override create→read: the override layer carries its own unit_config snapshot
319+ baseIntent := fetched .Intent .GetBaseIntent ()
320+ override := usagebased.IntentMutableFields {
321+ IntentMutableFields : chargesmeta.IntentMutableFields {
322+ Name : "override with unit config" ,
323+ TaxConfig : baseIntent .TaxConfig ,
324+ ServicePeriod : baseIntent .ServicePeriod ,
325+ FullServicePeriod : baseIntent .FullServicePeriod ,
326+ BillingPeriod : baseIntent .BillingPeriod ,
327+ },
328+ InvoiceAt : baseIntent .InvoiceAt ,
329+ FeatureKey : baseIntent .FeatureKey ,
330+ Price : baseIntent .Price ,
331+ Discounts : baseIntent .Discounts ,
332+ UnitConfig : newTestUnitConfig (1000 , "K" ),
333+ }
334+ withOverride , err := s .adapter .CreateChargeOverride (ctx , fetched .ChargeBase , override )
335+ s .Require ().NoError (err )
336+ s .Require ().NotNil (withOverride .Intent .GetOverrideLayerMutableFields ())
337+ s .True (withOverride .Intent .GetOverrideLayerMutableFields ().UnitConfig .Equal (newTestUnitConfig (1000 , "K" )))
338+
339+ fetched , err = s .adapter .GetByID (ctx , usagebased.GetByIDInput {ChargeID : charge .GetChargeID ()})
340+ s .Require ().NoError (err )
341+ s .Require ().NotNil (fetched .Intent .GetOverrideLayerMutableFields ())
342+ s .True (fetched .Intent .GetOverrideLayerMutableFields ().UnitConfig .Equal (newTestUnitConfig (1000 , "K" )))
343+
344+ // override update→read
345+ override = * fetched .Intent .GetOverrideLayerMutableFields ()
346+ override .UnitConfig = newTestUnitConfig (1000000 , "M" )
347+ fetched .Intent = usagebased .NewOverridableIntent (fetched .Intent .GetBaseIntent (), & override )
348+ updated , err = s .adapter .UpdateCharge (ctx , fetched .ChargeBase )
349+ s .Require ().NoError (err )
350+ s .True (updated .Intent .GetOverrideLayerMutableFields ().UnitConfig .Equal (newTestUnitConfig (1000000 , "M" )))
351+
352+ fetched , err = s .adapter .GetByID (ctx , usagebased.GetByIDInput {ChargeID : charge .GetChargeID ()})
353+ s .Require ().NoError (err )
354+ s .True (fetched .Intent .GetOverrideLayerMutableFields ().UnitConfig .Equal (newTestUnitConfig (1000000 , "M" )))
355+
356+ // override clear→read
357+ override = * fetched .Intent .GetOverrideLayerMutableFields ()
358+ override .UnitConfig = nil
359+ fetched .Intent = usagebased .NewOverridableIntent (fetched .Intent .GetBaseIntent (), & override )
360+ updated , err = s .adapter .UpdateCharge (ctx , fetched .ChargeBase )
361+ s .Require ().NoError (err )
362+ s .Nil (updated .Intent .GetOverrideLayerMutableFields ().UnitConfig )
363+
364+ fetched , err = s .adapter .GetByID (ctx , usagebased.GetByIDInput {ChargeID : charge .GetChargeID ()})
365+ s .Require ().NoError (err )
366+ s .Require ().NotNil (fetched .Intent .GetOverrideLayerMutableFields ())
367+ s .Nil (fetched .Intent .GetOverrideLayerMutableFields ().UnitConfig )
368+ }
369+
268370func (s * UsageBasedIntentOverrideAdapterSuite ) requireOverrideMatches (
269371 override * usagebased.IntentMutableFields ,
270372 servicePeriod timeutil.ClosedPeriod ,
@@ -331,6 +433,7 @@ func (s *UsageBasedIntentOverrideAdapterSuite) createCharge(namespace string) us
331433 Price : * productcatalog .NewPriceFrom (productcatalog.UnitPrice {
332434 Amount : alpacadecimal .NewFromFloat (0.1 ),
333435 }),
436+ UnitConfig : newTestUnitConfig (1000 , "K" ),
334437 },
335438 SettlementMode : productcatalog .CreditThenInvoiceSettlementMode ,
336439 }.AsOverridableIntent (),
0 commit comments