package routes_test import ( "net/http" "os" "strings" "testing" "time" "doormile/constants" "doormile/db" "doormile/internal/testpg" "doormile/models" "doormile/utils" ) // A rider whose duty from earlier today is still open (reinstall, crash, // signing in again) and who starts duty again. The server used to answer 400 // "already on duty" and leave the profile Offline, while the app showed the // rider online: the console listed them Offline and auto-assignment skipped // them. Postgres-gated; the DSN must be a THROWAWAY database. func TestStartDutyResumesAnOpenDutyAndMarksTheRiderAvailable(t *testing.T) { dsn := os.Getenv("REGISTRY_TEST_DSN") if dsn == "" { t.Skip("REGISTRY_TEST_DSN not set; skipping Postgres duty test") } gdb := testpg.Open(t, dsn, "miler_duty_routes_test") all := []any{&models.AppUser{}, &models.MilerProfile{}, &models.MilerDutyLog{}} if err := gdb.Migrator().DropTable(all...); err != nil { t.Fatal(err) } if err := gdb.AutoMigrate(all...); err != nil { t.Fatal(err) } prev := db.DB db.DB = gdb t.Cleanup(func() { db.DB = prev }) app := onboardingApp() rider := func(name, phone, status string) (models.AppUser, string) { u := models.AppUser{Authname: name, Email: name + "@riders.test", Contactno: phone, Password: "x", Roleid: 5, Status: "Active", Configid: 1001} if err := gdb.Create(&u).Error; err != nil { t.Fatal(err) } if err := gdb.Create(&models.MilerProfile{Userid: u.Userid, Displayname: name, Phone: phone, Availabilitystatus: status}).Error; err != nil { t.Fatal(err) } // The duty left open from earlier today. if err := gdb.Create(&models.MilerDutyLog{Userid: u.Userid, Loginat: time.Now(), Onduty: true}).Error; err != nil { t.Fatal(err) } tok, err := utils.GenerateToken(u.Userid, u.Email, 5, 0, 1001, jwtSecret) if err != nil { t.Fatal(err) } return u, tok } statusOf := func(uid int) string { var p models.MilerProfile gdb.Where("userid = ?", uid).First(&p) return p.Availabilitystatus } offline, tok := rider("nagalakshmi", "9000000101", constants.MilerOffline) code, body := do(t, app, http.MethodPost, "/api/v1/miler/duty/start", tok, `{"lat":0,"lon":0}`) if code != 200 || !strings.Contains(body, `"resumed":true`) { t.Fatalf("start duty with an open duty = %d %s, want 200 resumed", code, body) } if got := statusOf(offline.Userid); got != constants.MilerAvailable { t.Fatalf("rider status after resuming = %q, want Available", got) } var open int64 gdb.Model(&models.MilerDutyLog{}).Where("userid = ? AND logoutat IS NULL", offline.Userid).Count(&open) if open != 1 { t.Fatalf("open duty logs = %d, want the one resumed, not a second", open) } // A rider mid-delivery keeps that status; resuming only lifts Offline. busy, tok := rider("busy", "9000000102", constants.MilerOnDelivery) if code, body := do(t, app, http.MethodPost, "/api/v1/miler/duty/start", tok, `{"lat":0,"lon":0}`); code != 200 { t.Fatalf("start duty = %d %s", code, body) } if got := statusOf(busy.Userid); got != constants.MilerOnDelivery { t.Fatalf("on-delivery rider became %q, want On_Delivery kept", got) } }