package main import ( "context" "net/http" "net/http/httptest" "testing" "time" ) func TestCoordinatorClientWaitsForBothAgentsAndMergesLedger(t *testing.T) { state := &coordinatorState{ start: StartInfo{RunID: "run1", Scenario: "p2p"}, startDelay: time.Second, expectedAgents: 2, ready: make(map[string]AgentReady), ledgers: make(map[string][]AcceptedMessage), 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(), AgentReady{Machine: "a", ReadyConnections: 1000}) aDone <- start }() time.Sleep(50 * time.Millisecond) startB, err := client.ready(context.Background(), AgentReady{Machine: "b", ReadyConnections: 1000}) if err != nil || startB.StartAt.IsZero() { 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 }