123 lines
3.9 KiB
Go
123 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.cin.su/m/Assistant/adapters/notificationexecutor"
|
|
commitcontract "git.cin.su/m/Assistant/contracts/commit"
|
|
)
|
|
|
|
func delivery(t *testing.T, intent, message string) notificationexecutor.DeliveryRequest {
|
|
t.Helper()
|
|
payload := notificationexecutor.Payload{ActionIntentID: commitcontract.ActionIntentID(intent), TemplateCode: commitcontract.ProspectiveDeliveredTemplateV1, ProspectiveItemID: "item-1", ExpectedRevision: 1, Title: "Домовой", Message: message}
|
|
digest, err := notificationexecutor.Digest(payload)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return notificationexecutor.DeliveryRequest{Payload: payload, RequestDigest: digest}
|
|
}
|
|
|
|
func request(t *testing.T, handler http.Handler, method, path, token string, value any) *httptest.ResponseRecorder {
|
|
t.Helper()
|
|
var body bytes.Buffer
|
|
if value != nil {
|
|
if err := json.NewEncoder(&body).Encode(value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
r := httptest.NewRequest(method, path, &body)
|
|
if token != "" {
|
|
r.Header.Set("Authorization", "Bearer "+token)
|
|
}
|
|
w := httptest.NewRecorder()
|
|
handler.ServeHTTP(w, r)
|
|
return w
|
|
}
|
|
|
|
func TestReceiverIdempotencyConflictAndRestart(t *testing.T) {
|
|
directory := t.TempDir()
|
|
clock := func() time.Time { return time.UnixMilli(100) }
|
|
server, err := newReceiverServer(directory, "write-secret", "read-secret", clock)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first := delivery(t, "intent-1", "message one")
|
|
w := request(t, server.routes(), http.MethodPut, "/delivery/v1/deliveries/intent-1", "write-secret", first)
|
|
if w.Code != http.StatusCreated {
|
|
t.Fatalf("create status %d: %s", w.Code, w.Body.String())
|
|
}
|
|
w = request(t, server.routes(), http.MethodPut, "/delivery/v1/deliveries/intent-1", "write-secret", first)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("repeat status %d", w.Code)
|
|
}
|
|
conflict := delivery(t, "intent-1", "different")
|
|
w = request(t, server.routes(), http.MethodPut, "/delivery/v1/deliveries/intent-1", "write-secret", conflict)
|
|
if w.Code != http.StatusConflict {
|
|
t.Fatalf("conflict status %d", w.Code)
|
|
}
|
|
restarted, err := newReceiverServer(directory, "write-secret", "read-secret", clock)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w = request(t, restarted.routes(), http.MethodGet, "/delivery/v1/receipts/intent-1", "write-secret", nil)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("restart get status %d", w.Code)
|
|
}
|
|
w = request(t, restarted.routes(), http.MethodGet, "/delivery/v1/receipts?limit=10", "read-secret", nil)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("list status %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestReceiverSeparatesReadAndWriteAuthority(t *testing.T) {
|
|
server, err := newReceiverServer(t.TempDir(), "write-secret", "read-secret", time.Now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
handler := server.routes()
|
|
value := delivery(t, "intent-1", "message")
|
|
if w := request(t, handler, http.MethodPut, "/delivery/v1/deliveries/intent-1", "read-secret", value); w.Code != http.StatusUnauthorized {
|
|
t.Fatalf("reader wrote: %d", w.Code)
|
|
}
|
|
if w := request(t, handler, http.MethodGet, "/delivery/v1/receipts", "write-secret", nil); w.Code != http.StatusUnauthorized {
|
|
t.Fatalf("writer listed: %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestConcurrentDuplicateCreatesOneReceipt(t *testing.T) {
|
|
server, err := newReceiverServer(t.TempDir(), "write-secret", "read-secret", time.Now)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
value := delivery(t, "intent-concurrent", "message")
|
|
handler := server.routes()
|
|
codes := make(chan int, 8)
|
|
var group sync.WaitGroup
|
|
for range 8 {
|
|
group.Add(1)
|
|
go func() {
|
|
defer group.Done()
|
|
codes <- request(t, handler, http.MethodPut, "/delivery/v1/deliveries/intent-concurrent", "write-secret", value).Code
|
|
}()
|
|
}
|
|
group.Wait()
|
|
close(codes)
|
|
created := 0
|
|
for code := range codes {
|
|
if code == http.StatusCreated {
|
|
created++
|
|
} else if code != http.StatusOK {
|
|
t.Fatalf("unexpected status %d", code)
|
|
}
|
|
}
|
|
if created != 1 {
|
|
t.Fatalf("created=%d", created)
|
|
}
|
|
}
|