package integration import ( "bytes" "context" "encoding/json" "fmt" "log" "net/http" "net/http/httptest" "os" "testing" "time" "wish-list-api/api/handlers" "wish-list-api/api/middleware" "wish-list-api/pkg/auth" "wish-list-api/pkg/entities" "wish-list-api/pkg/user" wishlist "wish-list-api/pkg/wish-list" "github.com/gofiber/fiber/v2" "github.com/golang-jwt/jwt/v5" "github.com/stretchr/testify/assert" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) var ( app *fiber.App testDB *mongo.Database client *mongo.Client authSvc auth.Service ) func TestMain(m *testing.M) { setup() exitCode := m.Run() teardown() os.Exit(exitCode) } func setup() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() var err error mongoURI := os.Getenv("MONGODB_URI") if mongoURI == "" { mongoURI = "mongodb://mongo_user:mongo_password@localhost:27017/admin" } client, err = mongo.Connect(ctx, options.Client().ApplyURI(mongoURI)) if err != nil { log.Fatalf("Error connecting to MongoDB: %v", err) } err = client.Ping(ctx, nil) if err != nil { log.Fatalf("Could not connect to MongoDB: %v", err) } dbName := fmt.Sprintf("wishlist_test_%d", time.Now().UnixNano()) testDB = client.Database(dbName) userCollection := testDB.Collection("users") wishlistCollection := testDB.Collection("wishlists") wishlistItemCollection := testDB.Collection("wishlist_items") userRepo := user.NewMongoRepository(userCollection) wishlistRepo := wishlist.NewMongoRepository(wishlistCollection, wishlistItemCollection) userService := user.NewService(userRepo) authSvc = auth.NewService(auth.ServiceConfig{ UserService: userService, }) wishlistSvc := wishlist.NewService(wishlistRepo) app = fiber.New() api := app.Group("/api") api.Post("/auth/register", handlers.Register(authSvc)) api.Post("/auth/login", handlers.Login(authSvc)) api.Post("/auth/refresh", handlers.RefreshToken(authSvc)) wishListHandler := handlers.NewWishListHandler(wishlistSvc, authSvc) wishList := api.Group("/wishlist") wishList.Get("/:id", wishListHandler.GetWishList) wishList.Get("/user/:userId", wishListHandler.GetUserWishLists) wishList.Get("/:wishlistId/items", wishListHandler.GetWishListItems) wishList.Get("/item/:id", wishListHandler.GetWishListItem) wishList.Post("/", middleware.Protected(authSvc), wishListHandler.CreateWishList) wishList.Put("/:id", middleware.Protected(authSvc), wishListHandler.UpdateWishList) wishList.Delete("/:id", middleware.Protected(authSvc), wishListHandler.DeleteWishList) wishList.Post("/item", middleware.Protected(authSvc), wishListHandler.CreateWishListItem) wishList.Put("/item/:id", middleware.Protected(authSvc), wishListHandler.UpdateWishListItem) wishList.Delete("/item/:id", middleware.Protected(authSvc), wishListHandler.DeleteWishListItem) } func teardown() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() if err := testDB.Drop(ctx); err != nil { log.Printf("Error dropping test database: %v", err) } if err := client.Disconnect(ctx); err != nil { log.Printf("Error disconnecting from MongoDB: %v", err) } } func registerTestUser(t *testing.T) (string, primitive.ObjectID) { userData := entities.RegisterRequest{ Email: fmt.Sprintf("test%d@example.com", time.Now().UnixNano()), Password: "password123", } jsonBody, _ := json.Marshal(userData) req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err := app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) var response struct { Status bool `json:"status"` Data entities.TokenPair `json:"data"` Error *string `json:"error"` } err = json.NewDecoder(resp.Body).Decode(&response) assert.NoError(t, err) claims, err := authSvc.ValidateToken(response.Data.AccessToken) assert.NoError(t, err) mapClaims := claims.Claims.(jwt.MapClaims) userIDStr := mapClaims["user_id"].(string) userID, err := primitive.ObjectIDFromHex(userIDStr) assert.NoError(t, err) return response.Data.AccessToken, userID } func TestRegister(t *testing.T) { userData := entities.RegisterRequest{ Email: fmt.Sprintf("test%d@example.com", time.Now().UnixNano()), Password: "password123", } jsonBody, _ := json.Marshal(userData) req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err := app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("POST", "/api/auth/register", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusConflict, resp.StatusCode) } func TestLogin(t *testing.T) { email := fmt.Sprintf("test%d@example.com", time.Now().UnixNano()) userData := entities.RegisterRequest{ Email: email, Password: "password123", } jsonBody, _ := json.Marshal(userData) req := httptest.NewRequest("POST", "/api/auth/register", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err := app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) loginData := entities.LoginRequest{ Email: email, Password: "password123", } jsonBody, _ = json.Marshal(loginData) req = httptest.NewRequest("POST", "/api/auth/login", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) loginData.Password = "wrongpassword" jsonBody, _ = json.Marshal(loginData) req = httptest.NewRequest("POST", "/api/auth/login", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) } func TestWishListCRUD(t *testing.T) { token, userID := registerTestUser(t) wishlistData := entities.WishList{ Title: "Test Wishlist", Description: "Integration test wishlist", IsPublic: true, } jsonBody, _ := json.Marshal(wishlistData) req := httptest.NewRequest("POST", "/api/wishlist", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) resp, err := app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusCreated, resp.StatusCode) var createResponse struct { Status bool `json:"status"` Data entities.WishList `json:"data"` Error *string `json:"error"` } err = json.NewDecoder(resp.Body).Decode(&createResponse) assert.NoError(t, err) wishlistID := createResponse.Data.ID req = httptest.NewRequest("GET", "/api/wishlist/"+wishlistID, nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) updatedData := entities.WishList{ Title: "Updated Title", Description: "Updated description", IsPublic: false, } jsonBody, _ = json.Marshal(updatedData) req = httptest.NewRequest("PUT", "/api/wishlist/"+wishlistID, bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("GET", "/api/wishlist/"+wishlistID, nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusUnauthorized, resp.StatusCode) req = httptest.NewRequest("GET", "/api/wishlist/"+wishlistID, nil) req.Header.Set("Authorization", "Bearer "+token) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("GET", "/api/wishlist/user/"+userID.Hex(), nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("DELETE", "/api/wishlist/"+wishlistID, nil) req.Header.Set("Authorization", "Bearer "+token) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("GET", "/api/wishlist/"+wishlistID, nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) } func TestWishListItemsCRUD(t *testing.T) { token, _ := registerTestUser(t) wishlistData := entities.WishList{ Title: "Test Wishlist for Items", Description: "Testing wishlist items", IsPublic: true, } jsonBody, _ := json.Marshal(wishlistData) req := httptest.NewRequest("POST", "/api/wishlist", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) resp, err := app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusCreated, resp.StatusCode) var createResponse struct { Status bool `json:"status"` Data entities.WishList `json:"data"` Error *string `json:"error"` } err = json.NewDecoder(resp.Body).Decode(&createResponse) assert.NoError(t, err) wishlistID := createResponse.Data.ID itemData := entities.WishListItem{ Title: "Test Item", Description: "Test item description", URL: "https://example.com", Cost: 99.99, WishListID: wishlistID, } jsonBody, _ = json.Marshal(itemData) req = httptest.NewRequest("POST", "/api/wishlist/item", bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusCreated, resp.StatusCode) var itemResponse struct { Status bool `json:"status"` Data entities.WishListItem `json:"data"` Error *string `json:"error"` } err = json.NewDecoder(resp.Body).Decode(&itemResponse) assert.NoError(t, err) itemID := itemResponse.Data.ID req = httptest.NewRequest("GET", "/api/wishlist/item/"+itemID, nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) updatedItemData := entities.WishListItem{ Title: "Updated Item Title", Description: "Updated item description", URL: "https://example.com", Cost: 149.99, } jsonBody, _ = json.Marshal(updatedItemData) req = httptest.NewRequest("PUT", "/api/wishlist/item/"+itemID, bytes.NewReader(jsonBody)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("GET", "/api/wishlist/"+wishlistID+"/items", nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) var itemsResponse struct { Status bool `json:"status"` Data []entities.WishListItem `json:"data"` Error *string `json:"error"` } err = json.NewDecoder(resp.Body).Decode(&itemsResponse) assert.NoError(t, err) assert.Equal(t, 1, len(itemsResponse.Data)) req = httptest.NewRequest("DELETE", "/api/wishlist/item/"+itemID, nil) req.Header.Set("Authorization", "Bearer "+token) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) req = httptest.NewRequest("GET", "/api/wishlist/item/"+itemID, nil) resp, err = app.Test(req, -1) assert.NoError(t, err) assert.Equal(t, http.StatusNotFound, resp.StatusCode) }