package httpapi import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/easyai/easyai-ai-gateway/apps/api/internal/store" ) func TestOptionalTaskIdempotencyKeyRejectsMultipleValues(t *testing.T) { t.Parallel() request := httptest.NewRequest("POST", "/", nil) if _, present, err := optionalTaskIdempotencyKey(request); err != nil || present { t.Fatalf("missing key present=%v err=%v", present, err) } request.Header.Add("Idempotency-Key", "one") request.Header.Add("Idempotency-Key", "two") if _, _, err := optionalTaskIdempotencyKey(request); err == nil { t.Fatal("multiple keys must be rejected") } } func TestTaskIdempotencyRequestHashIsCanonical(t *testing.T) { t.Parallel() first := map[string]any{"model": "m", "n": float64(1), "nested": map[string]any{"b": true, "a": "x"}} second := map[string]any{"nested": map[string]any{"a": "x", "b": true}, "n": float64(1), "model": "m"} if taskIdempotencyRequestHash("images.generations", false, false, first) != taskIdempotencyRequestHash("images.generations", false, false, second) { t.Fatal("equivalent JSON objects must have the same request hash") } if taskIdempotencyRequestHash("images.generations", true, false, first) == taskIdempotencyRequestHash("images.generations", false, false, first) { t.Fatal("async response semantics must be part of the request hash") } } func TestWriteIdempotentAsyncTaskReplayKeepsEasyAICompatibilityFields(t *testing.T) { t.Parallel() task := store.GatewayTask{ ID: "async-replay-1", Kind: "images.generations", Status: "queued", AsyncMode: true, } recorder := httptest.NewRecorder() writeIdempotentTaskReplay(recorder, task, true) if recorder.Code != http.StatusAccepted || recorder.Header().Get("Idempotent-Replayed") != "true" || recorder.Header().Get("X-Gateway-Task-Id") != task.ID { t.Fatalf("unexpected replay status/headers: code=%d headers=%v", recorder.Code, recorder.Header()) } var got map[string]any if err := json.NewDecoder(recorder.Body).Decode(&got); err != nil { t.Fatal(err) } if got["status"] != "submitted" || got["task_id"] != task.ID || got["taskId"] != task.ID { t.Fatalf("unexpected replay body: %+v", got) } }