@@ -853,6 +853,26 @@ describe('cancel_at_period_end sync', () => {
853853 expect ( stripe . subscription ( pro . stripeSubscriptionId ) . cancel_at_period_end ) . toBe ( true )
854854 } )
855855
856+ it ( 'restores the personal Pro when its member leaves while the plugin has overwritten the row' , async ( ) => {
857+ const pro = await createProUserInPaidOrganization ( )
858+ await pauseProSubscriptionForOrgCoverage ( pro . userId )
859+ const pauseSync = await latestOutboxEventId (
860+ OUTBOX_EVENT_TYPES . STRIPE_SYNC_CANCEL_AT_PERIOD_END ,
861+ pro . subscriptionId
862+ )
863+
864+ beforeReconcile = async ( ) => {
865+ beforeReconcile = undefined
866+ await leaveOrganization ( pro . userId , pro . paidOrganization . organizationId )
867+ await restoreUserProSubscription ( pro . userId )
868+ }
869+ await deliverUnrelatedUpdate ( pro . stripeSubscriptionId )
870+
871+ expect ( ( await storedSubscription ( pro . subscriptionId ) ) . cancelAtPeriodEnd ) . toBe ( false )
872+ await expect ( processEvent ( pauseSync ) ) . resolves . toBe ( 'completed' )
873+ expect ( stripe . subscription ( pro . stripeSubscriptionId ) . cancel_at_period_end ) . toBe ( false )
874+ } )
875+
856876 it ( 'does not revive an older value when a retry path resets its sync without re-committing' , async ( ) => {
857877 const pro = await createProUserInPaidOrganization ( )
858878 await pauseProSubscriptionForOrgCoverage ( pro . userId )
@@ -1237,6 +1257,32 @@ describe('Team seat sync', () => {
12371257 expect ( stripe . subscription ( org . stripeSubscriptionId ) . items . data [ 0 ] . quantity ) . toBe ( 2 )
12381258 } )
12391259
1260+ it ( 'drops a seat when a member leaves while the plugin has overwritten the row' , async ( ) => {
1261+ const [ owner , joiner ] = await Promise . all ( [ createUser ( 'owner' ) , createUser ( 'joiner' ) ] )
1262+ const org = await createOrganizationWithPlan ( 'team' , 1 )
1263+ await addMember ( org . organizationId , owner . id , 'owner' )
1264+ await addMember ( org . organizationId , joiner . id )
1265+ await reconcileOrganizationSeats ( { organizationId : org . organizationId , reason : 'member-added' } )
1266+ const growSync = await latestOutboxEventId (
1267+ OUTBOX_EVENT_TYPES . STRIPE_SYNC_SUBSCRIPTION_SEATS ,
1268+ org . subscriptionId
1269+ )
1270+
1271+ beforeReconcile = async ( ) => {
1272+ beforeReconcile = undefined
1273+ await leaveOrganization ( joiner . id , org . organizationId )
1274+ await reconcileOrganizationSeats ( {
1275+ organizationId : org . organizationId ,
1276+ reason : 'member-removed' ,
1277+ } )
1278+ }
1279+ await deliverUnrelatedUpdate ( org . stripeSubscriptionId )
1280+
1281+ expect ( ( await storedSubscription ( org . subscriptionId ) ) . seats ) . toBe ( 1 )
1282+ await expect ( processEvent ( growSync ) ) . resolves . toBe ( 'completed' )
1283+ expect ( stripe . subscription ( org . stripeSubscriptionId ) . items . data [ 0 ] . quantity ) . toBe ( 1 )
1284+ } )
1285+
12401286 it ( 'does not revive an older seat count when its dead-lettered sync is requeued' , async ( ) => {
12411287 const [ owner , joiner ] = await Promise . all ( [ createUser ( 'owner' ) , createUser ( 'joiner' ) ] )
12421288 const org = await createOrganizationWithPlan ( 'team' , 1 )
0 commit comments