package server import ( "bytes" "encoding/json" "fmt" "github.com/gofiber/fiber/v2" "github.com/oapi-codegen/runtime/types" "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" "gitlab.mareshq.com/hq/yggdrasil/internal/money" "gitlab.mareshq.com/hq/yggdrasil/pkg/slug" "gitlab.mareshq.com/hq/yggdrasil/pkg/training" "net/http" "net/http/httptest" "net/url" "testing" "time" ) func doGet(t *testing.T, app *fiber.App, rawURL string) (*http.Response, error) { u, err := url.Parse(rawURL) if err != nil { t.Fatalf("Invalid url: %s", rawURL) } req := httptest.NewRequest("GET", u.RequestURI(), nil) req.Header.Add("Accept", "application/json") req.Host = u.Host return app.Test(req) } func doPost(t *testing.T, app *fiber.App, rawURL string, jsonBody interface{}) (*http.Response, error) { u, err := url.Parse(rawURL) if err != nil { t.Fatalf("Invalid url: %s", rawURL) } buf, err := json.Marshal(jsonBody) if err != nil { return nil, err } req := httptest.NewRequest("POST", u.RequestURI(), bytes.NewReader(buf)) req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") req.Host = u.Host return app.Test(req) } func doPut(t *testing.T, app *fiber.App, rawURL string, jsonBody interface{}) (*http.Response, error) { u, err := url.Parse(rawURL) if err != nil { t.Fatalf("Invalid url: %s", rawURL) } buf, err := json.Marshal(jsonBody) if err != nil { return nil, err } req := httptest.NewRequest("PUT", u.RequestURI(), bytes.NewReader(buf)) req.Header.Add("Accept", "application/json") req.Header.Add("Content-Type", "application/json") req.Host = u.Host return app.Test(req) } func doDelete(t *testing.T, app *fiber.App, rawURL string) (*http.Response, error) { u, err := url.Parse(rawURL) if err != nil { t.Fatalf("Invalid url: %s", rawURL) } req := httptest.NewRequest("DELETE", u.RequestURI(), nil) req.Header.Add("Accept", "application/json") req.Host = u.Host return app.Test(req) } func TestServer(t *testing.T) { //var err error trainingRepo := training.NewInMemoryTrainingRepository() trainingDateRepo := training.NewInMemoryDateRepository() trainingDateAttendeeRepo := training.NewInMemoryAttendeeRepository() handlers := NewAPIHandlers(trainingRepo, trainingDateRepo, trainingDateAttendeeRepo) app := NewTestingServer(handlers) t.Run("Add training", func(t *testing.T) { newTraining := NewTraining{ Name: "Testing Training: add", Description: "This is a test training", Days: 1, Pricing: []TrainingPrice{ { Amount: "200", Currency: "EUR", Type: OPEN, }, { Amount: "1000", Currency: "EUR", Type: CORPORATE, }, { Amount: "4900", Currency: "CZK", Type: OPEN, }, { Amount: "24000", Currency: "CZK", Type: CORPORATE, }, }, } slugString := slug.NewString(newTraining.Name) createResp, _ := doPost(t, app, "/v1/trainings", newTraining) defer func() { _ = createResp.Body.Close() }() assert.Equal(t, http.StatusCreated, createResp.StatusCode) var resultTraining Training err := json.NewDecoder(createResp.Body).Decode(&resultTraining) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, newTraining.Name, resultTraining.Name) assert.Equal(t, slugString, resultTraining.Slug) assert.Equal(t, newTraining.Description, resultTraining.Description) assert.Equal(t, newTraining.Days, resultTraining.Days) assert.Equal(t, newTraining.Pricing, resultTraining.Pricing) }) t.Run("Training not found", func(t *testing.T) { getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d", 9999999)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusNotFound, getResp.StatusCode) var trainingError NotFoundError err := json.NewDecoder(getResp.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) }) t.Run("Get training by slug", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: get with slug", Description: "This is a test training with slug", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/by-slug/%s", tr.Slug)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var trr Training err := json.NewDecoder(getResp.Body).Decode(&trr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, tr.Name, trr.Name) assert.Equal(t, tr.ID, trr.Id) assert.Equal(t, tr.Slug, trr.Slug) }) t.Run("List all trainings", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: list", Description: "This is a test training", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) getResp, _ := doGet(t, app, "/v1/trainings") assert.Equal(t, http.StatusOK, getResp.StatusCode) var trainings []Training err := json.NewDecoder(getResp.Body).Decode(&trainings) assert.NoError(t, err, "error unmarshalling response") assert.Greater(t, len(trainings), 0) for _, trr := range trainings { assert.NotEmpty(t, trr.Name) assert.NotEmpty(t, trr.Slug) assert.NotEmpty(t, trr.Description) assert.NotEmpty(t, trr.Days) } }) t.Run("Update training", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: update", Description: "This is a test training", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) updatedSlug := "updated-training" updTr := NewTraining{ Name: "Updated Training", Slug: &updatedSlug, Description: tr.Description, Days: tr.Days, Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(200).String(), Currency: money.EUR, Type: OPEN, }, }, } updateResp, _ := doPut(t, app, fmt.Sprintf("/v1/trainings/%d", tr.ID), updTr) defer func() { _ = updateResp.Body.Close() }() assert.Equal(t, http.StatusOK, updateResp.StatusCode) var trr Training err := json.NewDecoder(updateResp.Body).Decode(&trr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, updTr.Name, trr.Name) assert.Equal(t, updatedSlug, trr.Slug) }) t.Run("Delete training", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: delete", Description: "This is a test training", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d", tr.ID)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var trr Training err := json.NewDecoder(getResp.Body).Decode(&trr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, tr.ID, trr.Id) drr, _ := doDelete(t, app, fmt.Sprintf("/v1/trainings/%d", tr.ID)) defer func() { _ = drr.Body.Close() }() assert.Equal(t, http.StatusNoContent, drr.StatusCode) deletedTrainingResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d", tr.ID)) defer func() { _ = deletedTrainingResp.Body.Close() }() assert.Equal(t, http.StatusNotFound, deletedTrainingResp.StatusCode) var trainingError NotFoundError err = json.NewDecoder(deletedTrainingResp.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) }) t.Run("Add training date", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: add date", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 0, 0, 0, 0, time.UTC) startTime := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) newTrainingDate := NewTrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: types.Date{Time: date}, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: startTime.Format("15:04"), Price: Price{ Amount: "200", Currency: "EUR", }, } createResp, err := doPost(t, app, fmt.Sprintf("/v1/trainings/%d/dates", tr.ID), newTrainingDate) defer func() { _ = createResp.Body.Close() }() assert.Equal(t, http.StatusCreated, createResp.StatusCode) var resultTrainingDate TrainingDate err = json.NewDecoder(createResp.Body).Decode(&resultTrainingDate) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, newTrainingDate.Address, resultTrainingDate.Address) assert.Equal(t, newTrainingDate.Location, resultTrainingDate.Location) assert.Equal(t, newTrainingDate.Capacity, resultTrainingDate.Capacity) assert.Equal(t, newTrainingDate.Days, resultTrainingDate.Days) assert.Equal(t, newTrainingDate.StartTime, resultTrainingDate.StartTime) assert.Equal(t, newTrainingDate.IsOnline, resultTrainingDate.IsOnline) assert.Equal(t, newTrainingDate.Price, resultTrainingDate.Price) assert.Equal(t, newTrainingDate.Date, resultTrainingDate.Date) }) t.Run("Training date not found", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: date not found", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d", tr.ID, 999999)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusNotFound, getResp.StatusCode) var trainingError NotFoundError err := json.NewDecoder(getResp.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) }) t.Run("List all training dates", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: list dates", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 0, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } trainingCount := 5 for range trainingCount { _ = handlers.trainingDateRepository.Create(tr.ID, td) } getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates", tr.ID)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var trainingDates []TrainingDate err := json.NewDecoder(getResp.Body).Decode(&trainingDates) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, len(trainingDates), trainingCount) }) t.Run("Update training date", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: update date", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 0, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) updTd := NewTrainingDate{ Address: "Updated Address 123, NYC", Capacity: 8, Date: types.Date{Time: date}, Days: tr.Days, IsOnline: true, Location: "NYC", StartTime: date.Format("15:04"), Price: Price{ Amount: "200", Currency: "EUR", }, } updateResp, _ := doPut(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d", tr.ID, td.ID), updTd) defer func() { _ = updateResp.Body.Close() }() assert.Equal(t, http.StatusOK, updateResp.StatusCode) var tdrr TrainingDate err := json.NewDecoder(updateResp.Body).Decode(&tdrr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, updTd.Address, tdrr.Address) assert.Equal(t, updTd.Capacity, tdrr.Capacity) assert.Equal(t, updTd.Date, tdrr.Date) assert.Equal(t, updTd.Days, tdrr.Days) assert.Equal(t, updTd.IsOnline, tdrr.IsOnline) assert.Equal(t, updTd.Location, tdrr.Location) assert.Equal(t, updTd.StartTime, tdrr.StartTime) assert.Equal(t, updTd.Price, tdrr.Price) }) t.Run("Delete training date", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: delete date", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 0, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d", tr.ID, td.ID)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var tdrr TrainingDate err := json.NewDecoder(getResp.Body).Decode(&tdrr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, td.ID, tdrr.Id) drr, _ := doDelete(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d", tr.ID, td.ID)) defer func() { _ = drr.Body.Close() }() assert.Equal(t, http.StatusNoContent, drr.StatusCode) deletedTrainingResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d", tr.ID, td.ID)) defer func() { _ = deletedTrainingResp.Body.Close() }() assert.Equal(t, http.StatusNotFound, deletedTrainingResp.StatusCode) var trainingError NotFoundError err = json.NewDecoder(deletedTrainingResp.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) lrr, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates", tr.ID)) defer func() { _ = lrr.Body.Close() }() assert.Equal(t, http.StatusOK, lrr.StatusCode) var trainingDates []TrainingDate err = json.NewDecoder(lrr.Body).Decode(&trainingDates) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, len(trainingDates), 0) }) t.Run("List upcoming training dates for training", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: list upcoming dates", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) now := time.Now() upcomingTrainingCount := 5 for i := -4; i <= 5; i++ { date := now.AddDate(0, 0, i) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) } getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/upcoming-dates", tr.ID)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var trainingDatesReturned []TrainingDate err := json.NewDecoder(getResp.Body).Decode(&trainingDatesReturned) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, len(trainingDatesReturned), upcomingTrainingCount) }) t.Run("List all upcoming training dates", func(t *testing.T) { tr1 := &training.Training{ Name: "Testing Training: list dates for training id=1", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr1) tr2 := &training.Training{ Name: "Testing Training for training date 2", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr2) tr3 := &training.Training{ Name: "Testing Training for training date 3", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr3) allTrainings, _ := handlers.trainingRepository.FindAll() now := time.Now() upcomingTrainingCount := 5 for i := -4; i <= upcomingTrainingCount; i++ { date := now.AddDate(0, 0, i) td1 := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr1.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr1.ID, td1) td2 := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr2.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr2.ID, td2) td3 := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr3.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr3.ID, td3) } getResp, _ := doGet(t, app, "/v1/trainings/upcoming-dates") defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var trainingDatesReturned ListAllUpcomingTrainingDatesResponse err := json.NewDecoder(getResp.Body).Decode(&trainingDatesReturned) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, len(allTrainings), len(trainingDatesReturned)) for _, tdr := range trainingDatesReturned { if tdr.TrainingID == tr1.ID || tdr.TrainingID == tr2.ID || tdr.TrainingID == tr3.ID { assert.Equal(t, len(tdr.Dates), upcomingTrainingCount) } } }) t.Run("Training date attendee not found", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: attendee not found", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, 99999999)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusNotFound, getResp.StatusCode) var notFound NotFoundError err := json.NewDecoder(getResp.Body).Decode(¬Found) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, notFound.Status) }) t.Run("Add training date attendee", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: add attendee", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) newTrainingDateAttendee := NewTrainingDateAttendee{ Name: "John Doe", Email: types.Email("john.doe@example.com"), Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: new(bool), } createResp, _ := doPost(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees", tr.ID, td.ID), newTrainingDateAttendee) defer func() { _ = createResp.Body.Close() }() assert.Equal(t, http.StatusCreated, createResp.StatusCode) var attendeeResponse TrainingDateAttendee err := json.NewDecoder(createResp.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error getting response", err) assert.Equal(t, newTrainingDateAttendee.Name, attendeeResponse.Name) assert.Equal(t, newTrainingDateAttendee.Email, attendeeResponse.Email) assert.Equal(t, newTrainingDateAttendee.Company, attendeeResponse.Company) assert.Equal(t, newTrainingDateAttendee.Position, attendeeResponse.Position) assert.Equal(t, newTrainingDateAttendee.Phone, attendeeResponse.Phone) assert.Equal(t, newTrainingDateAttendee.IsStudent, attendeeResponse.IsStudent) assert.Equal(t, td.PriceAmount.String(), attendeeResponse.Bill.Amount) assert.Equal(t, td.PriceCurrency, attendeeResponse.Bill.Currency) assert.Equal(t, false, attendeeResponse.HasPaid) assert.Equal(t, false, attendeeResponse.HasAttended) }) t.Run("List all training date attendees", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: list attendees", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) attendeeCount := 9 attendees := make([]training.Attendee, attendeeCount) for i := range attendeeCount { nAtd := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: false, HasAttended: false, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, nAtd) attendees[i] = *nAtd } getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees", tr.ID, td.ID)) defer func() { _ = getResp.Body.Close() }() assert.Equal(t, http.StatusOK, getResp.StatusCode) var attendeesResponse ListTrainingDateAttendeesResponse err := json.NewDecoder(getResp.Body).Decode(&attendeesResponse) assert.NoError(t, err, "error getting response", err) assert.Equal(t, attendeeCount, len(attendeesResponse)) for i, attendee := range attendeesResponse { assert.Equal(t, attendees[i].Name, attendee.Name) assert.Equal(t, attendees[i].Email, string(attendee.Email)) assert.Equal(t, attendees[i].Company, attendee.Company) assert.Equal(t, attendees[i].Position, attendee.Position) assert.Equal(t, attendees[i].IsStudent, *attendee.IsStudent) assert.Equal(t, attendees[i].Phone, attendee.Phone) assert.Equal(t, attendees[i].BillAmount.String(), attendee.Bill.Amount) assert.Equal(t, attendees[i].BillCurrency, attendee.Bill.Currency) assert.Equal(t, attendees[i].HasPaid, attendee.HasPaid) assert.Equal(t, attendees[i].HasAttended, attendee.HasAttended) } }) t.Run("Get training date attendee", func(t *testing.T) { tr := &training.Training{ Name: "Test training: get attendee", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) attendee := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: false, HasAttended: false, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, attendee) getResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = getResp.Body.Close() }() var resultTrainingDateAttendee TrainingDateAttendee err := json.NewDecoder(getResp.Body).Decode(&resultTrainingDateAttendee) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, attendee.Name, resultTrainingDateAttendee.Name) assert.Equal(t, attendee.Email, string(resultTrainingDateAttendee.Email)) assert.Equal(t, attendee.Company, resultTrainingDateAttendee.Company) assert.Equal(t, attendee.Position, resultTrainingDateAttendee.Position) assert.Equal(t, attendee.IsStudent, *resultTrainingDateAttendee.IsStudent) assert.Equal(t, attendee.Phone, resultTrainingDateAttendee.Phone) assert.Equal(t, attendee.BillAmount.String(), resultTrainingDateAttendee.Bill.Amount) assert.Equal(t, attendee.BillCurrency, resultTrainingDateAttendee.Bill.Currency) assert.Equal(t, attendee.HasPaid, resultTrainingDateAttendee.HasPaid) assert.Equal(t, attendee.HasAttended, resultTrainingDateAttendee.HasAttended) }) t.Run("Update training date attendee", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: update attendee", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) attendee := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: false, HasAttended: false, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, attendee) newTrainingDateAttendee := NewTrainingDateAttendee{ Name: "John Doe", Email: types.Email("jdoe@example.com"), Company: "Acme Corp Inc.", Position: "Senior Software Engineer", Phone: "+420 987 654 321", IsStudent: new(bool), } updateResp, _ := doPut(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID), newTrainingDateAttendee) defer func() { _ = updateResp.Body.Close() }() assert.Equal(t, http.StatusOK, updateResp.StatusCode) var attendeeResponse UpdateTrainingDateAttendeeResponse err := json.NewDecoder(updateResp.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error getting response", err) assert.Equal(t, newTrainingDateAttendee.Name, attendeeResponse.Name) assert.Equal(t, newTrainingDateAttendee.Email, attendeeResponse.Email) assert.Equal(t, newTrainingDateAttendee.Company, attendeeResponse.Company) assert.Equal(t, newTrainingDateAttendee.Position, attendeeResponse.Position) assert.Equal(t, newTrainingDateAttendee.Phone, attendeeResponse.Phone) assert.Equal(t, newTrainingDateAttendee.IsStudent, attendeeResponse.IsStudent) assert.Equal(t, td.PriceAmount.String(), attendeeResponse.Bill.Amount) assert.Equal(t, td.PriceCurrency, attendeeResponse.Bill.Currency) assert.Equal(t, attendee.HasPaid, attendeeResponse.HasPaid) assert.Equal(t, attendee.HasAttended, attendeeResponse.HasAttended) }) t.Run("Delete training date attendee", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: delete attendee", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) attendee := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: false, HasAttended: false, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, attendee) rr, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = rr.Body.Close() }() assert.Equal(t, http.StatusOK, rr.StatusCode) deleteResp, _ := doDelete(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = deleteResp.Body.Close() }() assert.Equal(t, http.StatusNoContent, deleteResp.StatusCode) deletedResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = deletedResp.Body.Close() }() assert.Equal(t, http.StatusNotFound, deletedResp.StatusCode) }) t.Run("Mark training date attendee as paid", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training for training date", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) paid := TrainingDateAttendeePaymentUpdateRequest{ Paid: true, } attendee := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: !paid.Paid, HasAttended: false, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, attendee) rr, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = rr.Body.Close() }() assert.Equal(t, http.StatusOK, rr.StatusCode) var attendeeResponse TrainingDateAttendee err := json.NewDecoder(rr.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, !paid.Paid, attendeeResponse.HasPaid) updateResp, _ := doPut(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d/paid", tr.ID, td.ID, attendee.ID), paid) defer func() { _ = updateResp.Body.Close() }() assert.Equal(t, http.StatusOK, updateResp.StatusCode) err = json.NewDecoder(updateResp.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, paid.Paid, attendeeResponse.HasPaid) getAttendeeResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = getAttendeeResp.Body.Close() }() assert.Equal(t, http.StatusOK, getAttendeeResp.StatusCode) err = json.NewDecoder(getAttendeeResp.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, paid.Paid, attendeeResponse.HasPaid) }) t.Run("Mark training date attendee as attended", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: mark attendee as attended", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) attended := TrainingDateAttendeeAttendanceUpdateRequest{ Attended: true, } attendee := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: false, HasAttended: !attended.Attended, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, attendee) rr, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = rr.Body.Close() }() assert.Equal(t, http.StatusOK, rr.StatusCode) var attendeeResponse TrainingDateAttendee err := json.NewDecoder(rr.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, !attended.Attended, attendeeResponse.HasAttended) updateResp, _ := doPut(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d/attended", tr.ID, td.ID, attendee.ID), attended) defer func() { _ = updateResp.Body.Close() }() assert.Equal(t, http.StatusOK, updateResp.StatusCode) err = json.NewDecoder(updateResp.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, attended.Attended, attendeeResponse.HasAttended) getAttendeeResp, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/%d", tr.ID, td.ID, attendee.ID)) defer func() { _ = getAttendeeResp.Body.Close() }() assert.Equal(t, http.StatusOK, getAttendeeResp.StatusCode) err = json.NewDecoder(getAttendeeResp.Body).Decode(&attendeeResponse) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, attended.Attended, attendeeResponse.HasAttended) }) t.Run("Get count of training date attendees", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training: get attendee count", Description: "This is a test training for training date", Days: 1, Pricing: []training.Price{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) date := time.Date(2024, time.May, 1, 9, 0, 0, 0, time.UTC) td := &training.Date{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, PriceAmount: decimal.NewFromInt(200), PriceCurrency: "EUR", } _ = handlers.trainingDateRepository.Create(tr.ID, td) attendeeCount := 9 for range attendeeCount { nAtd := &training.Attendee{ Name: "John Doe", Email: "john.doe@example.com", Company: "Acme Inc.", Position: "Software Engineer", Phone: "+420 123 456 789", IsStudent: false, BillAmount: td.PriceAmount, BillCurrency: td.PriceCurrency, HasPaid: false, HasAttended: false, } _ = handlers.trainingDateAttendeeRepository.Create(td.ID, nAtd) } rr, _ := doGet(t, app, fmt.Sprintf("/v1/trainings/%d/dates/%d/attendees/count", tr.ID, td.ID)) defer func() { _ = rr.Body.Close() }() assert.Equal(t, http.StatusOK, rr.StatusCode) var attendeesResponse GetTrainingDateAttendeeCountResponse err := json.NewDecoder(rr.Body).Decode(&attendeesResponse) assert.NoError(t, err, "error getting response", err) assert.Equal(t, int8(attendeeCount), attendeesResponse.Count) }) }