package bridge import ( "bytes" "encoding/json" "fmt" "net/http" "testing" "time" ) func TestServer_Ping(t *testing.T) { s := NewServer("", nil) port, err := s.Start(DefaultConfig()) if err != nil { t.Fatal(err) } defer s.Stop() resp, err := http.Get(fmt.Sprintf("http://127.0.0.1:%d/api/ping", port)) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != 200 { t.Errorf("expected 200, got %d", resp.StatusCode) } var body map[string]interface{} json.NewDecoder(resp.Body).Decode(&body) if body["status"] != "ok" { t.Errorf("expected status ok, got %v", body["status"]) } } func TestServer_Events_AuthRequired(t *testing.T) { s := NewServer("test-secret", nil) port, err := s.Start(Config{Secret: "test-secret"}) if err != nil { t.Fatal(err) } defer s.Stop() // Without auth header body := EventBatch{Version: 1, DeviceID: "test", Events: []Event{{ID: "1", Type: "page_visit", URL: "https://example.com"}}} b, _ := json.Marshal(body) resp, err := http.Post(fmt.Sprintf("http://127.0.0.1:%d/api/events", port), "application/json", bytes.NewReader(b)) if err != nil { t.Fatal(err) } if resp.StatusCode != 401 { t.Errorf("expected 401 without auth, got %d", resp.StatusCode) } } func TestServer_Events_Success(t *testing.T) { received := make(chan []Event, 1) s := NewServer("test-secret", func(evts []Event) { received <- evts }) port, err := s.Start(Config{Secret: "test-secret"}) if err != nil { t.Fatal(err) } defer s.Stop() events := []Event{ {ID: "1", Type: "page_visit", URL: "https://example.com", Title: "Example", Domain: "example.com", ActiveSeconds: 120}, } batch := EventBatch{Version: 1, DeviceID: "test-device", Events: events} b, _ := json.Marshal(batch) req, _ := http.NewRequest("POST", fmt.Sprintf("http://127.0.0.1:%d/api/events", port), bytes.NewReader(b)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Verstak-Secret", "test-secret") resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != 200 { t.Errorf("expected 200, got %d", resp.StatusCode) } select { case evts := <-received: if len(evts) != 1 { t.Errorf("expected 1 event, got %d", len(evts)) } if evts[0].ID != "1" { t.Errorf("expected event ID '1', got %s", evts[0].ID) } case <-time.After(time.Second): t.Fatal("timeout waiting for event handler") } } func TestServer_Events_EmptyBatch(t *testing.T) { s := NewServer("s", nil) port, err := s.Start(Config{Secret: "s"}) if err != nil { t.Fatal(err) } defer s.Stop() batch := EventBatch{Version: 1, DeviceID: "test", Events: []Event{}} b, _ := json.Marshal(batch) req, _ := http.NewRequest("POST", fmt.Sprintf("http://127.0.0.1:%d/api/events", port), bytes.NewReader(b)) req.Header.Set("Content-Type", "application/json") req.Header.Set("X-Verstak-Secret", "s") resp, err := http.DefaultClient.Do(req) if err != nil { t.Fatal(err) } if resp.StatusCode != 204 { t.Errorf("expected 204 for empty batch, got %d", resp.StatusCode) } } func TestGenerateSecret(t *testing.T) { s1 := GenerateSecret() s2 := GenerateSecret() if s1 == s2 { t.Error("secrets should be unique") } if len(s1) != 32 { t.Errorf("expected 32 chars, got %d", len(s1)) } } func TestServer_AutoPort(t *testing.T) { cfg := DefaultConfig() s := NewServer("", nil) port, err := s.Start(cfg) if err != nil { t.Fatal(err) } defer s.Stop() if port == 0 { t.Error("expected non-zero port") } if s.Port() != port { t.Errorf("Port() = %d, want %d", s.Port(), port) } if !s.Running() { t.Error("expected server running") } }