Files
doormile_backend/routes/routes_rto_test.go

88 lines
3.5 KiB
Go

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)
}
}