diff --git a/docs/README.md b/docs/README.md index 7763c11..af11eaa 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,6 +23,8 @@ The dry-run mode generates a synthetic, hardware-free frame summary based on the current config. It is intended as a no-hardware smoke path for the CLI and config/control-adjacent logic. +The HTTP control plane also exposes `GET /dry-run` for quick inspection of the currently effective no-hardware summary. + ## Offline generation `cmd/offline` generates a deterministic no-hardware IQ/composite-style file using the repository's output backend path. diff --git a/internal/control/control.go b/internal/control/control.go index 1e79adf..e8ca1ab 100644 --- a/internal/control/control.go +++ b/internal/control/control.go @@ -3,11 +3,14 @@ package control import ( "encoding/json" "net/http" + "sync" "github.com/jan/fm-rds-tx/internal/config" + drypkg "github.com/jan/fm-rds-tx/internal/dryrun" ) type Server struct { + mu sync.RWMutex cfg config.Config } @@ -17,6 +20,7 @@ func (s *Server) Handler() http.Handler { mux := http.NewServeMux() mux.HandleFunc("/healthz", s.handleHealth) mux.HandleFunc("/status", s.handleStatus) + mux.HandleFunc("/dry-run", s.handleDryRun) return mux } @@ -26,12 +30,25 @@ func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) { } func (s *Server) handleStatus(w http.ResponseWriter, _ *http.Request) { + s.mu.RLock() + cfg := s.cfg + s.mu.RUnlock() + w.Header().Set("Content-Type", "application/json") _ = json.NewEncoder(w).Encode(map[string]any{ "service": "fm-rds-tx", - "backend": s.cfg.Backend.Kind, - "frequencyMHz": s.cfg.FM.FrequencyMHz, - "stereoEnabled": s.cfg.FM.StereoEnabled, - "rdsEnabled": s.cfg.RDS.Enabled, + "backend": cfg.Backend.Kind, + "frequencyMHz": cfg.FM.FrequencyMHz, + "stereoEnabled": cfg.FM.StereoEnabled, + "rdsEnabled": cfg.RDS.Enabled, }) } + +func (s *Server) handleDryRun(w http.ResponseWriter, _ *http.Request) { + s.mu.RLock() + cfg := s.cfg + s.mu.RUnlock() + + w.Header().Set("Content-Type", "application/json") + _ = json.NewEncoder(w).Encode(drypkg.Generate(cfg)) +} diff --git a/internal/control/control_test.go b/internal/control/control_test.go index 92c77bd..16f4259 100644 --- a/internal/control/control_test.go +++ b/internal/control/control_test.go @@ -35,3 +35,20 @@ func TestStatus(t *testing.T) { t.Fatalf("unexpected service: %v", body["service"]) } } + +func TestDryRunEndpoint(t *testing.T) { + srv := NewServer(cfgpkg.Default()) + req := httptest.NewRequest(http.MethodGet, "/dry-run", nil) + rec := httptest.NewRecorder() + srv.Handler().ServeHTTP(rec, req) + if rec.Code != http.StatusOK { + t.Fatalf("unexpected status: %d", rec.Code) + } + var body map[string]any + if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { + t.Fatalf("decode body: %v", err) + } + if body["mode"] != "dry-run" { + t.Fatalf("unexpected mode: %v", body["mode"]) + } +}