240 lines
6 KiB
Go
240 lines
6 KiB
Go
|
|
package receiver
|
||
|
|
|
||
|
|
import (
|
||
|
|
"bytes"
|
||
|
|
"encoding/json"
|
||
|
|
"io"
|
||
|
|
"log/slog"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"path/filepath"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"edp.buildth.ing/DevFW-CICD/forgejo-runner-resource-collector/internal/summary"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestHandler_ReceiveMetrics(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
payload := MetricsPayload{
|
||
|
|
Execution: ExecutionContext{
|
||
|
|
Organization: "test-org",
|
||
|
|
Repository: "test-repo",
|
||
|
|
Workflow: "ci.yml",
|
||
|
|
Job: "build",
|
||
|
|
RunID: "run-123",
|
||
|
|
},
|
||
|
|
Summary: summary.RunSummary{
|
||
|
|
DurationSeconds: 60.0,
|
||
|
|
SampleCount: 12,
|
||
|
|
},
|
||
|
|
}
|
||
|
|
|
||
|
|
body, _ := json.Marshal(payload)
|
||
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/metrics", bytes.NewReader(body))
|
||
|
|
req.Header.Set("Content-Type", "application/json")
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusCreated {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusCreated)
|
||
|
|
}
|
||
|
|
|
||
|
|
var resp map[string]any
|
||
|
|
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
|
||
|
|
t.Fatalf("failed to decode response: %v", err)
|
||
|
|
}
|
||
|
|
if resp["status"] != "created" {
|
||
|
|
t.Errorf("response status = %v, want %q", resp["status"], "created")
|
||
|
|
}
|
||
|
|
if resp["id"] == nil || resp["id"].(float64) == 0 {
|
||
|
|
t.Error("response id is missing or zero")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandler_ReceiveMetrics_InvalidJSON(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/metrics", bytes.NewReader([]byte("not json")))
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusBadRequest {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandler_ReceiveMetrics_MissingRunID(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
payload := MetricsPayload{
|
||
|
|
Execution: ExecutionContext{
|
||
|
|
Organization: "test-org",
|
||
|
|
Repository: "test-repo",
|
||
|
|
// RunID is missing
|
||
|
|
},
|
||
|
|
Summary: summary.RunSummary{},
|
||
|
|
}
|
||
|
|
|
||
|
|
body, _ := json.Marshal(payload)
|
||
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/metrics", bytes.NewReader(body))
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusBadRequest {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusBadRequest)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandler_GetByRunID(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
// First, save a metric
|
||
|
|
payload := &MetricsPayload{
|
||
|
|
Execution: ExecutionContext{
|
||
|
|
Organization: "test-org",
|
||
|
|
Repository: "test-repo",
|
||
|
|
Workflow: "ci.yml",
|
||
|
|
Job: "build",
|
||
|
|
RunID: "run-get-test",
|
||
|
|
},
|
||
|
|
Summary: summary.RunSummary{SampleCount: 5},
|
||
|
|
}
|
||
|
|
if _, err := h.store.SaveMetric(payload); err != nil {
|
||
|
|
t.Fatalf("SaveMetric() error = %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/metrics/run/run-get-test", nil)
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusOK {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
|
||
|
|
}
|
||
|
|
|
||
|
|
var metrics []Metric
|
||
|
|
if err := json.NewDecoder(rec.Body).Decode(&metrics); err != nil {
|
||
|
|
t.Fatalf("failed to decode response: %v", err)
|
||
|
|
}
|
||
|
|
if len(metrics) != 1 {
|
||
|
|
t.Errorf("got %d metrics, want 1", len(metrics))
|
||
|
|
}
|
||
|
|
if metrics[0].RunID != "run-get-test" {
|
||
|
|
t.Errorf("RunID = %q, want %q", metrics[0].RunID, "run-get-test")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandler_GetByRunID_NotFound(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/metrics/run/nonexistent", nil)
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusOK {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
|
||
|
|
}
|
||
|
|
|
||
|
|
var metrics []Metric
|
||
|
|
if err := json.NewDecoder(rec.Body).Decode(&metrics); err != nil {
|
||
|
|
t.Fatalf("failed to decode response: %v", err)
|
||
|
|
}
|
||
|
|
if len(metrics) != 0 {
|
||
|
|
t.Errorf("got %d metrics, want 0", len(metrics))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandler_GetByRepository(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
// Save metrics for different repos
|
||
|
|
payloads := []*MetricsPayload{
|
||
|
|
{Execution: ExecutionContext{Organization: "org-x", Repository: "repo-y", RunID: "r1"}},
|
||
|
|
{Execution: ExecutionContext{Organization: "org-x", Repository: "repo-y", RunID: "r2"}},
|
||
|
|
{Execution: ExecutionContext{Organization: "org-x", Repository: "repo-z", RunID: "r3"}},
|
||
|
|
}
|
||
|
|
for _, p := range payloads {
|
||
|
|
if _, err := h.store.SaveMetric(p); err != nil {
|
||
|
|
t.Fatalf("SaveMetric() error = %v", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/metrics/repo/org-x/repo-y", nil)
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusOK {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
|
||
|
|
}
|
||
|
|
|
||
|
|
var metrics []Metric
|
||
|
|
if err := json.NewDecoder(rec.Body).Decode(&metrics); err != nil {
|
||
|
|
t.Fatalf("failed to decode response: %v", err)
|
||
|
|
}
|
||
|
|
if len(metrics) != 2 {
|
||
|
|
t.Errorf("got %d metrics, want 2", len(metrics))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestHandler_Health(t *testing.T) {
|
||
|
|
h, cleanup := newTestHandler(t)
|
||
|
|
defer cleanup()
|
||
|
|
|
||
|
|
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
|
||
|
|
mux := http.NewServeMux()
|
||
|
|
h.RegisterRoutes(mux)
|
||
|
|
mux.ServeHTTP(rec, req)
|
||
|
|
|
||
|
|
if rec.Code != http.StatusOK {
|
||
|
|
t.Errorf("status = %d, want %d", rec.Code, http.StatusOK)
|
||
|
|
}
|
||
|
|
|
||
|
|
var resp map[string]string
|
||
|
|
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
|
||
|
|
t.Fatalf("failed to decode response: %v", err)
|
||
|
|
}
|
||
|
|
if resp["status"] != "ok" {
|
||
|
|
t.Errorf("status = %q, want %q", resp["status"], "ok")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func newTestHandler(t *testing.T) (*Handler, func()) {
|
||
|
|
t.Helper()
|
||
|
|
dbPath := filepath.Join(t.TempDir(), "test.db")
|
||
|
|
store, err := NewStore(dbPath)
|
||
|
|
if err != nil {
|
||
|
|
t.Fatalf("NewStore() error = %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||
|
|
handler := NewHandler(store, logger)
|
||
|
|
|
||
|
|
return handler, func() { _ = store.Close() }
|
||
|
|
}
|