package routes_test import ( "net/http" "strings" "testing" ) // Reverse logistics (RTO) gates, over real HTTP. Refusals happen in // middleware (or before any query), so no database is needed. var rtoWrites = []struct{ method, path, body string }{ {http.MethodPost, "/api/v1/admin/consignments/5/rto", `{"reason":"receiver_refused"}`}, {http.MethodPost, "/api/v1/admin/consignments/5/rto/cancel", `{}`}, {http.MethodPost, "/api/v1/admin/consignments/5/rto/complete", `{}`}, } func TestRTONeedsALogin(t *testing.T) { app := newApp() for _, w := range rtoWrites { if code, _ := do(t, app, w.method, w.path, "", w.body); code != http.StatusUnauthorized { t.Errorf("%s %s with no token = %d, want 401", w.method, w.path, code) } } if code, _ := do(t, app, http.MethodGet, "/api/v1/admin/returns", "", ""); code != http.StatusUnauthorized { t.Errorf("GET /admin/returns with no token = %d, want 401", code) } } // A client login may read its returns but never start, cancel or close one. func TestRTOActionsAreDoormileStaffOnly(t *testing.T) { app := newApp() client := tenantToken(t, 50, 1, 7) for _, w := range rtoWrites { code, body := do(t, app, w.method, w.path, client, w.body) if code != http.StatusForbidden || !strings.Contains(body, "Doormile staff only") { t.Errorf("client %s %s = %d %s, want 403 staff only", w.method, w.path, code, body) } } } func TestRTORefusesNonConsoleRoles(t *testing.T) { app := newApp() for _, role := range []int{5, 6, 9} { for _, w := range rtoWrites { if code, _ := do(t, app, w.method, w.path, token(t, 1, role), w.body); code != http.StatusForbidden { t.Errorf("role %d %s %s = %d, want 403", role, w.method, w.path, code) } } } } // Staff pass every gate (no database here, so the handler's first query // panics and recover answers 500 — proof nothing in front of it refused). // A missing reason is refused before any query. func TestRTOStaffPassTheGate(t *testing.T) { app := newApp() staff := token(t, 1, 1) for _, w := range rtoWrites { if code, _ := do(t, app, w.method, w.path, staff, w.body); code == 401 || code == 403 || code == 404 { t.Errorf("staff %s %s = %d; a gate refused or the route is missing", w.method, w.path, code) } } if code, body := do(t, app, http.MethodPost, "/api/v1/admin/consignments/5/rto", staff, `{"reason":"teleported"}`); code != http.StatusBadRequest { t.Errorf("unknown reason = %d %s, want 400", code, body) } if code, body := do(t, app, http.MethodPost, "/api/v1/admin/consignments/5/rto", staff, `{"reason":"other"}`); code != http.StatusBadRequest { t.Errorf("other without a note = %d %s, want 400", code, body) } if code, _ := do(t, app, http.MethodGet, "/api/v1/admin/returns?status=bogus", staff, ""); code != http.StatusBadRequest { t.Errorf("bad status filter = %d, want 400", code) } } // The rider endpoint stays shut until MILER_RTO_FLOW_ENABLED=true — the // deployed rider app does not know returns yet. func TestRiderReturnIsOffByDefault(t *testing.T) { t.Setenv("MILER_RTO_FLOW_ENABLED", "") app := newApp() code, body := do(t, app, http.MethodPost, "/api/v1/miler/consignments/5/return-complete", token(t, 9, 5), `{}`) if code != http.StatusForbidden || !strings.Contains(body, "RTO_FLOW_DISABLED") { t.Fatalf("flag off = %d %s, want 403 RTO_FLOW_DISABLED", code, body) } if code, _ := do(t, app, http.MethodPost, "/api/v1/miler/consignments/5/return-complete", token(t, 1, 1), `{}`); code != http.StatusForbidden { t.Fatalf("a console token on the rider route = %d, want 403", code) } }