This commit is contained in:
2026-06-22 08:55:57 +08:00
parent dbb87823e8
commit 6ade6e8fa9
325 changed files with 41131 additions and 855 deletions
+140
View File
@@ -0,0 +1,140 @@
package changerequest
import (
"bytes"
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/yan1h/agent-coding-workflow/internal/infra/errs"
"github.com/yan1h/agent-coding-workflow/internal/transport/http/middleware"
)
// fakeSvc implements Service for handler tests.
type fakeSvc struct {
mergeErr error
cr *ChangeRequest
}
func (f *fakeSvc) Create(_ context.Context, c Caller, wsID uuid.UUID, _ CreateInput) (*ChangeRequest, error) {
return &ChangeRequest{ID: uuid.New(), WorkspaceID: wsID, Number: 1, State: StateOpen, ReviewVerdict: VerdictPending, CreatedBy: c.UserID}, nil
}
func (f *fakeSvc) Get(_ context.Context, _ Caller, id uuid.UUID) (*ChangeRequest, error) {
return &ChangeRequest{ID: id, State: StateOpen}, nil
}
func (f *fakeSvc) GetByNumber(_ context.Context, _ Caller, _ string, n int) (*ChangeRequest, error) {
return &ChangeRequest{ID: uuid.New(), Number: n}, nil
}
func (f *fakeSvc) ListByWorkspace(_ context.Context, _ Caller, wsID uuid.UUID) ([]*ChangeRequest, error) {
return []*ChangeRequest{{ID: uuid.New(), WorkspaceID: wsID, Number: 1}}, nil
}
func (f *fakeSvc) Review(_ context.Context, _ Caller, id uuid.UUID, v Verdict, _ string) (*ChangeRequest, error) {
return &ChangeRequest{ID: id, ReviewVerdict: v}, nil
}
func (f *fakeSvc) Merge(_ context.Context, _ Caller, id uuid.UUID, _ string) (*ChangeRequest, error) {
if f.mergeErr != nil {
return nil, f.mergeErr
}
return &ChangeRequest{ID: id, State: StateMerged}, nil
}
func (f *fakeSvc) SyncStatus(_ context.Context, _ Caller, id uuid.UUID) (*ChangeRequest, error) {
return &ChangeRequest{ID: id, CIState: CISuccess}, nil
}
type fakeResolver struct{ valid map[string]uuid.UUID }
func (f *fakeResolver) ResolveSession(_ context.Context, token string) (uuid.UUID, bool, error) {
uid, ok := f.valid[token]
return uid, ok, nil
}
type fakeAdmin struct{}
func (fakeAdmin) IsAdmin(_ context.Context, _ uuid.UUID) (bool, error) { return false, nil }
func newTestHandler(svc Service, tok string, uid uuid.UUID) chi.Router {
resolver := &fakeResolver{valid: map[string]uuid.UUID{tok: uid}}
h := NewHandler(svc, resolver, fakeAdmin{})
r := chi.NewRouter()
r.Use(middleware.RequestID)
h.Mount(r)
return r
}
func TestHandler_Unauthenticated(t *testing.T) {
t.Parallel()
r := newTestHandler(&fakeSvc{}, "tok", uuid.New())
req := httptest.NewRequest("GET", "/api/v1/workspaces/"+uuid.NewString()+"/change-requests", nil)
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusUnauthorized, w.Code)
}
func TestHandler_CreateAndList(t *testing.T) {
t.Parallel()
uid := uuid.New()
r := newTestHandler(&fakeSvc{}, "tok", uid)
wsID := uuid.New()
body, _ := json.Marshal(createReq{SourceBranch: "feat", Title: "T"})
req := httptest.NewRequest("POST", "/api/v1/workspaces/"+wsID.String()+"/change-requests", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer tok")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusCreated, w.Code)
req = httptest.NewRequest("GET", "/api/v1/workspaces/"+wsID.String()+"/change-requests", nil)
req.Header.Set("Authorization", "Bearer tok")
w = httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
}
func TestHandler_MergeGateReturns412(t *testing.T) {
t.Parallel()
svc := &fakeSvc{mergeErr: errs.New(errs.CodeFailedPrecondition, "merge blocked: review not approved")}
r := newTestHandler(svc, "tok", uuid.New())
req := httptest.NewRequest("POST", "/api/v1/change-requests/"+uuid.NewString()+"/merge", nil)
req.Header.Set("Authorization", "Bearer tok")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusPreconditionFailed, w.Code)
}
func TestHandler_MergeHappyPath(t *testing.T) {
t.Parallel()
r := newTestHandler(&fakeSvc{}, "tok", uuid.New())
req := httptest.NewRequest("POST", "/api/v1/change-requests/"+uuid.NewString()+"/merge", bytes.NewReader([]byte(`{"method":"squash"}`)))
req.Header.Set("Authorization", "Bearer tok")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
var resp changeRequestResp
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
require.Equal(t, string(StateMerged), resp.State)
}
func TestHandler_Review(t *testing.T) {
t.Parallel()
r := newTestHandler(&fakeSvc{}, "tok", uuid.New())
body, _ := json.Marshal(reviewReq{Verdict: "approved", Note: "lgtm"})
req := httptest.NewRequest("POST", "/api/v1/change-requests/"+uuid.NewString()+"/review", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer tok")
w := httptest.NewRecorder()
r.ServeHTTP(w, req)
require.Equal(t, http.StatusOK, w.Code)
var resp changeRequestResp
require.NoError(t, json.NewDecoder(w.Body).Decode(&resp))
require.Equal(t, "approved", resp.ReviewVerdict)
}