package services import ( "context" "regexp" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "github.com/rede5/gohorsejobs/backend/internal/dto" ) func TestAdminService_ListCompanies(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewAdminService(db) ctx := context.Background() t.Run("returns empty list when no companies", func(t *testing.T) { // Count query mock.ExpectQuery("SELECT COUNT").WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0)) // Data query mock.ExpectQuery("SELECT id, name, slug").WillReturnRows(sqlmock.NewRows([]string{ "id", "name", "slug", "type", "document", "address", "region_id", "city_id", "phone", "email", "website", "logo_url", "description", "active", "verified", "created_at", "updated_at", })) companies, total, err := service.ListCompanies(ctx, nil, 1, 10) if err != nil { t.Fatalf("Unexpected error: %v", err) } if total != 0 { t.Errorf("Expected total=0, got %d", total) } if len(companies) != 0 { t.Errorf("Expected 0 companies, got %d", len(companies)) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("filters by verified status", func(t *testing.T) { verified := true mock.ExpectQuery("SELECT COUNT").WithArgs(verified).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(0)) mock.ExpectQuery("SELECT id, name, slug").WithArgs(verified, 10, 0).WillReturnRows(sqlmock.NewRows([]string{ "id", "name", "slug", "type", "document", "address", "region_id", "city_id", "phone", "email", "website", "logo_url", "description", "active", "verified", "created_at", "updated_at", })) _, _, err := service.ListCompanies(ctx, &verified, 1, 10) if err != nil { t.Fatalf("Unexpected error: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) } func TestAdminService_ListTags(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewAdminService(db) ctx := context.Background() t.Run("returns empty list when no tags", func(t *testing.T) { mock.ExpectQuery("SELECT id, name, category, active, created_at, updated_at FROM job_tags"). WillReturnRows(sqlmock.NewRows([]string{"id", "name", "category", "active", "created_at", "updated_at"})) tags, err := service.ListTags(ctx, nil) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(tags) != 0 { t.Errorf("Expected 0 tags, got %d", len(tags)) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("filters by category", func(t *testing.T) { category := "stack" mock.ExpectQuery("SELECT id, name, category, active, created_at, updated_at FROM job_tags WHERE category"). WithArgs(category). WillReturnRows(sqlmock.NewRows([]string{"id", "name", "category", "active", "created_at", "updated_at"})) _, err := service.ListTags(ctx, &category) if err != nil { t.Fatalf("Unexpected error: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) } func TestAdminService_CreateTag(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewAdminService(db) ctx := context.Background() t.Run("creates a new tag", func(t *testing.T) { mock.ExpectQuery("INSERT INTO job_tags"). WithArgs("Go", "stack", true, sqlmock.AnyArg(), sqlmock.AnyArg()). WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(1)) tag, err := service.CreateTag(ctx, "Go", "stack") if err != nil { t.Fatalf("Unexpected error: %v", err) } if tag == nil { t.Fatal("Expected tag, got nil") } if tag.Name != "Go" { t.Errorf("Expected name='Go', got '%s'", tag.Name) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("rejects empty tag name", func(t *testing.T) { _, err := service.CreateTag(ctx, " ", "stack") if err == nil { t.Error("Expected error for empty tag name") } }) } func TestAdminService_GetUser(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewAdminService(db) ctx := context.Background() t.Run("returns user by id", func(t *testing.T) { userID := "019b5290-9680-7c06-9ee3-c9e0e117251b" now := time.Now() mock.ExpectQuery("SELECT id, full_name, email, role, created_at FROM users WHERE id"). WithArgs(userID). WillReturnRows(sqlmock.NewRows([]string{"id", "name", "email", "role", "created_at"}). AddRow(userID, "Test User", "test@example.com", "admin", now)) user, err := service.GetUser(ctx, userID) if err != nil { t.Fatalf("Unexpected error: %v", err) } if user == nil { t.Fatal("Expected user, got nil") } if user.Name != "Test User" { t.Errorf("Expected name='Test User', got '%s'", user.Name) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) } func TestAdminService_UpdateCompanyStatus(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("Failed to create mock db: %v", err) } defer db.Close() service := NewAdminService(db) ctx := context.Background() companyColumns := []string{ "id", "name", "slug", "type", "document", "address", "region_id", "city_id", "phone", "email", "website", "logo_url", "description", "active", "verified", "created_at", "updated_at", } t.Run("updates active status successfully", func(t *testing.T) { companyID := "019b5290-9680-7c06-9ee3-c9e0e117251c" now := time.Now() active := false // First: getCompanyByID query mock.ExpectQuery("SELECT id, name, slug"). WithArgs(companyID). WillReturnRows(sqlmock.NewRows(companyColumns). AddRow(companyID, "Test Company", "test-company", "employer", nil, nil, nil, nil, nil, "test@company.com", nil, nil, nil, true, false, now, now)) // Then: UPDATE query mock.ExpectExec("UPDATE companies"). WithArgs(false, false, sqlmock.AnyArg(), companyID). WillReturnResult(sqlmock.NewResult(0, 1)) company, err := service.UpdateCompanyStatus(ctx, companyID, &active, nil) if err != nil { t.Fatalf("Unexpected error: %v", err) } if company == nil { t.Fatal("Expected company, got nil") } if company.Active != false { t.Errorf("Expected active=false, got %v", company.Active) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("updates verified status successfully", func(t *testing.T) { companyID := "019b5290-9680-7c06-9ee3-c9e0e117251d" now := time.Now() verified := true mock.ExpectQuery("SELECT id, name, slug"). WithArgs(companyID). WillReturnRows(sqlmock.NewRows(companyColumns). AddRow(companyID, "Pending Company", "pending-company", "employer", nil, nil, nil, nil, nil, "pending@company.com", nil, nil, nil, true, false, now, now)) mock.ExpectExec("UPDATE companies"). WithArgs(true, true, sqlmock.AnyArg(), companyID). WillReturnResult(sqlmock.NewResult(0, 1)) company, err := service.UpdateCompanyStatus(ctx, companyID, nil, &verified) if err != nil { t.Fatalf("Unexpected error: %v", err) } if company == nil { t.Fatal("Expected company, got nil") } if company.Verified != true { t.Errorf("Expected verified=true, got %v", company.Verified) } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) t.Run("returns error when company not found", func(t *testing.T) { companyID := "non-existent-id" active := false mock.ExpectQuery("SELECT id, name, slug"). WithArgs(companyID). WillReturnError(err) _, updateErr := service.UpdateCompanyStatus(ctx, companyID, &active, nil) if updateErr == nil { t.Error("Expected error for non-existent company") } if err := mock.ExpectationsWereMet(); err != nil { t.Errorf("Unmet expectations: %v", err) } }) } func TestAdminService_ListUsers(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewAdminService(db) t.Run("returns users list", func(t *testing.T) { mock.ExpectQuery(regexp.QuoteMeta(`SELECT COUNT(*) FROM users`)). WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(2)) mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, COALESCE(name, full_name, identifier, ''), email, role, COALESCE(status, 'active'), created_at FROM users`)). WillReturnRows(sqlmock.NewRows([]string{"id", "name", "email", "role", "status", "created_at"}). AddRow("1", "User 1", "u1@test.com", "admin", "active", time.Now()). AddRow("2", "User 2", "u2@test.com", "user", "inactive", time.Now())) users, count, err := service.ListUsers(context.Background(), 1, 10, nil) if err != nil { t.Errorf("ListUsers() error = %v", err) } if count != 2 { t.Errorf("ListUsers() count = %d, want 2", count) } if len(users) != 2 { t.Errorf("ListUsers() length = %d, want 2", len(users)) } }) } func TestAdminService_DuplicateJob(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewAdminService(db) jobID := "job-123" // Expect fetch original job mock.ExpectQuery(regexp.QuoteMeta(`SELECT company_id`)). WithArgs(jobID). WillReturnRows(sqlmock.NewRows([]string{ "company_id", "created_by", "title", "description", "salary_min", "salary_max", "salary_type", "employment_type", "work_mode", "working_hours", "location", "region_id", "city_id", "requirements", "benefits", "visa_support", "language_level", }).AddRow( "comp-1", "user-1", "Job 1", "Desc", 100, 200, "m", "ft", "remote", "40h", "Remote", 0, 0, nil, nil, false, "N2", )) // Expect insert duplicated job mock.ExpectQuery(regexp.QuoteMeta(`INSERT INTO jobs`)). WithArgs( "comp-1", "user-1", "Job 1", "Desc", 100.0, 200.0, "m", "ft", "remote", "40h", "Remote", 0, 0, sqlmock.AnyArg(), sqlmock.AnyArg(), false, "N2", "draft", false, sqlmock.AnyArg(), sqlmock.AnyArg(), // Status, IsFeatured, CreatedAt, UpdatedAt ). WillReturnRows(sqlmock.NewRows([]string{"id"}). AddRow("new-job-id")) job, err := service.DuplicateJob(context.Background(), jobID) if err != nil { t.Errorf("DuplicateJob() error = %v", err) } if job.ID != "new-job-id" { t.Errorf("DuplicateJob() ID = %s, want new-job-id", job.ID) } } func TestAdminService_EmailTemplates(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("an error '%s' was not expected when opening a stub database connection", err) } defer db.Close() service := NewAdminService(db) t.Run("ListEmailTemplates", func(t *testing.T) { mock.ExpectQuery(regexp.QuoteMeta(`SELECT id, slug, subject, body_html, variables, created_at, updated_at FROM email_templates`)). WillReturnRows(sqlmock.NewRows([]string{"id", "slug", "subject", "body_html", "variables", "created_at", "updated_at"}). AddRow("1", "welcome", "Welcome", "