304 lines
9.9 KiB
Go
304 lines
9.9 KiB
Go
package handlers
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"git.threesix.ai/jordan/slate-v3-1770514618/pkg/app"
|
|
"git.threesix.ai/jordan/slate-v3-1770514618/pkg/auth"
|
|
"git.threesix.ai/jordan/slate-v3-1770514618/pkg/logging"
|
|
"git.threesix.ai/jordan/slate-v3-1770514618/services/preferences-api/internal/adapter/memory"
|
|
"git.threesix.ai/jordan/slate-v3-1770514618/services/preferences-api/internal/service"
|
|
)
|
|
|
|
func newTestPreferencesHandler() *Preferences {
|
|
repo := memory.NewPreferencesRepository()
|
|
svc := service.NewPreferencesService(repo, logging.Nop())
|
|
return NewPreferences(svc)
|
|
}
|
|
|
|
func withAuthUser(r *http.Request, userID string, roles ...string) *http.Request {
|
|
user := &auth.User{ID: userID, Roles: roles}
|
|
ctx := auth.SetUser(r.Context(), user)
|
|
return r.WithContext(ctx)
|
|
}
|
|
|
|
func setupRouter(handler *Preferences) *chi.Mux {
|
|
r := chi.NewRouter()
|
|
r.Get("/api/preferences-api/preferences/{user_id}", app.Wrap(handler.Get))
|
|
r.Put("/api/preferences-api/preferences/{user_id}", app.Wrap(handler.Update))
|
|
return r
|
|
}
|
|
|
|
func TestPreferences_Get_NotFound(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("expected 404, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Get_InvalidUUID(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/preferences-api/preferences/not-a-uuid", nil)
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Get_Forbidden(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
|
|
r := chi.NewRouter()
|
|
r.Get("/api/preferences-api/preferences/{user_id}", app.Wrap(handler.Get))
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", nil)
|
|
req = withAuthUser(req, "different-user-id")
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusForbidden {
|
|
t.Errorf("expected 403, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Get_AdminAccess(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
|
|
// First create preferences
|
|
r := chi.NewRouter()
|
|
r.Put("/api/preferences-api/preferences/{user_id}", app.Wrap(handler.Update))
|
|
r.Get("/api/preferences-api/preferences/{user_id}", app.Wrap(handler.Get))
|
|
|
|
body := `{"preferences":{"theme":"dark"}}`
|
|
putReq := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", bytes.NewBufferString(body))
|
|
putReq.Header.Set("Content-Type", "application/json")
|
|
putReq = withAuthUser(putReq, "550e8400-e29b-41d4-a716-446655440000")
|
|
putW := httptest.NewRecorder()
|
|
r.ServeHTTP(putW, putReq)
|
|
|
|
// Admin accesses another user's prefs
|
|
getReq := httptest.NewRequest(http.MethodGet, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", nil)
|
|
getReq = withAuthUser(getReq, "admin-user-id", "admin")
|
|
getW := httptest.NewRecorder()
|
|
r.ServeHTTP(getW, getReq)
|
|
|
|
if getW.Code != http.StatusOK {
|
|
t.Errorf("expected 200 for admin access, got %d", getW.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_CreateNew(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
body := `{"preferences":{"theme":"dark","language":"fr"}}`
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Errorf("expected 200, got %d; body: %s", w.Code, w.Body.String())
|
|
}
|
|
|
|
var resp map[string]any
|
|
if err := json.NewDecoder(w.Body).Decode(&resp); err != nil {
|
|
t.Fatalf("failed to decode response: %v", err)
|
|
}
|
|
|
|
data, ok := resp["data"].(map[string]any)
|
|
if !ok {
|
|
t.Fatal("expected 'data' field in response")
|
|
}
|
|
|
|
prefs, ok := data["preferences"].(map[string]any)
|
|
if !ok {
|
|
t.Fatal("expected 'preferences' field in data")
|
|
}
|
|
|
|
if prefs["theme"] != "dark" {
|
|
t.Errorf("expected theme 'dark', got %v", prefs["theme"])
|
|
}
|
|
if prefs["language"] != "fr" {
|
|
t.Errorf("expected language 'fr', got %v", prefs["language"])
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_MergeExisting(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
userID := "550e8400-e29b-41d4-a716-446655440000"
|
|
|
|
// Create initial
|
|
body1 := `{"preferences":{"theme":"dark","language":"fr"}}`
|
|
req1 := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/"+userID, bytes.NewBufferString(body1))
|
|
req1.Header.Set("Content-Type", "application/json")
|
|
w1 := httptest.NewRecorder()
|
|
router.ServeHTTP(w1, req1)
|
|
|
|
// Update only theme
|
|
body2 := `{"preferences":{"theme":"light"}}`
|
|
req2 := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/"+userID, bytes.NewBufferString(body2))
|
|
req2.Header.Set("Content-Type", "application/json")
|
|
w2 := httptest.NewRecorder()
|
|
router.ServeHTTP(w2, req2)
|
|
|
|
if w2.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d; body: %s", w2.Code, w2.Body.String())
|
|
}
|
|
|
|
var resp map[string]any
|
|
_ = json.NewDecoder(w2.Body).Decode(&resp)
|
|
data := resp["data"].(map[string]any)
|
|
prefs := data["preferences"].(map[string]any)
|
|
|
|
if prefs["theme"] != "light" {
|
|
t.Errorf("expected theme 'light', got %v", prefs["theme"])
|
|
}
|
|
if prefs["language"] != "fr" {
|
|
t.Errorf("expected language 'fr' preserved, got %v", prefs["language"])
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_InvalidTheme(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
body := `{"preferences":{"theme":"blue"}}`
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_UnknownPreferenceKey(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
body := `{"preferences":{"theme":"dark","unknown_key":"value"}}`
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 for unknown preference key, got %d; body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_MissingPreferencesField(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
body := `{"theme":"dark"}`
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 for missing preferences field, got %d; body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_EmptyBody(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", nil)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400 for empty body, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_Forbidden(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
|
|
r := chi.NewRouter()
|
|
r.Put("/api/preferences-api/preferences/{user_id}", app.Wrap(handler.Update))
|
|
|
|
body := `{"preferences":{"theme":"dark"}}`
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/550e8400-e29b-41d4-a716-446655440000", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req = withAuthUser(req, "different-user-id")
|
|
w := httptest.NewRecorder()
|
|
r.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusForbidden {
|
|
t.Errorf("expected 403, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_Update_InvalidUUID(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
|
|
body := `{"preferences":{"theme":"dark"}}`
|
|
req := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/not-valid", bytes.NewBufferString(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
w := httptest.NewRecorder()
|
|
router.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("expected 400, got %d", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestPreferences_GetAfterUpdate(t *testing.T) {
|
|
handler := newTestPreferencesHandler()
|
|
router := setupRouter(handler)
|
|
userID := "550e8400-e29b-41d4-a716-446655440000"
|
|
|
|
// Create via PUT
|
|
body := `{"preferences":{"theme":"dark"}}`
|
|
putReq := httptest.NewRequest(http.MethodPut, "/api/preferences-api/preferences/"+userID, bytes.NewBufferString(body))
|
|
putReq.Header.Set("Content-Type", "application/json")
|
|
putW := httptest.NewRecorder()
|
|
router.ServeHTTP(putW, putReq)
|
|
|
|
// GET
|
|
getReq := httptest.NewRequest(http.MethodGet, "/api/preferences-api/preferences/"+userID, nil)
|
|
getW := httptest.NewRecorder()
|
|
router.ServeHTTP(getW, getReq)
|
|
|
|
if getW.Code != http.StatusOK {
|
|
t.Fatalf("expected 200, got %d; body: %s", getW.Code, getW.Body.String())
|
|
}
|
|
|
|
var resp map[string]any
|
|
_ = json.NewDecoder(getW.Body).Decode(&resp)
|
|
data := resp["data"].(map[string]any)
|
|
prefs := data["preferences"].(map[string]any)
|
|
|
|
if prefs["theme"] != "dark" {
|
|
t.Errorf("expected theme 'dark', got %v", prefs["theme"])
|
|
}
|
|
if prefs["language"] != "en" {
|
|
t.Errorf("expected default language 'en', got %v", prefs["language"])
|
|
}
|
|
}
|