add: contactController,tests & refactored tests

This commit is contained in:
$(pass /github/name)
2024-08-18 14:17:17 +02:00
parent 6441ad3ef9
commit ae0ec8bf88
17 changed files with 886 additions and 320 deletions

View File

@@ -15,30 +15,33 @@ type ContactController struct {
EmailService *services.EmailService
}
type contactData struct {
email string `validate:"required,email"`
name string
message string `validate:"required"`
Email string `form:"email" validate:"required,email"`
Name string `form:"name"`
Message string `form:"message" validate:"required"`
Honeypot string `form:"username" validate:"eq="`
}
func (cc *ContactController) RelayContactRequest(c *gin.Context) {
var msgData contactData
if c.Query("username") != "" {
if err := c.ShouldBind(&msgData); err != nil {
// A bot is talking to us
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Not Acceptable"})
return
}
msgData.name = c.Query("name")
msgData.email = c.Query("email")
msgData.message = c.Query("message")
validate := validator.New()
if err := validate.Struct(msgData); err != nil {
logger.Error.Printf("Couldn't validate contact form data: %v", err)
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Couldn't validate contact form data"})
return
}
if err := cc.EmailService.RelayContactFormMessage(&msgData.email, &msgData.name, &msgData.message); err != nil {
if err := cc.EmailService.RelayContactFormMessage(msgData.Email, msgData.Name, msgData.Message); err != nil {
logger.Error.Printf("Couldn't send contact message mail: %v", err)
c.JSON(http.StatusInternalServerError, gin.H{"error": "Couldn't send mail"})
return
}
c.JSON(http.StatusOK, "Your message has been sent")
c.JSON(http.StatusAccepted, "Your message has been sent")
}

View File

@@ -0,0 +1,157 @@
package controllers
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"GoMembership/internal/config"
"GoMembership/internal/constants"
"GoMembership/internal/utils"
"github.com/gin-gonic/gin"
)
type RelayContactRequestTest struct {
Input url.Values
Name string
WantResponse int
Assert bool
}
func TestContactController(t *testing.T) {
tests := getContactData()
for _, tt := range tests {
t.Run(tt.Name, func(t *testing.T) {
if err := runSingleTest(&tt); err != nil {
t.Errorf("Test failed: %v", err.Error())
}
})
}
}
func (rt *RelayContactRequestTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder) {
return GetMockedFormContext(rt.Input, "/contact")
}
func (rt *RelayContactRequestTest) RunHandler(c *gin.Context) {
Cc.RelayContactRequest(c)
}
func (rt *RelayContactRequestTest) 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 *RelayContactRequestTest) ValidateResult() error {
messages := utils.SMTPGetMessages()
for _, message := range messages {
mail, err := utils.DecodeMail(message.MsgRequest())
if err != nil {
return err
}
if strings.Contains(mail.Subject, constants.MailContactSubject) {
if err := checkContactRequestMail(mail, rt); err != nil {
return err
}
} else {
return fmt.Errorf("Subject not expected: %v", mail.Subject)
}
}
return nil
}
func checkContactRequestMail(mail *utils.Email, rt *RelayContactRequestTest) error {
if !strings.Contains(mail.To, config.Recipients.ContactForm) {
return fmt.Errorf("Contact Information didn't reach the admin! Recipient was: %v instead of %v", mail.To, config.Recipients.ContactForm)
}
if !strings.Contains(mail.From, config.SMTP.User) {
return fmt.Errorf("Contact Information was sent from unexpected address! Sender was: %v instead of %v", mail.From, config.SMTP.User)
}
//Check if all the relevant data has been passed to the mail.
if !strings.Contains(mail.Body, rt.Input.Get("name")) {
return fmt.Errorf("User name(%v) has not been rendered in contact mail.", rt.Input.Get("name"))
}
if !strings.Contains(mail.Body, rt.Input.Get("message")) {
return fmt.Errorf("User message(%v) has not been rendered in contact mail.", rt.Input.Get("message"))
}
return nil
}
func getBaseRequest() *url.Values {
return &url.Values{
"username": {""},
"name": {"My-First and-Last-Name"},
"email": {"name@domain.de"},
"message": {"My message to the world"},
}
}
func customizeRequest(updates map[string]string) *url.Values {
form := getBaseRequest()
for key, value := range updates {
form.Set(key, value)
}
return form
}
func getContactData() []RelayContactRequestTest {
return []RelayContactRequestTest{
{
Name: "mail empty, should fail",
WantResponse: http.StatusNotAcceptable,
Assert: false,
Input: *customizeRequest(
map[string]string{
"email": "",
}),
},
{
Name: "mail invalid, should fail",
WantResponse: http.StatusNotAcceptable,
Assert: false,
Input: *customizeRequest(
map[string]string{
"email": "novalid#email.de",
}),
},
{
Name: "No message should fail",
WantResponse: http.StatusNotAcceptable,
Assert: true,
Input: *customizeRequest(
map[string]string{
"message": "",
}),
},
{
Name: "Honeypot set, should fail",
WantResponse: http.StatusNotAcceptable,
Assert: true,
Input: *customizeRequest(
map[string]string{
"username": "I'm a bot",
}),
},
{
Name: "Correct message, should pass",
WantResponse: http.StatusAccepted,
Assert: true,
Input: *customizeRequest(
map[string]string{}),
},
}
}

