internal/handler/subscription_handler_test.go
Rama main · réplica de lectura del repositorio original.
package handler
import (
"errors"
"net/http"
"net/http/httptest"
"testing"
)
// errMockFailure is a standard error used in mock error tests
var errMockFailure = errors.New("mock failure")
func TestNewSubscriptionHandler(t *testing.T) {
handler := NewSubscriptionHandler(nil, createTestLogger())
if handler == nil {
t.Fatal("NewSubscriptionHandler() should not return nil")
}
}
// Removed struct field assignment tests:
// - TestCreateUserSubscriptionRequest_Struct, TestCreateUserSubscriptionRequest_PermanentFree
// - TestCreateOrganizationSubscriptionRequest_Struct
// - TestCancelSubscriptionRequest_Struct
// - TestMarkUserSubscriptionAsPaidRequest_Struct, TestMarkUserSubscriptionAsPaidRequest_NilFields
// - TestSetUserSubscriptionPermanentRequest_Struct
// These tests verified Go struct assignment works, not business logic.
func TestSubscriptionHandler_GetMySubscriptionStatus_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodGet, "/api/subscription/status", "")
rr := httptest.NewRecorder()
handler.GetMySubscriptionStatus(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_CreateUserSubscription_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/user", `{"user_id": 1, "subscription_type": "monthly"}`)
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_CreateUserSubscription_InvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user", "{bad json", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid request body")
}
func TestSubscriptionHandler_CreateUserSubscription_MissingUserID(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user", `{"subscription_type": "monthly"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "user_id is required")
}
func TestSubscriptionHandler_CreateUserSubscription_MissingType(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user", `{"user_id": 2}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "subscription_type is required")
}
func TestSubscriptionHandler_CreateOrganizationSubscription_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/organization", `{"organization_id": 1, "subscription_type": "monthly"}`)
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_CreateOrganizationSubscription_InvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization", "{bad json", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid request body")
}
func TestSubscriptionHandler_CreateOrganizationSubscription_MissingOrgID(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization", `{"subscription_type": "monthly"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "organization_id is required")
}
func TestSubscriptionHandler_CreateOrganizationSubscription_MissingType(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization", `{"organization_id": 1}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "subscription_type is required")
}
func TestSubscriptionHandler_GetUserSubscriptions_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/users/abc", "")
rr := httptest.NewRecorder()
handler.GetUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid user_id")
}
func TestSubscriptionHandler_GetOrganizationSubscriptions_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/organizations/abc", "")
rr := httptest.NewRecorder()
handler.GetOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid org_id")
}
func TestSubscriptionHandler_MarkUserSubscriptionAsPaid_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/user/1/mark-paid", "")
rr := httptest.NewRecorder()
handler.MarkUserSubscriptionAsPaid(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_MarkUserSubscriptionAsPaid_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/abc/mark-paid", `{}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.MarkUserSubscriptionAsPaid(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid subscription ID")
}
func TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/mark-paid", "")
rr := httptest.NewRecorder()
handler.MarkOrganizationSubscriptionAsPaid(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/abc/mark-paid", `{}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.MarkOrganizationSubscriptionAsPaid(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid subscription ID")
}
func TestSubscriptionHandler_CancelUserSubscription_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel", `{"reason": "Test"}`)
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_CancelUserSubscription_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/abc/cancel", `{"reason": "Test"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid subscription ID")
}
func TestSubscriptionHandler_CancelUserSubscription_InvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel", "{bad json", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
// Without chi context, fails on URL param first
assertStatusCode(t, rr, http.StatusBadRequest)
}
func TestSubscriptionHandler_CancelUserSubscription_EmptyReason(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel", `{"reason": ""}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
// Without chi context, fails on URL param first
assertStatusCode(t, rr, http.StatusBadRequest)
}
func TestSubscriptionHandler_CancelOrganizationSubscription_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel", `{"reason": "Test"}`)
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_CancelOrganizationSubscription_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/abc/cancel", `{"reason": "Test"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid subscription ID")
}
func TestSubscriptionHandler_CancelOrganizationSubscription_InvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel", "{bad json", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
// Without chi context, fails on URL param first
assertStatusCode(t, rr, http.StatusBadRequest)
}
func TestSubscriptionHandler_CancelOrganizationSubscription_EmptyReason(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel", `{"reason": ""}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
// Without chi context, fails on URL param first
assertStatusCode(t, rr, http.StatusBadRequest)
}
func TestSubscriptionHandler_SetUserSubscriptionPermanent_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/user/1/permanent", `{"is_permanent": true}`)
rr := httptest.NewRecorder()
handler.SetUserSubscriptionPermanent(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_SetUserSubscriptionPermanent_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/abc/permanent", `{"is_permanent": true}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.SetUserSubscriptionPermanent(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid subscription ID")
}
func TestSubscriptionHandler_SetUserSubscriptionPermanent_InvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/permanent", "{bad json", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.SetUserSubscriptionPermanent(rr, req)
// Without chi context, fails on URL param first
assertStatusCode(t, rr, http.StatusBadRequest)
}
func TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_Unauthorized(t *testing.T) {
handler := &SubscriptionHandler{}
req := createTestRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/permanent", `{"is_permanent": true}`)
rr := httptest.NewRecorder()
handler.SetOrganizationSubscriptionPermanent(rr, req)
assertStatusCode(t, rr, http.StatusUnauthorized)
assertBodyContains(t, rr, "Unauthorized")
}
func TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_InvalidID(t *testing.T) {
handler := &SubscriptionHandler{}
// Without chi router context, URLParam returns empty string
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/abc/permanent", `{"is_permanent": true}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.SetOrganizationSubscriptionPermanent(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid subscription ID")
}
func TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_InvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/permanent", "{bad json", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.SetOrganizationSubscriptionPermanent(rr, req)
// Without chi context, fails on URL param first
assertStatusCode(t, rr, http.StatusBadRequest)
}
// Additional tests for Cancel subscription with valid IDs and empty reason
func TestSubscriptionHandler_CancelUserSubscription_ValidIDEmptyReason(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel", `{"reason": ""}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Cancellation reason is required")
}
func TestSubscriptionHandler_CancelOrganizationSubscription_ValidIDEmptyReason(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel", `{"reason": ""}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Cancellation reason is required")
}
func TestSubscriptionHandler_CancelUserSubscription_ValidIDInvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel", "{bad json", 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid request body")
}
func TestSubscriptionHandler_CancelOrganizationSubscription_ValidIDInvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel", "{bad json", 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid request body")
}
func TestSubscriptionHandler_SetUserSubscriptionPermanent_ValidIDInvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/permanent", "{bad json", 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.SetUserSubscriptionPermanent(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid request body")
}
func TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_ValidIDInvalidJSON(t *testing.T) {
handler := &SubscriptionHandler{}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/permanent", "{bad json", 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.SetOrganizationSubscriptionPermanent(rr, req)
assertStatusCode(t, rr, http.StatusBadRequest)
assertBodyContains(t, rr, "Invalid request body")
}
// Removed 23 panic-expectation tests:
// - TestSubscriptionHandler_GetMySubscriptionStatus_NilService
// - TestSubscriptionHandler_CreateUserSubscription_NilService
// - TestSubscriptionHandler_CreateOrganizationSubscription_NilService
// - TestSubscriptionHandler_ListAllUserSubscriptions_NilService
// - TestSubscriptionHandler_ListAllOrganizationSubscriptions_NilService
// - TestSubscriptionHandler_AllSubscriptionTypes (struct assignment test)
// - TestSubscriptionHandler_GetUserSubscriptions_ValidID
// - TestSubscriptionHandler_GetOrganizationSubscriptions_ValidID
// - TestSubscriptionHandler_MarkUserSubscriptionAsPaid_ValidID
// - TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_ValidID
// - TestSubscriptionHandler_CancelUserSubscription_ValidIDWithReason
// - TestSubscriptionHandler_CancelOrganizationSubscription_ValidIDWithReason
// - TestSubscriptionHandler_SetUserSubscriptionPermanent_ValidID
// - TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_ValidID
// - TestSubscriptionHandler_GetUserSubscriptions_DifferentIDs
// - TestSubscriptionHandler_GetOrganizationSubscriptions_DifferentIDs
// - TestSubscriptionHandler_MarkUserSubscriptionAsPaid_DifferentIDs
// - TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_DifferentIDs
// - TestSubscriptionHandler_ListExpiringUserSubscriptions_NilService
// - TestSubscriptionHandler_ListExpiringUserSubscriptions_QueryParam
// - TestSubscriptionHandler_ListExpiredUserSubscriptions_NilService
// - TestSubscriptionHandler_ListExpiringOrganizationSubscriptions_NilService
// - TestSubscriptionHandler_ListExpiringOrganizationSubscriptions_QueryParam
// - TestSubscriptionHandler_ListExpiredOrganizationSubscriptions_NilService
// These tests verified nil pointer panics, not business logic.
// =============================================================================
// Success Path Tests with Real Service
// =============================================================================
func TestSubscriptionHandler_ListAllUserSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListAllUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
}
func TestSubscriptionHandler_ListAllOrganizationSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListAllOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
}
func TestSubscriptionHandler_ListExpiringUserSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users/expiring", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
assertBodyContains(t, rr, "days")
}
func TestSubscriptionHandler_ListExpiringUserSubscriptions_CustomDays(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
testCases := []struct {
name string
days string
expectedDays string
}{
{"7 days", "7", "7"},
{"14 days", "14", "14"},
{"60 days", "60", "60"},
{"invalid falls to default", "invalid", "30"},
{"negative falls to default", "-5", "30"},
{"zero falls to default", "0", "30"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
url := "/api/admin/subscriptions/users/expiring?days=" + tc.days
req := createAuthenticatedRequest(http.MethodGet, url, "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, tc.expectedDays)
})
}
}
func TestSubscriptionHandler_ListExpiredUserSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users/expired", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiredUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
}
func TestSubscriptionHandler_ListExpiringOrganizationSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations/expiring", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
assertBodyContains(t, rr, "days")
}
func TestSubscriptionHandler_ListExpiringOrganizationSubscriptions_CustomDays(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
testCases := []struct {
name string
days string
expectedDays string
}{
{"7 days", "7", "7"},
{"14 days", "14", "14"},
{"60 days", "60", "60"},
{"invalid falls to default", "invalid", "30"},
{"negative falls to default", "-5", "30"},
{"zero falls to default", "0", "30"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
url := "/api/admin/subscriptions/organizations/expiring?days=" + tc.days
req := createAuthenticatedRequest(http.MethodGet, url, "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, tc.expectedDays)
})
}
}
func TestSubscriptionHandler_ListExpiredOrganizationSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations/expired", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiredOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
}
func TestSubscriptionHandler_GetMySubscriptionStatus_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/subscription/status", "", 1, "test@example.com", "user")
rr := httptest.NewRecorder()
handler.GetMySubscriptionStatus(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "has_access")
}
// =============================================================================
// Error Path Tests with Mock Errors
// =============================================================================
func TestSubscriptionHandler_ListAllUserSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListAllUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get subscriptions")
}
func TestSubscriptionHandler_ListAllUserSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil, // No logger to test nil logger branch
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListAllUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_ListAllOrganizationSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListAllOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get subscriptions")
}
func TestSubscriptionHandler_ListAllOrganizationSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListAllOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_ListExpiredUserSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users/expired", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiredUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get expired subscriptions")
}
func TestSubscriptionHandler_ListExpiredUserSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users/expired", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiredUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_ListExpiredOrganizationSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations/expired", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiredOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get expired subscriptions")
}
func TestSubscriptionHandler_ListExpiredOrganizationSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations/expired", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiredOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_ListExpiringUserSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users/expiring", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get expiring subscriptions")
}
func TestSubscriptionHandler_ListExpiringUserSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/users/expiring", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_ListExpiringOrganizationSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations/expiring", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get expiring subscriptions")
}
func TestSubscriptionHandler_ListExpiringOrganizationSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodGet, "/api/admin/subscriptions/organizations/expiring", "", 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.ListExpiringOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_GetMySubscriptionStatus_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.accessRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodGet, "/api/subscription/status", "", 1, "test@example.com", "user")
rr := httptest.NewRecorder()
handler.GetMySubscriptionStatus(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_GetMySubscriptionStatus_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.accessRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodGet, "/api/subscription/status", "", 1, "test@example.com", "user")
rr := httptest.NewRecorder()
handler.GetMySubscriptionStatus(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
// =============================================================================
// GetUserSubscriptions and GetOrganizationSubscriptions with Service
// =============================================================================
func TestSubscriptionHandler_GetUserSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/users/1", "")
req = addChiURLParam(req, "user_id", "1")
rr := httptest.NewRecorder()
handler.GetUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
}
func TestSubscriptionHandler_GetUserSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/users/1", "")
req = addChiURLParam(req, "user_id", "1")
rr := httptest.NewRecorder()
handler.GetUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get subscriptions")
}
func TestSubscriptionHandler_GetUserSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/users/1", "")
req = addChiURLParam(req, "user_id", "1")
rr := httptest.NewRecorder()
handler.GetUserSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
func TestSubscriptionHandler_GetOrganizationSubscriptions_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/organizations/1", "")
req = addChiURLParam(req, "org_id", "1")
rr := httptest.NewRecorder()
handler.GetOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusOK)
assertBodyContains(t, rr, "subscriptions")
assertBodyContains(t, rr, "count")
}
func TestSubscriptionHandler_GetOrganizationSubscriptions_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/organizations/1", "")
req = addChiURLParam(req, "org_id", "1")
rr := httptest.NewRecorder()
handler.GetOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
assertBodyContains(t, rr, "Failed to get subscriptions")
}
func TestSubscriptionHandler_GetOrganizationSubscriptions_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createTestRequest(http.MethodGet, "/api/admin/subscriptions/organizations/1", "")
req = addChiURLParam(req, "org_id", "1")
rr := httptest.NewRecorder()
handler.GetOrganizationSubscriptions(rr, req)
assertStatusCode(t, rr, http.StatusInternalServerError)
}
// =============================================================================
// MarkUserSubscriptionAsPaid and MarkOrganizationSubscriptionAsPaid with Service
// =============================================================================
func TestSubscriptionHandler_MarkUserSubscriptionAsPaid_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPut, "/api/admin/subscriptions/users/1/paid", `{}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.MarkUserSubscriptionAsPaid(rr, req)
// May return not found or internal server error since mock has no subscriptions
// The service wraps errors so 500 is also possible
// Just verify it doesn't panic and exercises the code path
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_MarkUserSubscriptionAsPaid_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPut, "/api/admin/subscriptions/users/1/paid", `{}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.MarkUserSubscriptionAsPaid(rr, req)
// Service error should result in 500 or specific error status
if rr.Code != http.StatusInternalServerError && rr.Code != http.StatusNotFound {
t.Errorf("Expected StatusInternalServerError or StatusNotFound, got %d", rr.Code)
}
}
func TestSubscriptionHandler_MarkUserSubscriptionAsPaid_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.userSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPut, "/api/admin/subscriptions/users/1/paid", `{}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.MarkUserSubscriptionAsPaid(rr, req)
// Service error should result in 500 or specific error status
if rr.Code != http.StatusInternalServerError && rr.Code != http.StatusNotFound {
t.Errorf("Expected StatusInternalServerError or StatusNotFound, got %d", rr.Code)
}
}
func TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPut, "/api/admin/subscriptions/organizations/1/paid", `{}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.MarkOrganizationSubscriptionAsPaid(rr, req)
// May return not found since mock has no subscriptions
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound {
t.Errorf("Expected StatusOK or StatusNotFound, got %d", rr.Code)
}
}
func TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_ServiceError(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPut, "/api/admin/subscriptions/organizations/1/paid", `{}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.MarkOrganizationSubscriptionAsPaid(rr, req)
// Service error should result in 500 or specific error status
if rr.Code != http.StatusInternalServerError && rr.Code != http.StatusNotFound {
t.Errorf("Expected StatusInternalServerError or StatusNotFound, got %d", rr.Code)
}
}
func TestSubscriptionHandler_MarkOrganizationSubscriptionAsPaid_ServiceError_NoLogger(t *testing.T) {
svc, mocks := createTestSubscriptionServiceWithMocks()
mocks.orgSubRepo.SetError(errMockFailure)
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPut, "/api/admin/subscriptions/organizations/1/paid", `{}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.MarkOrganizationSubscriptionAsPaid(rr, req)
// Service error should result in 500 or specific error status
if rr.Code != http.StatusInternalServerError && rr.Code != http.StatusNotFound {
t.Errorf("Expected StatusInternalServerError or StatusNotFound, got %d", rr.Code)
}
}
// =============================================================================
// CreateUserSubscription with Service
// =============================================================================
func TestSubscriptionHandler_CreateUserSubscription_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user",
`{"user_id": 1, "subscription_type": "monthly"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
// May return 201 (created), 400 (already exists), 403 (auth), or 500 (error)
if rr.Code != http.StatusCreated && rr.Code != http.StatusBadRequest && rr.Code != http.StatusForbidden && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusCreated, StatusBadRequest, StatusForbidden, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_CreateUserSubscription_PermanentFree(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user",
`{"user_id": 2, "subscription_type": "permanent_free"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
// Test the permanent_free code path
if rr.Code != http.StatusCreated && rr.Code != http.StatusBadRequest && rr.Code != http.StatusForbidden && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusCreated, StatusBadRequest, StatusForbidden, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_CreateUserSubscription_NoLogger(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user",
`{"user_id": 1, "subscription_type": "annual"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateUserSubscription(rr, req)
// May return 201 (created), 400 (already exists), 403 (auth), or 500 (error)
if rr.Code != http.StatusCreated && rr.Code != http.StatusBadRequest && rr.Code != http.StatusForbidden && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusCreated, StatusBadRequest, StatusForbidden, or StatusInternalServerError, got %d", rr.Code)
}
}
// =============================================================================
// CreateOrganizationSubscription with Service
// =============================================================================
func TestSubscriptionHandler_CreateOrganizationSubscription_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization",
`{"organization_id": 1, "subscription_type": "monthly"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
// May return 201 (created), 400 (already exists), 403 (auth), or 500 (error)
if rr.Code != http.StatusCreated && rr.Code != http.StatusBadRequest && rr.Code != http.StatusForbidden && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusCreated, StatusBadRequest, StatusForbidden, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_CreateOrganizationSubscription_PermanentFree(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization",
`{"organization_id": 2, "subscription_type": "permanent_free"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
// Test the permanent_free code path
if rr.Code != http.StatusCreated && rr.Code != http.StatusBadRequest && rr.Code != http.StatusForbidden && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusCreated, StatusBadRequest, StatusForbidden, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_CreateOrganizationSubscription_NoLogger(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization",
`{"organization_id": 1, "subscription_type": "annual"}`, 1, "admin@example.com", "admin")
rr := httptest.NewRecorder()
handler.CreateOrganizationSubscription(rr, req)
// May return 201 (created), 400 (already exists), 403 (auth), or 500 (error)
if rr.Code != http.StatusCreated && rr.Code != http.StatusBadRequest && rr.Code != http.StatusForbidden && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusCreated, StatusBadRequest, StatusForbidden, or StatusInternalServerError, got %d", rr.Code)
}
}
// =============================================================================
// CancelUserSubscription with Service
// =============================================================================
func TestSubscriptionHandler_CancelUserSubscription_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel",
`{"reason": "User requested cancellation"}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_CancelUserSubscription_NoLogger(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/cancel",
`{"reason": "No longer needed"}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelUserSubscription(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
// =============================================================================
// CancelOrganizationSubscription with Service
// =============================================================================
func TestSubscriptionHandler_CancelOrganizationSubscription_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel",
`{"reason": "Organization requested cancellation"}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_CancelOrganizationSubscription_NoLogger(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/cancel",
`{"reason": "No longer needed"}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.CancelOrganizationSubscription(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
// =============================================================================
// SetUserSubscriptionPermanent with Service
// =============================================================================
func TestSubscriptionHandler_SetUserSubscriptionPermanent_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/permanent",
`{"is_permanent": true}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.SetUserSubscriptionPermanent(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_SetUserSubscriptionPermanent_NoLogger(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/user/1/permanent",
`{"is_permanent": false}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.SetUserSubscriptionPermanent(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
// =============================================================================
// SetOrganizationSubscriptionPermanent with Service
// =============================================================================
func TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_Success(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: createTestLogger(),
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/permanent",
`{"is_permanent": true}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.SetOrganizationSubscriptionPermanent(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}
func TestSubscriptionHandler_SetOrganizationSubscriptionPermanent_NoLogger(t *testing.T) {
svc := createTestSubscriptionService()
handler := &SubscriptionHandler{
subscriptionService: svc,
logger: nil,
}
req := createAuthenticatedRequest(http.MethodPost, "/api/admin/subscriptions/organization/1/permanent",
`{"is_permanent": false}`, 1, "admin@example.com", "admin")
req = addChiURLParam(req, "id", "1")
rr := httptest.NewRecorder()
handler.SetOrganizationSubscriptionPermanent(rr, req)
// May return 200 (success), 404 (not found), or 500 (error)
if rr.Code != http.StatusOK && rr.Code != http.StatusNotFound && rr.Code != http.StatusInternalServerError {
t.Errorf("Expected StatusOK, StatusNotFound, or StatusInternalServerError, got %d", rr.Code)
}
}