@@ -1968,7 +1968,8 @@ func TestPGXMock_CancelAllPendingExchanges_Success(t *testing.T) {
19681968 store := storeWith (mock )
19691969 ctx := context .Background ()
19701970
1971- mock .ExpectExec ("UPDATE" ).WillReturnResult (pgxmock .NewResult ("UPDATE" , 3 ))
1971+ // Now parameterized: $1 = status value.
1972+ mock .ExpectExec ("UPDATE" ).WithArgs (pgxmock .AnyArg ()).WillReturnResult (pgxmock .NewResult ("UPDATE" , 3 ))
19721973
19731974 n , err := store .CancelAllPendingExchanges (ctx )
19741975 require .NoError (t , err )
@@ -1987,7 +1988,8 @@ func TestPGXMock_CancelPendingExchangesByOrigin_Standalone(t *testing.T) {
19871988 ctx := context .Background ()
19881989
19891990 // Standalone origin must target WHERE ladder_run_id IS NULL only.
1990- mock .ExpectExec ("ladder_run_id IS NULL" ).WillReturnResult (pgxmock .NewResult ("UPDATE" , 2 ))
1991+ // Now parameterized: $1 = status value.
1992+ mock .ExpectExec ("ladder_run_id IS NULL" ).WithArgs (pgxmock .AnyArg ()).WillReturnResult (pgxmock .NewResult ("UPDATE" , 2 ))
19911993
19921994 n , err := store .CancelPendingExchangesByOrigin (ctx , common .ExchangeOriginStandalone )
19931995 require .NoError (t , err )
@@ -2001,7 +2003,8 @@ func TestPGXMock_CancelPendingExchangesByOrigin_Ladder(t *testing.T) {
20012003 ctx := context .Background ()
20022004
20032005 // Ladder origin must target WHERE ladder_run_id IS NOT NULL only.
2004- mock .ExpectExec ("ladder_run_id IS NOT NULL" ).WillReturnResult (pgxmock .NewResult ("UPDATE" , 1 ))
2006+ // Now parameterized: $1 = status value.
2007+ mock .ExpectExec ("ladder_run_id IS NOT NULL" ).WithArgs (pgxmock .AnyArg ()).WillReturnResult (pgxmock .NewResult ("UPDATE" , 1 ))
20052008
20062009 n , err := store .CancelPendingExchangesByOrigin (ctx , common .ExchangeOriginLadder )
20072010 require .NoError (t , err )
@@ -2521,3 +2524,136 @@ func TestPGXMock_GetExecutionByPlanAndDate_NotFoundWrapsErrNotFound(t *testing.T
25212524 "zero-row GetExecutionByPlanAndDate must wrap ErrNotFound so getOrCreateExecution can create a new execution; got: %v" , err )
25222525 assert .NoError (t , mock .ExpectationsWereMet ())
25232526}
2527+
2528+ // ─── Canonical-cancel write regression tests (#1277 follow-up) ───────────────
2529+ //
2530+ // These tests assert that every cancel write path emits the canonical US
2531+ // spelling 'canceled' (StatusCanceled) rather than the legacy 'cancelled'.
2532+ // They fail on the pre-fix code (which hardcoded 'cancelled') and pass only
2533+ // when the parameterized $N arg receives StatusCanceled = "canceled".
2534+
2535+ // TestPGXMock_CancelExecutionAtomic_WritesCanonicalStatus verifies that
2536+ // CancelExecutionAtomic sends StatusCanceled ("canceled") as the status
2537+ // parameter and sets canceled_by (not cancelled_by).
2538+ // Regression guard for the #1277 follow-up fix.
2539+ func TestPGXMock_CancelExecutionAtomic_WritesCanonicalStatus (t * testing.T ) {
2540+ mock := newMock (t )
2541+ store := storeWith (mock )
2542+ ctx := context .Background ()
2543+
2544+ actor := "user@example.com"
2545+
2546+ // The mock uses regexp matching. We pin on:
2547+ // - "canceled_by" to confirm the new column is targeted (not cancelled_by)
2548+ // - WithArgs: $1=execID, $2=&actor, $3=StatusCanceled
2549+ // The RETURNING row also carries "canceled" so the caller's returned
2550+ // status is the canonical spelling end-to-end.
2551+ rows := pgxmock .NewRows ([]string {"status" }).AddRow (StatusCanceled )
2552+ mock .ExpectQuery ("canceled_by" ).
2553+ WithArgs ("exec-1" , & actor , StatusCanceled ).
2554+ WillReturnRows (rows )
2555+
2556+ // Pass mock directly as pgx.Tx (PgxPoolIface embeds pgx.Tx).
2557+ ok , status , err := store .CancelExecutionAtomic (ctx , mock , "exec-1" , & actor )
2558+ require .NoError (t , err )
2559+ assert .True (t , ok , "row should have been updated" )
2560+ assert .Equal (t , StatusCanceled , status , "returned status must be canonical 'canceled'" )
2561+ assert .NoError (t , mock .ExpectationsWereMet ())
2562+ }
2563+
2564+ // TestPGXMock_CancelScheduledExecutionAtomic_WritesCanonicalStatus mirrors the
2565+ // above for the scheduled-execution cancel path.
2566+ func TestPGXMock_CancelScheduledExecutionAtomic_WritesCanonicalStatus (t * testing.T ) {
2567+ mock := newMock (t )
2568+ store := storeWith (mock )
2569+ ctx := context .Background ()
2570+
2571+ actor := "scheduler@system"
2572+
2573+ rows := pgxmock .NewRows ([]string {"status" }).AddRow (StatusCanceled )
2574+ mock .ExpectQuery ("canceled_by" ).
2575+ WithArgs ("exec-sched" , & actor , StatusCanceled ).
2576+ WillReturnRows (rows )
2577+
2578+ ok , status , err := store .CancelScheduledExecutionAtomic (ctx , mock , "exec-sched" , & actor )
2579+ require .NoError (t , err )
2580+ assert .True (t , ok , "row should have been updated" )
2581+ assert .Equal (t , StatusCanceled , status , "returned status must be canonical 'canceled'" )
2582+ assert .NoError (t , mock .ExpectationsWereMet ())
2583+ }
2584+
2585+ // TestPGXMock_CancelAllPendingExchanges_WritesCanonicalStatus pins
2586+ // CancelAllPendingExchanges to emit StatusCanceled as the $1 argument.
2587+ // A pre-fix version of the code sent no parameter (the value was inlined as
2588+ // the literal 'cancelled'), so this test double-checks that the parameterized
2589+ // form is used and the value is the canonical spelling.
2590+ func TestPGXMock_CancelAllPendingExchanges_WritesCanonicalStatus (t * testing.T ) {
2591+ mock := newMock (t )
2592+ store := storeWith (mock )
2593+ ctx := context .Background ()
2594+
2595+ mock .ExpectExec ("UPDATE ri_exchange_history" ).
2596+ WithArgs (StatusCanceled ).
2597+ WillReturnResult (pgxmock .NewResult ("UPDATE" , 2 ))
2598+
2599+ n , err := store .CancelAllPendingExchanges (ctx )
2600+ require .NoError (t , err )
2601+ assert .Equal (t , int64 (2 ), n )
2602+ assert .NoError (t , mock .ExpectationsWereMet ())
2603+ }
2604+
2605+ // TestPGXMock_CancelPendingExchangesByOrigin_Standalone_WritesCanonicalStatus
2606+ // asserts the standalone branch passes StatusCanceled.
2607+ func TestPGXMock_CancelPendingExchangesByOrigin_Standalone_WritesCanonicalStatus (t * testing.T ) {
2608+ mock := newMock (t )
2609+ store := storeWith (mock )
2610+ ctx := context .Background ()
2611+
2612+ mock .ExpectExec ("ladder_run_id IS NULL" ).
2613+ WithArgs (StatusCanceled ).
2614+ WillReturnResult (pgxmock .NewResult ("UPDATE" , 1 ))
2615+
2616+ n , err := store .CancelPendingExchangesByOrigin (ctx , common .ExchangeOriginStandalone )
2617+ require .NoError (t , err )
2618+ assert .Equal (t , int64 (1 ), n )
2619+ assert .NoError (t , mock .ExpectationsWereMet ())
2620+ }
2621+
2622+ // TestPGXMock_CancelPendingExchangesByOrigin_Ladder_WritesCanonicalStatus
2623+ // asserts the ladder branch passes StatusCanceled.
2624+ func TestPGXMock_CancelPendingExchangesByOrigin_Ladder_WritesCanonicalStatus (t * testing.T ) {
2625+ mock := newMock (t )
2626+ store := storeWith (mock )
2627+ ctx := context .Background ()
2628+
2629+ mock .ExpectExec ("ladder_run_id IS NOT NULL" ).
2630+ WithArgs (StatusCanceled ).
2631+ WillReturnResult (pgxmock .NewResult ("UPDATE" , 3 ))
2632+
2633+ n , err := store .CancelPendingExchangesByOrigin (ctx , common .ExchangeOriginLadder )
2634+ require .NoError (t , err )
2635+ assert .Equal (t , int64 (3 ), n )
2636+ assert .NoError (t , mock .ExpectationsWereMet ())
2637+ }
2638+
2639+ // TestPGXMock_CleanupOldExecutions_IncludesCanonicalStatus verifies that the
2640+ // cleanup DELETE includes 'canceled' (canonical) in the terminal-status filter,
2641+ // so Plans-canceled rows written by new code are not missed.
2642+ // Regression guard for the #1277 follow-up Defect 2 fix.
2643+ func TestPGXMock_CleanupOldExecutions_IncludesCanonicalStatus (t * testing.T ) {
2644+ mock := newMock (t )
2645+ store := storeWith (mock )
2646+ ctx := context .Background ()
2647+
2648+ // The regex pins on both 'cancelled' (legacy) and 'canceled' (canonical)
2649+ // appearing together in the SQL. If either is missing the mock won't
2650+ // match, causing an "unexpected query" error from ExpectationsWereMet.
2651+ mock .ExpectExec (`'cancelled'.*'canceled'` ).
2652+ WithArgs (pgxmock .AnyArg ()).
2653+ WillReturnResult (pgxmock .NewResult ("DELETE" , 4 ))
2654+
2655+ n , err := store .CleanupOldExecutions (ctx , 90 )
2656+ require .NoError (t , err )
2657+ assert .Equal (t , int64 (4 ), n )
2658+ assert .NoError (t , mock .ExpectationsWereMet ())
2659+ }
0 commit comments