Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion internal/config/store_postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ func (s *PostgresStore) GetPlannedExecutions(ctx context.Context, statuses []str
created_by_user_id, retry_execution_id, retry_attempt_n,
approval_token_expires_at,
executed_by_user_id, executed_at, pre_approval_skip_reason,
idempotency_key
idempotency_key, scheduled_execution_at
FROM purchase_executions
WHERE status = ANY($1)
ORDER BY scheduled_date ASC NULLS LAST, id ASC
Expand Down
29 changes: 26 additions & 3 deletions internal/config/store_postgres_pgxmock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,7 @@ func TestPGXMock_GetPlannedExecutions_ProjectsAllScanColumns(t *testing.T) {
"executed_by_user_id", "executed_at", "pre_approval_skip_reason",
"idempotency_key", "scheduled_execution_at",
}
schedAt := now.Add(2 * time.Hour)
rows := pgxmock.NewRows(cols).AddRow(
"plan-1", "exec-1", "pending", 1, now,
sql.NullTime{}, "tok-123", recsJSON,
Expand All @@ -686,16 +687,38 @@ func TestPGXMock_GetPlannedExecutions_ProjectsAllScanColumns(t *testing.T) {
nil, sql.NullTime{}, nil,
"idem-key-planned",
sql.NullTime{}, // scheduled_execution_at (NULL: not on the pre-fire delay path)
).AddRow(
"plan-1", "exec-2", "pending", 1, now,
sql.NullTime{}, "tok-456", recsJSON,
100.0, 200.0, sql.NullTime{}, "", sql.NullTime{},
nil, "", nil, nil, 100,
nil, nil, 0,
sql.NullTime{},
nil, sql.NullTime{}, nil,
"idem-key-delayed",
sql.NullTime{Time: schedAt, Valid: true}, // scheduled_execution_at populated (pre-fire delay path)
)
// Regexp matcher: only matches if the issued SELECT projects idempotency_key.
mock.ExpectQuery("idempotency_key").
// Regexp matcher: only matches if the issued SELECT projects both
// idempotency_key and scheduled_execution_at. The alternation forces both
// names to appear; a projection missing either column fails to match, the
// mock returns no rows, and the test catches the column-count drift.
mock.ExpectQuery(`idempotency_key.*scheduled_execution_at|scheduled_execution_at.*idempotency_key`).
WithArgs(pgxmock.AnyArg(), pgxmock.AnyArg()).
WillReturnRows(rows)

execs, err := store.GetPlannedExecutions(ctx, []string{"pending"}, 10)
require.NoError(t, err)
require.Len(t, execs, 1)
require.Len(t, execs, 2)
assert.Equal(t, "idem-key-planned", execs[0].IdempotencyKey)
// NULL scheduled_execution_at must deserialise as nil (*time.Time), not a zero
// value; applyNullTimesToExecution only sets the pointer when Valid is true.
assert.Nil(t, execs[0].ScheduledExecutionAt, "NULL scheduled_execution_at must be nil, not zero time")
// Non-NULL scheduled_execution_at must round-trip into the pointer field. This
// is the direct regression guard for the fix: with the column absent from the
// SELECT projection the value never reaches ScheduledExecutionAt and every
// delayed execution reads back as unscheduled.
require.NotNil(t, execs[1].ScheduledExecutionAt, "populated scheduled_execution_at must round-trip, not be dropped")
assert.Equal(t, schedAt, *execs[1].ScheduledExecutionAt)
assert.NoError(t, mock.ExpectationsWereMet())
}

Expand Down
Loading