View File

@@ -0,0 +1,178 @@
package controllers
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strconv"
"testing"
"log"
"github.com/gin-gonic/gin"
"GoMembership/internal/config"
"GoMembership/internal/database"
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"GoMembership/internal/services"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
)
type TestCase interface {
SetupContext() (*gin.Context, *httptest.ResponseRecorder)
RunHandler(*gin.Context)
ValidateResponse(*httptest.ResponseRecorder) error
ValidateResult() error
}
const (
Host = "127.0.0.1"
Port int = 2525
)
var (
Uc *UserController
Mc *MembershipController
Cc *ContactController
)
func TestSuite(t *testing.T) {
_ = deleteTestDB("test.db")
if err := database.InitDB("test.db"); err != nil {
log.Fatalf("Failed to create DB: %#v", err)
}
if err := os.Setenv("SMTP_HOST", Host); err != nil {
log.Fatalf("Error setting environment variable: %v", err)
}
if err := os.Setenv("SMTP_PORT", strconv.Itoa(Port)); err != nil {
log.Fatalf("Error setting environment variable: %v", err)
}
if err := os.Setenv("BASE_URL", "http://"+Host+":2525"); err != nil {
log.Fatalf("Error setting environment variable: %v", err)
}
config.LoadConfig()
utils.SMTPStart(Host, Port)
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password)
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}
Mc = &MembershipController{Service: *membershipService}
Cc = &ContactController{EmailService: emailService}
if err := initSubscriptionPlans(); err != nil {
log.Fatalf("Failed to init Subscription plans: %#v", err)
}
// Run all tests
// code := m.Run()
t.Run("userController", func(t *testing.T) {
TestUserController(t)
})
t.Run("contactController", func(t *testing.T) {
TestContactController(t)
})
t.Run("membershipController", func(t *testing.T) {
TestMembershipController(t)
})
if err := utils.SMTPStop(); err != nil {
log.Fatalf("Failed to stop SMTP Mockup Server: %#v", err)
}
if err := deleteTestDB("test.db"); err != nil {
log.Fatalf("Failed to tear down DB: %#v", err)
}
}
func initSubscriptionPlans() error {
subscription := models.SubscriptionModel{
Name: "Basic",
Details: "Test Plan",
MonthlyFee: 2,
HourlyRate: 3,
}
result := database.DB.Create(&subscription)
if result.Error != nil {
return result.Error
}
return nil
}
func GetMockedJSONContext(jsonStr []byte, url string) (*gin.Context, *httptest.ResponseRecorder) {
gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
var err error
c.Request, err = http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
if err != nil {
log.Fatalf("Failed to create new Request: %#v", err)
}
c.Request.Header.Set("Content-Type", "application/json")
return c, w
}
func GetMockedFormContext(formData url.Values, url string) (*gin.Context, *httptest.ResponseRecorder) {
gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
req, err := http.NewRequest("POST", url, bytes.NewBufferString(formData.Encode()))
if err != nil {
log.Fatalf("Failed to create new Request: %#v", err)
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
c.Request = req
return c, w
}
func deleteTestDB(dbPath string) error {
err := os.Remove(dbPath)
if err != nil {
return err
}
return nil
}
func runSingleTest(tc TestCase) error {
c, w := tc.SetupContext()
tc.RunHandler(c)
if err := tc.ValidateResponse(w); err != nil {
return err
}
return tc.ValidateResult()
}
func GenerateInputJSON(aStruct interface{}) string {
// Marshal the object into JSON
jsonBytes, err := json.Marshal(aStruct)
if err != nil {
logger.Error.Fatalf("Couldn't generate JSON: %#v\nERROR: %#v", aStruct, err)
return ""
}
return string(jsonBytes)
}

View File

@@ -40,15 +40,17 @@ func (mc *MembershipController) RegisterSubscription(c *gin.Context) {
c.JSON(http.StatusExpectationFailed, "API Key is missing")
return
}
logger.Info.Printf("registering subscription: %+v", regData)
// Register Subscription
id, err := mc.Service.RegisterSubscription(&regData.Model)
if err != nil {
logger.Error.Printf("Couldn't register Membershipmodel: %v", err)
c.JSON(http.StatusInternalServerError, "Couldn't register Membershipmodel")
c.JSON(int(id), "Couldn't register Membershipmodel")
return
}
regData.Model.ID = id
logger.Info.Printf("registering subscription: %+v", regData)
c.JSON(http.StatusCreated, gin.H{
"status": "success",
"id": id,
})
}

