package main import ( "context" "database/sql" "errors" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) func TestCustomerHydrogenJobClaimUsesTransactionAndOwner(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } defer db.Close() mock.ExpectBegin() mock.ExpectQuery("SELECT DATE_FORMAT.*FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"date"}).AddRow("2026-09-29")) mock.ExpectExec("UPDATE platform_customer_hydrogen_job").WithArgs(sqlmock.AnyArg(), "2026-09-29").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() date, owner, err := claimCustomerHydrogenDay(context.Background(), db) if err != nil || date != "2026-09-29" || len(owner) != 32 { t.Fatalf("invalid claim %s %s %v", date, owner, err) } if err = mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestCustomerHydrogenBoundedWorkerPublishesOneJobThenExits(t *testing.T) { t.Setenv("CUSTOMER_HYDROGEN_MAX_JOBS", "1") db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } defer db.Close() td, raw, err := sqlmock.New() if err != nil { t.Fatal(err) } defer td.Close() mock.ExpectBegin() mock.ExpectQuery("SELECT DATE_FORMAT.*FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"date"}).AddRow("2026-09-29")) mock.ExpectExec("UPDATE platform_customer_hydrogen_job").WithArgs(sqlmock.AnyArg(), "2026-09-29").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() raw.ExpectQuery("SELECT").WillReturnRows(sqlmock.NewRows([]string{"event_id"})) mock.ExpectBegin() mock.ExpectQuery("SELECT 1.*FOR UPDATE").WithArgs("2026-09-29", sqlmock.AnyArg()).WillReturnRows(sqlmock.NewRows([]string{"valid"}).AddRow(1)) mock.ExpectCommit() mock.ExpectExec("UPDATE platform_customer_hydrogen_job SET status").WithArgs("complete", "", "complete", "2026-09-29", sqlmock.AnyArg()).WillReturnResult(sqlmock.NewResult(0, 1)) ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() err = runCustomerHydrogenWorker(ctx, db, td, config{Location: time.UTC, TDengineDatabase: "vehicle_ingest", Protocols: []envelope.Protocol{envelope.ProtocolGB32960}}) if err != nil || ctx.Err() != nil { t.Fatalf("bounded worker did not exit after publication: %v / %v", err, ctx.Err()) } if err = mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } if err = raw.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestCustomerHydrogenStaleWorkerCannotPublish(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } defer db.Close() mock.ExpectBegin() mock.ExpectQuery("SELECT 1.*lease_until>NOW.*FOR UPDATE").WithArgs("2026-09-29", "old-owner").WillReturnRows(sqlmock.NewRows([]string{"valid"})) mock.ExpectRollback() days := map[string]*customerHydrogenDay{"sample": {VIN: "VIN", Date: "2026-09-29", HydrogenKM: 10}} if err = publishCustomerHydrogenDays(context.Background(), db, days, "2026-09-29", "old-owner"); !errors.Is(err, sql.ErrNoRows) { t.Fatalf("stale worker must fail before writing: %v", err) } if err = mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestCustomerHydrogenPublicationLocksCurrentLease(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } defer db.Close() mock.ExpectBegin() mock.ExpectQuery("SELECT 1.*lease_until>NOW.*FOR UPDATE").WithArgs("2026-09-29", "current-owner").WillReturnRows(sqlmock.NewRows([]string{"valid"}).AddRow(1)) mock.ExpectExec("INSERT INTO platform_customer_hydrogen_daily").WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectCommit() days := map[string]*customerHydrogenDay{"sample": {VIN: "VIN", Date: "2026-09-29", HydrogenKM: 10}} if err = publishCustomerHydrogenDays(context.Background(), db, days, "2026-09-29", "current-owner"); err != nil { t.Fatal(err) } if err = mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestCustomerHydrogenIdleWorkerDoesNotCreateJobs(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatal(err) } defer db.Close() mock.ExpectBegin() mock.ExpectQuery("SELECT DATE_FORMAT.*FOR UPDATE").WillReturnRows(sqlmock.NewRows([]string{"date"})) mock.ExpectRollback() if _, _, err = claimCustomerHydrogenDay(context.Background(), db); !errors.Is(err, sql.ErrNoRows) { t.Fatal(err) } if err = mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } }