package mongo import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" ) // TestUser represents a test user model type TestUser struct { Model Email string `bson:"email" json:"email"` FirstName string `bson:"first_name" json:"first_name"` LastName string `bson:"last_name" json:"last_name"` Age int `bson:"age" json:"age"` IsActive bool `bson:"is_active" json:"is_active"` } // TestLogger implements Logger for testing type TestLogger struct{} func (l *TestLogger) Info(message string, fields map[string]interface{}) {} func (l *TestLogger) Error(message string, fields map[string]interface{}) {} func (l *TestLogger) Debug(message string, fields map[string]interface{}) {} func (l *TestLogger) Warn(message string, fields map[string]interface{}) {} // setupTestConnection creates a test connection manager func setupTestConnection(t *testing.T) *ConnectionManager { config := ConnectionConfig{ URI: "mongodb://localhost:27017", Database: "test_database", } logger := &TestLogger{} connManager, err := NewConnectionManager(config, logger) require.NoError(t, err) return connManager } // cleanupTestData cleans up test data func cleanupTestData(t *testing.T, connManager *ConnectionManager, collectionName string) { collection := connManager.GetCollection(collectionName) _, err := collection.DeleteMany(context.Background(), bson.M{}) require.NoError(t, err) } func TestRepository_Create(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) user := &TestUser{ Email: "test@example.com", FirstName: "Test", LastName: "User", Age: 25, IsActive: true, } err := repo.Create(context.Background(), user) require.NoError(t, err) assert.NotEmpty(t, user.ID) assert.NotZero(t, user.CreatedAt) assert.NotZero(t, user.UpdatedAt) } func TestRepository_FindByID(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create a user user := &TestUser{ Email: "test@example.com", FirstName: "Test", LastName: "User", Age: 25, IsActive: true, } err := repo.Create(context.Background(), user) require.NoError(t, err) // Find by ID foundUser, err := repo.FindByID(context.Background(), user.ID) require.NoError(t, err) assert.Equal(t, user.Email, foundUser.Email) assert.Equal(t, user.FirstName, foundUser.FirstName) // Test not found _, err = repo.FindByID(context.Background(), "507f1f77bcf86cd799439011") assert.ErrorIs(t, err, ErrDocumentNotFound) } func TestRepository_FindOne(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create a user user := &TestUser{ Email: "test@example.com", FirstName: "Test", LastName: "User", Age: 25, IsActive: true, } err := repo.Create(context.Background(), user) require.NoError(t, err) // Find by email foundUser, err := repo.FindOne(context.Background(), bson.M{"email": "test@example.com"}) require.NoError(t, err) assert.Equal(t, user.Email, foundUser.Email) // Test not found _, err = repo.FindOne(context.Background(), bson.M{"email": "nonexistent@example.com"}) assert.ErrorIs(t, err, ErrDocumentNotFound) } func TestRepository_Update(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create a user user := &TestUser{ Email: "test@example.com", FirstName: "Test", LastName: "User", Age: 25, IsActive: true, } err := repo.Create(context.Background(), user) require.NoError(t, err) originalUpdatedAt := user.UpdatedAt // Update user user.FirstName = "Updated" user.Age = 30 time.Sleep(1 * time.Second) // Ensure timestamp difference err = repo.Update(context.Background(), user) require.NoError(t, err) // Verify update foundUser, err := repo.FindByID(context.Background(), user.ID) require.NoError(t, err) assert.Equal(t, "Updated", foundUser.FirstName) assert.Equal(t, 30, foundUser.Age) assert.Greater(t, foundUser.UpdatedAt, originalUpdatedAt) } func TestRepository_Delete(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create a user user := &TestUser{ Email: "test@example.com", FirstName: "Test", LastName: "User", Age: 25, IsActive: true, } err := repo.Create(context.Background(), user) require.NoError(t, err) // Delete user err = repo.Delete(context.Background(), user.ID) require.NoError(t, err) // Verify deletion _, err = repo.FindByID(context.Background(), user.ID) assert.ErrorIs(t, err, ErrDocumentNotFound) } func TestRepository_FindAll_OffsetPagination(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create multiple users users := []TestUser{ {Email: "user1@example.com", FirstName: "User1", Age: 25, IsActive: true}, {Email: "user2@example.com", FirstName: "User2", Age: 30, IsActive: true}, {Email: "user3@example.com", FirstName: "User3", Age: 35, IsActive: false}, {Email: "user4@example.com", FirstName: "User4", Age: 40, IsActive: true}, {Email: "user5@example.com", FirstName: "User5", Age: 45, IsActive: true}, } err := repo.BulkCreate(context.Background(), users) require.NoError(t, err) // Test offset-based pagination pagination := NewPaginationBuilder(). Limit(2). Skip(1). SortAsc("created_at"). Build() results, err := repo.FindAll(context.Background(), bson.M{}, pagination) require.NoError(t, err) assert.Len(t, results.Items, 2) assert.Equal(t, int64(5), results.TotalCount) assert.False(t, results.HasMore) // Should be false since we're using offset } func TestRepository_FindAll_CursorPagination(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create multiple users users := []TestUser{ {Email: "user1@example.com", FirstName: "User1", Age: 25, IsActive: true}, {Email: "user2@example.com", FirstName: "User2", Age: 30, IsActive: true}, {Email: "user3@example.com", FirstName: "User3", Age: 35, IsActive: false}, {Email: "user4@example.com", FirstName: "User4", Age: 40, IsActive: true}, {Email: "user5@example.com", FirstName: "User5", Age: 45, IsActive: true}, } err := repo.BulkCreate(context.Background(), users) require.NoError(t, err) // Test cursor-based pagination pagination := NewPaginationBuilder(). Limit(2). SortDesc("created_at"). Build() firstPage, err := repo.FindAll(context.Background(), bson.M{}, pagination) require.NoError(t, err) assert.Len(t, firstPage.Items, 2) assert.Equal(t, int64(5), firstPage.TotalCount) assert.True(t, firstPage.HasMore) assert.NotEmpty(t, firstPage.NextCursor) // Get second page secondPagePagination := NewPaginationBuilder(). Limit(2). SortDesc("created_at"). Cursor(firstPage.NextCursor). Build() secondPage, err := repo.FindAll(context.Background(), bson.M{}, secondPagePagination) require.NoError(t, err) assert.Len(t, secondPage.Items, 2) assert.Equal(t, int64(5), secondPage.TotalCount) assert.True(t, secondPage.HasMore) assert.NotEmpty(t, secondPage.NextCursor) } func TestRepository_Count(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create users users := []TestUser{ {Email: "user1@example.com", FirstName: "User1", Age: 25, IsActive: true}, {Email: "user2@example.com", FirstName: "User2", Age: 30, IsActive: true}, {Email: "user3@example.com", FirstName: "User3", Age: 35, IsActive: false}, } err := repo.BulkCreate(context.Background(), users) require.NoError(t, err) // Count all users totalCount, err := repo.Count(context.Background(), bson.M{}) require.NoError(t, err) assert.Equal(t, int64(3), totalCount) // Count active users activeCount, err := repo.Count(context.Background(), bson.M{"is_active": true}) require.NoError(t, err) assert.Equal(t, int64(2), activeCount) } func TestRepository_BulkOperations(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Test bulk create users := []TestUser{ {Email: "user1@example.com", FirstName: "User1", Age: 25, IsActive: true}, {Email: "user2@example.com", FirstName: "User2", Age: 30, IsActive: true}, {Email: "user3@example.com", FirstName: "User3", Age: 35, IsActive: false}, } err := repo.BulkCreate(context.Background(), users) require.NoError(t, err) // Verify bulk create count, err := repo.Count(context.Background(), bson.M{}) require.NoError(t, err) assert.Equal(t, int64(3), count) // Test bulk update updateFilter := bson.M{"is_active": false} update := bson.M{"$set": bson.M{"age": 50}} err = repo.BulkUpdate(context.Background(), updateFilter, update) require.NoError(t, err) // Verify bulk update updatedUser, err := repo.FindOne(context.Background(), bson.M{"email": "user3@example.com"}) require.NoError(t, err) assert.Equal(t, 50, updatedUser.Age) // Test bulk delete deleteFilter := bson.M{"is_active": false} err = repo.BulkDelete(context.Background(), deleteFilter) require.NoError(t, err) // Verify bulk delete count, err = repo.Count(context.Background(), bson.M{}) require.NoError(t, err) assert.Equal(t, int64(2), count) } func TestRepository_Aggregate(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create users users := []TestUser{ {Email: "user1@example.com", FirstName: "User1", Age: 25, IsActive: true}, {Email: "user2@example.com", FirstName: "User2", Age: 30, IsActive: true}, {Email: "user3@example.com", FirstName: "User3", Age: 35, IsActive: false}, } err := repo.BulkCreate(context.Background(), users) require.NoError(t, err) // Test aggregation pipeline := mongo.Pipeline{ {{Key: "$match", Value: bson.M{"is_active": true}}}, {{Key: "$group", Value: bson.M{ "_id": nil, "count": bson.M{"$sum": 1}, "avgAge": bson.M{"$avg": "$age"}, }}}, } results, err := repo.Aggregate(context.Background(), pipeline) require.NoError(t, err) assert.Len(t, results, 1) result := results[0] assert.Equal(t, int64(2), result["count"]) assert.Equal(t, 27.5, result["avgAge"]) } func TestRepository_QueryBuilder(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Create users users := []TestUser{ {Email: "user1@example.com", FirstName: "User1", Age: 25, IsActive: true}, {Email: "user2@example.com", FirstName: "User2", Age: 30, IsActive: true}, {Email: "user3@example.com", FirstName: "User3", Age: 35, IsActive: false}, } err := repo.BulkCreate(context.Background(), users) require.NoError(t, err) // Test QueryBuilder query := NewQueryBuilder(). Where("is_active", true). WhereGreaterThan("age", 20). WhereLessThan("age", 35). Build() results, err := repo.FindMany(context.Background(), query, 10) require.NoError(t, err) assert.Len(t, results, 2) // Test complex query with AND/OR complexQuery := NewQueryBuilder(). WhereAnd( bson.M{"is_active": true}, bson.M{"age": bson.M{"$gte": 25}}, ). WhereOr( bson.M{"email": "user1@example.com"}, bson.M{"email": "user2@example.com"}, ). Build() complexResults, err := repo.FindMany(context.Background(), complexQuery, 10) require.NoError(t, err) assert.Len(t, complexResults, 2) } func TestRepository_Validation(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Test invalid pagination invalidPagination := Pagination{ Limit: -1, // Invalid SortOrder: 2, // Invalid } _, err := repo.FindAll(context.Background(), bson.M{}, invalidPagination) assert.Error(t, err) assert.ErrorIs(t, err, ErrInvalidPagination) // Test invalid cursor invalidCursorPagination := NewPaginationBuilder(). Limit(10). Cursor("invalid-base64"). Build() _, err = repo.FindAll(context.Background(), bson.M{}, invalidCursorPagination) assert.Error(t, err) assert.ErrorIs(t, err, ErrInvalidCursor) } func TestRepository_IndexManagement(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) // Test creating indexes indexes := []Index{ *CreateUniqueIndex("email_idx", bson.D{{Key: "email", Value: 1}}), *NewIndex("age_idx", bson.D{{Key: "age", Value: 1}}), } err := connManager.CreateIndexes("test_users", indexes) require.NoError(t, err) // Test listing indexes listIndexes, err := connManager.ListIndexes("test_users") require.NoError(t, err) assert.GreaterOrEqual(t, len(listIndexes), 3) // _id + created_at + our indexes // Test dropping indexes err = connManager.DropIndexes("test_users", []string{"age_idx"}) require.NoError(t, err) } func TestRepository_ConnectionManagement(t *testing.T) { config := ConnectionConfig{ URI: "mongodb://localhost:27017", Database: "test_database", } logger := &TestLogger{} connManager, err := NewConnectionManager(config, logger) require.NoError(t, err) // Test ping err = connManager.Ping(context.Background()) require.NoError(t, err) // Test get stats stats, err := connManager.GetStats(context.Background()) require.NoError(t, err) assert.NotNil(t, stats) // Test collection stats collectionStats, err := connManager.GetCollectionStats(context.Background(), "test_users") require.NoError(t, err) assert.NotNil(t, collectionStats) // Test close err = connManager.Close(context.Background()) require.NoError(t, err) } func TestRepository_ErrorHandling(t *testing.T) { connManager := setupTestConnection(t) defer connManager.Close(context.Background()) defer cleanupTestData(t, connManager, "test_users") repo := NewRepository[TestUser](connManager.GetCollection("test_users"), &TestLogger{}) // Test invalid ObjectID _, err := repo.FindByID(context.Background(), "invalid-id") assert.Error(t, err) // Test update non-existent document nonExistentUser := &TestUser{ Model: Model{ID: "507f1f77bcf86cd799439011"}, Email: "nonexistent@example.com", } err = repo.Update(context.Background(), nonExistentUser) assert.ErrorIs(t, err, ErrDocumentNotFound) // Test delete non-existent document err = repo.Delete(context.Background(), "507f1f77bcf86cd799439011") assert.ErrorIs(t, err, ErrDocumentNotFound) }