package server import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "os" "testing" "git.dracodev.net/Projets/clip-sync/internal/dedup" ) // mockClipboard is an in-memory clipboard for testing. type mockClipboard struct { text string } func (m *mockClipboard) Read() (string, error) { return m.text, nil } func (m *mockClipboard) Write(s string) error { m.text = s; return nil } func TestHandleClipAcceptsValidPayload(t *testing.T) { cb := &mockClipboard{} filter := dedup.NewFilter("my-machine") srv := New(":0", cb, filter) payload := dedup.Payload{ Text: "test text", Ts: 1690000000000000000, Origin: "other-machine", } body, err := json.Marshal(payload) if err != nil { t.Fatalf("json.Marshal: %v", err) } req := httptest.NewRequest(http.MethodPost, "/clip", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() srv.handleClip(w, req) if w.Code != http.StatusNoContent { t.Errorf("status = %d, want %d", w.Code, http.StatusNoContent) } if cb.text != "test text" { t.Errorf("clipboard = %q, want \"test text\"", cb.text) } } func TestHandleClipIgnoresEcho(t *testing.T) { cb := &mockClipboard{text: "existing"} filter := dedup.NewFilter("my-machine") // Simulate we just wrote "echo text" locally. filter.MarkWritten("echo text") srv := New(":0", cb, filter) payload := dedup.Payload{ Text: "echo text", // same text we just wrote Ts: 1690000000000000000, Origin: "other-machine", } body, err := json.Marshal(payload) if err != nil { t.Fatalf("json.Marshal: %v", err) } req := httptest.NewRequest(http.MethodPost, "/clip", bytes.NewReader(body)) w := httptest.NewRecorder() srv.handleClip(w, req) if w.Code != http.StatusNoContent { t.Errorf("status = %d, want %d", w.Code, http.StatusNoContent) } // Clipboard should NOT be overwritten. if cb.text != "existing" { t.Errorf("clipboard = %q, want \"existing\" (should not be overwritten)", cb.text) } } func TestHandleClipRejectsInvalidMethod(t *testing.T) { cb := &mockClipboard{} filter := dedup.NewFilter("my-machine") srv := New(":0", cb, filter) req := httptest.NewRequest(http.MethodGet, "/clip", nil) w := httptest.NewRecorder() srv.handleClip(w, req) if w.Code != http.StatusMethodNotAllowed { t.Errorf("status = %d, want %d", w.Code, http.StatusMethodNotAllowed) } } func TestHandleClipRejectsEmptyText(t *testing.T) { cb := &mockClipboard{} filter := dedup.NewFilter("my-machine") srv := New(":0", cb, filter) payload := dedup.Payload{ Text: "", Ts: 1690000000000000000, Origin: "other-machine", } body, err := json.Marshal(payload) if err != nil { t.Fatalf("json.Marshal: %v", err) } req := httptest.NewRequest(http.MethodPost, "/clip", bytes.NewReader(body)) w := httptest.NewRecorder() srv.handleClip(w, req) if w.Code != http.StatusBadRequest { t.Errorf("status = %d, want %d", w.Code, http.StatusBadRequest) } } func TestHandleClipRejectsInvalidJSON(t *testing.T) { cb := &mockClipboard{} filter := dedup.NewFilter("my-machine") srv := New(":0", cb, filter) req := httptest.NewRequest(http.MethodPost, "/clip", bytes.NewReader([]byte("not json"))) w := httptest.NewRecorder() srv.handleClip(w, req) if w.Code != http.StatusBadRequest { t.Errorf("status = %d, want %d", w.Code, http.StatusBadRequest) } } func TestMain(m *testing.M) { // Ensure clipboard.New() won't be called in test (mock is used directly). os.Exit(m.Run()) }