slate-final-1770513664/services/preferences-api/internal/service/example_test.go
jordan a49f36b8de
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Add service component: preferences-api
2026-02-08 01:21:11 +00:00

283 lines
7.4 KiB
Go

package service
import (
"context"
"sync"
"testing"
"git.threesix.ai/jordan/slate-final-1770513664/pkg/logging"
"git.threesix.ai/jordan/slate-final-1770513664/services/preferences-api/internal/domain"
"git.threesix.ai/jordan/slate-final-1770513664/services/preferences-api/internal/port"
)
// mockExampleRepository implements port.ExampleRepository for testing.
type mockExampleRepository struct {
mu sync.RWMutex
examples map[domain.ExampleID]*domain.Example
}
var _ port.ExampleRepository = (*mockExampleRepository)(nil)
func newMockExampleRepository() *mockExampleRepository {
return &mockExampleRepository{
examples: make(map[domain.ExampleID]*domain.Example),
}
}
func (m *mockExampleRepository) List(ctx context.Context) ([]domain.Example, error) {
m.mu.RLock()
defer m.mu.RUnlock()
result := make([]domain.Example, 0, len(m.examples))
for _, e := range m.examples {
result = append(result, *e)
}
return result, nil
}
func (m *mockExampleRepository) Get(ctx context.Context, id domain.ExampleID) (*domain.Example, error) {
m.mu.RLock()
defer m.mu.RUnlock()
e, ok := m.examples[id]
if !ok {
return nil, domain.ErrExampleNotFound
}
// Return a copy to avoid mutation
copy := *e
return &copy, nil
}
func (m *mockExampleRepository) Create(ctx context.Context, example *domain.Example) error {
m.mu.Lock()
defer m.mu.Unlock()
// Store a copy
copy := *example
m.examples[example.ID] = &copy
return nil
}
func (m *mockExampleRepository) Update(ctx context.Context, example *domain.Example) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, ok := m.examples[example.ID]; !ok {
return domain.ErrExampleNotFound
}
// Store a copy
copy := *example
m.examples[example.ID] = &copy
return nil
}
func (m *mockExampleRepository) Delete(ctx context.Context, id domain.ExampleID) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, ok := m.examples[id]; !ok {
return domain.ErrExampleNotFound
}
delete(m.examples, id)
return nil
}
func (m *mockExampleRepository) ExistsByName(ctx context.Context, name string) (bool, error) {
m.mu.RLock()
defer m.mu.RUnlock()
for _, e := range m.examples {
if e.Name == name {
return true, nil
}
}
return false, nil
}
func TestExampleService_Create(t *testing.T) {
repo := newMockExampleRepository()
svc := NewExampleService(repo, logging.Nop())
t.Run("creates example successfully", func(t *testing.T) {
example, err := svc.Create(context.Background(), CreateInput{
Name: "Test Example",
Description: "A test description",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if example.Name != "Test Example" {
t.Errorf("expected name 'Test Example', got '%s'", example.Name)
}
if example.ID.IsZero() {
t.Error("expected non-empty ID")
}
})
t.Run("rejects duplicate name", func(t *testing.T) {
_, err := svc.Create(context.Background(), CreateInput{
Name: "Test Example",
Description: "Another description",
})
if err != domain.ErrDuplicateExample {
t.Errorf("expected ErrDuplicateExample, got %v", err)
}
})
t.Run("rejects empty name", func(t *testing.T) {
_, err := svc.Create(context.Background(), CreateInput{
Name: "",
Description: "Description",
})
if err != domain.ErrInvalidExampleName {
t.Errorf("expected ErrInvalidExampleName, got %v", err)
}
})
}
func TestExampleService_Get(t *testing.T) {
repo := newMockExampleRepository()
svc := NewExampleService(repo, logging.Nop())
// Create an example first
created, _ := svc.Create(context.Background(), CreateInput{
Name: "Get Test",
Description: "Description",
})
t.Run("returns existing example", func(t *testing.T) {
example, err := svc.Get(context.Background(), created.ID)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if example.Name != "Get Test" {
t.Errorf("expected name 'Get Test', got '%s'", example.Name)
}
})
t.Run("returns not found for missing example", func(t *testing.T) {
_, err := svc.Get(context.Background(), "nonexistent-id")
if err != domain.ErrExampleNotFound {
t.Errorf("expected ErrExampleNotFound, got %v", err)
}
})
}
func TestExampleService_Update(t *testing.T) {
repo := newMockExampleRepository()
svc := NewExampleService(repo, logging.Nop())
// Create examples
example1, _ := svc.Create(context.Background(), CreateInput{
Name: "Update Test 1",
Description: "Original",
})
_, _ = svc.Create(context.Background(), CreateInput{
Name: "Update Test 2",
Description: "Other",
})
t.Run("updates example successfully", func(t *testing.T) {
updated, err := svc.Update(context.Background(), example1.ID, UpdateInput{
Name: "Updated Name",
Description: "Updated description",
})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if updated.Name != "Updated Name" {
t.Errorf("expected name 'Updated Name', got '%s'", updated.Name)
}
})
t.Run("allows same name on same example", func(t *testing.T) {
_, err := svc.Update(context.Background(), example1.ID, UpdateInput{
Name: "Updated Name",
Description: "Same name",
})
if err != nil {
t.Errorf("unexpected error updating with same name: %v", err)
}
})
t.Run("rejects name conflict", func(t *testing.T) {
_, err := svc.Update(context.Background(), example1.ID, UpdateInput{
Name: "Update Test 2",
Description: "Conflict",
})
if err != domain.ErrDuplicateExample {
t.Errorf("expected ErrDuplicateExample, got %v", err)
}
})
t.Run("returns not found for missing example", func(t *testing.T) {
_, err := svc.Update(context.Background(), "nonexistent-id", UpdateInput{
Name: "Anything",
Description: "",
})
if err != domain.ErrExampleNotFound {
t.Errorf("expected ErrExampleNotFound, got %v", err)
}
})
}
func TestExampleService_Delete(t *testing.T) {
repo := newMockExampleRepository()
svc := NewExampleService(repo, logging.Nop())
// Create an example first
created, _ := svc.Create(context.Background(), CreateInput{
Name: "Delete Test",
Description: "To be deleted",
})
t.Run("deletes example successfully", func(t *testing.T) {
err := svc.Delete(context.Background(), created.ID)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
// Verify deleted
_, err = svc.Get(context.Background(), created.ID)
if err != domain.ErrExampleNotFound {
t.Errorf("expected ErrExampleNotFound after delete, got %v", err)
}
})
t.Run("returns not found for missing example", func(t *testing.T) {
err := svc.Delete(context.Background(), "nonexistent-id")
if err != domain.ErrExampleNotFound {
t.Errorf("expected ErrExampleNotFound, got %v", err)
}
})
}
func TestExampleService_List(t *testing.T) {
repo := newMockExampleRepository()
svc := NewExampleService(repo, logging.Nop())
t.Run("returns empty list initially", func(t *testing.T) {
examples, err := svc.List(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(examples) != 0 {
t.Errorf("expected 0 examples, got %d", len(examples))
}
})
// Create some examples
_, _ = svc.Create(context.Background(), CreateInput{Name: "List Test 1", Description: ""})
_, _ = svc.Create(context.Background(), CreateInput{Name: "List Test 2", Description: ""})
t.Run("returns all examples", func(t *testing.T) {
examples, err := svc.List(context.Background())
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if len(examples) != 2 {
t.Errorf("expected 2 examples, got %d", len(examples))
}
})
}