test: add admin, notification, audit service tests (+30% coverage increase)
This commit is contained in:
parent
48dad702d6
commit
59df524ed5
3 changed files with 410 additions and 0 deletions
183
backend/internal/services/admin_service_test.go
Normal file
183
backend/internal/services/admin_service_test.go
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
|
||||
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, 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
108
backend/internal/services/audit_service_test.go
Normal file
108
backend/internal/services/audit_service_test.go
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
|
||||
func TestAuditService_RecordLogin(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
service := NewAuditService(db)
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("records login successfully", func(t *testing.T) {
|
||||
ipAddress := "192.168.1.1"
|
||||
userAgent := "Mozilla/5.0"
|
||||
|
||||
mock.ExpectExec("INSERT INTO login_audit").
|
||||
WithArgs("user-123", "test@example.com", "admin,recruiter", &ipAddress, &userAgent).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
|
||||
err := service.RecordLogin(ctx, LoginAuditInput{
|
||||
UserID: "user-123",
|
||||
Identifier: "test@example.com",
|
||||
Roles: []string{"admin", "recruiter"},
|
||||
IPAddress: &ipAddress,
|
||||
UserAgent: &userAgent,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("records login without optional fields", func(t *testing.T) {
|
||||
mock.ExpectExec("INSERT INTO login_audit").
|
||||
WithArgs("user-456", "admin@test.com", "superadmin", nil, nil).
|
||||
WillReturnResult(sqlmock.NewResult(2, 1))
|
||||
|
||||
err := service.RecordLogin(ctx, LoginAuditInput{
|
||||
UserID: "user-456",
|
||||
Identifier: "admin@test.com",
|
||||
Roles: []string{"superadmin"},
|
||||
IPAddress: nil,
|
||||
UserAgent: nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAuditService_ListLogins(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
service := NewAuditService(db)
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("returns empty list when no audits", func(t *testing.T) {
|
||||
mock.ExpectQuery("SELECT id, user_id, identifier, roles, ip_address, user_agent, created_at FROM login_audit").
|
||||
WithArgs(50).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "user_id", "identifier", "roles", "ip_address", "user_agent", "created_at"}))
|
||||
|
||||
audits, err := service.ListLogins(ctx, 0) // Should default to 50
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(audits) != 0 {
|
||||
t.Errorf("Expected 0 audits, got %d", len(audits))
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("respects custom limit", func(t *testing.T) {
|
||||
mock.ExpectQuery("SELECT id, user_id, identifier, roles, ip_address, user_agent, created_at FROM login_audit").
|
||||
WithArgs(10).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "user_id", "identifier", "roles", "ip_address", "user_agent", "created_at"}))
|
||||
|
||||
_, err := service.ListLogins(ctx, 10)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
119
backend/internal/services/notification_service_test.go
Normal file
119
backend/internal/services/notification_service_test.go
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
)
|
||||
|
||||
func TestNotificationService_ListNotifications(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
service := NewNotificationService(db)
|
||||
ctx := context.Background()
|
||||
userID := "019b5290-9680-7c06-9ee3-c9e0e117251b"
|
||||
|
||||
t.Run("returns empty list when no notifications", func(t *testing.T) {
|
||||
mock.ExpectQuery("SELECT id, user_id, type, title, message, link, read_at, created_at, updated_at FROM notifications").
|
||||
WithArgs(userID).
|
||||
WillReturnRows(sqlmock.NewRows([]string{"id", "user_id", "type", "title", "message", "link", "read_at", "created_at", "updated_at"}))
|
||||
|
||||
notifications, err := service.ListNotifications(ctx, userID)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
if len(notifications) != 0 {
|
||||
t.Errorf("Expected 0 notifications, got %d", len(notifications))
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotificationService_CreateNotification(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
service := NewNotificationService(db)
|
||||
ctx := context.Background()
|
||||
userID := "019b5290-9680-7c06-9ee3-c9e0e117251b"
|
||||
|
||||
t.Run("creates a new notification", func(t *testing.T) {
|
||||
mock.ExpectExec("INSERT INTO notifications").
|
||||
WithArgs(userID, "info", "Test Title", "Test message", nil).
|
||||
WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
|
||||
err := service.CreateNotification(ctx, userID, "info", "Test Title", "Test message", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotificationService_MarkAsRead(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
service := NewNotificationService(db)
|
||||
ctx := context.Background()
|
||||
userID := "019b5290-9680-7c06-9ee3-c9e0e117251b"
|
||||
|
||||
t.Run("marks notification as read", func(t *testing.T) {
|
||||
mock.ExpectExec("UPDATE notifications SET read_at").
|
||||
WithArgs("1", userID).
|
||||
WillReturnResult(sqlmock.NewResult(0, 1))
|
||||
|
||||
err := service.MarkAsRead(ctx, "1", userID)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotificationService_MarkAllAsRead(t *testing.T) {
|
||||
db, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create mock db: %v", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
service := NewNotificationService(db)
|
||||
ctx := context.Background()
|
||||
userID := "019b5290-9680-7c06-9ee3-c9e0e117251b"
|
||||
|
||||
t.Run("marks all notifications as read", func(t *testing.T) {
|
||||
mock.ExpectExec("UPDATE notifications SET read_at").
|
||||
WithArgs(userID).
|
||||
WillReturnResult(sqlmock.NewResult(0, 5))
|
||||
|
||||
err := service.MarkAllAsRead(ctx, userID)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if err := mock.ExpectationsWereMet(); err != nil {
|
||||
t.Errorf("Unmet expectations: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
Loading…
Reference in a new issue