package main import ( "context" "database/sql" "regexp" "strings" "testing" "github.com/DATA-DOG/go-sqlmock" ) func TestDeliverySelectorFlagKeepsColonInsideEventID(t *testing.T) { var values deliverySelectorFlag if err := values.Set("wallet_outbox: FAMI :wallet:balance:evt-1"); err != nil { t.Fatalf("parse exact delivery selector: %v", err) } if len(values) != 1 || values[0].SourceName != walletSource || values[0].AppCode != "fami" || values[0].EventID != "wallet:balance:evt-1" { t.Fatalf("parsed selector mismatch: %+v", values) } if err := values.Set("unknown:lalu:event-2"); err == nil { t.Fatal("unsupported source must be rejected before any database access") } if err := values.Set("room_outbox::event-3"); err == nil { t.Fatal("empty app_code must not default to lalu") } } func TestLoadExactDueDeliveriesUsesFullPrimaryKey(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("create sqlmock: %v", err) } defer db.Close() nowMS := int64(1_700_000_000_000) query := regexp.QuoteMeta(` SELECT source_name, app_code, source_event_id, status, retry_count, updated_at_ms FROM notice_delivery_events FORCE INDEX (PRIMARY) WHERE source_name = ? AND app_code = ? AND source_event_id = ? AND channel = ? AND ((status = 'retryable' AND next_retry_at_ms <= ?) OR (status = 'delivering' AND lock_until_ms <= ?))`) mock.ExpectQuery(query). WithArgs(walletSource, "fami", "wallet:evt-1", c2cChannel, nowMS, nowMS). WillReturnRows(sqlmock.NewRows([]string{"source_name", "app_code", "source_event_id", "status", "retry_count", "updated_at_ms"}). AddRow(walletSource, "fami", "wallet:evt-1", "retryable", 2, nowMS-1000)) candidates, err := loadExactDueDeliveries(context.Background(), db, []deliverySelector{{SourceName: walletSource, AppCode: " FAMI ", EventID: "wallet:evt-1"}}, nowMS) if err != nil { t.Fatalf("load exact due delivery: %v", err) } if len(candidates) != 1 || candidates[0].Selector != "wallet_outbox:fami:wallet:evt-1" || candidates[0].RetryCount != 2 { t.Fatalf("exact candidate mismatch: %+v", candidates) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("exact query did not use the expected primary key: %v", err) } } func TestLoadExactDueDeliveriesRejectsDuplicateBeforeQuery(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("create sqlmock: %v", err) } defer db.Close() selectors := []deliverySelector{ {SourceName: roomSource, AppCode: "lalu", EventID: "evt-1"}, {SourceName: roomSource, AppCode: " LALU ", EventID: "evt-1"}, } if _, err := loadExactDueDeliveries(context.Background(), db, selectors, 1000); err == nil || !strings.Contains(err.Error(), "duplicate") { t.Fatalf("duplicate selector error = %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("duplicate validation unexpectedly queried MySQL: %v", err) } } func TestLoadExactDueDeliveriesRejectsNotDueRow(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("create sqlmock: %v", err) } defer db.Close() mock.ExpectQuery("FROM notice_delivery_events FORCE INDEX \\(PRIMARY\\)"). WillReturnError(sql.ErrNoRows) _, err = loadExactDueDeliveries(context.Background(), db, []deliverySelector{{SourceName: roomSource, AppCode: "lalu", EventID: "evt-1"}}, 1000) if err == nil || !strings.Contains(err.Error(), "no longer due") { t.Fatalf("not-due selector error = %v", err) } } func TestDueDeliveryBranchUsesCompleteRetryIndexPrefix(t *testing.T) { query, args := dueDeliveryBranch(walletSource, "huwaa", "idx_notice_delivery_retry", "retryable", "next_retry_at_ms", []string{"evt-1", "evt-2"}, 20, 1234) for _, fragment := range []string{ "FORCE INDEX (idx_notice_delivery_retry)", "source_name = ? AND app_code = ? AND channel = ? AND status = ?", "next_retry_at_ms <= ?", "source_event_id IN (?,?)", } { if !strings.Contains(query, fragment) { t.Fatalf("indexed preview query is missing %q:\n%s", fragment, query) } } wantArgs := []any{walletSource, "huwaa", c2cChannel, "retryable", int64(1234), "evt-1", "evt-2", 20} if len(args) != len(wantArgs) { t.Fatalf("preview args = %v, want %v", args, wantArgs) } for index := range args { if args[index] != wantArgs[index] { t.Fatalf("preview arg[%d] = %#v, want %#v", index, args[index], wantArgs[index]) } } }