View File

@@ -0,0 +1,119 @@
package controllers
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
"GoMembership/internal/config"
"GoMembership/internal/models"
"github.com/gin-gonic/gin"
)
type RegisterSubscriptionTest struct {
WantDBData map[string]interface{}
Input string
Name string
WantResponse int
Assert bool
}
func TestMembershipController(t *testing.T) {
tests := getSubscriptionData()
for _, tt := range tests {
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) {
return GetMockedJSONContext([]byte(rt.Input), "register/subscription")
}
func (rt *RegisterSubscriptionTest) RunHandler(c *gin.Context) {
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 getBaseSubscription() MembershipData {
return MembershipData{
APIKey: config.Auth.APIKEY,
Model: models.SubscriptionModel{
Name: "Just a Subscription",
Details: "A subscription detail",
MonthlyFee: 12.0,
HourlyRate: 14.0,
},
}
}
func customizeSubscription(customize func(MembershipData) MembershipData) MembershipData {
subscription := getBaseSubscription()
return customize(subscription)
}
func getSubscriptionData() []RegisterSubscriptionTest {
return []RegisterSubscriptionTest{
{
Name: "No Details should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"name": "Just a Subscription"},
Assert: false,
Input: GenerateInputJSON(
customizeSubscription(func(subscription MembershipData) MembershipData {
subscription.Model.Details = ""
return subscription
})),
},
{
Name: "No Model Name should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"name": ""},
Assert: false,
Input: GenerateInputJSON(
customizeSubscription(func(subscription MembershipData) MembershipData {
subscription.Model.Name = ""
return subscription
})),
},
{
Name: "correct entry should pass",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"name": "Just a Subscription"},
Assert: true,
Input: GenerateInputJSON(
customizeSubscription(func(subscription MembershipData) MembershipData {
subscription.Model.Conditions = "Some Condition"
subscription.Model.IncludedPerYear = 0
subscription.Model.IncludedPerMonth = 1
return subscription
})),
},
}
}

View File

@@ -88,7 +88,7 @@ func (uc *UserController) RegisterUser(c *gin.Context) {
}
// Notify admin of new user registration
if err := uc.EmailService.NotifyAdminOfNewUser(&regData.User); err != nil {
if err := uc.EmailService.SendRegistrationNotification(&regData.User); err != nil {
logger.Error.Printf("Failed to notify admin of new user registration: %v", err)
// Proceed without returning error since user registration is successful
}
@@ -112,6 +112,7 @@ func (uc *UserController) VerifyMailHandler(c *gin.Context) {
c.HTML(http.StatusUnauthorized, "verification_error.html", gin.H{"ErrorMessage": "Emailadresse wurde schon bestätigt. Sollte dies nicht der Fall sein, wende Dich bitte an info@carsharing-hasloh.de."})
return
}
logger.Info.Printf("User: %#v", user)
uc.EmailService.SendWelcomeEmail(user)
c.HTML(http.StatusOK, "verification_success.html", gin.H{"FirstName": user.FirstName})

View File

@@ -1,130 +1,66 @@
package controllers
import (
"bytes"
"encoding/json"
"fmt"
"strconv"
"strings"
"net/http"
"net/http/httptest"
"os"
"net/url"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"github.com/gin-gonic/gin"
smtpmock "github.com/mocktools/go-smtp-mock/v2"
"GoMembership/internal/config"
"GoMembership/internal/constants"
"GoMembership/internal/database"
"GoMembership/internal/models"
"GoMembership/internal/repositories"
"GoMembership/internal/services"
"GoMembership/internal/utils"
"GoMembership/pkg/logger"
)
type test struct {
name string
input string
wantDBData map[string]interface{}
wantResponse uint16
assert bool
type RegisterUserTest struct {
WantDBData map[string]interface{}
Name string
Input string
WantResponse int
Assert bool
}
// type RegistrationData struct {
// User models.User `json:"user"`
// }
func (rt *RegisterUserTest) SetupContext() (*gin.Context, *httptest.ResponseRecorder) {
return GetMockedJSONContext([]byte(rt.Input), "register")
}
const (
Host = "127.0.0.1"
Port int = 2525
User = "alex@mail.de"
Pass = "secret"
AdminMail = "admin@mail.de"
)
func (rt *RegisterUserTest) RunHandler(c *gin.Context) {
Uc.RegisterUser(c)
}
var (
uc UserController
)
func (rt *RegisterUserTest) 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 *RegisterUserTest) ValidateResult() error {
return validateUser(rt.Assert, rt.WantDBData)
}
func TestUserController(t *testing.T) {
_ = deleteTestDB("test.db")
if err := database.InitDB("test.db"); err != nil {
t.Errorf("Failed to create DB: %#v", err)
}
if err := os.Setenv("SMTP_HOST", Host); err != nil {
t.Errorf("Error setting environment variable: %v", err)
}
if err := os.Setenv("SMTP_PORT", strconv.Itoa(Port)); err != nil {
t.Errorf("Error setting environment variable: %v", err)
}
if err := os.Setenv("ADMIN_MAIL", AdminMail); err != nil {
t.Errorf("Error setting environment variable: %v", err)
}
if err := os.Setenv("SMTP_USER", User); err != nil {
t.Errorf("Error setting environment variable: %v", err)
}
if err := os.Setenv("SMTP_PASS", Pass); err != nil {
t.Errorf("Error setting environment variable: %v", err)
}
config.LoadConfig()
utils.SMTPStart(Host, Port)
emailService := services.NewEmailService(config.SMTP.Host, config.SMTP.Port, config.SMTP.User, config.SMTP.Password, config.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 {
t.Run(tt.name, func(t *testing.T) {
t.Run(tt.Name, func(t *testing.T) {
if err := runSingleTest(&tt); err != nil {
t.Errorf("Test failed: %v", err.Error())
}
})
}
if err := utils.SMTPStop(); err != nil {
t.Errorf("Failed to stop SMTP Mockup Server: %#v", err)
}
if err := deleteTestDB("test.db"); err != nil {
t.Errorf("Failed to tear down DB: %#v", err)
}
}
func runSingleTest(tt *test) error {
c, w := getMockedContext([]byte(tt.input))
uc.RegisterUser(c)
if w.Code != int(tt.wantResponse) {
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", w.Code, tt.wantResponse)
}
return validateUser(tt.assert, tt.wantDBData)
}
func validateUser(assert bool, wantDBData map[string]interface{}) error {
users, err := uc.Service.GetUsers(wantDBData)
users, err := Uc.Service.GetUsers(wantDBData)
if err != nil {
return fmt.Errorf("Error in database ops: %#v", err)
}
@@ -137,131 +73,179 @@ func validateUser(assert bool, wantDBData map[string]interface{}) error {
//check for email delivery
messages := utils.SMTPGetMessages()
for _, message := range messages {
if strings.Contains(message.MsgRequest(), constants.MailWelcomeSubject) {
if err := checkWelcomeMail(&message); err != nil {
mail, err := utils.DecodeMail(message.MsgRequest())
if err != nil {
return err
}
if strings.Contains(mail.Subject, constants.MailRegistrationSubject) {
if err := checkRegistrationMail(mail, &(*users)[0]); err != nil {
return err
}
} else if strings.Contains(message.MsgRequest(), constants.MailRegistrationSubject) {
if err := checkRegistrationMail(&message, &(*users)[0]); err != nil {
} else if strings.Contains(mail.Subject, constants.MailVerificationSubject) {
if err := checkVerificationMail(mail, &(*users)[0]); err != nil {
return err
}
} else if strings.Contains(message.MsgRequest(), constants.MailVerificationSubject) {
if err := checkVerificationMail(&message, &(*users)[0]); err != nil {
verifiedUsers, err := Uc.Service.GetUsers(wantDBData)
if err != nil {
return err
}
if (*verifiedUsers)[0].Status != constants.VerifiedStatus {
return fmt.Errorf("Users status isn't verified after email verification. Status is: %#v", (*verifiedUsers)[0].Status)
}
} else {
return fmt.Errorf("Subject not expected: %v", message.MsgRequest())
return fmt.Errorf("Subject not expected: %v", mail.Subject)
}
}
}
return nil
}
func checkWelcomeMail(message *smtpmock.Message) error {
return nil
}
func checkWelcomeMail(message *utils.Email, user *models.User) error {
func checkRegistrationMail(message *smtpmock.Message, user *models.User) error {
if !strings.Contains(message.To, user.Email) {
return fmt.Errorf("Registration Information didn't reach the user! Recipient was: %v instead of %v", message.To, user.Email)
}
if !strings.Contains(message.From, config.SMTP.User) {
return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.From, config.SMTP.User)
}
for _, responses := range message.RcpttoRequestResponse() {
if !strings.Contains(responses[0], AdminMail) {
return fmt.Errorf("Registration Information didn't reach admin! Recipient was: %v instead of %v", responses[0], AdminMail)
}
}
if !strings.Contains(message.MailfromRequest(), User) {
return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.MailfromRequest(), User)
}
//Check if all the relevant data has been passed to the mail.
if !strings.Contains(message.MsgRequest(), user.FirstName+" "+user.LastName) {
return fmt.Errorf("User first and last name(%v) has not been rendered in registration mail.", user.FirstName+" "+user.LastName)
if !strings.Contains(message.Body, user.FirstName) {
return fmt.Errorf("User first name(%v) has not been rendered in registration mail.", user.FirstName)
}
if !strings.Contains(message.MsgRequest(), fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
if !strings.Contains(message.Body, fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
}
if !strings.Contains(message.MsgRequest(), fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
if !strings.Contains(message.Body, fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
}
if user.Company != "" && !strings.Contains(message.MsgRequest(), user.Company) {
if user.Company != "" && !strings.Contains(message.Body, user.Company) {
return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
}
if !strings.Contains(message.MsgRequest(), fmt.Sprintf("Mitgliedsnr:</strong> %v", user.Membership.ID)) {
if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnummer</strong>: %v", user.Membership.ID)) {
return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
}
if !strings.Contains(message.MsgRequest(), user.Address+","+user.ZipCode) {
return fmt.Errorf("Users address(%v) has not been rendered in registration mail.", user.Address+","+user.ZipCode)
}
if !strings.Contains(message.MsgRequest(), user.City) {
return fmt.Errorf("Users city(%v) has not been rendered in registration mail.", user.City)
}
if !strings.Contains(message.MsgRequest(), user.DateOfBirth.Format("20060102")) {
return fmt.Errorf("Users birthday(%v) has not been rendered in registration mail.", user.DateOfBirth.Format("20060102"))
}
if !strings.Contains(message.MsgRequest(), "Email:</strong> "+user.Email) {
return fmt.Errorf("Users email(%v) has not been rendered in registration mail.", user.Email)
}
if !strings.Contains(message.MsgRequest(), user.Phone) {
return fmt.Errorf("Users phone(%v) has not been rendered in registration mail.", user.Phone)
}
if !strings.Contains(message.MsgRequest(), user.BankAccount.IBAN) {
return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN)
}
if !strings.Contains(message.MsgRequest(), config.BaseURL) {
if !strings.Contains(message.Body, config.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL)
}
return nil
}
func checkVerificationMail(message *smtpmock.Message, user *models.User) error {
for _, responses := range message.RcpttoRequestResponse() {
if !strings.Contains(responses[0], "RCPT TO:<"+user.Email) {
return fmt.Errorf("Registration Information didn't reach client! Recipient was: %v instead of %v", responses[0], user.Email)
}
func checkRegistrationMail(message *utils.Email, user *models.User) error {
if !strings.Contains(message.To, config.Recipients.UserRegistration) {
return fmt.Errorf("Registration Information didn't reach admin! Recipient was: %v instead of %v", message.To, config.Recipients.UserRegistration)
}
// the email is encoded with a lowercase %3d while the url.encodeQuery returns an uppercase %3D. Therefore we remove the last char(padded base64 '='
if !strings.Contains(message.MsgRequest(), user.Verification.VerificationToken[:len(user.Verification.VerificationToken)-1]) {
return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, message.MsgRequest())
if !strings.Contains(message.From, config.SMTP.User) {
return fmt.Errorf("Registration Information was sent from unexpected address! Sender was: %v instead of %v", message.From, config.SMTP.User)
}
if !strings.Contains(message.MsgRequest(), config.BaseURL) {
//Check if all the relevant data has been passed to the mail.
if !strings.Contains(message.Body, user.FirstName+" "+user.LastName) {
return fmt.Errorf("User first and last name(%v) has not been rendered in registration mail.", user.FirstName+" "+user.LastName)
}
if !strings.Contains(message.Body, fmt.Sprintf("Preis/Monat</strong>: %v", user.Membership.SubscriptionModel.MonthlyFee)) {
return fmt.Errorf("Users monthly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.MonthlyFee)
}
if !strings.Contains(message.Body, fmt.Sprintf("Preis/h</strong>: %v", user.Membership.SubscriptionModel.HourlyRate)) {
return fmt.Errorf("Users hourly subscription fee(%v) has not been rendered in registration mail.", user.Membership.SubscriptionModel.HourlyRate)
}
if user.Company != "" && !strings.Contains(message.Body, user.Company) {
return fmt.Errorf("Users Company(%v) has not been rendered in registration mail.", user.Company)
}
if !strings.Contains(message.Body, fmt.Sprintf("Mitgliedsnr:</strong> %v", user.Membership.ID)) {
return fmt.Errorf("Users membership Id(%v) has not been rendered in registration mail.", user.Membership.ID)
}
if !strings.Contains(message.Body, user.Address+","+user.ZipCode) {
return fmt.Errorf("Users address(%v) has not been rendered in registration mail.", user.Address+" sv,"+user.ZipCode)
}
if !strings.Contains(message.Body, user.City) {
return fmt.Errorf("Users city(%v) has not been rendered in registration mail.", user.City)
}
if !strings.Contains(message.Body, user.DateOfBirth.Format("20060102")) {
return fmt.Errorf("Users birthday(%v) has not been rendered in registration mail.", user.DateOfBirth.Format("20060102"))
}
if !strings.Contains(message.Body, "Email:</strong> "+user.Email) {
return fmt.Errorf("Users email(%v) has not been rendered in registration mail.", user.Email)
}
if !strings.Contains(message.Body, user.Phone) {
return fmt.Errorf("Users phone(%v) has not been rendered in registration mail.", user.Phone)
}
if !strings.Contains(message.Body, user.BankAccount.IBAN) {
return fmt.Errorf("Users IBAN(%v) has not been rendered in registration mail.", user.BankAccount.IBAN)
}
if !strings.Contains(message.Body, config.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in registration mail.", config.BaseURL)
}
return nil
}
func checkVerificationMail(message *utils.Email, user *models.User) error {
if !strings.Contains(message.To, user.Email) {
return fmt.Errorf("Registration Information didn't reach client! Recipient was: %v instead of %v", message.To, user.Email)
}
verificationURL, err := getVerificationURL(message.Body)
if err != nil {
return fmt.Errorf("Error parsing verification URL: %#v", err.Error())
}
if !strings.Contains(verificationURL, user.Verification.VerificationToken) {
return fmt.Errorf("Users Verification link token(%v) has not been rendered in email verification mail. %v", user.Verification.VerificationToken, verificationURL)
}
if !strings.Contains(message.Body, config.BaseURL) {
return fmt.Errorf("Base Url (%v) has not been rendered in email verification mail.", config.BaseURL)
}
return nil
}
func initSubscriptionPlans() error {
subscription := models.SubscriptionModel{
Name: "Basic",
Details: "Test Plan",
MonthlyFee: 2,
HourlyRate: 3,
}
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)
var err error
c.Request, err = http.NewRequest("POST", "/register", bytes.NewBuffer(jsonStr))
if err != nil {
logger.Error.Fatalf("Failed to create new Request: %#v", err)
}
c.Request.Header.Set("Content-Type", "application/json")
return c, w
}
func deleteTestDB(dbPath string) error {
err := os.Remove(dbPath)
if err != nil {
// open the provided link:
if err := verifyMail(verificationURL); err != nil {
return err
}
messages := utils.SMTPGetMessages()
for _, message := range messages {
mail, err := utils.DecodeMail(message.MsgRequest())
if err != nil {
return err
}
if err := checkWelcomeMail(mail, user); err != nil {
return err
}
}
return nil
}
func verifyMail(verificationURL string) error {
gin.SetMode(gin.TestMode)
router := gin.New()
router.LoadHTMLGlob(filepath.Join(config.Templates.HTMLPath, "*")) // Adjust the path to your HTML templates
router.GET("/backend/verify", Uc.VerifyMailHandler)
wv := httptest.NewRecorder()
cv, _ := gin.CreateTestContext(wv)
var err error
cv.Request, err = http.NewRequest("GET", verificationURL, nil)
if err != nil {
return fmt.Errorf("Failed to create new GET Request: %v", err.Error())
}
router.ServeHTTP(wv, cv.Request)
if wv.Code != 200 {
return fmt.Errorf("Didn't get the expected response code: got: %v; expected: %v", wv.Code, 200)
}
return nil
}
func getVerificationURL(mailBody string) (string, error) {
re := regexp.MustCompile(`"([^"]*verify[^"]*)"`)
// Find the matching URL in the email content
match := re.FindStringSubmatch(mailBody)
if len(match) == 0 {
return "", fmt.Errorf("No mail verification link found in email body: %#v", mailBody)
}
verificationURL, err := url.QueryUnescape(match[1])
if err != nil {
return "", fmt.Errorf("Error decoding URL: %v", err)
}
logger.Info.Printf("VerificationURL: %#v", verificationURL)
return verificationURL, nil
}
// TEST DATA:
@@ -283,158 +267,151 @@ func getBaseUser() models.User {
}
}
func generateInputJSON(customize func(models.User) models.User) string {
func customizeInput(customize func(models.User) models.User) *RegistrationData {
user := getBaseUser()
user = customize(user) // Apply the customization
regData := RegistrationData{User: user}
jsonBytes, err := json.Marshal(regData)
if err != nil {
logger.Error.Printf("couldn't generate Json from User: %#v\nERROR: %#v", regData, err)
return ""
}
return string(jsonBytes)
return &RegistrationData{User: user}
}
func getTestUsers() []test {
return []test{
func getTestUsers() []RegisterUserTest {
return []RegisterUserTest{
{
name: "birthday < 18 should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "birthday < 18 should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.DateOfBirth = time.Date(2020, time.January, 1, 0, 0, 0, 0, time.UTC)
return user
}),
})),
},
{
name: "FirstName empty, should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "FirstName empty, should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.FirstName = ""
return user
}),
})),
},
{
name: "LastName Empty should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "LastName Empty should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.LastName = ""
return user
}),
})),
},
{
name: "EMail wrong format should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "johnexample.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "EMail wrong format should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "johnexample.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "johnexample.com"
return user
}),
})),
},
{
name: "Missing Zip Code should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Missing Zip Code should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.ZipCode = ""
return user
}),
})),
},
{
name: "Missing Address should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Missing Address should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Address = ""
return user
}),
})),
},
{
name: "Missing City should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Missing City should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.City = ""
return user
}),
})),
},
{
name: "Missing IBAN should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Missing IBAN should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.BankAccount.IBAN = ""
return user
}),
})),
},
{
name: "Invalid IBAN should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Invalid IBAN should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.BankAccount.IBAN = "DE1234234123134"
return user
}),
})),
},
{
name: "Missing subscription plan should fail",
wantResponse: http.StatusNotAcceptable,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Missing subscription plan should fail",
WantResponse: http.StatusNotAcceptable,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Membership.SubscriptionModel.Name = ""
return user
}),
})),
},
{
name: "Invalid subscription plan should fail",
wantResponse: http.StatusNotFound,
wantDBData: map[string]interface{}{"email": "john.doe@example.com"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Invalid subscription plan should fail",
WantResponse: http.StatusNotFound,
WantDBData: map[string]interface{}{"email": "john.doe@example.com"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Membership.SubscriptionModel.Name = "NOTEXISTENTPLAN"
return user
}),
})),
},
{
name: "Correct Entry should pass",
wantResponse: http.StatusCreated,
wantDBData: map[string]interface{}{"Email": "john.doe@example.com"},
assert: true,
input: generateInputJSON(func(user models.User) models.User { return user }),
Name: "Correct Entry should pass",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"Email": "john.doe@example.com"},
Assert: true,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User { return user })),
},
{
name: "Email duplicate should fail",
wantResponse: http.StatusConflict,
wantDBData: map[string]interface{}{"first_name": "Jane"},
assert: false,
input: generateInputJSON(func(user models.User) models.User {
Name: "Email duplicate should fail",
WantResponse: http.StatusConflict,
WantDBData: map[string]interface{}{"first_name": "Jane"},
Assert: false,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.FirstName = "Jane"
return user
}),
})),
},
{
name: "Company present should pass",
wantResponse: http.StatusCreated,
wantDBData: map[string]interface{}{"Email": "john.doe2@example.com"},
assert: true,
input: generateInputJSON(func(user models.User) models.User {
Name: "Company present should pass",
WantResponse: http.StatusCreated,
WantDBData: map[string]interface{}{"Email": "john.doe2@example.com"},
Assert: true,
Input: GenerateInputJSON(customizeInput(func(user models.User) models.User {
user.Email = "john.doe2@example.com"
user.Company = "ACME"
return user
}),
})),
},
}
}