package admin
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/Wei-Shaw/sub2api/internal/server/middleware"
"github.com/Wei-Shaw/sub2api/internal/service"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
)
type intelligentHandlerRepo struct {
service.IntelligentTestRepository
admin bool
record *service.IntelligentTestRecord
calls int
}
func (r *intelligentHandlerRepo) IsAdmin(context.Context, int64) (bool, error) { return r.admin, nil }
func (r *intelligentHandlerRepo) Get(context.Context, int64) (*service.IntelligentTestRecord, error) {
r.calls++
return r.record, nil
}
func (r *intelligentHandlerRepo) Accounts(context.Context, service.IntelligentTestFilter) (*service.IntelligentTestAccounts, error) {
r.calls++
return &service.IntelligentTestAccounts{}, nil
}
func intelligentHandlerEngine(repo *intelligentHandlerRepo, role string) *gin.Engine {
gin.SetMode(gin.TestMode)
engine := gin.New()
h := NewIntelligentTestHandler(service.NewIntelligentTestService(repo, nil))
engine.Use(func(c *gin.Context) {
if role != "" {
c.Set(string(middleware.ContextKeyUser), middleware.AuthSubject{UserID: 1})
c.Set(string(middleware.ContextKeyUserRole), role)
}
})
engine.GET("/accounts", h.Accounts)
engine.GET("/records/:id", h.Get)
engine.GET("/records/:id/image", h.Image)
engine.POST("/run", h.Run)
engine.POST("/evaluate-preview", h.PreviewEvaluation)
engine.POST("/records/:id/cancel", h.Cancel)
engine.POST("/records/:id/reevaluate", h.Reevaluate)
return engine
}
func TestIntelligentHandlerAdminAuthorization(t *testing.T) {
for _, tc := range []struct {
role string
dbAdmin bool
want int
}{{"", false, 401}, {"user", false, 403}, {"admin", false, 403}, {"admin", true, 200}, {"super_admin", true, 200}} {
t.Run(tc.role, func(t *testing.T) {
repo := &intelligentHandlerRepo{admin: tc.dbAdmin}
engine := intelligentHandlerEngine(repo, tc.role)
w := httptest.NewRecorder()
engine.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/accounts", nil))
require.Equal(t, tc.want, w.Code)
if tc.want != 200 {
require.Zero(t, repo.calls)
}
})
}
}
func TestIntelligentHandlerRejectsMalformedInputs(t *testing.T) {
repo := &intelligentHandlerRepo{admin: true}
engine := intelligentHandlerEngine(repo, "admin")
for _, path := range []string{"/records/-1", "/accounts?anti_degradation=maybe", "/accounts?from=tomorrow", "/accounts?page=0", "/accounts?group_id=-1"} {
w := httptest.NewRecorder()
engine.ServeHTTP(w, httptest.NewRequest(http.MethodGet, path, nil))
require.Equal(t, 400, w.Code, path)
}
w := httptest.NewRecorder()
engine.ServeHTTP(w, httptest.NewRequest(http.MethodPost, "/run", strings.NewReader(`{"account_ids":[1,1],"test_types":["candy"],"idempotency_key":"repeat-operation-key"}`)))
require.Equal(t, 400, w.Code)
require.Zero(t, repo.calls)
}
func TestIntelligentHandlerImageSandboxAndValidation(t *testing.T) {
repo := &intelligentHandlerRepo{admin: true, record: &service.IntelligentTestRecord{ResultImage: ``}}
engine := intelligentHandlerEngine(repo, "admin")
w := httptest.NewRecorder()
engine.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/records/1/image", nil))
require.Equal(t, 200, w.Code)
require.Contains(t, w.Header().Get("Content-Type"), "image/svg+xml")
require.Contains(t, w.Header().Get("Content-Security-Policy"), "sandbox")
require.Contains(t, w.Header().Get("Cache-Control"), "no-store")
repo.record.ResultImage = ``
w = httptest.NewRecorder()
engine.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/records/1/image", nil))
require.Equal(t, 404, w.Code)
require.NotContains(t, w.Body.String(), "onload")
}
func TestIntelligentHandlerHistoricalImageAndNewActionsAuthorization(t *testing.T) {
repo := &intelligentHandlerRepo{admin: true, record: &service.IntelligentTestRecord{Result: ``}}
engine := intelligentHandlerEngine(repo, "admin")
w := httptest.NewRecorder()
engine.ServeHTTP(w, httptest.NewRequest(http.MethodGet, "/records/1/image", nil))
require.Equal(t, 200, w.Code)
require.Contains(t, w.Body.String(), `fill="orange"`)
require.NotContains(t, w.Body.String(), "script")
require.NotContains(t, w.Body.String(), "