//go:build integration package httpx import ( "bytes" "mime/multipart" "net/http" "strings" "testing" "bountyboard/internal/store" ) func TestMessagingFlow(t *testing.T) { ts, _, _, _ := newAuthStack(t, nil) a := newClient(t) userA := registerUser(t, ts.URL, a, "alice@example.com", "Alice A") aCSRF := csrfFrom(t, a, ts.URL) b := newClient(t) userB := registerUser(t, ts.URL, b, "bob@example.com", "Bob B") bCSRF := csrfFrom(t, b, ts.URL) c := newClient(t) registerUser(t, ts.URL, c, "carol@example.com", "Carol C") // Alice opens a DM with Bob resp := mustPost(t, a, ts.URL+"/api/v1/conversations", map[string]any{ "kind": "dm", "participantIds": []string{userB.ID}, }, aCSRF, http.StatusOK) var created struct { Conversation store.Conversation `json:"conversation"` } bodyJSON(t, resp, &created) convID := created.Conversation.ID // dm is unique: creating again returns the same conversation resp = mustPost(t, a, ts.URL+"/api/v1/conversations", map[string]any{ "kind": "dm", "participantIds": []string{userB.ID}, }, aCSRF, http.StatusOK) var again struct { Conversation store.Conversation `json:"conversation"` } bodyJSON(t, resp, &again) if again.Conversation.ID != convID { t.Fatalf("dm not deduplicated: %s vs %s", again.Conversation.ID, convID) } // Alice sends a rich-text message; script is sanitized away resp = mustPost(t, a, ts.URL+"/api/v1/conversations/"+convID+"/messages", map[string]any{ "body": `
Hi Bob!
`, }, aCSRF, http.StatusCreated) var sent struct { Message store.Message `json:"message"` } bodyJSON(t, resp, &sent) if strings.Contains(sent.Message.Body, "