package web import ( "encoding/json" "net/http" "net/http/httptest" "reflect" "strings" "testing" "mqtt_washingmachine/internal/status" ) func TestStatusAPI(t *testing.T) { for _, tt := range []struct { name, state, message string readings []status.Sample }{ {"startup", "waiting", "Waiting for data...", []status.Sample{}}, {"waiting", "waiting", "Waiting for data...", []status.Sample{{Power: 47.7, Valid: true}}}, {"incomplete", "incomplete", "Incomplete data", []status.Sample{{Power: 47.7, Valid: true}, {}, {Valid: true}}}, {"running", "running", "Laundry not yet done", []status.Sample{{Valid: true}, {Power: 3, Valid: true}, {Valid: true}}}, {"done", "done", "Laundry done", []status.Sample{{Power: 2.99, Valid: true}, {Valid: true}, {Valid: true}}}, } { t.Run(tt.name, func(t *testing.T) { handler := NewHandler(func() status.Result { return status.Evaluate(tt.readings) }) response := httptest.NewRecorder() handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/api/status", nil)) if response.Code != http.StatusOK { t.Fatalf("status = %d", response.Code) } if got := response.Header().Get("Content-Type"); got != "application/json" { t.Errorf("content type = %q", got) } if got := response.Header().Get("Cache-Control"); got != "no-store" { t.Errorf("cache control = %q", got) } var got status.Result if err := json.Unmarshal(response.Body.Bytes(), &got); err != nil { t.Fatal(err) } want := status.Result{State: tt.state, Message: tt.message, Readings: tt.readings} if !reflect.DeepEqual(got, want) { t.Fatalf("body = %+v; want %+v", got, want) } }) } } func TestPageAndRouting(t *testing.T) { handler := NewHandler(func() status.Result { return status.Evaluate(nil) }) page := httptest.NewRecorder() handler.ServeHTTP(page, httptest.NewRequest(http.MethodGet, "/", nil)) if page.Code != http.StatusOK || !strings.HasPrefix(page.Header().Get("Content-Type"), "text/html") { t.Fatalf("page response: %+v", page.Result()) } for _, text := range []string{`name="viewport"`, "Waiting for data...", "/api/status", "60000", "toFixed(2)", "--", "clamp("} { if !strings.Contains(page.Body.String(), text) { t.Errorf("page missing %q", text) } } for _, tt := range []struct { method, path string code int }{ {http.MethodGet, "/missing", http.StatusNotFound}, {http.MethodPost, "/api/status", http.StatusMethodNotAllowed}, {http.MethodPost, "/", http.StatusMethodNotAllowed}, } { response := httptest.NewRecorder() handler.ServeHTTP(response, httptest.NewRequest(tt.method, tt.path, nil)) if response.Code != tt.code { t.Errorf("%s %s = %d; want %d", tt.method, tt.path, response.Code, tt.code) } } } func TestStatusAPIReadsCurrentSnapshot(t *testing.T) { var window status.Window handler := NewHandler(func() status.Result { return status.Evaluate(window.Readings()) }) for range 3 { window.Add(status.Sample{Valid: true}) response := httptest.NewRecorder() handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, "/api/status", nil)) var got status.Result if err := json.Unmarshal(response.Body.Bytes(), &got); err != nil { t.Fatal(err) } if !reflect.DeepEqual(got.Readings, window.Readings()) { t.Fatal("handler returned stale readings") } } }