| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- package service
- import (
- "context"
- "errors"
- "io"
- "net/http"
- "strings"
- "testing"
- "time"
- coderws "github.com/coder/websocket"
- "github.com/stretchr/testify/require"
- )
- type trafficCacheStub struct {
- AccountTrafficCache
- acquired bool
- calls, finishes, status int
- }
- func (s *trafficCacheStub) Acquire(context.Context, AccountTrafficPlan, string) (AccountTrafficAdmission, error) {
- s.calls++
- return AccountTrafficAdmission{Allowed: s.acquired, RetryAfter: time.Second, Reason: "local budget"}, nil
- }
- func (s *trafficCacheStub) Finish(_ context.Context, _ AccountTrafficPlan, _ string, status int, _ int64) error {
- s.finishes++
- s.status = status
- return nil
- }
- func TestAccountTrafficDisabledDoesNotTouchCache(t *testing.T) {
- a := &Account{ID: 42, Concurrency: 4}
- req, _ := http.NewRequest("POST", "http://localhost/responses", strings.NewReader("{}"))
- cache := &trafficCacheStub{}
- control := NewAccountTrafficService(cache)
- called := 0
- resp, err := control.DoHTTP(WithAccountTrafficRequest(req, a), func(r *http.Request) (*http.Response, error) {
- called++
- return &http.Response{StatusCode: 200, Body: io.NopCloser(strings.NewReader("ok"))}, nil
- })
- require.NoError(t, err)
- resp.Body.Close()
- require.Equal(t, 1, called)
- require.Zero(t, cache.calls)
- require.Zero(t, cache.finishes)
- }
- func TestAccountTrafficHoldsLeaseThroughStreamAndClassifiesTerminal429(t *testing.T) {
- a := &Account{ID: 42, Concurrency: 4, Extra: map[string]any{AccountTrafficPolicyKey: map[string]any{"adaptive_enabled": true}}}
- req, _ := http.NewRequest("POST", "http://localhost/responses", nil)
- cache := &trafficCacheStub{acquired: true}
- control := NewAccountTrafficService(cache)
- resp, err := control.DoHTTP(WithAccountTrafficRequest(req, a), func(r *http.Request) (*http.Response, error) {
- return &http.Response{StatusCode: 200, Header: http.Header{"Content-Type": []string{"text/event-stream"}}, Body: io.NopCloser(strings.NewReader("data: {\"type\":\"response.failed\",\"response\":{\"error\":{\"code\":\"rate_limit_exceeded\"}}}\n\n"))}, nil
- })
- require.NoError(t, err)
- require.Zero(t, cache.finishes, "response headers alone must not release concurrency")
- _, err = io.ReadAll(resp.Body)
- require.NoError(t, err)
- require.NoError(t, resp.Body.Close())
- require.Equal(t, 1, cache.finishes)
- require.Equal(t, 429, cache.status)
- }
- func TestAccountTrafficLocalDenialNeverCallsUpstream(t *testing.T) {
- a := &Account{ID: 42, Concurrency: 4, Extra: map[string]any{AccountTrafficPolicyKey: map[string]any{"strict_rpm_enabled": true}}}
- req, _ := http.NewRequest("POST", "http://localhost/responses", nil)
- cache := &trafficCacheStub{}
- control := NewAccountTrafficService(cache)
- _, err := control.DoHTTP(WithAccountTrafficRequest(req, a), func(*http.Request) (*http.Response, error) { t.Fatal("must not send"); return nil, nil })
- var local *AccountTrafficLimitError
- require.ErrorAs(t, err, &local)
- var failover *UpstreamFailoverError
- require.ErrorAs(t, err, &failover)
- require.False(t, failover.ShouldRetryNextAccount())
- require.False(t, failover.ShouldReportAccountScheduleFailure())
- require.Equal(t, GatewayFailureScopeRequest, failover.Scope)
- require.Zero(t, cache.finishes)
- _, err = (*AccountTrafficService)(nil).DoHTTP(WithAccountTrafficRequest(req, a), func(*http.Request) (*http.Response, error) {
- t.Fatal("missing limiter must not fail open")
- return nil, nil
- })
- require.True(t, errors.As(err, &local))
- require.Equal(t, 503, local.Status)
- }
- func TestAccountTrafficStreamCompletionCanRecoverWithoutReadingEOF(t *testing.T) {
- for _, kind := range []string{"response.completed", "message_stop", "response.incomplete"} {
- t.Run(kind, func(t *testing.T) {
- a := &Account{ID: 42, Concurrency: 8, Extra: map[string]any{AccountTrafficPolicyKey: map[string]any{"adaptive_enabled": true}}}
- req, _ := http.NewRequest("POST", "http://localhost/responses", nil)
- cache := &trafficCacheStub{acquired: true}
- control := NewAccountTrafficService(cache)
- resp, err := control.DoHTTP(WithAccountTrafficRequest(req, a), func(*http.Request) (*http.Response, error) {
- return &http.Response{StatusCode: 200, Header: http.Header{"Content-Type": []string{"text/event-stream"}}, Body: io.NopCloser(strings.NewReader("data: {\"type\":\"" + kind + "\"}\n\n"))}, nil
- })
- require.NoError(t, err)
- buffer := make([]byte, 1000)
- _, err = resp.Body.Read(buffer)
- require.NoError(t, err)
- require.NoError(t, resp.Body.Close())
- if kind == "response.incomplete" {
- require.Equal(t, 499, cache.status)
- } else {
- require.Equal(t, 200, cache.status)
- }
- require.Equal(t, 1, cache.finishes)
- })
- }
- }
- func TestAccountTrafficPolicyRejectsInvalidValues(t *testing.T) {
- for _, input := range []map[string]any{{"rpm": 0}, {"rpm": 1, "burst": 2}, {"adaptive_mode": "unknown"}, {"strict_rpm_enabled": "true"}, {"unexpected": 1}} {
- _, err := ParseAccountTrafficPolicy(map[string]any{AccountTrafficPolicyKey: input})
- require.Error(t, err)
- }
- a := &Account{ID: 42, Concurrency: 4, Extra: map[string]any{AccountTrafficPolicyKey: map[string]any{"adaptive_enabled": true}}}
- p, err := AccountTrafficPlanFor(a)
- require.NoError(t, err)
- require.Zero(t, p.Revision)
- }
- type trafficFrameStub struct {
- payload []byte
- writes int
- }
- func (f *trafficFrameStub) ReadFrame(context.Context) (coderws.MessageType, []byte, error) {
- return coderws.MessageText, f.payload, nil
- }
- func (f *trafficFrameStub) WriteFrame(_ context.Context, _ coderws.MessageType, _ []byte) error {
- f.writes++
- return nil
- }
- func (f *trafficFrameStub) Close() error { return nil }
- func TestAccountTrafficPassthroughCountsEachTurnAndIgnoresControlFrames(t *testing.T) {
- cache := &trafficCacheStub{acquired: true}
- inner := &trafficFrameStub{payload: []byte(`{"type":"response.completed"}`)}
- a := &Account{ID: 42, Concurrency: 8, Extra: map[string]any{AccountTrafficPolicyKey: map[string]any{"strict_rpm_enabled": true}}}
- plan, err := AccountTrafficPlanFor(a)
- require.NoError(t, err)
- frame := &accountTrafficFrameConn{inner: inner, control: NewAccountTrafficService(cache), plan: plan, ctx: context.Background()}
- for i := 0; i < 2; i++ {
- writeCtx, cancel := context.WithCancel(context.Background())
- require.NoError(t, frame.WriteFrame(writeCtx, coderws.MessageText, []byte(`{"type":"response.create"}`)))
- cancel()
- require.Equal(t, i+1, cache.calls)
- require.Equal(t, i, cache.finishes, "the write deadline is not the active-turn lifetime")
- require.NoError(t, frame.WriteFrame(context.Background(), coderws.MessageText, []byte(`{"type":"session.update"}`)))
- require.Equal(t, i+1, cache.calls)
- _, _, err := frame.ReadFrame(context.Background())
- require.NoError(t, err)
- require.Equal(t, i+1, cache.finishes)
- require.Equal(t, 200, cache.status)
- }
- require.NoError(t, frame.Close())
- require.Equal(t, 2, cache.finishes)
- cache.acquired = false
- require.Error(t, frame.WriteFrame(context.Background(), coderws.MessageText, []byte(`{"type":"response.create"}`)))
- require.Equal(t, 4, inner.writes)
- }
- func TestAccountTrafficObserveCacheOutageDoesNotRestrictRequests(t *testing.T) {
- a := &Account{ID: 42, Concurrency: 8, Extra: map[string]any{AccountTrafficPolicyKey: map[string]any{"adaptive_enabled": true, "adaptive_mode": "observe"}}}
- req, _ := http.NewRequest("POST", "http://localhost/responses", nil)
- called := false
- _, err := (*AccountTrafficService)(nil).DoHTTP(WithAccountTrafficRequest(req, a), func(*http.Request) (*http.Response, error) {
- called = true
- return &http.Response{StatusCode: 200, Body: http.NoBody}, nil
- })
- require.NoError(t, err)
- require.True(t, called)
- }
- func TestAccountTrafficLeaseLossOnlyCancelsEnforcedRequests(t *testing.T) {
- for _, mode := range []string{"observe", "automatic"} {
- t.Run(mode, func(t *testing.T) {
- cache := &trafficCacheStub{acquired: true}
- policy := DefaultAccountTrafficPolicy()
- policy.AdaptiveEnabled, policy.AdaptiveMode = true, mode
- ctx, permit, err := NewAccountTrafficService(cache).Begin(context.Background(), AccountTrafficPlan{AccountID: 42, Policy: policy})
- require.NoError(t, err)
- closed := false
- permit.loseLease([]func(){func() { closed = true }})
- require.Equal(t, mode == "automatic", closed)
- require.Equal(t, mode == "automatic", ctx.Err() != nil)
- require.Equal(t, 1, cache.finishes)
- permit.Finish(200)
- require.ErrorIs(t, ctx.Err(), context.Canceled)
- require.Equal(t, 1, cache.finishes, "lost telemetry must not count a later successful completion")
- })
- }
- }
- func TestAccountTrafficPassthroughCancellationReleasesTurn(t *testing.T) {
- for _, terminal := range []string{`{"type":"response.cancelled"}`, `{"type":"response.canceled"}`, `{"type":"response.done","response":{"status":"cancelled"}}`} {
- cache := &trafficCacheStub{acquired: true}
- inner := &trafficFrameStub{payload: []byte(terminal)}
- policy := DefaultAccountTrafficPolicy()
- policy.StrictRPMEnabled = true
- frame := &accountTrafficFrameConn{inner: inner, control: NewAccountTrafficService(cache), plan: AccountTrafficPlan{AccountID: 42, Policy: policy}, ctx: context.Background()}
- require.NoError(t, frame.WriteFrame(context.Background(), coderws.MessageText, []byte(`{"type":"response.create"}`)))
- _, _, err := frame.ReadFrame(context.Background())
- require.NoError(t, err)
- require.Equal(t, 499, cache.status)
- require.NoError(t, frame.WriteFrame(context.Background(), coderws.MessageText, []byte(`{"type":"response.create"}`)), "cancelled turn must not block the next request")
- require.NoError(t, frame.Close())
- }
- }
- func TestAccountTrafficIncompleteStreamIsNotRecoveryEvidence(t *testing.T) {
- for _, payload := range []string{`{"type":"response.cancelled"}`, `{"type":"response.done","response":{"status":"failed"}}`, `{"type":"error","error":{"code":"invalid_request"}}`} {
- cache := &trafficCacheStub{acquired: true}
- policy := DefaultAccountTrafficPolicy()
- policy.AdaptiveEnabled = true
- _, permit, err := NewAccountTrafficService(cache).Begin(context.Background(), AccountTrafficPlan{AccountID: 42, Policy: policy})
- require.NoError(t, err)
- body := &accountTrafficBody{ReadCloser: io.NopCloser(strings.NewReader("data: " + payload + "\n\ndata: [DONE]\n\n")), permit: permit, status: 200, sse: true}
- _, err = io.ReadAll(body)
- require.NoError(t, err)
- require.NoError(t, body.Close())
- require.Equal(t, 499, cache.status)
- }
- }
|