180 lines
5.2 KiB
Go
180 lines
5.2 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"git.ipao.vip/rogee/creator-hub/internal/environment"
|
|
"github.com/gorilla/websocket"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync/atomic"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestEventChannelSharesConnectionAndAcknowledges(t *testing.T) {
|
|
var connections atomic.Int32
|
|
acks := make(chan struct{}, 2)
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/v1/channel" || r.Header.Get("Authorization") != "Bearer test" {
|
|
t.Errorf("unexpected channel request")
|
|
w.WriteHeader(400)
|
|
return
|
|
}
|
|
c, err := (&websocket.Upgrader{}).Upgrade(w, r, nil)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
defer c.Close()
|
|
connections.Add(1)
|
|
for {
|
|
var v map[string]any
|
|
if c.ReadJSON(&v) != nil {
|
|
return
|
|
}
|
|
switch v["type"] {
|
|
case "subscribe":
|
|
_ = c.WriteJSON(map[string]any{"type": "subscribed", "subscription": v["subscription"]})
|
|
_ = c.WriteJSON(map[string]any{"type": "deliveries", "subscription": v["subscription"], "deliveries": []any{map[string]any{"kind": "open", "delivery_id": v["alias"]}}})
|
|
case "ack":
|
|
acks <- struct{}{}
|
|
}
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
|
|
defer cancel()
|
|
target := environment.Gateway{Endpoint: server.URL, Token: "test"}
|
|
first, err := acquireEventSubscription(ctx, target, map[string]any{"alias": "a"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer first.close()
|
|
second, err := acquireEventSubscription(ctx, target, map[string]any{"alias": "b"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer second.close()
|
|
if connections.Load() != 1 {
|
|
t.Fatalf("connections=%d", connections.Load())
|
|
}
|
|
items, err := first.poll(ctx, nil)
|
|
if err != nil || items[0].DeliveryID != "a" {
|
|
t.Fatalf("%+v %v", items, err)
|
|
}
|
|
items, err = second.poll(ctx, nil)
|
|
if err != nil || items[0].DeliveryID != "b" {
|
|
t.Fatalf("%+v %v", items, err)
|
|
}
|
|
short, c := context.WithTimeout(ctx, 30*time.Millisecond)
|
|
defer c()
|
|
_, _ = first.poll(short, []string{"a"})
|
|
select {
|
|
case <-acks:
|
|
case <-ctx.Done():
|
|
t.Fatal("ACK not received")
|
|
}
|
|
}
|
|
func TestEventChannelRejectsHandshakeAndSubscriptionTimeout(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(503) }))
|
|
defer server.Close()
|
|
for _, endpoint := range []string{server.URL, "invalid://gateway", "http://%"} {
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
_, err := acquireEventSubscription(ctx, environment.Gateway{Endpoint: endpoint}, map[string]any{"alias": "a"})
|
|
cancel()
|
|
if err == nil {
|
|
t.Fatalf("invalid connection accepted: %s", endpoint)
|
|
}
|
|
}
|
|
waiting := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
c, e := (&websocket.Upgrader{}).Upgrade(w, r, nil)
|
|
if e != nil {
|
|
return
|
|
}
|
|
defer c.Close()
|
|
for {
|
|
var value map[string]any
|
|
if c.ReadJSON(&value) != nil {
|
|
return
|
|
}
|
|
}
|
|
}))
|
|
defer waiting.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 40*time.Millisecond)
|
|
defer cancel()
|
|
_, err := acquireEventSubscription(ctx, environment.Gateway{Endpoint: waiting.URL}, map[string]any{"alias": "a"})
|
|
if !errors.Is(err, context.DeadlineExceeded) {
|
|
t.Fatalf("subscription wait did not honor cancellation: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestEventChannelReportsDisconnect(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
c, e := (&websocket.Upgrader{}).Upgrade(w, r, nil)
|
|
if e != nil {
|
|
return
|
|
}
|
|
defer c.Close()
|
|
var v map[string]any
|
|
_ = c.ReadJSON(&v)
|
|
_ = c.WriteJSON(map[string]any{"type": "subscribed", "subscription": v["subscription"]})
|
|
}))
|
|
defer server.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
|
|
defer cancel()
|
|
sub, err := acquireEventSubscription(ctx, environment.Gateway{Endpoint: server.URL}, map[string]any{"alias": "a"})
|
|
if err == nil {
|
|
defer sub.close()
|
|
_, err = sub.poll(ctx, nil)
|
|
}
|
|
if err == nil {
|
|
t.Fatal("disconnect hidden")
|
|
}
|
|
}
|
|
|
|
func TestEventChannelClosingLastAccountDoesNotRetireJoiningAccount(t *testing.T) {
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
c, err := (&websocket.Upgrader{}).Upgrade(w, r, nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
defer c.Close()
|
|
for {
|
|
var value map[string]any
|
|
if c.ReadJSON(&value) != nil {
|
|
return
|
|
}
|
|
if value["type"] == "subscribe" {
|
|
_ = c.WriteJSON(map[string]any{"type": "subscribed", "subscription": value["subscription"]})
|
|
}
|
|
}
|
|
}))
|
|
defer server.Close()
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
target := environment.Gateway{Endpoint: server.URL}
|
|
previous, err := acquireEventSubscription(ctx, target, map[string]any{"alias": "a"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer func() { previous.close() }()
|
|
for i := 0; i < 50; i++ {
|
|
type result struct {
|
|
sub *eventSubscription
|
|
err error
|
|
}
|
|
joined := make(chan result, 1)
|
|
go func() {
|
|
sub, err := acquireEventSubscription(ctx, target, map[string]any{"alias": "b"})
|
|
joined <- result{sub, err}
|
|
}()
|
|
previous.close()
|
|
next := <-joined
|
|
if next.err != nil {
|
|
t.Fatalf("joining account interrupted at iteration %d: %v", i, next.err)
|
|
}
|
|
previous = next.sub
|
|
}
|
|
}
|