feat: fix workout taxonomy to 7 types, add pace/HR-zone fields to the API
This commit is contained in:
@@ -58,51 +58,69 @@ func TestHealth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkoutKindCRUD(t *testing.T) {
|
||||
func TestWorkoutKindList_ReturnsSevenSeededTypesWithPaceFields(t *testing.T) {
|
||||
s, _ := newTestServer(t)
|
||||
router := s.Router()
|
||||
|
||||
createBody := map[string]any{
|
||||
"name": "Tempo",
|
||||
"rule": json.RawMessage(`{"match":"all","conditions":[{"metric":"avg_pace_sec_per_km","op":"between","value":[270,300]}]}`),
|
||||
}
|
||||
rec := doJSON(t, router, http.MethodPost, "/api/workout-kinds/", createBody)
|
||||
if rec.Code != http.StatusCreated {
|
||||
t.Fatalf("create status = %d, body = %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
var created store.WorkoutKind
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &created); err != nil {
|
||||
t.Fatalf("unmarshal created kind: %v", err)
|
||||
}
|
||||
if created.ID == 0 {
|
||||
t.Fatal("expected non-zero id")
|
||||
}
|
||||
|
||||
rec = doJSON(t, router, http.MethodGet, "/api/workout-kinds/", nil)
|
||||
rec := doJSON(t, s.Router(), http.MethodGet, "/api/workout-kinds/", nil)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("list status = %d", rec.Code)
|
||||
t.Fatalf("status = %d", rec.Code)
|
||||
}
|
||||
var kinds []store.WorkoutKind
|
||||
json.Unmarshal(rec.Body.Bytes(), &kinds)
|
||||
if len(kinds) != 1 {
|
||||
t.Fatalf("expected 1 kind, got %d", len(kinds))
|
||||
var kinds []workoutKindResponse
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &kinds); err != nil {
|
||||
t.Fatalf("unmarshal: %v", err)
|
||||
}
|
||||
|
||||
rec = doJSON(t, router, http.MethodDelete, "/api/workout-kinds/"+itoa(created.ID), nil)
|
||||
if rec.Code != http.StatusNoContent {
|
||||
t.Fatalf("delete status = %d", rec.Code)
|
||||
if len(kinds) != 7 {
|
||||
t.Fatalf("expected 7 seeded kinds, got %d", len(kinds))
|
||||
}
|
||||
rec = doJSON(t, router, http.MethodGet, "/api/workout-kinds/", nil)
|
||||
json.Unmarshal(rec.Body.Bytes(), &kinds)
|
||||
if len(kinds) != 0 {
|
||||
t.Fatalf("expected 0 active kinds after soft delete, got %d", len(kinds))
|
||||
for _, k := range kinds {
|
||||
if k.PaceMinSecPerKm != nil || k.PaceMaxSecPerKm != nil || k.ExpectedHRZone != nil {
|
||||
t.Errorf("expected freshly-seeded kind %q to have nil pace fields, got %+v", k.Name, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkoutKindCreate_RejectsInvalidRule(t *testing.T) {
|
||||
func TestWorkoutKindUpdate_SetsRuleAndPace(t *testing.T) {
|
||||
s, _ := newTestServer(t)
|
||||
rec := doJSON(t, s.Router(), http.MethodPost, "/api/workout-kinds/", map[string]any{
|
||||
"name": "Bad",
|
||||
router := s.Router()
|
||||
|
||||
rec := doJSON(t, router, http.MethodGet, "/api/workout-kinds/", nil)
|
||||
var kinds []workoutKindResponse
|
||||
json.Unmarshal(rec.Body.Bytes(), &kinds)
|
||||
target := kinds[0]
|
||||
|
||||
minPace, maxPace, zone := 330.0, 420.0, 2
|
||||
body := map[string]any{
|
||||
"name": target.Name,
|
||||
"rule": json.RawMessage(`{"match":"all","conditions":[{"metric":"avg_pace_sec_per_km","op":"between","value":[270,300]}]}`),
|
||||
"pace_min_sec_per_km": minPace,
|
||||
"pace_max_sec_per_km": maxPace,
|
||||
"expected_hr_zone": zone,
|
||||
}
|
||||
rec = doJSON(t, router, http.MethodPut, "/api/workout-kinds/"+itoa(target.ID), body)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("update status = %d, body = %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
|
||||
rec = doJSON(t, router, http.MethodGet, "/api/workout-kinds/"+itoa(target.ID), nil)
|
||||
var updated workoutKindResponse
|
||||
json.Unmarshal(rec.Body.Bytes(), &updated)
|
||||
if updated.PaceMinSecPerKm == nil || *updated.PaceMinSecPerKm != 330 {
|
||||
t.Errorf("PaceMinSecPerKm = %v, want 330", updated.PaceMinSecPerKm)
|
||||
}
|
||||
if updated.ExpectedHRZone == nil || *updated.ExpectedHRZone != 2 {
|
||||
t.Errorf("ExpectedHRZone = %v, want 2", updated.ExpectedHRZone)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkoutKindUpdate_RejectsInvalidRule(t *testing.T) {
|
||||
s, _ := newTestServer(t)
|
||||
router := s.Router()
|
||||
|
||||
rec := doJSON(t, router, http.MethodGet, "/api/workout-kinds/", nil)
|
||||
var kinds []workoutKindResponse
|
||||
json.Unmarshal(rec.Body.Bytes(), &kinds)
|
||||
|
||||
rec = doJSON(t, router, http.MethodPut, "/api/workout-kinds/"+itoa(kinds[0].ID), map[string]any{
|
||||
"name": kinds[0].Name,
|
||||
"rule": json.RawMessage(`{"match":"xor","conditions":[]}`),
|
||||
})
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
@@ -110,6 +128,24 @@ func TestWorkoutKindCreate_RejectsInvalidRule(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWorkoutKindUpdate_RejectsInvalidHRZone(t *testing.T) {
|
||||
s, _ := newTestServer(t)
|
||||
router := s.Router()
|
||||
|
||||
rec := doJSON(t, router, http.MethodGet, "/api/workout-kinds/", nil)
|
||||
var kinds []workoutKindResponse
|
||||
json.Unmarshal(rec.Body.Bytes(), &kinds)
|
||||
|
||||
rec = doJSON(t, router, http.MethodPut, "/api/workout-kinds/"+itoa(kinds[0].ID), map[string]any{
|
||||
"name": kinds[0].Name,
|
||||
"rule": json.RawMessage(`{"match":"all","conditions":[]}`),
|
||||
"expected_hr_zone": 9,
|
||||
})
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Fatalf("status = %d, want 400, body = %s", rec.Code, rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReviewQueueResolve(t *testing.T) {
|
||||
s, db := newTestServer(t)
|
||||
ctx := newCtx()
|
||||
|
||||
Reference in New Issue
Block a user