package controllers import ( "fmt" "net/http" "net/http/httptest" "testing" "GoMembership/internal/constants" "GoMembership/internal/database" "GoMembership/internal/models" "GoMembership/pkg/logger" "github.com/gin-gonic/gin" ) type RegisterSubscriptionTest struct { WantDBData map[string]interface{} Input string Name string WantResponse int Assert bool } type UpdateSubscriptionTest struct { WantDBData map[string]interface{} Input string Name string WantResponse int Assert bool } type DeleteSubscriptionTest struct { WantDBData map[string]interface{} Input string Name string WantResponse int Assert bool } type MockUserController struct { UserController // Embed the UserController } func (m *MockUserController) ExtractUserFromContext(c *gin.Context) (*models.User, error) { return &models.User{ ID: 1, FirstName: "Admin", LastName: "User", Email: "admin@test.com", RoleID: constants.Roles.Admin, }, nil } func setupMockAuth() { // Create and assign the mock controller mockController := &MockUserController{} Mc.UserController = mockController } func testMembershipController(t *testing.T) { setupMockAuth() tests := getSubscriptionRegistrationData() for _, tt := range tests { logger.Error.Print("==============================================================") logger.Error.Printf("MembershipController : %v", tt.Name) logger.Error.Print("==============================================================") t.Run(tt.Name, func(t *testing.T) { if err := runSingleTest(&tt); err != nil { t.Errorf("Test failed: %v", err.Error()) } }) } updateTests := getSubscriptionUpdateData() for _, tt := range updateTests { logger.Error.Print("==============================================================") logger.Error.Printf("Update SubscriptionData : %v", tt.Name) logger.Error.Print("==============================================================") t.Run(tt.Name, func(t *testing.T) { if err := runSingleTest(&tt); err != nil { t.Errorf("Test failed: %v", err.Error()) } }) } deleteTests := getSubscriptionDeleteData() for _, tt := range deleteTests { logger.Error.Print("==============================================================") logger.Error.Printf("Delete SubscriptionData : %v", tt.Name) logger.Error.Print("==============================================================") t.Run(tt.Name, func(t *testing.T) { if err := runSingleTest(&tt); err != nil { t.Errorf("Test failed: %v", err.Error()) } }) } } func (rt *RegisterSubscriptionTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder, *gin.Engine) { return GetMockedJSONContext([]byte(rt.Input), "api/subscription") } func (rt *RegisterSubscriptionTest) RunHandler(c *gin.Context, router *gin.Engine) { Mc.RegisterSubscription(c) } func (rt *RegisterSubscriptionTest) ValidateResponse(w *httptest.ResponseRecorder) error { if w.Code != rt.WantResponse { return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, rt.WantResponse) } return nil } func (rt *RegisterSubscriptionTest) ValidateResult() error { return validateSubscription(rt.Assert, rt.WantDBData) } func validateSubscription(assert bool, wantDBData map[string]interface{}) error { subscriptions, err := Mc.Service.GetSubscriptions(wantDBData) if err != nil { return fmt.Errorf("Error in database ops: %#v", err) } if assert != (len(*subscriptions) != 0) { return fmt.Errorf("Subscription entry query didn't met expectation: %v != %#v", assert, *subscriptions) } return nil } func (ut *UpdateSubscriptionTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder, *gin.Engine) { return GetMockedJSONContext([]byte(ut.Input), "api/subscription/upsert") } func (ut *UpdateSubscriptionTest) RunHandler(c *gin.Context, router *gin.Engine) { Mc.UpdateHandler(c) } func (ut *UpdateSubscriptionTest) ValidateResponse(w *httptest.ResponseRecorder) error { if w.Code != ut.WantResponse { return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, ut.WantResponse) } return nil } func (ut *UpdateSubscriptionTest) ValidateResult() error { return validateSubscription(ut.Assert, ut.WantDBData) } func (dt *DeleteSubscriptionTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder, *gin.Engine) { return GetMockedJSONContext([]byte(dt.Input), "api/subscription/delete") } func (dt *DeleteSubscriptionTest) RunHandler(c *gin.Context, router *gin.Engine) { Mc.DeleteSubscription(c) } func (dt *DeleteSubscriptionTest) ValidateResponse(w *httptest.ResponseRecorder) error { if w.Code != dt.WantResponse { return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, dt.WantResponse) } return nil } func (dt *DeleteSubscriptionTest) ValidateResult() error { return validateSubscription(dt.Assert, dt.WantDBData) } func getBaseSubscription() MembershipData { return MembershipData{ // APIKey: config.Auth.APIKEY, Subscription: models.SubscriptionModel{ Name: "Premium", Details: "A subscription detail", MonthlyFee: 12.0, HourlyRate: 14.0, }, } } func customizeSubscription(customize func(MembershipData) MembershipData) MembershipData { subscription := getBaseSubscription() return customize(subscription) } func getSubscriptionRegistrationData() []RegisterSubscriptionTest { return []RegisterSubscriptionTest{ { Name: "Missing details should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Just a Subscription"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Details = "" return subscription })), }, { Name: "Missing model name should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": ""}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Name = "" return subscription })), }, { Name: "Negative monthly fee should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData { sub.Subscription.MonthlyFee = -10.0 return sub })), }, { Name: "Negative hourly rate should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON(customizeSubscription(func(sub MembershipData) MembershipData { sub.Subscription.HourlyRate = -1.0 return sub })), }, { Name: "correct entry should pass", WantResponse: http.StatusCreated, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Conditions = "Some Condition" subscription.Subscription.IncludedPerYear = 0 subscription.Subscription.IncludedPerMonth = 1 return subscription })), }, { Name: "Duplicate subscription name should fail", WantResponse: http.StatusConflict, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: true, // The original subscription should still exist Input: GenerateInputJSON(getBaseSubscription()), }, } } func getSubscriptionUpdateData() []UpdateSubscriptionTest { return []UpdateSubscriptionTest{ { Name: "Modified Monthly Fee, should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium", "monthly_fee": "12"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.MonthlyFee = 123.0 return subscription })), }, { Name: "Missing ID, should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.ID = 0 return subscription })), }, { Name: "Modified Hourly Rate, should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium", "hourly_rate": "14"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.HourlyRate = 3254.0 return subscription })), }, { Name: "IncludedPerYear changed, should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium", "included_per_year": "0"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.IncludedPerYear = 9873.0 return subscription })), }, { Name: "IncludedPerMonth changed, should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium", "included_per_month": "1"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.IncludedPerMonth = 23415.0 return subscription })), }, { Name: "Update non-existent subscription should fail", WantResponse: http.StatusNotFound, WantDBData: map[string]interface{}{"name": "NonExistentSubscription"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Name = "NonExistentSubscription" return subscription })), }, { Name: "Correct Update should pass", WantResponse: http.StatusAccepted, WantDBData: map[string]interface{}{"name": "Premium", "details": "Altered Details"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Details = "Altered Details" subscription.Subscription.Conditions = "Some Condition" subscription.Subscription.IncludedPerYear = 0 subscription.Subscription.IncludedPerMonth = 1 return subscription })), }, } } func getSubscriptionDeleteData() []DeleteSubscriptionTest { var premiumSub, basicSub models.SubscriptionModel database.DB.Where("name = ?", "Premium").First(&premiumSub) database.DB.Where("name = ?", "Basic").First(&basicSub) logger.Error.Printf("premiumSub.ID: %v", premiumSub.ID) logger.Error.Printf("basicSub.ID: %v", basicSub.ID) return []DeleteSubscriptionTest{ { Name: "Delete non-existent subscription should fail", WantResponse: http.StatusNotFound, WantDBData: map[string]interface{}{"name": "NonExistentSubscription"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Name = "NonExistentSubscription" subscription.Subscription.ID = basicSub.ID return subscription })), }, { Name: "Delete subscription without name should fail", WantResponse: http.StatusExpectationFailed, WantDBData: map[string]interface{}{"name": ""}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Name = "" subscription.Subscription.ID = basicSub.ID return subscription })), }, { Name: "Delete subscription with users should fail", WantResponse: http.StatusExpectationFailed, WantDBData: map[string]interface{}{"name": "Basic"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Name = "Basic" subscription.Subscription.ID = basicSub.ID return subscription })), }, { Name: "Delete valid subscription should succeed", WantResponse: http.StatusOK, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription MembershipData) MembershipData { subscription.Subscription.Name = "Premium" subscription.Subscription.ID = premiumSub.ID return subscription })), }, } }