fix(api): 400 на битое тело apply, маскирование internal-ошибок, лимит тела

This commit is contained in:
2026-07-03 14:35:43 +07:00
parent fdf90a7c23
commit 05dc586646
2 changed files with 49 additions and 4 deletions
+35
View File
@@ -76,6 +76,41 @@ func TestApplyDefaultsPruneFalse(t *testing.T) {
}
}
func TestApplyEmptyBodyOK(t *testing.T) {
a, m := newTestAPI()
router := NewRouter(a)
did := uuid.New().String()
req := httptest.NewRequest(http.MethodPost,
"/api/v1/projects/00000000-0000-0000-0000-000000000002/domains/"+did+"/apply", nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("status %d body %s", w.Code, w.Body.String())
}
if m.lastReq.ApplyPrunes != false {
t.Fatalf("expected ApplyPrunes=false for empty body, got %+v", m.lastReq)
}
}
func TestApplyMalformedBody(t *testing.T) {
a, _ := newTestAPI()
router := NewRouter(a)
did := uuid.New().String()
body := `{"applyUpdates":`
req := httptest.NewRequest(http.MethodPost,
"/api/v1/projects/00000000-0000-0000-0000-000000000002/domains/"+did+"/apply",
strings.NewReader(body))
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("expected 400 for malformed body, got %d body %s", w.Code, w.Body.String())
}
}
func TestApplyBadUUID(t *testing.T) {
a, _ := newTestAPI()
router := NewRouter(a)
+14 -4
View File
@@ -2,6 +2,9 @@ package api
import (
"encoding/json"
"errors"
"io"
"log"
"net/http"
"github.com/go-chi/chi/v5"
@@ -28,7 +31,8 @@ func (a *API) handleCheck(w http.ResponseWriter, r *http.Request) {
}
cs, err := a.Svc.Check(r.Context(), did)
if err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
log.Printf("api: check failed: %v", err)
writeErr(w, http.StatusInternalServerError, "internal error")
return
}
writeJSON(w, http.StatusOK, toChangesetResponse(cs))
@@ -42,14 +46,20 @@ func (a *API) handleApply(w http.ResponseWriter, r *http.Request) {
}
var req applyRequest
if r.Body != nil {
// пустое тело допустимо → значения по умолчанию (prune=false)
_ = json.NewDecoder(r.Body).Decode(&req)
r.Body = http.MaxBytesReader(w, r.Body, 1<<20) // 1 MiB
// пустое тело допустимо → значения по умолчанию (prune=false);
// любая другая ошибка decode (битый JSON, неверные типы) → 400
if err := json.NewDecoder(r.Body).Decode(&req); err != nil && !errors.Is(err, io.EOF) {
writeErr(w, http.StatusBadRequest, "invalid request body")
return
}
}
cs, err := a.Svc.Apply(r.Context(), did, service.ApplyRequest{
ApplyUpdates: req.ApplyUpdates, ApplyPrunes: req.ApplyPrunes,
})
if err != nil {
writeErr(w, http.StatusInternalServerError, err.Error())
log.Printf("api: apply failed: %v", err)
writeErr(w, http.StatusInternalServerError, "internal error")
return
}
writeJSON(w, http.StatusOK, toChangesetResponse(cs))