mirror of
https://github.com/shankar0123/certctl.git
synced 2026-06-07 22:01:36 +00:00
2497be496d
- Add ctx first param to 21 service-layer handler-interface methods
across policy.go (6), profile.go (5), owner.go (5), team.go (5)
- Replace 24 context.Background() call sites with received ctx; use
context.WithoutCancel(ctx) for subsidiary audit-recording ops to
preserve fire-and-forget audit semantics without inheriting caller
cancellation
- Add ctx first param to 21 handler-interface method signatures across
policies.go (6), profiles.go (5), owners.go (5), teams.go (5)
- Thread r.Context() through 21 HTTP handler sites (ListPolicies,
GetPolicy, CreatePolicy, UpdatePolicy, DeletePolicy, ListViolations,
ListProfiles, GetProfile, CreateProfile, UpdateProfile, DeleteProfile,
ListOwners, GetOwner, CreateOwner, UpdateOwner, DeleteOwner,
ListTeams, GetTeam, CreateTeam, UpdateTeam, DeleteTeam)
- Update MockPolicyService/MockProfileService/MockOwnerService/
MockTeamService mock method impls with _ context.Context first param
(Fn fields unchanged — closures do not need ctx); update mock impls
in integration/lifecycle_test.go for all four services
- Update 12 service-layer test callsites (policy_test.go ×2,
owner_test.go ×5, team_test.go ×5, profile_test.go ×13) to pass
context.Background() at the call site
Audit complete. Commit: 1f6cf0eafa. Sections: 12. Findings: 2/7/10/4/6.
815 lines
20 KiB
Go
815 lines
20 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/shankar0123/certctl/internal/domain"
|
|
)
|
|
|
|
// mockOwnerRepo is a test implementation of OwnerRepository
|
|
type mockOwnerRepo struct {
|
|
owners map[string]*domain.Owner
|
|
CreateErr error
|
|
UpdateErr error
|
|
DeleteErr error
|
|
GetErr error
|
|
ListErr error
|
|
}
|
|
|
|
func (m *mockOwnerRepo) List(ctx context.Context) ([]*domain.Owner, error) {
|
|
if m.ListErr != nil {
|
|
return nil, m.ListErr
|
|
}
|
|
var owners []*domain.Owner
|
|
for _, o := range m.owners {
|
|
owners = append(owners, o)
|
|
}
|
|
return owners, nil
|
|
}
|
|
|
|
func (m *mockOwnerRepo) Get(ctx context.Context, id string) (*domain.Owner, error) {
|
|
if m.GetErr != nil {
|
|
return nil, m.GetErr
|
|
}
|
|
owner, ok := m.owners[id]
|
|
if !ok {
|
|
return nil, errNotFound
|
|
}
|
|
return owner, nil
|
|
}
|
|
|
|
func (m *mockOwnerRepo) Create(ctx context.Context, owner *domain.Owner) error {
|
|
if m.CreateErr != nil {
|
|
return m.CreateErr
|
|
}
|
|
m.owners[owner.ID] = owner
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOwnerRepo) Update(ctx context.Context, owner *domain.Owner) error {
|
|
if m.UpdateErr != nil {
|
|
return m.UpdateErr
|
|
}
|
|
m.owners[owner.ID] = owner
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOwnerRepo) Delete(ctx context.Context, id string) error {
|
|
if m.DeleteErr != nil {
|
|
return m.DeleteErr
|
|
}
|
|
delete(m.owners, id)
|
|
return nil
|
|
}
|
|
|
|
func (m *mockOwnerRepo) AddOwner(owner *domain.Owner) {
|
|
m.owners[owner.ID] = owner
|
|
}
|
|
|
|
func newMockOwnerRepository() *mockOwnerRepo {
|
|
return &mockOwnerRepo{
|
|
owners: make(map[string]*domain.Owner),
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_List tests paginated listing of owners.
|
|
func TestOwnerService_List(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
owner1 := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
owner2 := &domain.Owner{
|
|
ID: "owner-002",
|
|
Name: "Bob Jones",
|
|
Email: "bob@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner1)
|
|
ownerRepo.AddOwner(owner2)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owners, total, err := ownerService.List(ctx, 1, 50)
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
|
|
if len(owners) != 2 {
|
|
t.Errorf("expected 2 owners, got %d", len(owners))
|
|
}
|
|
|
|
if total != 2 {
|
|
t.Errorf("expected total 2, got %d", total)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_List_DefaultPagination tests that default pagination values are applied.
|
|
func TestOwnerService_List_DefaultPagination(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
owner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
// Test with page < 1 (should default to 1)
|
|
owners, total, err := ownerService.List(ctx, 0, 0)
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
|
|
if len(owners) != 1 {
|
|
t.Errorf("expected 1 owner with default pagination, got %d", len(owners))
|
|
}
|
|
|
|
if total != 1 {
|
|
t.Errorf("expected total 1, got %d", total)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_List_RepositoryError tests handling of repository errors.
|
|
func TestOwnerService_List_RepositoryError(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.ListErr = errors.New("database error")
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
_, _, err := ownerService.List(ctx, 1, 50)
|
|
if err == nil {
|
|
t.Fatal("expected error from List, got nil")
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_List_EmptyResult tests listing with no owners.
|
|
func TestOwnerService_List_EmptyResult(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owners, total, err := ownerService.List(ctx, 1, 50)
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
|
|
if len(owners) != 0 {
|
|
t.Errorf("expected 0 owners, got %d", len(owners))
|
|
}
|
|
|
|
if total != 0 {
|
|
t.Errorf("expected total 0, got %d", total)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_List_PageBeyondRange tests pagination when page exceeds available data.
|
|
func TestOwnerService_List_PageBeyondRange(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
owner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
// Request page 3 with only 1 owner
|
|
owners, total, err := ownerService.List(ctx, 3, 1)
|
|
if err != nil {
|
|
t.Fatalf("List failed: %v", err)
|
|
}
|
|
|
|
if len(owners) != 0 {
|
|
t.Errorf("expected 0 owners on page beyond range, got %d", len(owners))
|
|
}
|
|
|
|
if total != 1 {
|
|
t.Errorf("expected total 1, got %d", total)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Get tests retrieving a single owner by ID.
|
|
func TestOwnerService_Get(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
owner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
retrieved, err := ownerService.Get(ctx, "owner-001")
|
|
if err != nil {
|
|
t.Fatalf("Get failed: %v", err)
|
|
}
|
|
|
|
if retrieved.Name != "Alice Smith" {
|
|
t.Errorf("expected name Alice Smith, got %s", retrieved.Name)
|
|
}
|
|
|
|
if retrieved.Email != "alice@example.com" {
|
|
t.Errorf("expected email alice@example.com, got %s", retrieved.Email)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Get_NotFound tests Get with a nonexistent owner.
|
|
func TestOwnerService_Get_NotFound(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
_, err := ownerService.Get(ctx, "nonexistent")
|
|
if err == nil {
|
|
t.Fatal("expected error for nonexistent owner, got nil")
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Create tests creating a new owner with audit recording.
|
|
func TestOwnerService_Create(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owner := &domain.Owner{
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
}
|
|
|
|
err := ownerService.Create(ctx, owner, "user-1")
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
if owner.ID == "" {
|
|
t.Fatal("expected non-empty owner ID after creation")
|
|
}
|
|
|
|
if !owner.CreatedAt.IsZero() && owner.CreatedAt.After(time.Now().Add(-time.Second)) {
|
|
// CreatedAt should have been set
|
|
} else if owner.CreatedAt.IsZero() {
|
|
t.Fatal("expected CreatedAt to be set")
|
|
}
|
|
|
|
if len(ownerRepo.owners) != 1 {
|
|
t.Errorf("expected 1 owner in repo, got %d", len(ownerRepo.owners))
|
|
}
|
|
|
|
if len(auditRepo.Events) != 1 {
|
|
t.Errorf("expected 1 audit event, got %d", len(auditRepo.Events))
|
|
}
|
|
|
|
auditEvent := auditRepo.Events[0]
|
|
if auditEvent.Action != "create_owner" {
|
|
t.Errorf("expected action create_owner, got %s", auditEvent.Action)
|
|
}
|
|
|
|
if auditEvent.ResourceType != "owner" {
|
|
t.Errorf("expected resource type owner, got %s", auditEvent.ResourceType)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Create_EmptyName tests that Create rejects empty name.
|
|
func TestOwnerService_Create_EmptyName(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owner := &domain.Owner{
|
|
Name: "",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
}
|
|
|
|
err := ownerService.Create(ctx, owner, "user-1")
|
|
if err == nil {
|
|
t.Fatal("expected error for empty owner name")
|
|
}
|
|
|
|
if len(ownerRepo.owners) != 0 {
|
|
t.Errorf("expected 0 owners in repo after validation failure, got %d", len(ownerRepo.owners))
|
|
}
|
|
|
|
if len(auditRepo.Events) != 0 {
|
|
t.Errorf("expected 0 audit events after validation failure, got %d", len(auditRepo.Events))
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Create_WithExistingID tests that Create preserves existing ID.
|
|
func TestOwnerService_Create_WithExistingID(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owner := &domain.Owner{
|
|
ID: "custom-id-123",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
}
|
|
|
|
err := ownerService.Create(ctx, owner, "user-1")
|
|
if err != nil {
|
|
t.Fatalf("Create failed: %v", err)
|
|
}
|
|
|
|
if owner.ID != "custom-id-123" {
|
|
t.Errorf("expected ID custom-id-123, got %s", owner.ID)
|
|
}
|
|
|
|
stored, ok := ownerRepo.owners["custom-id-123"]
|
|
if !ok {
|
|
t.Fatal("expected owner with custom ID in repo")
|
|
}
|
|
|
|
if stored.Name != "Alice Smith" {
|
|
t.Errorf("expected name Alice Smith, got %s", stored.Name)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Create_RepositoryError tests Create with repository failure.
|
|
func TestOwnerService_Create_RepositoryError(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.CreateErr = errors.New("database error")
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owner := &domain.Owner{
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
}
|
|
|
|
err := ownerService.Create(ctx, owner, "user-1")
|
|
if err == nil {
|
|
t.Fatal("expected error from Create")
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Update tests updating an existing owner.
|
|
func TestOwnerService_Update(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
originalOwner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(originalOwner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
updatedOwner := &domain.Owner{
|
|
Name: "Alice Johnson",
|
|
Email: "alice.j@example.com",
|
|
TeamID: "team-002",
|
|
}
|
|
|
|
err := ownerService.Update(ctx, "owner-001", updatedOwner, "user-1")
|
|
if err != nil {
|
|
t.Fatalf("Update failed: %v", err)
|
|
}
|
|
|
|
stored := ownerRepo.owners["owner-001"]
|
|
if stored.Name != "Alice Johnson" {
|
|
t.Errorf("expected updated name Alice Johnson, got %s", stored.Name)
|
|
}
|
|
|
|
if stored.Email != "alice.j@example.com" {
|
|
t.Errorf("expected updated email alice.j@example.com, got %s", stored.Email)
|
|
}
|
|
|
|
if stored.ID != "owner-001" {
|
|
t.Errorf("expected ID to remain owner-001, got %s", stored.ID)
|
|
}
|
|
|
|
if len(auditRepo.Events) != 1 {
|
|
t.Errorf("expected 1 audit event, got %d", len(auditRepo.Events))
|
|
}
|
|
|
|
auditEvent := auditRepo.Events[0]
|
|
if auditEvent.Action != "update_owner" {
|
|
t.Errorf("expected action update_owner, got %s", auditEvent.Action)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Update_EmptyName tests that Update rejects empty name.
|
|
func TestOwnerService_Update_EmptyName(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
originalOwner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(originalOwner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
updatedOwner := &domain.Owner{
|
|
Name: "",
|
|
Email: "alice.j@example.com",
|
|
TeamID: "team-002",
|
|
}
|
|
|
|
err := ownerService.Update(ctx, "owner-001", updatedOwner, "user-1")
|
|
if err == nil {
|
|
t.Fatal("expected error for empty owner name")
|
|
}
|
|
|
|
if len(auditRepo.Events) != 0 {
|
|
t.Errorf("expected 0 audit events after validation failure, got %d", len(auditRepo.Events))
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Update_RepositoryError tests Update with repository failure.
|
|
func TestOwnerService_Update_RepositoryError(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.UpdateErr = errors.New("database error")
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
updatedOwner := &domain.Owner{
|
|
Name: "Alice Johnson",
|
|
Email: "alice.j@example.com",
|
|
TeamID: "team-002",
|
|
}
|
|
|
|
err := ownerService.Update(ctx, "owner-001", updatedOwner, "user-1")
|
|
if err == nil {
|
|
t.Fatal("expected error from Update")
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Delete tests deleting an owner with audit recording.
|
|
func TestOwnerService_Delete(t *testing.T) {
|
|
ctx := context.Background()
|
|
now := time.Now()
|
|
|
|
owner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
err := ownerService.Delete(ctx, "owner-001", "user-1")
|
|
if err != nil {
|
|
t.Fatalf("Delete failed: %v", err)
|
|
}
|
|
|
|
if len(ownerRepo.owners) != 0 {
|
|
t.Errorf("expected 0 owners in repo after delete, got %d", len(ownerRepo.owners))
|
|
}
|
|
|
|
if len(auditRepo.Events) != 1 {
|
|
t.Errorf("expected 1 audit event, got %d", len(auditRepo.Events))
|
|
}
|
|
|
|
auditEvent := auditRepo.Events[0]
|
|
if auditEvent.Action != "delete_owner" {
|
|
t.Errorf("expected action delete_owner, got %s", auditEvent.Action)
|
|
}
|
|
|
|
if auditEvent.ResourceID != "owner-001" {
|
|
t.Errorf("expected resource ID owner-001, got %s", auditEvent.ResourceID)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_Delete_RepositoryError tests Delete with repository failure.
|
|
func TestOwnerService_Delete_RepositoryError(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.DeleteErr = errors.New("database error")
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
err := ownerService.Delete(ctx, "owner-001", "user-1")
|
|
if err == nil {
|
|
t.Fatal("expected error from Delete")
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_ListOwners_HandlerInterface tests the handler interface method ListOwners.
|
|
func TestOwnerService_ListOwners_HandlerInterface(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
owner1 := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
owner2 := &domain.Owner{
|
|
ID: "owner-002",
|
|
Name: "Bob Jones",
|
|
Email: "bob@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner1)
|
|
ownerRepo.AddOwner(owner2)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owners, total, err := ownerService.ListOwners(context.Background(), 1, 50)
|
|
if err != nil {
|
|
t.Fatalf("ListOwners failed: %v", err)
|
|
}
|
|
|
|
if len(owners) != 2 {
|
|
t.Errorf("expected 2 owners, got %d", len(owners))
|
|
}
|
|
|
|
if total != 2 {
|
|
t.Errorf("expected total 2, got %d", total)
|
|
}
|
|
|
|
// Verify value type conversion worked
|
|
if owners[0].ID == "" {
|
|
t.Fatal("expected non-empty owner ID in result")
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_GetOwner_HandlerInterface tests the handler interface method GetOwner.
|
|
func TestOwnerService_GetOwner_HandlerInterface(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
owner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
retrieved, err := ownerService.GetOwner(context.Background(), "owner-001")
|
|
if err != nil {
|
|
t.Fatalf("GetOwner failed: %v", err)
|
|
}
|
|
|
|
if retrieved.Name != "Alice Smith" {
|
|
t.Errorf("expected name Alice Smith, got %s", retrieved.Name)
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_CreateOwner_HandlerInterface tests the handler interface method CreateOwner.
|
|
func TestOwnerService_CreateOwner_HandlerInterface(t *testing.T) {
|
|
ownerRepo := newMockOwnerRepository()
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
owner := domain.Owner{
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
}
|
|
|
|
created, err := ownerService.CreateOwner(context.Background(), owner)
|
|
if err != nil {
|
|
t.Fatalf("CreateOwner failed: %v", err)
|
|
}
|
|
|
|
if created.ID == "" {
|
|
t.Fatal("expected non-empty owner ID after creation")
|
|
}
|
|
|
|
if created.Name != "Alice Smith" {
|
|
t.Errorf("expected name Alice Smith, got %s", created.Name)
|
|
}
|
|
|
|
if len(ownerRepo.owners) != 1 {
|
|
t.Errorf("expected 1 owner in repo, got %d", len(ownerRepo.owners))
|
|
}
|
|
|
|
// Note: handler interface method does NOT record audit events (no actor parameter)
|
|
if len(auditRepo.Events) != 0 {
|
|
t.Errorf("expected 0 audit events from handler interface method, got %d", len(auditRepo.Events))
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_UpdateOwner_HandlerInterface tests the handler interface method UpdateOwner.
|
|
func TestOwnerService_UpdateOwner_HandlerInterface(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
originalOwner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(originalOwner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
updatedOwner := domain.Owner{
|
|
Name: "Alice Johnson",
|
|
Email: "alice.j@example.com",
|
|
TeamID: "team-002",
|
|
}
|
|
|
|
updated, err := ownerService.UpdateOwner(context.Background(), "owner-001", updatedOwner)
|
|
if err != nil {
|
|
t.Fatalf("UpdateOwner failed: %v", err)
|
|
}
|
|
|
|
if updated.ID != "owner-001" {
|
|
t.Errorf("expected ID owner-001, got %s", updated.ID)
|
|
}
|
|
|
|
if updated.Name != "Alice Johnson" {
|
|
t.Errorf("expected updated name Alice Johnson, got %s", updated.Name)
|
|
}
|
|
|
|
// Verify in repo
|
|
stored := ownerRepo.owners["owner-001"]
|
|
if stored.Email != "alice.j@example.com" {
|
|
t.Errorf("expected updated email alice.j@example.com, got %s", stored.Email)
|
|
}
|
|
|
|
// Note: handler interface method does NOT record audit events
|
|
if len(auditRepo.Events) != 0 {
|
|
t.Errorf("expected 0 audit events from handler interface method, got %d", len(auditRepo.Events))
|
|
}
|
|
}
|
|
|
|
// TestOwnerService_DeleteOwner_HandlerInterface tests the handler interface method DeleteOwner.
|
|
func TestOwnerService_DeleteOwner_HandlerInterface(t *testing.T) {
|
|
now := time.Now()
|
|
|
|
owner := &domain.Owner{
|
|
ID: "owner-001",
|
|
Name: "Alice Smith",
|
|
Email: "alice@example.com",
|
|
TeamID: "team-001",
|
|
CreatedAt: now,
|
|
UpdatedAt: now,
|
|
}
|
|
|
|
ownerRepo := newMockOwnerRepository()
|
|
ownerRepo.AddOwner(owner)
|
|
|
|
auditRepo := newMockAuditRepository()
|
|
auditService := NewAuditService(auditRepo)
|
|
|
|
ownerService := NewOwnerService(ownerRepo, auditService)
|
|
|
|
err := ownerService.DeleteOwner(context.Background(), "owner-001")
|
|
if err != nil {
|
|
t.Fatalf("DeleteOwner failed: %v", err)
|
|
}
|
|
|
|
if len(ownerRepo.owners) != 0 {
|
|
t.Errorf("expected 0 owners in repo after delete, got %d", len(ownerRepo.owners))
|
|
}
|
|
|
|
// Note: handler interface method does NOT record audit events
|
|
if len(auditRepo.Events) != 0 {
|
|
t.Errorf("expected 0 audit events from handler interface method, got %d", len(auditRepo.Events))
|
|
}
|
|
}
|