Go-based FM stereo transmitter with RDS, Windows-first and cross-platform
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

162 líneas
4.8KB

  1. package control
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "errors"
  6. "net/http"
  7. "net/http/httptest"
  8. "testing"
  9. cfgpkg "github.com/jan/fm-rds-tx/internal/config"
  10. )
  11. func TestHealthz(t *testing.T) {
  12. srv := NewServer(cfgpkg.Default())
  13. rec := httptest.NewRecorder()
  14. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/healthz", nil))
  15. if rec.Code != 200 {
  16. t.Fatalf("status: %d", rec.Code)
  17. }
  18. }
  19. func TestStatus(t *testing.T) {
  20. srv := NewServer(cfgpkg.Default())
  21. rec := httptest.NewRecorder()
  22. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/status", nil))
  23. if rec.Code != 200 {
  24. t.Fatalf("status: %d", rec.Code)
  25. }
  26. var body map[string]any
  27. json.Unmarshal(rec.Body.Bytes(), &body)
  28. if body["service"] != "fm-rds-tx" {
  29. t.Fatal("missing service")
  30. }
  31. if _, ok := body["preEmphasisTauUS"]; !ok {
  32. t.Fatal("missing preEmphasisTauUS")
  33. }
  34. }
  35. func TestStatusReportsRuntimeIndicator(t *testing.T) {
  36. srv := NewServer(cfgpkg.Default())
  37. srv.SetTXController(&fakeTXController{stats: map[string]any{"runtimeIndicator": "degraded"}})
  38. rec := httptest.NewRecorder()
  39. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/status", nil))
  40. if rec.Code != 200 {
  41. t.Fatalf("status: %d", rec.Code)
  42. }
  43. var body map[string]any
  44. json.Unmarshal(rec.Body.Bytes(), &body)
  45. if body["runtimeIndicator"] != "degraded" {
  46. t.Fatalf("expected runtimeIndicator degraded, got %v", body["runtimeIndicator"])
  47. }
  48. }
  49. func TestDryRunEndpoint(t *testing.T) {
  50. srv := NewServer(cfgpkg.Default())
  51. rec := httptest.NewRecorder()
  52. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/dry-run", nil))
  53. if rec.Code != 200 {
  54. t.Fatalf("status: %d", rec.Code)
  55. }
  56. var body map[string]any
  57. json.Unmarshal(rec.Body.Bytes(), &body)
  58. if body["mode"] != "dry-run" {
  59. t.Fatal("wrong mode")
  60. }
  61. }
  62. func TestConfigPatch(t *testing.T) {
  63. srv := NewServer(cfgpkg.Default())
  64. body := []byte(`{"toneLeftHz":900,"radioText":"hello world","preEmphasisTauUS":75}`)
  65. rec := httptest.NewRecorder()
  66. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/config", bytes.NewReader(body)))
  67. if rec.Code != 200 {
  68. t.Fatalf("status: %d body=%s", rec.Code, rec.Body.String())
  69. }
  70. }
  71. func TestRuntimeWithoutDriver(t *testing.T) {
  72. srv := NewServer(cfgpkg.Default())
  73. rec := httptest.NewRecorder()
  74. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/runtime", nil))
  75. if rec.Code != 200 {
  76. t.Fatalf("status: %d", rec.Code)
  77. }
  78. }
  79. func TestTXStartWithoutController(t *testing.T) {
  80. srv := NewServer(cfgpkg.Default())
  81. rec := httptest.NewRecorder()
  82. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/tx/start", nil))
  83. if rec.Code != http.StatusServiceUnavailable {
  84. t.Fatalf("expected 503, got %d", rec.Code)
  85. }
  86. }
  87. func TestConfigPatchUpdatesSnapshot(t *testing.T) {
  88. srv := NewServer(cfgpkg.Default())
  89. srv.SetTXController(&fakeTXController{})
  90. rec := httptest.NewRecorder()
  91. body := []byte(`{"outputDrive":1.2}`)
  92. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/config", bytes.NewReader(body)))
  93. if rec.Code != 200 {
  94. t.Fatalf("status: %d", rec.Code)
  95. }
  96. var resp map[string]any
  97. if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
  98. t.Fatalf("unmarshal response: %v", err)
  99. }
  100. if live, ok := resp["live"].(bool); !ok || !live {
  101. t.Fatalf("expected live true, got %v", resp["live"])
  102. }
  103. rec = httptest.NewRecorder()
  104. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/config", nil))
  105. var cfg cfgpkg.Config
  106. if err := json.NewDecoder(rec.Body).Decode(&cfg); err != nil {
  107. t.Fatalf("decode config: %v", err)
  108. }
  109. if cfg.FM.OutputDrive != 1.2 {
  110. t.Fatalf("expected snapshot to reflect new drive, got %v", cfg.FM.OutputDrive)
  111. }
  112. }
  113. func TestConfigPatchEngineRejectsDoesNotUpdateSnapshot(t *testing.T) {
  114. srv := NewServer(cfgpkg.Default())
  115. srv.SetTXController(&fakeTXController{updateErr: errors.New("boom")})
  116. body := []byte(`{"outputDrive":2.2}`)
  117. rec := httptest.NewRecorder()
  118. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodPost, "/config", bytes.NewReader(body)))
  119. if rec.Code != http.StatusBadRequest {
  120. t.Fatalf("expected 400, got %d", rec.Code)
  121. }
  122. rec = httptest.NewRecorder()
  123. srv.Handler().ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/config", nil))
  124. var cfg cfgpkg.Config
  125. if err := json.NewDecoder(rec.Body).Decode(&cfg); err != nil {
  126. t.Fatalf("decode config: %v", err)
  127. }
  128. if cfg.FM.OutputDrive != cfgpkg.Default().FM.OutputDrive {
  129. t.Fatalf("expected snapshot untouched, got %v", cfg.FM.OutputDrive)
  130. }
  131. }
  132. type fakeTXController struct {
  133. updateErr error
  134. stats map[string]any
  135. }
  136. func (f *fakeTXController) StartTX() error { return nil }
  137. func (f *fakeTXController) StopTX() error { return nil }
  138. func (f *fakeTXController) TXStats() map[string]any {
  139. if f.stats != nil {
  140. return f.stats
  141. }
  142. return map[string]any{}
  143. }
  144. func (f *fakeTXController) UpdateConfig(_ LivePatch) error { return f.updateErr }