Files
im_server/loadtest/im-pressure/coordinator_test.go
T

104 lines
3.6 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
)
func TestCoordinatorResetsBarrierWhenConnectionCountDrops(t *testing.T) {
state := &coordinatorState{
start: StartInfo{RunID: "run-reset", Scenario: "group"}, startDelay: time.Minute,
commitDelay: time.Second, expectedAgents: 2, ready: make(map[string]AgentReady),
armed: make(map[string]bool), ledgers: make(map[string][]AcceptedMessage),
summaries: make(map[string]AgentSummary), complete: make(chan struct{}),
}
postReady := func(ready AgentReady) StartInfo {
raw, err := json.Marshal(ready)
if err != nil {
t.Fatal(err)
}
req := httptest.NewRequest(http.MethodPost, "/v1/ready", bytes.NewReader(raw))
w := httptest.NewRecorder()
state.handleReady(w, req)
if w.Code != http.StatusOK {
t.Fatalf("ready status=%d body=%s", w.Code, w.Body.String())
}
var start StartInfo
if err = json.Unmarshal(w.Body.Bytes(), &start); err != nil {
t.Fatal(err)
}
return start
}
fullA := AgentReady{Machine: "a", ReadyConnections: 1000, ExpectedConnections: 1000}
fullB := AgentReady{Machine: "b", ReadyConnections: 1000, ExpectedConnections: 1000}
postReady(fullA)
if start := postReady(fullB); start.StartAt.IsZero() {
t.Fatal("full agents must create a validation barrier")
}
fullA.ReadyConnections = 999
if start := postReady(fullA); !start.StartAt.IsZero() || start.Committed {
t.Fatalf("connection drop must reset barrier: %+v", start)
}
}
func TestCoordinatorClientWaitsForBothAgentsAndMergesLedger(t *testing.T) {
state := &coordinatorState{
start: StartInfo{RunID: "run1", Scenario: "p2p"}, startDelay: 50 * time.Millisecond,
commitDelay: 50 * time.Millisecond, expectedAgents: 2,
ready: make(map[string]AgentReady), ledgers: make(map[string][]AcceptedMessage),
armed: make(map[string]bool), summaries: make(map[string]AgentSummary), complete: make(chan struct{}),
}
server := httptest.NewServer(testCoordinatorMux(state))
defer server.Close()
client := newCoordinatorClient(server.URL)
aDone := make(chan StartInfo, 1)
go func() {
start, _ := client.ready(context.Background(), func() AgentReady {
return AgentReady{Machine: "a", ReadyConnections: 1000, ExpectedConnections: 1000}
})
aDone <- start
}()
time.Sleep(50 * time.Millisecond)
startB, err := client.ready(context.Background(), func() AgentReady {
return AgentReady{Machine: "b", ReadyConnections: 1000, ExpectedConnections: 1000}
})
if err != nil || startB.StartAt.IsZero() || !startB.Committed {
t.Fatalf("machine B ready: start=%+v err=%v", startB, err)
}
select {
case startA := <-aDone:
if !startA.StartAt.Equal(startB.StartAt) {
t.Fatalf("different start times A=%s B=%s", startA.StartAt, startB.StartAt)
}
case <-time.After(2 * time.Second):
t.Fatal("machine A did not receive start barrier")
}
if err = client.submitLedger(context.Background(), "a", []AcceptedMessage{{ClientMid: "b"}}); err != nil {
t.Fatal(err)
}
if err = client.submitLedger(context.Background(), "b", []AcceptedMessage{{ClientMid: "a"}}); err != nil {
t.Fatal(err)
}
ledger, err := client.ledger(context.Background())
if err != nil || len(ledger) != 2 || ledger[0].ClientMid != "a" || ledger[1].ClientMid != "b" {
t.Fatalf("ledger=%+v err=%v", ledger, err)
}
}
func testCoordinatorMux(state *coordinatorState) *http.ServeMux {
mux := http.NewServeMux()
mux.HandleFunc("/v1/ready", state.handleReady)
mux.HandleFunc("/v1/start", state.handleStart)
mux.HandleFunc("/v1/ledger", state.handleLedger)
mux.HandleFunc("/v1/summary", state.handleSummary)
mux.HandleFunc("/v1/status", state.handleStatus)
return mux
}