package controllers import ( "bytes" "encoding/json" "fmt" "net/http" "net/http/httptest" "os" "testing" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" "reflect" "GoMembership/internal/config" "GoMembership/internal/database" "GoMembership/internal/models" "GoMembership/internal/repositories" "GoMembership/internal/services" "GoMembership/pkg/logger" ) type keyValuePair struct { value interface{} key string } type test struct { name string wantResponse uint16 userEmail string wantDBData []keyValuePair assert bool input string } func TestUserController(t *testing.T) { err := database.InitDB("test.db") if err != nil { t.Errorf("Failed to create DB: %#v", err) } cfg := config.LoadConfig() emailService := services.NewEmailService(cfg.SMTP.Host, cfg.SMTP.Port, cfg.SMTP.User, cfg.SMTP.Password, cfg.SMTP.AdminEmail) var consentRepo repositories.ConsentRepositoryInterface = &repositories.ConsentRepository{} consentService := &services.ConsentService{Repo: consentRepo} var bankAccountRepo repositories.BankAccountRepositoryInterface = &repositories.BankAccountRepository{} bankAccountService := &services.BankAccountService{Repo: bankAccountRepo} var membershipRepo repositories.MembershipRepositoryInterface = &repositories.MembershipRepository{} var subscriptionRepo repositories.SubscriptionModelsRepositoryInterface = &repositories.SubscriptionModelsRepository{} membershipService := &services.MembershipService{Repo: membershipRepo, SubscriptionRepo: subscriptionRepo} var userRepo repositories.UserRepositoryInterface = &repositories.UserRepository{} userService := &services.UserService{Repo: userRepo} uc := UserController{Service: userService, EmailService: emailService, ConsentService: consentService, BankAccountService: bankAccountService, MembershipService: membershipService} if err := initSubscriptionPlans(); err != nil { t.Errorf("Failed to init Susbcription plans: %#v", err) } tests := getTestUsers() for _, tt := range tests { c, _ := getMockedContext([]byte(tt.input)) t.Run(tt.name, func(t *testing.T) { uc.RegisterUser(c) user, err := userRepo.FindUserByEmail(tt.userEmail) if err == gorm.ErrRecordNotFound && !tt.assert { //pass } else if err == nil && tt.assert { // check value: if tt.wantDBData != nil { for _, kvp := range tt.wantDBData { /* dbValue, err := getFieldValue(*user, kvp.key) if err != nil { t.Errorf("getFieldValue failed: %#v", err) } */ dbValue, err := getAttr(&user, kvp.key) if err != nil { t.Errorf("Couldn't get Attribute: %#v", err) } if kvp.value != dbValue { // fail t.Errorf("Value is not expected: %v != %v", kvp.value, dbValue) } } } } else { // fail t.Errorf("FindUserByEmail failed: %#v", err) } }) } if err := deleteTestDB("test.db"); err != nil { t.Errorf("Failed to tear down DB: %#v", err) } } func initSubscriptionPlans() error { subscription := models.SubscriptionModel{ Name: "basic", } result := database.DB.Create(&subscription) if result.Error != nil { return result.Error } return nil } func getMockedContext(jsonStr []byte) (*gin.Context, *httptest.ResponseRecorder) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) c.Request, _ = http.NewRequest("POST", "/register", bytes.NewBuffer(jsonStr)) c.Request.Header.Set("Content-Type", "application/json") return c, w } func getAttr(obj interface{}, fieldName string) (interface{}, error) { pointToStruct := reflect.ValueOf(obj) // addressable curStruct := pointToStruct.Elem() if curStruct.Kind() != reflect.Struct { return nil, fmt.Errorf("expected a struct, but got %v", curStruct.Kind()) } curField := curStruct.FieldByName(fieldName) // type: reflect.Value if !curField.IsValid() { return nil, fmt.Errorf("no such field: %s in user", fieldName) } return curField.Interface(), nil } /* func getFieldValue(user models.User, fieldName string) (interface{}, error) { v := reflect.ValueOf(user) if v.Kind() == reflect.Ptr { v = v.Elem() } if v.Kind() != reflect.Struct { return nil, fmt.Errorf("expected a struct, but got %v", v.Kind()) } fieldVal := v.FieldByName(fieldName) if !fieldVal.IsValid() { return nil, fmt.Errorf("no such field: %s in user", fieldName) } return fieldVal.Interface(), nil } */ func deleteTestDB(dbPath string) error { err := os.Remove(dbPath) if err != nil { return err } return nil } // TEST DATA: func generateUserJSON(user models.User) string { data, err := json.Marshal(user) if err != nil { logger.Error.Printf("couldn't generate Json from Uer: %#v\nERROR: %#v", user, err) return "" } return string(data) } func getTestUsers() []test { return []test{ { name: "birthday < 18 should fail", wantResponse: http.StatusNotAcceptable, wantDBData: []keyValuePair{ { key: "Email", value: "john.doe@example.com"}, }, assert: false, input: generateUserJSON( models.User{ DateOfBirth: time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC), FirstName: "John", LastName: "Doe", Email: "john.doe@example.com", Address: "123 Main St", ZipCode: "12345", City: "Cityville", Phone: "123-456-7890", BankAccount: models.BankAccount{IBAN: "DE89370400440532013000"}, Membership: models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}}, ProfilePicture: "http://example.com/profile.jpg", Company: nil, Notes: nil, Password: "password123", ID: 1, PaymentStatus: 1, Status: 1, RoleID: 1, }), }, { name: "FirstName empty, should fail", wantResponse: http.StatusNotAcceptable, wantDBData: []keyValuePair{ { key: "Email", value: "john.doe@example.com"}, }, assert: false, input: generateUserJSON( models.User{ DateOfBirth: time.Date(1990, time.January, 1, 0, 0, 0, 0, time.UTC), FirstName: "", LastName: "Doe", Email: "john.doe@example.com", Address: "123 Main St", ZipCode: "12345", City: "Cityville", Phone: "123-456-7890", BankAccount: models.BankAccount{IBAN: "DE89370400440532013000"}, Membership: models.Membership{SubscriptionModel: models.SubscriptionModel{Name: "Basic"}}, ProfilePicture: "http://example.com/profile.jpg", Company: nil, Notes: nil, Password: "password123", ID: 1, PaymentStatus: 1, Status: 1, RoleID: 1, }), }, /* Vjjjjjjjj models.User { DateOfBirth: time.Date(1985, time.March, 15, 0, 0, 0, 0, time.UTC), FirstName: "Jane", LastName: "Smith", Email: "jane.smith@example.com", Address: "456 Oak St", ZipCode: "67890", City: "Townsville", Phone: "098-765-4321", BankAccount: models.BankAccount{IBAN: "FR7630006000011234567890189"}, Membership: models.Membership{SubscriptionModel: SubscriptionModel{Name: "Premium", Details: "Premium Subscription", Conditions: "None", MonthlyFee: 19.99, HourlyRate: 25.00}}, ProfilePicture: "http://example.com/profile2.jpg", Company: stringPtr("ExampleCorp"), Notes: stringPtr("Important client"), Password: "securepassword456", ID: 2, PaymentStatus: 0, Status: 2, RoleID: 2, }, { DateOfBirth: time.Date(2000, time.July, 30, 0, 0, 0, 0, time.UTC), FirstName: "Alice", LastName: "Brown", Email: "alice.brown@example.com", Address: "789 Pine St", ZipCode: "11223", City: "Villageville", Phone: "555-555-5555", BankAccount: models.BankAccount{IBAN: "GB29NWBK60161331926819"}, Membership: models.Membership{SubscriptionModel: SubscriptionModel{Name: "Standard", Details: "Standard Subscription", Conditions: "None", MonthlyFee: 14.99, HourlyRate: 20.00}}, ProfilePicture: "", Company: stringPtr("AnotherCorp"), Notes: nil, Password: "mypassword789", ID: 3, PaymentStatus: 1, Status: 1, RoleID: 3, }, */ } }