Просмотр исходного кода

feat: expose dry-run summary via control api

tags/v0.3.0-pre
Jan Svabenik 1 месяц назад
Родитель
Сommit
dc32142cf2
3 измененных файлов: 40 добавлений и 4 удалений
  1. +2
    -0
      docs/README.md
  2. +21
    -4
      internal/control/control.go
  3. +17
    -0
      internal/control/control_test.go

+ 2
- 0
docs/README.md Просмотреть файл

@@ -23,6 +23,8 @@
The dry-run mode generates a synthetic, hardware-free frame summary based on the current config. 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. 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 ## Offline generation


`cmd/offline` generates a deterministic no-hardware IQ/composite-style file using the repository's output backend path. `cmd/offline` generates a deterministic no-hardware IQ/composite-style file using the repository's output backend path.


+ 21
- 4
internal/control/control.go Просмотреть файл

@@ -3,11 +3,14 @@ package control
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"sync"


"github.com/jan/fm-rds-tx/internal/config" "github.com/jan/fm-rds-tx/internal/config"
drypkg "github.com/jan/fm-rds-tx/internal/dryrun"
) )


type Server struct { type Server struct {
mu sync.RWMutex
cfg config.Config cfg config.Config
} }


@@ -17,6 +20,7 @@ func (s *Server) Handler() http.Handler {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.HandleFunc("/healthz", s.handleHealth) mux.HandleFunc("/healthz", s.handleHealth)
mux.HandleFunc("/status", s.handleStatus) mux.HandleFunc("/status", s.handleStatus)
mux.HandleFunc("/dry-run", s.handleDryRun)
return mux return mux
} }


@@ -26,12 +30,25 @@ func (s *Server) handleHealth(w http.ResponseWriter, _ *http.Request) {
} }


func (s *Server) handleStatus(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") w.Header().Set("Content-Type", "application/json")
_ = json.NewEncoder(w).Encode(map[string]any{ _ = json.NewEncoder(w).Encode(map[string]any{
"service": "fm-rds-tx", "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))
}

+ 17
- 0
internal/control/control_test.go Просмотреть файл

@@ -35,3 +35,20 @@ func TestStatus(t *testing.T) {
t.Fatalf("unexpected service: %v", body["service"]) 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"])
}
}

Загрузка…
Отмена
Сохранить