package directstream import ( "context" "errors" "testing" ) type poolTicketEndpoint struct { ticket string claims TicketClaims status TicketServiceSnapshot consumeErr error heartbeats int events int releases int closes int } func (e *poolTicketEndpoint) ConsumeTicket(_ context.Context, ticket string, _ TicketBinding) (TicketClaims, error) { if e.consumeErr != nil { return TicketClaims{}, e.consumeErr } if ticket != e.ticket { return TicketClaims{}, ErrInvalidTicket } return e.claims, nil } func (e *poolTicketEndpoint) ReleaseDirectWriter(context.Context, TicketBinding) error { e.releases++ return nil } func (e *poolTicketEndpoint) HeartbeatDirectStream(context.Context, TicketBinding) error { e.heartbeats++ return nil } func (e *poolTicketEndpoint) RecordDirectStreamEvent(context.Context, TicketBinding, SessionEvent) error { e.events++ return nil } func (e *poolTicketEndpoint) CloseDirectStream(context.Context, TicketBinding) error { e.closes++ return nil } func (e *poolTicketEndpoint) Status(context.Context) (TicketServiceSnapshot, error) { return e.status, nil } func TestTicketSocketPoolRoutesLifecycleToConsumingAuthority(t *testing.T) { base := &poolTicketEndpoint{ticket: "base", claims: TicketClaims{SessionID: "base-session", Channel: "CH01"}} clone := &poolTicketEndpoint{ticket: "clone", claims: TicketClaims{SessionID: "clone-session", Channel: "CH18"}} pool := newTicketSocketPool([]ticketEndpoint{base, clone}) binding := TicketBinding{SessionID: "clone-session", Channel: "CH18"} claims, err := pool.ConsumeTicket(context.Background(), "clone", binding) if err != nil || claims.SessionID != "clone-session" { t.Fatalf("consume clone ticket = %#v, %v", claims, err) } if err := pool.RecordDirectStreamEvent(context.Background(), binding, SessionEvent{Type: SessionEventConnected}); err != nil { t.Fatal(err) } if err := pool.HeartbeatDirectStream(context.Background(), binding); err != nil { t.Fatal(err) } if err := pool.ReleaseDirectWriter(context.Background(), binding); err != nil { t.Fatal(err) } if err := pool.CloseDirectStream(context.Background(), binding); err != nil { t.Fatal(err) } if base.heartbeats != 0 || base.events != 0 || base.releases != 0 || base.closes != 0 { t.Fatalf("base lifecycle was called: %#v", base) } if clone.heartbeats != 1 || clone.events != 1 || clone.releases != 1 || clone.closes != 1 { t.Fatalf("clone lifecycle calls = %#v", clone) } if err := pool.HeartbeatDirectStream(context.Background(), binding); !errors.Is(err, ErrTicketBindingInactive) { t.Fatalf("heartbeat after close error = %v", err) } } func TestTicketSocketPoolAggregatesReachableAuthorities(t *testing.T) { base := &poolTicketEndpoint{status: TicketServiceSnapshot{ActiveBindings: 1, MaxActiveBindings: 256, Consumed: 2}} clone := &poolTicketEndpoint{status: TicketServiceSnapshot{ActiveBindings: 2, MaxActiveBindings: 128, Consumed: 3}} pool := newTicketSocketPool([]ticketEndpoint{base, clone}) status, err := pool.Status(context.Background()) if err != nil { t.Fatal(err) } if status.ActiveBindings != 3 || status.MaxActiveBindings != 384 || status.Consumed != 5 { t.Fatalf("aggregate status = %#v", status) } }