tercul-backend/internal/app/book/commands.go
google-labs-jules[bot] f675c98e80 Fix: Correct authorization logic in integration tests
The integration tests for admin-only mutations were failing due to an authorization issue. The root cause was that the JWT token used in the tests did not reflect the user's admin role, which was being set directly in the database.

This commit fixes the issue by:
1.  Updating the `CreateAuthenticatedUser` test helper to generate a new JWT token after a user's role is changed. This ensures the token contains the correct, up-to-date role.
2.  Removing all uses of `auth.ContextWithAdminUser` from the integration tests, making the JWT token the single source of truth for authorization.

This change also removes unused imports and variables that were causing build failures after the refactoring. All integration tests now pass.
2025-10-04 23:48:44 +00:00

118 lines
2.5 KiB
Go

package book
import (
"context"
"tercul/internal/app/authz"
"tercul/internal/domain"
)
// BookCommands contains the command handlers for the book aggregate.
type BookCommands struct {
repo domain.BookRepository
authzSvc *authz.Service
}
// NewBookCommands creates a new BookCommands handler.
func NewBookCommands(repo domain.BookRepository, authzSvc *authz.Service) *BookCommands {
return &BookCommands{
repo: repo,
authzSvc: authzSvc,
}
}
// CreateBookInput represents the input for creating a new book.
type CreateBookInput struct {
Title string
Description string
Language string
ISBN *string
AuthorIDs []uint
}
// CreateBook creates a new book.
func (c *BookCommands) CreateBook(ctx context.Context, input CreateBookInput) (*domain.Book, error) {
can, err := c.authzSvc.CanCreateBook(ctx)
if err != nil {
return nil, err
}
if !can {
return nil, domain.ErrForbidden
}
book := &domain.Book{
Title: input.Title,
Description: input.Description,
TranslatableModel: domain.TranslatableModel{
Language: input.Language,
},
}
if input.ISBN != nil {
book.ISBN = *input.ISBN
}
// In a real implementation, we would associate the authors here.
// for _, authorID := range input.AuthorIDs { ... }
err = c.repo.Create(ctx, book)
if err != nil {
return nil, err
}
return book, nil
}
// UpdateBookInput represents the input for updating an existing book.
type UpdateBookInput struct {
ID uint
Title *string
Description *string
Language *string
ISBN *string
AuthorIDs []uint
}
// UpdateBook updates an existing book.
func (c *BookCommands) UpdateBook(ctx context.Context, input UpdateBookInput) (*domain.Book, error) {
can, err := c.authzSvc.CanUpdateBook(ctx)
if err != nil {
return nil, err
}
if !can {
return nil, domain.ErrForbidden
}
book, err := c.repo.GetByID(ctx, input.ID)
if err != nil {
return nil, err
}
if input.Title != nil {
book.Title = *input.Title
}
if input.Description != nil {
book.Description = *input.Description
}
if input.Language != nil {
book.Language = *input.Language
}
if input.ISBN != nil {
book.ISBN = *input.ISBN
}
err = c.repo.Update(ctx, book)
if err != nil {
return nil, err
}
return book, nil
}
// DeleteBook deletes a book by ID.
func (c *BookCommands) DeleteBook(ctx context.Context, id uint) error {
can, err := c.authzSvc.CanDeleteBook(ctx)
if err != nil {
return err
}
if !can {
return domain.ErrForbidden
}
return c.repo.Delete(ctx, id)
}