You've already forked agentic-coding-workflow
141 lines
4.8 KiB
Go
141 lines
4.8 KiB
Go
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)
|
|
}
|