package main import ( "bytes" "net/http" "net/http/httptest" "testing" "github.com/go-chi/chi/v5" "git.agentview.dev/profit/golangLAKEHOUSE/internal/observer" "git.agentview.dev/profit/golangLAKEHOUSE/internal/workflow" ) // newTestRouter builds the observerd router with an in-memory store // and a workflow runner with no modes registered. Closes R-005 for // observerd. func newTestRouter(t *testing.T) http.Handler { t.Helper() h := &handlers{ store: observer.NewStore(nil), runner: workflow.NewRunner(), } r := chi.NewRouter() h.register(r) return r } func TestRoutesMounted(t *testing.T) { r := newTestRouter(t) want := map[string]bool{ "GET /observer/stats": false, "POST /observer/event": false, "POST /observer/workflow/run": false, "GET /observer/workflow/modes": false, } router := r.(chi.Router) _ = chi.Walk(router, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error { key := method + " " + route if _, ok := want[key]; ok { want[key] = true } return nil }) for k, mounted := range want { if !mounted { t.Errorf("route not mounted: %s", k) } } } func TestStats_GET(t *testing.T) { r := newTestRouter(t) req := httptest.NewRequest("GET", "/observer/stats", nil) w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200, got %d", w.Code) } } func TestWorkflowModes_GET(t *testing.T) { r := newTestRouter(t) req := httptest.NewRequest("GET", "/observer/workflow/modes", nil) w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("expected 200, got %d", w.Code) } } // TestEvent_InvalidOp locks the validation path: an ObservedOp with // missing required fields must 400, not 500. Without this assertion, // observer.ErrInvalidOp could silently slip into the 500 branch on a // future refactor and clients would see "internal" instead of the // actual validation error. func TestEvent_InvalidOp(t *testing.T) { r := newTestRouter(t) // Empty body — no endpoint, no source — fails ObservedOp validation. body := []byte(`{}`) req := httptest.NewRequest("POST", "/observer/event", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400 on invalid op, got %d (body=%s)", w.Code, w.Body.String()) } } // TestWorkflowRun_UnknownMode locks the 400 path on workflow definitions // that reference modes not registered with the runner. The harness's // reality test runs depend on this so an unknown-mode misconfiguration // surfaces as a definition error, not a server error. func TestWorkflowRun_UnknownMode(t *testing.T) { r := newTestRouter(t) body := []byte(`{"workflow":{"name":"t","nodes":[{"id":"n1","mode":"does.not.exist"}]}}`) req := httptest.NewRequest("POST", "/observer/workflow/run", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() r.ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("expected 400 on unknown mode, got %d (body=%s)", w.Code, w.Body.String()) } }