package main import ( "context" "fmt" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" "time" "github.com/coder/websocket" "github.com/coder/websocket/wsjson" "github.com/edwardtay/injective-booklab/client" ) // A fake LCD whose best bid quantity and seq change on every request. func fakeLCD(hits *int32) *httptest.Server { return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { n := atomic.AddInt32(hits, 1) fmt.Fprintf(w, `{"buys_price_level":[{"p":"9","q":"%d"}],"sells_price_level":[{"p":"10","q":"1"}],"seq":"%d"}`, n, n) })) } func TestStreamFansOutOnePoller(t *testing.T) { var hits int32 up := fakeLCD(&hits) defer up.Close() h := &hub{c: client.New(up.URL), every: 30 * time.Millisecond, feeds: map[string]*feed{}} srv := httptest.NewServer(routes(h)) defer srv.Close() ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() url := "ws" + strings.TrimPrefix(srv.URL, "http") + "/v1/stream/m1?kind=spot" var conns []*websocket.Conn for i := 0; i < 3; i++ { c, _, err := websocket.Dial(ctx, url, nil) if err != nil { t.Fatal(err) } defer c.CloseNow() conns = append(conns, c) } sawDiff := 0 for _, c := range conns { for i := 0; i < 3; i++ { var m msg if err := wsjson.Read(ctx, c, &m); err != nil { t.Fatal(err) } if m.Type == "diff" && len(m.Changes) == 1 && m.Changes[0].Side == "bid" { sawDiff++ break } } } if sawDiff != 3 { t.Fatalf("every client should receive bid diffs, got %d", sawDiff) } h.mu.Lock() feeds := len(h.feeds) h.mu.Unlock() if feeds != 1 { t.Errorf("3 clients on one market should share 1 poller, got %d", feeds) } } func TestBadSimRequest(t *testing.T) { h := &hub{c: client.New("http://127.0.0.1:1"), every: time.Second, feeds: map[string]*feed{}} srv := httptest.NewServer(routes(h)) defer srv.Close() resp, err := http.Get(srv.URL + "/v1/sim/m1?side=up&qty=x") if err != nil { t.Fatal(err) } if resp.StatusCode != http.StatusBadRequest { t.Errorf("want 400, got %d", resp.StatusCode) } }