package controllers import ( "fmt" "net/http" "net/http/httptest" "testing" "GoMembership/internal/database" "GoMembership/internal/models" "GoMembership/pkg/logger" "github.com/gin-gonic/gin" ) type RegisterSubscriptionTest struct { SetupCookie func(r *http.Request) WantDBData map[string]interface{} Input string Name string WantResponse int Assert bool } type UpdateSubscriptionTest struct { SetupCookie func(r *http.Request) WantDBData map[string]interface{} Input string Name string WantResponse int Assert bool } type DeleteSubscriptionTest struct { SetupCookie func(r *http.Request) WantDBData map[string]interface{} Input string Name string WantResponse int Assert bool } func testMembershipController(t *testing.T) { 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) { rt.SetupCookie(c.Request) 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) { ut.SetupCookie(c.Request) 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) { dt.SetupCookie(c.Request) 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() models.SubscriptionModel { return models.SubscriptionModel{ Name: "Premium", Details: "A subscription detail", MonthlyFee: 12.0, HourlyRate: 14.0, } } func customizeSubscription(customize func(models.SubscriptionModel) models.SubscriptionModel) models.SubscriptionModel { subscription := getBaseSubscription() return customize(subscription) } func getSubscriptionRegistrationData() []RegisterSubscriptionTest { return []RegisterSubscriptionTest{ { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Missing details should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Just a Subscription"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Details = "" return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Missing model name should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": ""}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "" return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Negative monthly fee should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON(customizeSubscription(func(sub models.SubscriptionModel) models.SubscriptionModel { sub.MonthlyFee = -10.0 return sub })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Negative hourly rate should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON(customizeSubscription(func(sub models.SubscriptionModel) models.SubscriptionModel { sub.HourlyRate = -1.0 return sub })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(MemberCookie) }, Name: "correct entry but not authorized", WantResponse: http.StatusUnauthorized, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Conditions = "Some Condition" subscription.IncludedPerYear = 0 subscription.IncludedPerMonth = 1 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "correct entry should pass", WantResponse: http.StatusCreated, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Conditions = "Some Condition" subscription.IncludedPerYear = 0 subscription.IncludedPerMonth = 1 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, 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{ { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, 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 models.SubscriptionModel) models.SubscriptionModel { subscription.MonthlyFee = 123.0 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Missing ID, should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.ID = 0 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, 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 models.SubscriptionModel) models.SubscriptionModel { subscription.HourlyRate = 3254.0 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, 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 models.SubscriptionModel) models.SubscriptionModel { subscription.IncludedPerYear = 9873.0 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, 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 models.SubscriptionModel) models.SubscriptionModel { subscription.IncludedPerMonth = 23415.0 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Update non-existent subscription should fail", WantResponse: http.StatusNotFound, WantDBData: map[string]interface{}{"name": "NonExistentSubscription"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "NonExistentSubscription" return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(MemberCookie) }, Name: "Correct Update but unauthorized", WantResponse: http.StatusUnauthorized, WantDBData: map[string]interface{}{"name": "Premium", "details": "Altered Details"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Details = "Altered Details" subscription.Conditions = "Some Condition" subscription.IncludedPerYear = 0 subscription.IncludedPerMonth = 1 return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Correct Update should pass", WantResponse: http.StatusAccepted, WantDBData: map[string]interface{}{"name": "Premium", "details": "Altered Details"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Details = "Altered Details" subscription.Conditions = "Some Condition" subscription.IncludedPerYear = 0 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) return []DeleteSubscriptionTest{ { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Delete non-existent subscription should fail", WantResponse: http.StatusNotFound, WantDBData: map[string]interface{}{"name": "NonExistentSubscription"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "NonExistentSubscription" subscription.ID = basicSub.ID logger.Error.Printf("subscription to delete: %#v", subscription) return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Delete subscription without name should fail", WantResponse: http.StatusBadRequest, WantDBData: map[string]interface{}{"name": ""}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "" subscription.ID = basicSub.ID return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Delete subscription with users should fail", WantResponse: http.StatusExpectationFailed, WantDBData: map[string]interface{}{"name": "Basic"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "Basic" subscription.ID = basicSub.ID return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(MemberCookie) }, Name: "Delete valid subscription should succeed", WantResponse: http.StatusUnauthorized, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: true, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "Premium" subscription.ID = premiumSub.ID return subscription })), }, { SetupCookie: func(req *http.Request) { req.AddCookie(AdminCookie) }, Name: "Delete valid subscription should succeed", WantResponse: http.StatusOK, WantDBData: map[string]interface{}{"name": "Premium"}, Assert: false, Input: GenerateInputJSON( customizeSubscription(func(subscription models.SubscriptionModel) models.SubscriptionModel { subscription.Name = "Premium" subscription.ID = premiumSub.ID return subscription })), }, } }