updates on the admincontroller and the milercontroller and changes in the creaet single order calculations as well
This commit is contained in:
@@ -396,6 +396,7 @@ func RegisterRoutes(app *fiber.App, cfg *config.Config) {
|
||||
adminAuth.Get("/milers/:id/activity", controllers.GetMilerActivity)
|
||||
adminAuth.Put("/milers/:id", controllers.UpdateMiler)
|
||||
adminAuth.Put("/milers/:id/block", controllers.BlockMiler)
|
||||
adminAuth.Put("/milers/:id/unblock", controllers.UnblockMiler)
|
||||
adminAuth.Put("/milers/:id/assign-vehicle", controllers.AssignMilerVehicle)
|
||||
adminAuth.Post("/milers/:id/notify", controllers.AdminNotifyMiler)
|
||||
|
||||
|
||||
223
routes/routes_miler_account_pg_test.go
Normal file
223
routes/routes_miler_account_pg_test.go
Normal file
@@ -0,0 +1,223 @@
|
||||
package routes_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"doormile/constants"
|
||||
"doormile/db"
|
||||
"doormile/internal/testpg"
|
||||
"doormile/models"
|
||||
"doormile/utils"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Rider accounts end to end against a real Postgres: create-time checks,
|
||||
// duplicate phones, editing the phone, and block / unblock actually holding.
|
||||
// Postgres-gated; the DSN must be a THROWAWAY database.
|
||||
|
||||
func milerAccountDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
dsn := os.Getenv("REGISTRY_TEST_DSN")
|
||||
if dsn == "" {
|
||||
t.Skip("REGISTRY_TEST_DSN not set; skipping Postgres miler account test")
|
||||
}
|
||||
gdb := testpg.Open(t, dsn, "miler_account_routes_test")
|
||||
all := []any{&models.AppUser{}, &models.MilerProfile{}, &models.MilerDutyLog{}, &models.Hub{}, &models.AppLocation{}, &models.Tenant{}}
|
||||
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 })
|
||||
|
||||
gdb.Create(&models.AppLocation{Applocationid: 1, Applocationname: "Coimbatore", Status: "Active"})
|
||||
gdb.Create(&models.AppLocation{Applocationid: 2, Applocationname: "Hyderabad", Status: "Active"})
|
||||
gdb.Create(&models.Hub{Hubid: 11, Hubname: "Coimbatore Neptune Hub", Hubtype: "delivery_hub", Applocationid: 1, Status: "Active"})
|
||||
gdb.Create(&models.Hub{Hubid: 21, Hubname: "Hyderabad Mars Hub", Hubtype: "delivery_hub", Applocationid: 2, Status: "Active"})
|
||||
gdb.Create(&models.Tenant{Tenantid: 5, Tenantname: "Sai's Kitchen", Primaryemail: "sai@k.test", Primarycontact: "9000000005", Status: "Active"})
|
||||
// A client login whose email a rider must not reuse.
|
||||
gdb.Create(&models.AppUser{Authname: "Sai", Email: "sai@k.test", Contactno: "9000000005", Password: "x", Roleid: 3, Status: "Active", Configid: 1})
|
||||
return gdb
|
||||
}
|
||||
|
||||
func riderBody(name, phone, email, extra string) string {
|
||||
return fmt.Sprintf(`{"authname":%q,"displayname":%q,"contactno":%q,"email":%q,"tenantid":5,"applocationid":1%s}`,
|
||||
name, name, phone, email, extra)
|
||||
}
|
||||
|
||||
func TestCreateMilerChecksEverythingAndRefusesDuplicates(t *testing.T) {
|
||||
gdb := milerAccountDB(t)
|
||||
app := onboardingApp()
|
||||
staff := consoleToken(t, "ops@doormile.com", 1, 0)
|
||||
|
||||
refused := []struct{ name, body, want string }{
|
||||
{"bad phone", riderBody("Ravi", "12345", "ravi@r.test", ""), "10-digit"},
|
||||
{"no email", riderBody("Ravi", "9876500001", "", ""), "valid email"},
|
||||
{"no name", riderBody("", "9876500001", "ravi@r.test", ""), "login name"},
|
||||
{"unknown vehicle", riderBody("Ravi", "9876500001", "ravi@r.test", `,"defaultvehicletype":"Rocket"`), "vehicle type"},
|
||||
{"hub in another city", riderBody("Ravi", "9876500001", "ravi@r.test", `,"hubid":21`), "different city"},
|
||||
{"unknown client", strings.Replace(riderBody("Ravi", "9876500001", "ravi@r.test", ""), `"tenantid":5`, `"tenantid":99`, 1), "client does not exist"},
|
||||
{"email used by a client login", riderBody("Ravi", "9876500001", "SAI@k.test", ""), "already used by another login"},
|
||||
}
|
||||
for _, r := range refused {
|
||||
code, body := do(t, app, http.MethodPost, "/api/v1/admin/milers", staff, r.body)
|
||||
if code < 400 || code >= 500 || !strings.Contains(body, r.want) {
|
||||
t.Errorf("%s: %d %s, want a 4xx containing %q", r.name, code, body, r.want)
|
||||
}
|
||||
}
|
||||
|
||||
// A good rider, typed the way people type numbers, with a lower-case vehicle.
|
||||
code, body := do(t, app, http.MethodPost, "/api/v1/admin/milers", staff,
|
||||
riderBody("Ravi", "+91 98765 00001", "Ravi@R.test", `,"defaultvehicletype":"bike","hubid":11`))
|
||||
if code != 201 {
|
||||
t.Fatalf("create = %d %s", code, body)
|
||||
}
|
||||
var u models.AppUser
|
||||
gdb.Where("roleid = 5").First(&u)
|
||||
var p models.MilerProfile
|
||||
gdb.Where("userid = ?", u.Userid).First(&p)
|
||||
if u.Contactno != "9876500001" || u.Email != "ravi@r.test" || u.Configid != 1001 || p.Defaultvehicletype != "Bike" || p.Phone != "9876500001" {
|
||||
t.Fatalf("stored rider = %+v / %+v", u, p)
|
||||
}
|
||||
|
||||
// The same number again, however it is written, is refused.
|
||||
code, body = do(t, app, http.MethodPost, "/api/v1/admin/milers", staff, riderBody("Ravi Two", "098765 00001", "ravi2@r.test", ""))
|
||||
if code != 409 || !strings.Contains(body, "phone number already exists") {
|
||||
t.Fatalf("duplicate phone = %d %s, want 409", code, body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMilerLoginPrefersTheActiveRiderWhenAPhoneIsShared(t *testing.T) {
|
||||
gdb := milerAccountDB(t)
|
||||
app := onboardingApp()
|
||||
// Rows from before duplicates were refused: an old blocked rider first.
|
||||
gdb.Create(&models.AppUser{Authname: "Old", Email: "old@r.test", Contactno: "9876500002", Password: "", Roleid: 5, Status: "Blocked", Configid: 1001})
|
||||
gdb.Create(&models.AppUser{Authname: "New", Email: "new@r.test", Contactno: "9876500002", Password: "", Roleid: 5, Status: "Active", Configid: 1001})
|
||||
|
||||
code, body := do(t, app, http.MethodPost, "/api/v1/miler/login", "", `{"phone":"9876500002"}`)
|
||||
if code != 200 || !strings.Contains(body, `"pin_set":false`) {
|
||||
t.Fatalf("login = %d %s, want the active rider found", code, body)
|
||||
}
|
||||
|
||||
// Two Active rows: the rider's real account (has a PIN) and a later
|
||||
// PIN-less duplicate. The real one must win, or verify-pin says
|
||||
// "incorrect PIN" and set-pin could claim the duplicate.
|
||||
hash, _ := utils.HashPassword("1234")
|
||||
gdb.Create(&models.AppUser{Authname: "Real", Email: "real@r.test", Contactno: "9876500009", Password: hash, Roleid: 5, Status: "Active", Configid: 1001})
|
||||
gdb.Create(&models.AppUser{Authname: "Dup", Email: "dup@r.test", Contactno: "9876500009", Password: "", Roleid: 5, Status: "Active", Configid: 1001})
|
||||
code, body = do(t, app, http.MethodPost, "/api/v1/miler/login", "", `{"phone":"9876500009"}`)
|
||||
if code != 200 || !strings.Contains(body, `"pin_set":true`) {
|
||||
t.Fatalf("login with a PIN-less duplicate = %d %s, want the real account (pin_set true)", code, body)
|
||||
}
|
||||
if code, body := do(t, app, http.MethodPost, "/api/v1/miler/set-pin", "", `{"phone":"9876500009","new_pin":"9999"}`); code != 409 {
|
||||
t.Fatalf("set-pin on a phone whose real account has a PIN = %d %s, want 409", code, body)
|
||||
}
|
||||
|
||||
// A phone that belongs only to a blocked rider says so.
|
||||
gdb.Create(&models.AppUser{Authname: "Gone", Email: "gone@r.test", Contactno: "9876500003", Password: "", Roleid: 5, Status: "Blocked", Configid: 1001})
|
||||
code, body = do(t, app, http.MethodPost, "/api/v1/miler/login", "", `{"phone":"9876500003"}`)
|
||||
if code != 403 || !strings.Contains(body, "blocked") {
|
||||
t.Fatalf("blocked login = %d %s, want 403 naming the block", code, body)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockHoldsForASignedInRiderAndUnblockLiftsIt(t *testing.T) {
|
||||
gdb := milerAccountDB(t)
|
||||
app := onboardingApp()
|
||||
staff := consoleToken(t, "ops@doormile.com", 1, 0)
|
||||
|
||||
if code, body := do(t, app, http.MethodPost, "/api/v1/admin/milers", staff, riderBody("Nagalakshmi", "9876500004", "naga@r.test", "")); code != 201 {
|
||||
t.Fatalf("create = %d %s", code, body)
|
||||
}
|
||||
var u models.AppUser
|
||||
gdb.Where("contactno = ?", "9876500004").First(&u)
|
||||
var p models.MilerProfile
|
||||
gdb.Where("userid = ?", u.Userid).First(&p)
|
||||
// The token she already holds from before the block.
|
||||
rider, err := utils.GenerateToken(u.Userid, u.Email, 5, 0, 1001, jwtSecret)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if code, body := do(t, app, http.MethodPut, fmt.Sprintf("/api/v1/admin/milers/%d/block", p.Milerprofileid), staff, `{}`); code != 200 {
|
||||
t.Fatalf("block = %d %s", code, body)
|
||||
}
|
||||
if code, body := do(t, app, http.MethodPost, "/api/v1/miler/duty/start", rider, `{"lat":0,"lon":0}`); code != 403 || !strings.Contains(body, "blocked") {
|
||||
t.Fatalf("blocked rider start duty = %d %s, want 403", code, body)
|
||||
}
|
||||
if code, _ := do(t, app, http.MethodPut, "/api/v1/miler/availability", rider, `{"status":"Available"}`); code != 403 {
|
||||
t.Fatalf("blocked rider set Available = %d, want 403", code)
|
||||
}
|
||||
gdb.Where("userid = ?", u.Userid).First(&p)
|
||||
if p.Availabilitystatus != constants.MilerBlocked {
|
||||
t.Fatalf("after the rider's attempts status = %q, want still Blocked", p.Availabilitystatus)
|
||||
}
|
||||
|
||||
// Unblock: Offline, can sign in and start duty again.
|
||||
if code, body := do(t, app, http.MethodPut, fmt.Sprintf("/api/v1/admin/milers/%d/unblock", p.Milerprofileid), staff, `{}`); code != 200 {
|
||||
t.Fatalf("unblock = %d %s", code, body)
|
||||
}
|
||||
gdb.Where("userid = ?", u.Userid).First(&p)
|
||||
gdb.Where("userid = ?", u.Userid).First(&u)
|
||||
if p.Availabilitystatus != constants.MilerOffline || u.Status != "Active" {
|
||||
t.Fatalf("after unblock: profile %q, account %q", p.Availabilitystatus, u.Status)
|
||||
}
|
||||
if code, body := do(t, app, http.MethodPost, "/api/v1/miler/duty/start", rider, `{"lat":0,"lon":0}`); code != 200 {
|
||||
t.Fatalf("start duty after unblock = %d %s", code, body)
|
||||
}
|
||||
if code, _ := do(t, app, http.MethodPut, fmt.Sprintf("/api/v1/admin/milers/%d/unblock", p.Milerprofileid), staff, `{}`); code != 400 {
|
||||
t.Fatalf("unblocking a rider who is not blocked = %d, want 400", code)
|
||||
}
|
||||
// A rider cannot block themselves either.
|
||||
if code, _ := do(t, app, http.MethodPut, "/api/v1/miler/availability", rider, `{"status":"Blocked"}`); code != 400 {
|
||||
t.Fatalf("rider setting Blocked = %d, want 400", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEditingAMilerCanFixThePhoneAndKeepsTheHubInTheirCity(t *testing.T) {
|
||||
gdb := milerAccountDB(t)
|
||||
app := onboardingApp()
|
||||
staff := consoleToken(t, "ops@doormile.com", 1, 0)
|
||||
for i, phone := range []string{"9876500005", "9876500006"} {
|
||||
if code, body := do(t, app, http.MethodPost, "/api/v1/admin/milers", staff, riderBody(fmt.Sprintf("R%d", i), phone, fmt.Sprintf("r%d@r.test", i), "")); code != 201 {
|
||||
t.Fatalf("create = %d %s", code, body)
|
||||
}
|
||||
}
|
||||
var p models.MilerProfile
|
||||
gdb.Where("phone = ?", "9876500005").First(&p)
|
||||
url := fmt.Sprintf("/api/v1/admin/milers/%d", p.Milerprofileid)
|
||||
|
||||
if code, _ := do(t, app, http.MethodPut, url, staff, `{"contactno":"9876500006"}`); code != 409 {
|
||||
t.Fatalf("editing to another rider's phone = %d, want 409", code)
|
||||
}
|
||||
if code, _ := do(t, app, http.MethodPut, url, staff, `{"hubid":21}`); code != 400 {
|
||||
t.Fatalf("editing to a hub in another city = %d, want 400", code)
|
||||
}
|
||||
// An older rider whose CURRENT hub is in another city can still be edited
|
||||
// (the form sends the unchanged hub back on every save).
|
||||
gdb.Model(&models.MilerProfile{}).Where("milerprofileid = ?", p.Milerprofileid).Update("hubid", 21)
|
||||
if code, body := do(t, app, http.MethodPut, url, staff, `{"displayname":"Renamed","hubid":21}`); code != 200 {
|
||||
t.Fatalf("editing a legacy rider with an unchanged out-of-city hub = %d %s, want 200", code, body)
|
||||
}
|
||||
if code, body := do(t, app, http.MethodPut, url, staff, `{"contactno":"+91 98765 00007","hubid":11}`); code != 200 {
|
||||
t.Fatalf("edit = %d %s", code, body)
|
||||
}
|
||||
var u models.AppUser
|
||||
gdb.Where("userid = ?", p.Userid).First(&u)
|
||||
gdb.Where("userid = ?", p.Userid).First(&p)
|
||||
if u.Contactno != "9876500007" || p.Phone != "9876500007" || u.Hubid == nil || *u.Hubid != 11 {
|
||||
t.Fatalf("after edit: account %q hub %v, profile %q", u.Contactno, u.Hubid, p.Phone)
|
||||
}
|
||||
// The rider signs in with the corrected number.
|
||||
if code, body := do(t, app, http.MethodPost, "/api/v1/miler/login", "", `{"phone":"9876500007"}`); code != 200 {
|
||||
t.Fatalf("login with the new phone = %d %s", code, body)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user