package services import ( "database/sql" "reflect" "testing" "authentication/db" "github.com/DATA-DOG/go-sqlmock" ) func setupMockDB(t *testing.T) (sqlmock.Sqlmock, func()) { mockDB, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock database: %v", err) } originalDB := db.DB db.DB = mockDB cleanup := func() { mockDB.Close() db.DB = originalDB } return mock, cleanup } func TestGetUser(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "test@example.com" expectedID := "user123" rows := sqlmock.NewRows([]string{"users_id"}). AddRow(expectedID) mock.ExpectQuery(`SELECT users_id FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0 LIMIT 1;`). WithArgs(email). WillReturnRows(rows) } func TestGetUserNotFound(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "nonexistent@example.com" mock.ExpectQuery(`SELECT users_id FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0 LIMIT 1;`). WithArgs(email). WillReturnError(sql.ErrNoRows) id, err := GetUserID(email) if err != sql.ErrNoRows { t.Errorf("Expected sql.ErrNoRows, got: %v", err) } if id != "" { t.Errorf("Expected empty ID, got %s", id) } } func TestGetUserNullNames(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "test@example.com" expectedID := "user456" rows := sqlmock.NewRows([]string{"users_id"}). AddRow(expectedID) mock.ExpectQuery(`SELECT users_id FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0 LIMIT 1;`). WithArgs(email). WillReturnRows(rows) id, err := GetUserID(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if id != expectedID { t.Errorf("Expected ID %s, got %s", expectedID, id) } } func TestGetUserID(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "test@example.com" expectedID := "user789" rows := sqlmock.NewRows([]string{"users_id"}). AddRow(expectedID) mock.ExpectQuery(`SELECT users_id FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0 LIMIT 1;`). WithArgs(email). WillReturnRows(rows) id, err := GetUserID(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if id != expectedID { t.Errorf("Expected ID %s, got %s", expectedID, id) } } func TestCheckEmailInDB(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "existing@example.com" rows := sqlmock.NewRows([]string{"exists"}). AddRow(true) mock.ExpectQuery(`SELECT EXISTS \(\s*SELECT 1 FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0\)`). WithArgs(email). WillReturnRows(rows) exists, err := CheckEmailInDB(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if !exists { t.Error("Expected email to exist") } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unfulfilled expectations: %v", err) } } func TestCheckEmailInDBNotExists(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "nonexistent@example.com" rows := sqlmock.NewRows([]string{"exists"}). AddRow(false) mock.ExpectQuery(`SELECT EXISTS \(\s*SELECT 1 FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0\)`). WithArgs(email). WillReturnRows(rows) exists, err := CheckEmailInDB(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if exists { t.Error("Expected email to not exist") } } func TestCheckEmailInDBError(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "error@example.com" mock.ExpectQuery(`SELECT EXISTS \(\s*SELECT 1 FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0\)`). WithArgs(email). WillReturnError(sql.ErrConnDone) exists, err := CheckEmailInDB(email) if err == nil { t.Error("Expected error, got nil") } if exists { t.Error("Expected false when error occurs") } } func TestGetUserIDFromEmail(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "test@example.com" expectedID := "user999" rows := sqlmock.NewRows([]string{"id"}). AddRow(expectedID) mock.ExpectQuery(`SELECT users_id\s+FROM \(\s*SELECT users_id, 1 AS priority\s+FROM users\s+WHERE email_address = \?\s+AND is_deleted = 0\s*\) t\s+ORDER BY priority ASC\s+LIMIT 1;`). WithArgs(email). WillReturnRows(rows) id, err := GetUserIDFromEmail(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if id != expectedID { t.Errorf("Expected ID %s, got %s", expectedID, id) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unfulfilled expectations: %v", err) } } func TestGetUserIDFromEmailNotFound(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "notfound@example.com" mock.ExpectQuery(`SELECT users_id\s+FROM \(\s*SELECT users_id, 1 AS priority\s+FROM users\s+WHERE email_address = \?\s+AND is_deleted = 0\s*\) t\s+ORDER BY priority ASC\s+LIMIT 1;`). WithArgs(email). WillReturnError(sql.ErrNoRows) id, err := GetUserIDFromEmail(email) if err == nil { t.Error("Expected error, got nil") } if id != "" { t.Errorf("Expected empty ID on error, got %s", id) } } func TestGetUserIDFromEmailDBError(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "error@example.com" mock.ExpectQuery(`SELECT users_id\s+FROM \(\s*SELECT users_id, 1 AS priority\s+FROM users\s+WHERE email_address = \?\s+AND is_deleted = 0\s*\) t\s+ORDER BY priority ASC\s+LIMIT 1;`). WithArgs(email). WillReturnError(sql.ErrConnDone) id, err := GetUserIDFromEmail(email) if err == nil { t.Error("Expected error, got nil") } if err != sql.ErrConnDone { t.Errorf("Expected sql.ErrConnDone, got: %v", err) } if id != "" { t.Errorf("Expected empty ID on error, got %s", id) } } func TestGetUserMultipleEmails(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() testCases := []struct { email string userID string hasNames bool }{ {"user1@example.com", "id1", true}, {"user2@example.com", "id2", false}, {"user3@example.com", "id3", true}, } for _, tc := range testCases { t.Run(tc.email, func(t *testing.T) { rows := sqlmock.NewRows([]string{"users_id"}). AddRow(tc.userID) mock.ExpectQuery(`SELECT users_id FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0 LIMIT 1;`). WithArgs(tc.email). WillReturnRows(rows) id, err := GetUserID(tc.email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if id != tc.userID { t.Errorf("Expected ID %s, got %s", tc.userID, id) } }) } } func TestCheckEmailInDBVariousEmails(t *testing.T) { testEmails := []string{ "normal@example.com", "with+plus@example.com", "with.dot@example.com", "with-dash@example.com", } mock, cleanup := setupMockDB(t) defer cleanup() for i, email := range testEmails { exists := i%2 == 0 // Alternate between true and false rows := sqlmock.NewRows([]string{"exists"}). AddRow(exists) mock.ExpectQuery(`SELECT EXISTS \(\s*SELECT 1 FROM uess_user_management\.users WHERE email_address = \? AND is_deleted = 0\)`). WithArgs(email). WillReturnRows(rows) result, err := CheckEmailInDB(email) if err != nil { t.Errorf("Expected no error for %s, got: %v", email, err) } if result != exists { t.Errorf("Expected %v for %s, got %v", exists, email, result) } } } func TestGetRoleIDsFromEmail(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "roles@example.com" expectedRoleIDs := []int{2, 4, 8} globalRows := sqlmock.NewRows([]string{"role_id"}). AddRow(2). AddRow(8) projectRows := sqlmock.NewRows([]string{"role_id"}). AddRow(4). AddRow(8). AddRow(2) mock.ExpectQuery(`SELECT DISTINCT ur\.role_id\s+FROM uess_user_management\.users u\s+JOIN uess_user_management\.user_roles ur ON u\.users_id = ur\.users_id\s+WHERE u\.email_address = \?\s+AND u\.is_deleted = 0\s+AND ur\.role_id IS NOT NULL`). WithArgs(email). WillReturnRows(globalRows) mock.ExpectQuery(`SELECT DISTINCT pa\.role_id\s+FROM uess_user_management\.users u\s+JOIN uess_project_management\.project_assignment pa ON u\.users_id = pa\.users_id\s+WHERE u\.email_address = \?\s+AND u\.is_deleted = 0\s+AND pa\.is_active = 1\s+AND pa\.role_id IS NOT NULL`). WithArgs(email). WillReturnRows(projectRows) roleIDs, err := GetRoleIDsFromEmail(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if !reflect.DeepEqual(roleIDs, expectedRoleIDs) { t.Errorf("Expected role IDs %v, got %v", expectedRoleIDs, roleIDs) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unfulfilled expectations: %v", err) } } func TestGetRoleIDsFromEmailQueryError(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "roles-error@example.com" mock.ExpectQuery(`SELECT DISTINCT ur\.role_id\s+FROM uess_user_management\.users u\s+JOIN uess_user_management\.user_roles ur ON u\.users_id = ur\.users_id\s+WHERE u\.email_address = \?\s+AND u\.is_deleted = 0\s+AND ur\.role_id IS NOT NULL`). WithArgs(email). WillReturnError(sql.ErrConnDone) roleIDs, err := GetRoleIDsFromEmail(email) if err == nil { t.Error("Expected error, got nil") } if roleIDs != nil { t.Errorf("Expected nil role IDs on error, got %v", roleIDs) } } func TestGetRoleIDsFromEmailNoRowsReturnsEmptySlice(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "no-roles@example.com" globalRows := sqlmock.NewRows([]string{"role_id"}) projectRows := sqlmock.NewRows([]string{"role_id"}) mock.ExpectQuery(`SELECT DISTINCT ur\.role_id\s+FROM uess_user_management\.users u\s+JOIN uess_user_management\.user_roles ur ON u\.users_id = ur\.users_id\s+WHERE u\.email_address = \?\s+AND u\.is_deleted = 0\s+AND ur\.role_id IS NOT NULL`). WithArgs(email). WillReturnRows(globalRows) mock.ExpectQuery(`SELECT DISTINCT pa\.role_id\s+FROM uess_user_management\.users u\s+JOIN uess_project_management\.project_assignment pa ON u\.users_id = pa\.users_id\s+WHERE u\.email_address = \?\s+AND u\.is_deleted = 0\s+AND pa\.is_active = 1\s+AND pa\.role_id IS NOT NULL`). WithArgs(email). WillReturnRows(projectRows) roleIDs, err := GetRoleIDsFromEmail(email) if err != nil { t.Errorf("Expected no error, got: %v", err) } if roleIDs == nil { t.Error("Expected empty slice, got nil") } if len(roleIDs) != 0 { t.Errorf("Expected empty slice, got %v", roleIDs) } } func TestParseRoleIDsCSV(t *testing.T) { roleIDs, err := parseRoleIDsCSV("1, 12,3") if err != nil { t.Fatalf("Expected no error, got: %v", err) } expected := []int{1, 12, 3} if !reflect.DeepEqual(roleIDs, expected) { t.Fatalf("Expected %v, got %v", expected, roleIDs) } } func TestParseRoleIDsCSVEmpty(t *testing.T) { roleIDs, err := parseRoleIDsCSV("") if err != nil { t.Fatalf("Expected no error, got: %v", err) } if roleIDs == nil { t.Fatal("Expected empty slice, got nil") } if len(roleIDs) != 0 { t.Fatalf("Expected empty slice, got %v", roleIDs) } } func TestFetchUserByEmail(t *testing.T) { mock, cleanup := setupMockDB(t) defer cleanup() email := "d.israel.psa@gmail.com" userRows := sqlmock.NewRows([]string{ "users_id", "first_name", "middle_initial", "last_name", "suffix", "role_ids", "office_id", "email_address", "MIS", "CAPI", "CAWI", "DPS", "sex", "status_of_employment", "home_address", "contact_number", "user_type", }).AddRow( "U0000000001", "AAAAAAAA", "A", "Israel", "", "1", 103, email, 1, 1, 1, 1, nil, "COSW", "Quezon City", "09171234567", nil, ) mock.ExpectQuery(`SELECT u\.users_id, u\.first_name, u\.middle_initial, u\.last_name, u\.suffix, GROUP_CONCAT\(DISTINCT ur\.role_id ORDER BY ur\.role_id\) AS role_ids, u\.office_id, u\.email_address, MAX\(ur\.MIS\), MAX\(ur\.CAPI\), MAX\(ur\.CAWI\), MAX\(ur\.DPS\),\s+u\.sex, u\.status_of_employment, u\.home_address, u\.contact_number, u\.user_type\s+FROM uess_user_management\.users u\s+LEFT JOIN uess_user_management\.user_roles ur\s+ON u\.users_id = ur\.users_id\s+WHERE u\.email_address = \?\s+GROUP BY u\.users_id, u\.first_name, u\.middle_initial, u\.last_name, u\.suffix, u\.office_id, u\.email_address, u\.sex, u\.status_of_employment, u\.home_address, u\.contact_number, u\.user_type`). WithArgs(email). WillReturnRows(userRows) projectRows := sqlmock.NewRows([]string{"project_id", "alias", "role_ids", "office_id"}). AddRow(1, "TTT", "1,12", 103) mock.ExpectQuery(`SELECT pa\.project_id, p\.alias, GROUP_CONCAT\(DISTINCT pa\.role_id ORDER BY pa\.role_id\) AS role_ids, u\.office_id\s+FROM uess_user_management\.users u\s+LEFT JOIN uess_project_management\.project_assignment pa\s+ON u\.users_id = pa\.users_id AND pa\.is_active = 1\s+LEFT JOIN uess_project_management\.project p\s+ON pa\.project_id = p\.project_id\s+WHERE u\.email_address = \? AND pa\.project_id IS NOT NULL\s+GROUP BY pa\.project_id, p\.alias, u\.office_id`). WithArgs(email). WillReturnRows(projectRows) user, err := FetchUserByEmail(email) if err != nil { t.Fatalf("Expected no error, got: %v", err) } if user.RoleID == nil || *user.RoleID != 1 { t.Fatalf("Expected RoleID=1, got %v", user.RoleID) } if user.Projects == nil || len(*user.Projects) != 1 { t.Fatalf("Expected 1 project, got %v", user.Projects) } if !reflect.DeepEqual((*user.Projects)[0].RoleID, []int{1, 12}) { t.Fatalf("Expected project roles [1 12], got %v", (*user.Projects)[0].RoleID) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatalf("Unfulfilled expectations: %v", err) } }