Second checkpoint
This commit is contained in:
130
internal/service/category_test.go
Normal file
130
internal/service/category_test.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"gis/internal/domain"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// stubCategoryRepo is an in-memory CategoryRepository for tests.
|
||||
type stubCategoryRepo struct {
|
||||
store map[uuid.UUID]domain.Category
|
||||
}
|
||||
|
||||
func newStubCategoryRepo() *stubCategoryRepo {
|
||||
return &stubCategoryRepo{store: map[uuid.UUID]domain.Category{}}
|
||||
}
|
||||
|
||||
func (r *stubCategoryRepo) Create(_ context.Context, c domain.Category) (domain.Category, error) {
|
||||
if c.ID == uuid.Nil {
|
||||
c.ID = uuid.New()
|
||||
}
|
||||
r.store[c.ID] = c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (r *stubCategoryRepo) GetByID(_ context.Context, id uuid.UUID) (domain.Category, error) {
|
||||
c, ok := r.store[id]
|
||||
if !ok {
|
||||
return domain.Category{}, domain.ErrNotFound
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (r *stubCategoryRepo) List(_ context.Context, _ *uuid.UUID) ([]domain.Category, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *stubCategoryRepo) Update(_ context.Context, c domain.Category) (domain.Category, error) {
|
||||
r.store[c.ID] = c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (r *stubCategoryRepo) Delete(_ context.Context, id uuid.UUID) error {
|
||||
delete(r.store, id)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestCategoryService_Create(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("root category succeeds", func(t *testing.T) {
|
||||
svc := NewCategoryService(newStubCategoryRepo())
|
||||
got, err := svc.Create(ctx, CategoryInput{Name: "root"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got.Name != "root" || got.ParentID != nil {
|
||||
t.Fatalf("unexpected category: %+v", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("missing parent is a validation error", func(t *testing.T) {
|
||||
svc := NewCategoryService(newStubCategoryRepo())
|
||||
missing := uuid.New()
|
||||
_, err := svc.Create(ctx, CategoryInput{Name: "child", ParentID: &missing})
|
||||
if !errors.Is(err, domain.ErrValidation) {
|
||||
t.Fatalf("want ErrValidation, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("existing parent succeeds", func(t *testing.T) {
|
||||
repo := newStubCategoryRepo()
|
||||
svc := NewCategoryService(repo)
|
||||
root, _ := svc.Create(ctx, CategoryInput{Name: "root"})
|
||||
|
||||
child, err := svc.Create(ctx, CategoryInput{Name: "child", ParentID: &root.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if child.ParentID == nil || *child.ParentID != root.ID {
|
||||
t.Fatalf("child not linked to parent: %+v", child)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCategoryService_Update_PreventsCycles(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repo := newStubCategoryRepo()
|
||||
svc := NewCategoryService(repo)
|
||||
|
||||
root, _ := svc.Create(ctx, CategoryInput{Name: "root"})
|
||||
child, _ := svc.Create(ctx, CategoryInput{Name: "child", ParentID: &root.ID})
|
||||
|
||||
t.Run("category cannot be its own parent", func(t *testing.T) {
|
||||
_, err := svc.Update(ctx, root.ID, CategoryInput{Name: "root", ParentID: &root.ID})
|
||||
if !errors.Is(err, domain.ErrValidation) {
|
||||
t.Fatalf("want ErrValidation, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("category cannot descend from its own child", func(t *testing.T) {
|
||||
_, err := svc.Update(ctx, root.ID, CategoryInput{Name: "root", ParentID: &child.ID})
|
||||
if !errors.Is(err, domain.ErrValidation) {
|
||||
t.Fatalf("want ErrValidation, got %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid reparent succeeds", func(t *testing.T) {
|
||||
other, _ := svc.Create(ctx, CategoryInput{Name: "other"})
|
||||
updated, err := svc.Update(ctx, child.ID, CategoryInput{Name: "child", ParentID: &other.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if updated.ParentID == nil || *updated.ParentID != other.ID {
|
||||
t.Fatalf("reparent failed: %+v", updated)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestCategoryService_Update_MissingCategory(t *testing.T) {
|
||||
svc := NewCategoryService(newStubCategoryRepo())
|
||||
_, err := svc.Update(context.Background(), uuid.New(), CategoryInput{Name: "x"})
|
||||
if !errors.Is(err, domain.ErrNotFound) {
|
||||
t.Fatalf("want ErrNotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user