Ctrl K
ComunidadGuías…

internal/rest/ai_timeout_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package rest_test

import (
	"context"
	"io"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/go-chi/chi/v5"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"

	"github.com/sarathsp06/sparrow/internal/ai"
	"github.com/sarathsp06/sparrow/internal/rest"
	"github.com/sarathsp06/sparrow/internal/webhooks/store"
)

// slowDrafter answers after delay, or gives up when ctx ends first.
type slowDrafter struct{ delay time.Duration }

func (s slowDrafter) DraftTemplate(ctx context.Context, _ ai.Request) (*ai.Result, error) {
	select {
	case <-time.After(s.delay):
		return &ai.Result{Template: `{"id":"{{ .payload.id }}"}`, Attempts: 1}, nil
	case <-ctx.Done():
		return nil, ctx.Err()
	}
}
func (slowDrafter) Model() string    { return "slow" }
func (slowDrafter) Provider() string { return "anthropic" }

// draftOverServer posts a draft through a real http.Server whose
// WriteTimeout is shorter than the draft, behind the same response-writer
// wrapping middleware as in production.
func draftOverServer(t *testing.T, d rest.TemplateDrafter, timeout time.Duration) (int, string) {
	t.Helper()
	r := chi.NewRouter()
	r.Use(otelhttp.NewMiddleware("test"))
	svc := eventOnlyService{events: map[string]*store.EventRegistration{
		"order.created": {Name: "order.created", Schema: map[string]any{"type": "object"}, SamplePayload: map[string]any{"id": "ord_1"}},
	}}
	rest.Mount(r, svc, rest.AccessDeps{}, rest.AIDeps{Drafter: d, Timeout: timeout}, nil)
	srv := httptest.NewUnstartedServer(r)
	srv.Config.WriteTimeout = 200 * time.Millisecond
	srv.Start()
	defer srv.Close()

	resp, err := http.Post(srv.URL+"/v1/subscriptions:draftTemplate", "application/json",
		strings.NewReader(`{"event_name":"order.created","instructions":"just the id"}`))
	if err != nil {
		t.Fatalf("request failed (connection dropped by the write timeout?): %v", err)
	}
	defer func() { _ = resp.Body.Close() }()
	body, _ := io.ReadAll(resp.Body)
	return resp.StatusCode, string(body)
}

func TestDraftTemplate_OutlivesServerWriteTimeout(t *testing.T) {
	code, body := draftOverServer(t, slowDrafter{delay: 600 * time.Millisecond}, 5*time.Second)
	if code != http.StatusOK {
		t.Fatalf("status %d, want 200: %s", code, body)
	}
}

func TestDraftTemplate_TimeoutIs504(t *testing.T) {
	code, body := draftOverServer(t, slowDrafter{delay: time.Minute}, 300*time.Millisecond)
	if code != http.StatusGatewayTimeout {
		t.Fatalf("status %d, want 504: %s", code, body)
	}
	if !strings.Contains(body, "SPARROW_AI_TIMEOUT") {
		t.Fatalf("504 does not say how to raise the limit: %s", body)
	}
}