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
+122
View File
@@ -0,0 +1,122 @@
package security
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/go-chi/chi/v5"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"github.com/yan1h/agent-coding-workflow/internal/jobs"
)
type fakeRotator struct {
active int
rotated bool
}
func (f *fakeRotator) RotateActive(_ context.Context, _, _ string) (int, error) {
f.active++
f.rotated = true
return f.active, nil
}
func (f *fakeRotator) ActiveVersion(context.Context) (int, error) { return f.active, nil }
type fakeEnqueuer struct{ enqueued jobs.JobType }
func (f *fakeEnqueuer) Enqueue(_ context.Context, typ jobs.JobType, _ json.RawMessage, _ time.Time, _ int32) (*jobs.Job, error) {
f.enqueued = typ
return &jobs.Job{ID: uuid.New(), Type: typ}, nil
}
type fakeStatus struct{ stale map[string]int }
func (f *fakeStatus) Tables() []string { return []string{"workspaces"} }
func (f *fakeStatus) StaleCount(_ context.Context, t string, _ int) (int, error) {
return f.stale[t], nil
}
// fakeResolver maps a fixed token to a fixed user id.
type fakeResolver struct{ uid uuid.UUID }
func (f fakeResolver) ResolveSession(_ context.Context, token string) (uuid.UUID, bool, error) {
if token == "good" {
return f.uid, true, nil
}
return uuid.Nil, false, nil
}
type fakeAdmin struct{ admins map[uuid.UUID]bool }
func (f fakeAdmin) IsAdmin(_ context.Context, id uuid.UUID) (bool, error) {
return f.admins[id], nil
}
func mountHandler(t *testing.T, admin bool) (*chi.Mux, *fakeRotator, *fakeEnqueuer) {
t.Helper()
uid := uuid.New()
rot := &fakeRotator{active: 1}
enq := &fakeEnqueuer{}
st := &fakeStatus{stale: map[string]int{"workspaces": 2}}
h := NewHandler(rot, enq, st, fakeResolver{uid: uid}, fakeAdmin{admins: map[uuid.UUID]bool{uid: admin}}, nil)
r := chi.NewRouter()
h.Mount(r)
return r, rot, enq
}
func TestRotateKey_AdminEnqueuesReencrypt(t *testing.T) {
r, rot, enq := mountHandler(t, true)
req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/security/rotate-key", nil)
req.Header.Set("Authorization", "Bearer good")
rr := httptest.NewRecorder()
r.ServeHTTP(rr, req)
require.Equal(t, http.StatusAccepted, rr.Code)
require.True(t, rot.rotated)
require.Equal(t, jobs.TypeSecretReencrypt, enq.enqueued)
var resp rotateKeyResp
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
require.Equal(t, 2, resp.ActiveVersion)
require.NotEmpty(t, resp.JobID)
}
func TestRotateKey_NonAdminForbidden(t *testing.T) {
r, rot, enq := mountHandler(t, false)
req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/security/rotate-key", nil)
req.Header.Set("Authorization", "Bearer good")
rr := httptest.NewRecorder()
r.ServeHTTP(rr, req)
require.Equal(t, http.StatusForbidden, rr.Code)
require.False(t, rot.rotated)
require.Empty(t, enq.enqueued)
}
func TestRotateKey_Unauthenticated(t *testing.T) {
r, _, _ := mountHandler(t, true)
req := httptest.NewRequest(http.MethodPost, "/api/v1/admin/security/rotate-key", nil)
req.Header.Set("Authorization", "Bearer bad")
rr := httptest.NewRecorder()
r.ServeHTTP(rr, req)
require.Equal(t, http.StatusUnauthorized, rr.Code)
}
func TestReencryptStatus_ReportsStale(t *testing.T) {
r, _, _ := mountHandler(t, true)
req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/security/reencrypt-status", nil)
req.Header.Set("Authorization", "Bearer good")
rr := httptest.NewRecorder()
r.ServeHTTP(rr, req)
require.Equal(t, http.StatusOK, rr.Code)
var resp reencryptStatusResp
require.NoError(t, json.Unmarshal(rr.Body.Bytes(), &resp))
require.Equal(t, 2, resp.Stale["workspaces"])
require.False(t, resp.Complete)
}