package server import ( "bytes" "encoding/json" "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/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.NewInMemoryTrainingDateRepository() trainingDateAttendeeRepo := training.NewInMemoryTrainingDateAttendeeRepository() handlers := NewAPIHandlers(trainingRepo, trainingDateRepo, trainingDateAttendeeRepo) app := NewTestingServer(handlers) t.Run("Add training", func(t *testing.T) { newTraining := NewTraining{ Name: "Testing Training", 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, }, }, } rr, _ := doPost(t, app, "/v1/trainings", newTraining) assert.Equal(t, http.StatusCreated, rr.StatusCode) var resultTraining Training err := json.NewDecoder(rr.Body).Decode(&resultTraining) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, newTraining.Name, resultTraining.Name) 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) { rr, _ := doGet(t, app, "/v1/trainings/97b0e954-14f3-4908-98fa-271f505056d3") assert.Equal(t, http.StatusNotFound, rr.StatusCode) var trainingError NotFoundError err := json.NewDecoder(rr.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) }) t.Run("List all trainings", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training", Description: "This is a test training", Days: 1, Pricing: []training.TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) rr, _ := doGet(t, app, "/v1/trainings") assert.Equal(t, http.StatusOK, rr.StatusCode) var trainings []Training err := json.NewDecoder(rr.Body).Decode(&trainings) assert.NoError(t, err, "error unmarshalling response") assert.Greater(t, len(trainings), 0) }) t.Run("Update training", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training", Description: "This is a test training", Days: 1, Pricing: []training.TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) updTr := NewTraining{ Name: "Updated Training", Description: tr.Description, Days: tr.Days, Pricing: []TrainingPrice{ { Amount: decimal.NewFromInt(200).String(), Currency: money.EUR, Type: OPEN, }, }, } rr, _ := doPut(t, app, "/v1/trainings/"+tr.ID.String(), updTr) assert.Equal(t, http.StatusOK, rr.StatusCode) var trr Training err := json.NewDecoder(rr.Body).Decode(&trr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, updTr.Name, trr.Name) }) t.Run("Delete training", func(t *testing.T) { tr := &training.Training{ Name: "Testing Training", Description: "This is a test training", Days: 1, Pricing: []training.TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) rr, _ := doGet(t, app, "/v1/trainings/"+tr.ID.String()) assert.Equal(t, http.StatusOK, rr.StatusCode) var trr Training err := json.NewDecoder(rr.Body).Decode(&trr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, tr.ID, trr.Id) drr, _ := doDelete(t, app, "/v1/trainings/"+tr.ID.String()) assert.Equal(t, http.StatusNoContent, drr.StatusCode) rr, _ = doGet(t, app, "/v1/trainings/"+tr.ID.String()) assert.Equal(t, http.StatusNotFound, rr.StatusCode) var trainingError NotFoundError err = json.NewDecoder(rr.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) lrr, _ := doGet(t, app, "/v1/trainings") assert.Equal(t, http.StatusOK, lrr.StatusCode) }) t.Run("Add training date", 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.TrainingPrice{ { 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", }, } rr, err := doPost(t, app, "/v1/trainings/"+tr.ID.String()+"/dates", newTrainingDate) assert.Equal(t, http.StatusCreated, rr.StatusCode) var resultTrainingDate TrainingDate err = json.NewDecoder(rr.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 not found", 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.TrainingPrice{ { Amount: decimal.NewFromInt(200), Currency: "EUR", Type: training.OpenTrainingPrice, }, }, } _ = handlers.trainingRepository.Create(tr) rr, _ := doGet(t, app, "/v1/trainings/"+tr.ID.String()+"/dates/97b0e954-14f3-4908-98fa-271f505056d3") assert.Equal(t, http.StatusNotFound, rr.StatusCode) var trainingError NotFoundError err := json.NewDecoder(rr.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 for training date", Description: "This is a test training for training date", Days: 1, Pricing: []training.TrainingPrice{ { 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.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "EUR", }, } trainingCount := 5 for range trainingCount { _ = handlers.trainingDateRepository.Create(tr.ID, td) } rr, _ := doGet(t, app, "/v1/trainings/"+tr.ID.String()+"/dates") assert.Equal(t, http.StatusOK, rr.StatusCode) var trainingDates []TrainingDate err := json.NewDecoder(rr.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 for training date", Description: "This is a test training for training date", Days: 1, Pricing: []training.TrainingPrice{ { 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.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "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", }, } rr, _ := doPut(t, app, "/v1/trainings/"+tr.ID.String()+"/dates/"+td.ID.String(), updTd) assert.Equal(t, http.StatusOK, rr.StatusCode) var tdrr TrainingDate err := json.NewDecoder(rr.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 for training date", Description: "This is a test training for training date", Days: 1, Pricing: []training.TrainingPrice{ { 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.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "EUR", }, } _ = handlers.trainingDateRepository.Create(tr.ID, td) rr, _ := doGet(t, app, "/v1/trainings/"+tr.ID.String()+"/dates/"+td.ID.String()) assert.Equal(t, http.StatusOK, rr.StatusCode) var tdrr TrainingDate err := json.NewDecoder(rr.Body).Decode(&tdrr) assert.NoError(t, err, "error unmarshalling response") assert.Equal(t, td.ID, tdrr.Id) drr, _ := doDelete(t, app, "/v1/trainings/"+tr.ID.String()+"/dates/"+td.ID.String()) assert.Equal(t, http.StatusNoContent, drr.StatusCode) rr, _ = doGet(t, app, "/v1/trainings/"+tr.ID.String()+"/dates/"+td.ID.String()) assert.Equal(t, http.StatusNotFound, rr.StatusCode) var trainingError NotFoundError err = json.NewDecoder(rr.Body).Decode(&trainingError) assert.NoError(t, err, "error getting response", err) assert.Equal(t, http.StatusNotFound, trainingError.Status) lrr, _ := doGet(t, app, "/v1/trainings/"+tr.ID.String()+"/dates") 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 for training date", Description: "This is a test training for training date", Days: 1, Pricing: []training.TrainingPrice{ { 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.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "EUR", }, } _ = handlers.trainingDateRepository.Create(tr.ID, td) } rr, _ := doGet(t, app, "/v1/trainings/"+tr.ID.String()+"/upcoming-dates") assert.Equal(t, http.StatusOK, rr.StatusCode) var trainingDatesReturned []TrainingDate err := json.NewDecoder(rr.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 for training date 1", Description: "This is a test training for training date", Days: 1, Pricing: []training.TrainingPrice{ { 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.TrainingPrice{ { 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.TrainingPrice{ { 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.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr1.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "EUR", }, } _ = handlers.trainingDateRepository.Create(tr1.ID, td1) td2 := &training.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr2.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "EUR", }, } _ = handlers.trainingDateRepository.Create(tr2.ID, td2) td3 := &training.TrainingDate{ Address: "Test Address 123, NYC", Capacity: 12, Date: date, Days: tr3.Days, IsOnline: false, Location: "NYC", StartTime: date, Price: money.Price{ Amount: decimal.NewFromInt(200), Currency: "EUR", }, } _ = handlers.trainingDateRepository.Create(tr3.ID, td3) } rr, _ := doGet(t, app, "/v1/trainings/upcoming-dates") assert.Equal(t, http.StatusOK, rr.StatusCode) var trainingDatesReturned ListAllUpcomingTrainingDatesResponse err := json.NewDecoder(rr.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) } } }) }