Files
ilbinek 56d9e10621
Cross Compile Go / build (push) Canceled after 0s
admin dashboard, metrics and telemetry
- embedded React admin UI at /admin/ui (go:embed), login with ADMIN_SECRET
  via signed HttpOnly session cookie, X-ADMIN-KEY still works for scripts
- admin API: overview stats, indexing queue, addon management (priority,
  reindex, delete/restore/purge, bulk), whitelist CRUD, duplicate hashes,
  traffic, metrics explorer, services, events/audit log, system info
- built-in metrics: request latency per route, visitors, top addons and
  searches, indexer/scraper counters; minute buckets in the DB with hourly
  rollups and retention; optional Prometheus /metrics (METRICS_TOKEN)
- POST /back/telemetry for indexers/scrapers (metrics, events, heartbeat)
  and a stdlib-only Go client in telemetry/
- auth hardening: empty secrets no longer allow access, constant-time
  compare, CORS not applied to /admin and /back, TRUSTED_PROXIES setting
- Dockerfile builds the UI and no longer bakes config.yaml into the image;
  CI builds the UI, runs tests and builds arm64 with CGO
2026-10-03 18:10:57 +02:00

74 lines
1.8 KiB
Go

package telemetry
import (
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"sync/atomic"
"testing"
)
func TestFlushAndRetry(t *testing.T) {
var fail atomic.Bool
var got []batch
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/back/telemetry" || r.Header.Get("X-SECRET-KEY") != "k" {
w.WriteHeader(http.StatusForbidden)
return
}
if fail.Load() {
w.WriteHeader(http.StatusServiceUnavailable)
return
}
body, _ := io.ReadAll(r.Body)
var b batch
if err := json.Unmarshal(body, &b); err != nil {
t.Error(err)
}
got = append(got, b)
w.Write([]byte(`{"accepted":1}`))
}))
defer srv.Close()
c := New(Config{BaseURL: srv.URL + "/", SecretKey: "k", Service: "indexer", Instance: "i1"})
c.Counter("jobs_total", "result", "ok").Add(2)
c.Gauge("queue").Set(7)
c.Summary("took_seconds").Observe(1)
c.Summary("took_seconds").Observe(3)
c.Event(Warn, "hello", nil)
fail.Store(true)
if err := c.Flush(context.Background()); err == nil {
t.Fatal("expected an error while the server fails")
}
c.Counter("jobs_total", "result", "ok").Add(1)
fail.Store(false)
if err := c.Flush(context.Background()); err != nil {
t.Fatal(err)
}
if len(got) != 2 {
t.Fatalf("got %d batches, want the retried one plus the new one", len(got))
}
first := got[0]
if first.Service != "indexer" || first.Instance != "i1" || len(first.Events) != 1 {
t.Fatalf("unexpected first batch %+v", first)
}
var sum float64
for _, b := range got {
for _, m := range b.Metrics {
if m.Name == "jobs_total" {
sum += m.Value
}
if m.Name == "took_seconds" && (m.Count != 2 || m.Min != 1 || m.Max != 3) {
t.Fatalf("bad summary %+v", m)
}
}
}
if sum != 3 {
t.Fatalf("counter total = %v, want 3", sum)
}
}