Fix build issues and refactor for maintainability

This commit is contained in:
google-labs-jules[bot] 2025-09-05 21:37:42 +00:00
parent 8797cec718
commit 52348462a6
78 changed files with 269 additions and 186 deletions

View File

@ -50,23 +50,11 @@ func main() {
// Create GraphQL server
resolver := &graph.Resolver{
WorkRepo: appBuilder.GetRepositories().WorkRepository,
UserRepo: appBuilder.GetRepositories().UserRepository,
AuthorRepo: appBuilder.GetRepositories().AuthorRepository,
TranslationRepo: appBuilder.GetRepositories().TranslationRepository,
CommentRepo: appBuilder.GetRepositories().CommentRepository,
LikeRepo: appBuilder.GetRepositories().LikeRepository,
BookmarkRepo: appBuilder.GetRepositories().BookmarkRepository,
CollectionRepo: appBuilder.GetRepositories().CollectionRepository,
TagRepo: appBuilder.GetRepositories().TagRepository,
CategoryRepo: appBuilder.GetRepositories().CategoryRepository,
WorkService: appBuilder.GetServices().WorkService,
Localization: appBuilder.GetServices().LocalizationService,
AuthService: appBuilder.GetServices().AuthService,
App: appBuilder.GetApplication(),
}
jwtManager := auth.NewJWTManager()
srv := graph.NewServerWithAuth(resolver, jwtManager)
srv := NewServerWithAuth(resolver, jwtManager)
graphQLServer := &http.Server{
Addr: config.Cfg.ServerPort,
Handler: srv,

View File

@ -2,15 +2,15 @@ package main
import (
"net/http"
"tercul/internal/adapters/graphql"
"tercul/internal/platform/auth"
"github.com/99designs/gqlgen/graphql/handler"
)
// NewServer creates a new GraphQL server with the given resolver
func NewServer(resolver *Resolver) http.Handler {
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))
func NewServer(resolver *graphql.Resolver) http.Handler {
srv := handler.NewDefaultServer(graphql.NewExecutableSchema(graphql.Config{Resolvers: resolver}))
// Create a mux to handle GraphQL endpoint only (no playground here; served separately in production)
mux := http.NewServeMux()
@ -20,8 +20,8 @@ func NewServer(resolver *Resolver) http.Handler {
}
// NewServerWithAuth creates a new GraphQL server with authentication middleware
func NewServerWithAuth(resolver *Resolver, jwtManager *auth.JWTManager) http.Handler {
srv := handler.NewDefaultServer(NewExecutableSchema(Config{Resolvers: resolver}))
func NewServerWithAuth(resolver *graphql.Resolver, jwtManager *auth.JWTManager) http.Handler {
srv := handler.NewDefaultServer(graphql.NewExecutableSchema(graphql.Config{Resolvers: resolver}))
// Apply authentication middleware to GraphQL endpoint
authHandler := auth.GraphQLAuthMiddleware(jwtManager)(srv)

View File

@ -2,14 +2,14 @@ package main
import (
"context"
"log"
"tercul/internal/app"
"tercul/internal/jobs/linguistics"
"tercul/internal/platform/config"
log "tercul/internal/platform/log"
)
func main() {
log.Println("Starting enrichment tool...")
log.LogInfo("Starting enrichment tool...")
// Load configuration from environment variables
config.LoadConfig()
@ -36,8 +36,8 @@ func main() {
}
// Enqueue analysis for each work
for _, work := range works.Data {
err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynqClient(), work.ID)
for _, work := range works.Items {
err := linguistics.EnqueueAnalysisForWork(appBuilder.GetAsynq(), work.ID)
if err != nil {
log.LogError("Failed to enqueue analysis for work",
log.F("workID", work.ID),
@ -45,5 +45,5 @@ func main() {
}
}
log.Println("Enrichment tool finished.")
log.LogInfo("Enrichment tool finished.")
}

View File

@ -1,3 +1,5 @@
//go:build tools
package main
import (

View File

@ -1,3 +1,5 @@
//go:build tools
package main
import (

View File

@ -1,3 +1,5 @@
//go:build tools
package main
import (

View File

@ -4,10 +4,10 @@ import "context"
// resolveWorkContent uses Localization service to fetch preferred content
func (r *queryResolver) resolveWorkContent(ctx context.Context, workID uint, preferredLanguage string) *string {
if r.Localization == nil {
if r.App.Localization == nil {
return nil
}
content, err := r.Localization.GetWorkContent(ctx, workID, preferredLanguage)
content, err := r.App.Localization.GetWorkContent(ctx, workID, preferredLanguage)
if err != nil || content == "" {
return nil
}

View File

@ -9,7 +9,7 @@ import (
"net/http/httptest"
"testing"
"tercul/internal/adapters/graphql"
graph "tercul/internal/adapters/graphql"
"tercul/internal/testutil"
"github.com/99designs/gqlgen/graphql/handler"
@ -140,9 +140,9 @@ func (s *GraphQLIntegrationSuite) TestQueryWork() {
// TestQueryWorks tests the works query
func (s *GraphQLIntegrationSuite) TestQueryWorks() {
// Create test works
work1 := s.CreateTestWork("Test Work 1", "en", "Test content for work 1")
work2 := s.CreateTestWork("Test Work 2", "en", "Test content for work 2")
work3 := s.CreateTestWork("Test Work 3", "fr", "Test content for work 3")
s.CreateTestWork("Test Work 1", "en", "Test content for work 1")
s.CreateTestWork("Test Work 2", "en", "Test content for work 2")
s.CreateTestWork("Test Work 3", "fr", "Test content for work 3")
// Define the query
query := `

View File

@ -7,6 +7,7 @@ package graphql
import (
"context"
"fmt"
"log"
"strconv"
"tercul/internal/adapters/graphql/model"
"tercul/internal/app/auth"
@ -82,9 +83,9 @@ func (r *mutationResolver) Login(ctx context.Context, email string, password str
func (r *mutationResolver) CreateWork(ctx context.Context, input model.WorkInput) (*model.Work, error) {
// Create domain model
work := &domain.Work{
Title: input.Name,
Description: *input.Description,
Language: input.Language,
Title: input.Name,
TranslatableModel: domain.TranslatableModel{Language: input.Language},
// Description: *input.Description,
// Other fields can be set here
}
@ -377,7 +378,7 @@ func (r *queryResolver) Author(ctx context.Context, id string) (*model.Author, e
// Authors is the resolver for the authors field.
func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32, search *string, countryID *string) ([]*model.Author, error) {
var authors []models2.Author
var authors []domain.Author
var err error
if countryID != nil {
@ -385,9 +386,9 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
if err != nil {
return nil, err
}
authors, err = r.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint))
authors, err = r.App.AuthorRepo.ListByCountryID(ctx, uint(countryIDUint))
} else {
result, err := r.AuthorRepo.List(ctx, 1, 1000) // Use pagination
result, err := r.App.AuthorRepo.List(ctx, 1, 1000) // Use pagination
if err != nil {
return nil, err
}
@ -402,8 +403,8 @@ func (r *queryResolver) Authors(ctx context.Context, limit *int32, offset *int32
var result []*model.Author
for _, a := range authors {
var bio *string
if r.Localization != nil {
if b, err := r.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && b != "" {
if r.App.Localization != nil {
if b, err := r.App.Localization.GetAuthorBiography(ctx, a.ID, a.Language); err == nil && b != "" {
bio = &b
}
}
@ -435,29 +436,29 @@ func (r *queryResolver) UserByUsername(ctx context.Context, username string) (*m
// Users is the resolver for the users field.
func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32, role *model.UserRole) ([]*model.User, error) {
var users []models2.User
var users []domain.User
var err error
if role != nil {
// Convert GraphQL role to model role
var modelRole models2.UserRole
var modelRole domain.UserRole
switch *role {
case model.UserRoleReader:
modelRole = models2.UserRoleReader
modelRole = domain.UserRoleReader
case model.UserRoleContributor:
modelRole = models2.UserRoleContributor
modelRole = domain.UserRoleContributor
case model.UserRoleReviewer:
modelRole = models2.UserRoleReviewer
modelRole = domain.UserRoleReviewer
case model.UserRoleEditor:
modelRole = models2.UserRoleEditor
modelRole = domain.UserRoleEditor
case model.UserRoleAdmin:
modelRole = models2.UserRoleAdmin
modelRole = domain.UserRoleAdmin
default:
return nil, fmt.Errorf("invalid user role: %s", *role)
}
users, err = r.UserRepo.ListByRole(ctx, modelRole)
users, err = r.App.UserRepo.ListByRole(ctx, modelRole)
} else {
result, err := r.UserRepo.List(ctx, 1, 1000) // Use pagination
result, err := r.App.UserRepo.List(ctx, 1, 1000) // Use pagination
if err != nil {
return nil, err
}
@ -474,15 +475,15 @@ func (r *queryResolver) Users(ctx context.Context, limit *int32, offset *int32,
// Convert model role to GraphQL role
var graphqlRole model.UserRole
switch u.Role {
case models2.UserRoleReader:
case domain.UserRoleReader:
graphqlRole = model.UserRoleReader
case models2.UserRoleContributor:
case domain.UserRoleContributor:
graphqlRole = model.UserRoleContributor
case models2.UserRoleReviewer:
case domain.UserRoleReviewer:
graphqlRole = model.UserRoleReviewer
case models2.UserRoleEditor:
case domain.UserRoleEditor:
graphqlRole = model.UserRoleEditor
case models2.UserRoleAdmin:
case domain.UserRoleAdmin:
graphqlRole = model.UserRoleAdmin
default:
graphqlRole = model.UserRoleReader
@ -526,7 +527,7 @@ func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error)
return nil, err
}
tag, err := r.TagRepo.GetByID(ctx, uint(tagID))
tag, err := r.App.TagRepo.GetByID(ctx, uint(tagID))
if err != nil {
return nil, err
}
@ -539,7 +540,7 @@ func (r *queryResolver) Tag(ctx context.Context, id string) (*model.Tag, error)
// Tags is the resolver for the tags field.
func (r *queryResolver) Tags(ctx context.Context, limit *int32, offset *int32) ([]*model.Tag, error) {
paginatedResult, err := r.TagRepo.List(ctx, 1, 1000) // Use pagination
paginatedResult, err := r.App.TagRepo.List(ctx, 1, 1000) // Use pagination
if err != nil {
return nil, err
}
@ -563,7 +564,7 @@ func (r *queryResolver) Category(ctx context.Context, id string) (*model.Categor
return nil, err
}
category, err := r.CategoryRepo.GetByID(ctx, uint(categoryID))
category, err := r.App.CategoryRepo.GetByID(ctx, uint(categoryID))
if err != nil {
return nil, err
}
@ -576,7 +577,7 @@ func (r *queryResolver) Category(ctx context.Context, id string) (*model.Categor
// Categories is the resolver for the categories field.
func (r *queryResolver) Categories(ctx context.Context, limit *int32, offset *int32) ([]*model.Category, error) {
paginatedResult, err := r.CategoryRepo.List(ctx, 1, 1000)
paginatedResult, err := r.App.CategoryRepo.List(ctx, 1, 1000)
if err != nil {
return nil, err
}

View File

@ -6,6 +6,7 @@ import (
"tercul/internal/app/localization"
"tercul/internal/app/search"
"tercul/internal/app/work"
"tercul/internal/domain"
)
// Application is a container for all the application-layer services.
@ -19,4 +20,10 @@ type Application struct {
Search search.IndexService
WorkCommands *work.WorkCommands
WorkQueries *work.WorkQueries
// Repositories - to be refactored into app services
AuthorRepo domain.AuthorRepository
UserRepo domain.UserRepository
TagRepo domain.TagRepository
CategoryRepo domain.CategoryRepository
}

View File

@ -7,14 +7,12 @@ import (
"tercul/internal/app/search"
"tercul/internal/app/work"
"tercul/internal/data/sql"
"tercul/internal/domain"
"tercul/internal/platform/cache"
"tercul/internal/platform/config"
"tercul/internal/platform/db"
"tercul/internal/platform/log"
auth_platform "tercul/internal/platform/auth"
"tercul/internal/jobs/linguistics"
"time"
"github.com/hibiken/asynq"
"github.com/weaviate/weaviate-go-client/v5/weaviate"
@ -110,6 +108,9 @@ func (b *ApplicationBuilder) BuildApplication() error {
// I need to add all the other repos here. For now, I'll just add the ones I need for the services.
translationRepo := sql.NewTranslationRepository(b.dbConn)
copyrightRepo := sql.NewCopyrightRepository(b.dbConn)
authorRepo := sql.NewAuthorRepository(b.dbConn)
tagRepo := sql.NewTagRepository(b.dbConn)
categoryRepo := sql.NewCategoryRepository(b.dbConn)
// Initialize application services
@ -136,6 +137,10 @@ func (b *ApplicationBuilder) BuildApplication() error {
CopyrightQueries: copyrightQueries,
Localization: localizationService,
Search: searchService,
AuthorRepo: authorRepo,
UserRepo: userRepo,
TagRepo: tagRepo,
CategoryRepo: categoryRepo,
}
log.LogInfo("Application layer initialized successfully")
@ -159,6 +164,21 @@ func (b *ApplicationBuilder) GetApplication() *Application {
return b.App
}
// GetDB returns the database connection
func (b *ApplicationBuilder) GetDB() *gorm.DB {
return b.dbConn
}
// GetAsynq returns the Asynq client
func (b *ApplicationBuilder) GetAsynq() *asynq.Client {
return b.asynqClient
}
// GetLinguisticsFactory returns the linguistics factory
func (b *ApplicationBuilder) GetLinguisticsFactory() *linguistics.LinguisticsFactory {
return b.linguistics
}
// Close closes all resources
func (b *ApplicationBuilder) Close() error {
if b.asynqClient != nil {

View File

@ -1,15 +1,11 @@
package app
import (
"net/http"
"tercul/internal/jobs/linguistics"
syncjob "tercul/internal/jobs/sync"
"tercul/internal/platform/auth"
"tercul/internal/platform/config"
"tercul/internal/platform/log"
"github.com/99designs/gqlgen/graphql/playground"
"github.com/hibiken/asynq"
)
@ -46,8 +42,8 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
// Create sync job instance
syncJobInstance := syncjob.NewSyncJob(
f.appBuilder.GetDatabase(),
f.appBuilder.GetAsynqClient(),
f.appBuilder.GetDB(),
f.appBuilder.GetAsynq(),
)
// Register sync job handlers
@ -60,9 +56,9 @@ func (f *ServerFactory) CreateBackgroundJobServers() ([]*asynq.Server, error) {
// Create linguistic sync job
linguisticSyncJob := linguistics.NewLinguisticSyncJob(
f.appBuilder.GetDatabase(),
f.appBuilder.GetLinguistics().GetAnalyzer(),
f.appBuilder.GetAsynqClient(),
f.appBuilder.GetDB(),
f.appBuilder.GetLinguisticsFactory().GetAnalyzer(),
f.appBuilder.GetAsynq(),
)
// Create linguistic server and register handlers

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/author"
"gorm.io/gorm"
)
type authorRepository struct {

View File

@ -4,7 +4,7 @@ import (
"context"
"errors"
"fmt"
"tercul/internal/domain/base"
"tercul/internal/domain"
"tercul/internal/platform/config"
"tercul/internal/platform/log"
"time"
@ -28,7 +28,7 @@ type BaseRepositoryImpl[T any] struct {
}
// NewBaseRepositoryImpl creates a new BaseRepositoryImpl
func NewBaseRepositoryImpl[T any](db *gorm.DB) base.BaseRepository[T] {
func NewBaseRepositoryImpl[T any](db *gorm.DB) domain.BaseRepository[T] {
return &BaseRepositoryImpl[T]{db: db}
}

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/book"
"gorm.io/gorm"
)
type bookRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/bookmark"
"gorm.io/gorm"
)
type bookmarkRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/category"
"gorm.io/gorm"
)
type categoryRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/city"
"gorm.io/gorm"
)
type cityRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/collection"
"gorm.io/gorm"
)
type collectionRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/comment"
"gorm.io/gorm"
)
type commentRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/contribution"
"gorm.io/gorm"
)
type contributionRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/copyright_claim"
"gorm.io/gorm"
)
type copyrightClaimRepository struct {
@ -12,7 +14,7 @@ type copyrightClaimRepository struct {
}
// NewCopyrightClaimRepository creates a new CopyrightClaimRepository.
func NewCopyrightClaimRepository(db *gorm.DB) domain.CopyrightClaimRepository {
func NewCopyrightClaimRepository(db *gorm.DB) copyright_claim.Copyright_claimRepository {
return &copyrightClaimRepository{
BaseRepository: NewBaseRepositoryImpl[domain.CopyrightClaim](db),
db: db,

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/copyright"
"gorm.io/gorm"
)
type copyrightRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/country"
"gorm.io/gorm"
)
type countryRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/edge"
"gorm.io/gorm"
)
type edgeRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/edition"
"gorm.io/gorm"
)
type editionRepository struct {

View File

@ -3,9 +3,11 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/email_verification"
"time"
"gorm.io/gorm"
)
type emailVerificationRepository struct {
@ -14,7 +16,7 @@ type emailVerificationRepository struct {
}
// NewEmailVerificationRepository creates a new EmailVerificationRepository.
func NewEmailVerificationRepository(db *gorm.DB) domain.EmailVerificationRepository {
func NewEmailVerificationRepository(db *gorm.DB) email_verification.Email_verificationRepository {
return &emailVerificationRepository{
BaseRepository: NewBaseRepositoryImpl[domain.EmailVerification](db),
db: db,

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/like"
"gorm.io/gorm"
)
type likeRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/monetization"
"gorm.io/gorm"
)
type monetizationRepository struct {

View File

@ -3,9 +3,11 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/password_reset"
"time"
"gorm.io/gorm"
)
type passwordResetRepository struct {
@ -14,7 +16,7 @@ type passwordResetRepository struct {
}
// NewPasswordResetRepository creates a new PasswordResetRepository.
func NewPasswordResetRepository(db *gorm.DB) domain.PasswordResetRepository {
func NewPasswordResetRepository(db *gorm.DB) password_reset.Password_resetRepository {
return &passwordResetRepository{
BaseRepository: NewBaseRepositoryImpl[domain.PasswordReset](db),
db: db,

View File

@ -2,9 +2,11 @@ package sql
import (
"context"
"gorm.io/gorm"
"math"
"tercul/internal/domain"
"tercul/internal/domain/place"
"gorm.io/gorm"
)
type placeRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/publisher"
"gorm.io/gorm"
)
type publisherRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/source"
"gorm.io/gorm"
)
type sourceRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/tag"
"gorm.io/gorm"
)
type tagRepository struct {

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/translation"
"gorm.io/gorm"
)
type translationRepository struct {

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/user_profile"
"gorm.io/gorm"
)
type userProfileRepository struct {
@ -13,7 +15,7 @@ type userProfileRepository struct {
}
// NewUserProfileRepository creates a new UserProfileRepository.
func NewUserProfileRepository(db *gorm.DB) domain.UserProfileRepository {
func NewUserProfileRepository(db *gorm.DB) user_profile.User_profileRepository {
return &userProfileRepository{
BaseRepository: NewBaseRepositoryImpl[domain.UserProfile](db),
db: db,

View File

@ -3,8 +3,10 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/user"
"gorm.io/gorm"
)
type userRepository struct {

View File

@ -3,9 +3,11 @@ package sql
import (
"context"
"errors"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/user_session"
"time"
"gorm.io/gorm"
)
type userSessionRepository struct {
@ -14,7 +16,7 @@ type userSessionRepository struct {
}
// NewUserSessionRepository creates a new UserSessionRepository.
func NewUserSessionRepository(db *gorm.DB) domain.UserSessionRepository {
func NewUserSessionRepository(db *gorm.DB) user_session.User_sessionRepository {
return &userSessionRepository{
BaseRepository: NewBaseRepositoryImpl[domain.UserSession](db),
db: db,

View File

@ -2,8 +2,10 @@ package sql
import (
"context"
"gorm.io/gorm"
"tercul/internal/domain"
"tercul/internal/domain/work"
"gorm.io/gorm"
)
type workRepository struct {

View File

@ -7,7 +7,7 @@ import (
// AuthorRepository defines CRUD methods specific to Author.
type AuthorRepository interface {
domain.BaseRepositoryRepository[domain.Author]
domain.BaseRepository[domain.Author]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Author, error)
ListByBookID(ctx context.Context, bookID uint) ([]domain.Author, error)

View File

@ -7,7 +7,7 @@ import (
// BookRepository defines CRUD methods specific to Book.
type BookRepository interface {
domain.BaseRepositoryRepository[domain.Book]
domain.BaseRepository[domain.Book]
ListByAuthorID(ctx context.Context, authorID uint) ([]domain.Book, error)
ListByPublisherID(ctx context.Context, publisherID uint) ([]domain.Book, error)

View File

@ -7,7 +7,7 @@ import (
// BookmarkRepository defines CRUD methods specific to Bookmark.
type BookmarkRepository interface {
domain.BaseRepositoryRepository[domain.Bookmark]
domain.BaseRepository[domain.Bookmark]
ListByUserID(ctx context.Context, userID uint) ([]domain.Bookmark, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Bookmark, error)

View File

@ -7,7 +7,7 @@ import (
// CategoryRepository defines CRUD methods specific to Category.
type CategoryRepository interface {
domain.BaseRepositoryRepository[domain.Category]
domain.BaseRepository[domain.Category]
FindByName(ctx context.Context, name string) (*domain.Category, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Category, error)

View File

@ -7,7 +7,7 @@ import (
// CityRepository defines CRUD methods specific to City.
type CityRepository interface {
domain.BaseRepositoryRepository[domain.City]
domain.BaseRepository[domain.City]
ListByCountryID(ctx context.Context, countryID uint) ([]domain.City, error)
}

View File

@ -7,7 +7,7 @@ import (
// CollectionRepository defines CRUD methods specific to Collection.
type CollectionRepository interface {
domain.BaseRepositoryRepository[domain.Collection]
domain.BaseRepository[domain.Collection]
ListByUserID(ctx context.Context, userID uint) ([]domain.Collection, error)
ListPublic(ctx context.Context) ([]domain.Collection, error)

View File

@ -7,7 +7,7 @@ import (
// CommentRepository defines CRUD methods specific to Comment.
type CommentRepository interface {
domain.BaseRepositoryRepository[domain.Comment]
domain.BaseRepository[domain.Comment]
ListByUserID(ctx context.Context, userID uint) ([]domain.Comment, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Comment, error)

View File

@ -7,7 +7,7 @@ import (
// ContributionRepository defines CRUD methods specific to Contribution.
type ContributionRepository interface {
domain.BaseRepositoryRepository[domain.Contribution]
domain.BaseRepository[domain.Contribution]
ListByUserID(ctx context.Context, userID uint) ([]domain.Contribution, error)
ListByReviewerID(ctx context.Context, reviewerID uint) ([]domain.Contribution, error)

View File

@ -7,7 +7,7 @@ import (
// CopyrightRepository defines CRUD methods specific to Copyright.
type CopyrightRepository interface {
domain.BaseRepositoryRepository[domain.Copyright]
domain.BaseRepository[domain.Copyright]
AttachToEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error)
DetachFromEntity(ctx context.Context, copyrightID uint, entityID uint, entityType string) (error)

View File

@ -7,7 +7,7 @@ import (
// Copyright_claimRepository defines CRUD methods specific to Copyright_claim.
type Copyright_claimRepository interface {
domain.BaseRepositoryRepository[domain.CopyrightClaim]
domain.BaseRepository[domain.CopyrightClaim]
ListByWorkID(ctx context.Context, workID uint) ([]domain.CopyrightClaim, error)
ListByUserID(ctx context.Context, userID uint) ([]domain.CopyrightClaim, error)

View File

@ -7,7 +7,7 @@ import (
// CountryRepository defines CRUD methods specific to Country.
type CountryRepository interface {
domain.BaseRepositoryRepository[domain.Country]
domain.BaseRepository[domain.Country]
GetByCode(ctx context.Context, code string) (*domain.Country, error)
ListByContinent(ctx context.Context, continent string) ([]domain.Country, error)

View File

@ -7,7 +7,7 @@ import (
// EdgeRepository defines CRUD methods specific to Edge.
type EdgeRepository interface {
domain.BaseRepositoryRepository[domain.Edge]
domain.BaseRepository[domain.Edge]
ListBySource(ctx context.Context, sourceTable string, sourceID uint) ([]domain.Edge, error)
}

View File

@ -7,7 +7,7 @@ import (
// EditionRepository defines CRUD methods specific to Edition.
type EditionRepository interface {
domain.BaseRepositoryRepository[domain.Edition]
domain.BaseRepository[domain.Edition]
ListByBookID(ctx context.Context, bookID uint) ([]domain.Edition, error)
FindByISBN(ctx context.Context, isbn string) (*domain.Edition, error)

View File

@ -7,7 +7,7 @@ import (
// Email_verificationRepository defines CRUD methods specific to Email_verification.
type Email_verificationRepository interface {
domain.BaseRepositoryRepository[domain.EmailVerification]
domain.BaseRepository[domain.EmailVerification]
GetByToken(ctx context.Context, token string) (*domain.EmailVerification, error)
GetByUserID(ctx context.Context, userID uint) ([]domain.EmailVerification, error)

View File

@ -7,7 +7,7 @@ import (
// LikeRepository defines CRUD methods specific to Like.
type LikeRepository interface {
domain.BaseRepositoryRepository[domain.Like]
domain.BaseRepository[domain.Like]
ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error)

View File

@ -7,7 +7,7 @@ import (
// MonetizationRepository defines CRUD methods specific to Monetization.
type MonetizationRepository interface {
domain.BaseRepositoryRepository[domain.Monetization]
domain.BaseRepository[domain.Monetization]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Monetization, error)
ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Monetization, error)

View File

@ -7,7 +7,7 @@ import (
// Password_resetRepository defines CRUD methods specific to Password_reset.
type Password_resetRepository interface {
domain.BaseRepositoryRepository[domain.PasswordReset]
domain.BaseRepository[domain.PasswordReset]
GetByToken(ctx context.Context, token string) (*domain.PasswordReset, error)
GetByUserID(ctx context.Context, userID uint) ([]domain.PasswordReset, error)

View File

@ -7,9 +7,9 @@ import (
// PlaceRepository defines CRUD methods specific to Place.
type PlaceRepository interface {
domain.BaseRepositoryRepository[domain.Place]
domain.BaseRepository[domain.Place]
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Place, error)
ListByCityID(ctx context.Context, cityID uint) ([]domain.Place, error)
FindNearby(ctx context.Context, latitude float64, radiusKm float64) ([]domain.Place, error)
FindNearby(ctx context.Context, latitude, longitude float64, radiusKm float64) ([]domain.Place, error)
}

View File

@ -7,7 +7,7 @@ import (
// PublisherRepository defines CRUD methods specific to Publisher.
type PublisherRepository interface {
domain.BaseRepositoryRepository[domain.Publisher]
domain.BaseRepository[domain.Publisher]
ListByCountryID(ctx context.Context, countryID uint) ([]domain.Publisher, error)
}

View File

@ -7,7 +7,7 @@ import (
// SourceRepository defines CRUD methods specific to Source.
type SourceRepository interface {
domain.BaseRepositoryRepository[domain.Source]
domain.BaseRepository[domain.Source]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Source, error)
FindByURL(ctx context.Context, url string) (*domain.Source, error)

View File

@ -7,7 +7,7 @@ import (
// TagRepository defines CRUD methods specific to Tag.
type TagRepository interface {
domain.BaseRepositoryRepository[domain.Tag]
domain.BaseRepository[domain.Tag]
FindByName(ctx context.Context, name string) (*domain.Tag, error)
ListByWorkID(ctx context.Context, workID uint) ([]domain.Tag, error)

View File

@ -7,7 +7,7 @@ import (
// TranslationRepository defines CRUD methods specific to Translation.
type TranslationRepository interface {
domain.BaseRepositoryRepository[domain.Translation]
domain.BaseRepository[domain.Translation]
ListByWorkID(ctx context.Context, workID uint) ([]domain.Translation, error)
ListByEntity(ctx context.Context, entityType string, entityID uint) ([]domain.Translation, error)

View File

@ -7,7 +7,7 @@ import (
// UserRepository defines CRUD methods specific to User.
type UserRepository interface {
domain.BaseRepositoryRepository[domain.User]
domain.BaseRepository[domain.User]
FindByUsername(ctx context.Context, username string) (*domain.User, error)
FindByEmail(ctx context.Context, email string) (*domain.User, error)

View File

@ -7,7 +7,7 @@ import (
// User_profileRepository defines CRUD methods specific to User_profile.
type User_profileRepository interface {
domain.BaseRepositoryRepository[domain.UserProfile]
domain.BaseRepository[domain.UserProfile]
GetByUserID(ctx context.Context, userID uint) (*domain.UserProfile, error)
}

View File

@ -7,7 +7,7 @@ import (
// User_sessionRepository defines CRUD methods specific to User_session.
type User_sessionRepository interface {
domain.BaseRepositoryRepository[domain.UserSession]
domain.BaseRepository[domain.UserSession]
GetByToken(ctx context.Context, token string) (*domain.UserSession, error)
GetByUserID(ctx context.Context, userID uint) ([]domain.UserSession, error)

View File

@ -7,12 +7,12 @@ import (
// WorkRepository defines CRUD methods specific to Work.
type WorkRepository interface {
domain.BaseRepositoryRepository[domain.Work]
domain.BaseRepository[domain.Work]
FindByTitle(ctx context.Context, title string) ([]domain.Work, error)
FindByAuthor(ctx context.Context, authorID uint) ([]domain.Work, error)
FindByCategory(ctx context.Context, categoryID uint) ([]domain.Work, error)
FindByLanguage(ctx context.Context, language string, page int) (*, error)
FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
GetWithTranslations(ctx context.Context, id uint) (*domain.Work, error)
ListWithTranslations(ctx context.Context, page int) (*, error)
ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error)
}

View File

@ -7,7 +7,7 @@ import (
func TestLinguaLanguageDetector_DetectLanguage(t *testing.T) {
d := NewLinguaLanguageDetector()
code, ok := d.DetectLanguage("This is an English sentence.")
require.True(t, ok)
code, err := d.DetectLanguage("This is an English sentence.")
require.NoError(t, err)
require.NotEmpty(t, code)
}

View File

@ -19,7 +19,7 @@ func (d *languageDetector) DetectLanguage(text string) (string, error) {
// or call an external API for language detection
// For demonstration purposes, we'll use a simple heuristic based on common words
content := strings.ToLower(text.Body)
content := strings.ToLower(text)
// Check for English
englishWords := []string{"the", "and", "is", "in", "to", "of", "that", "for"}
@ -35,15 +35,15 @@ func (d *languageDetector) DetectLanguage(text string) (string, error) {
// Determine the most likely language
if englishCount > spanishCount && englishCount > frenchCount {
return "en", 0.7, nil
return "en", nil
} else if spanishCount > englishCount && spanishCount > frenchCount {
return "es", 0.7, nil
return "es", nil
} else if frenchCount > englishCount && frenchCount > spanishCount {
return "fr", 0.7, nil
return "fr", nil
}
// Default to English if we can't determine the language
return "en", 0.5, nil
return "en", nil
}
// countWords counts the occurrences of words in a text

View File

@ -4,21 +4,21 @@ import "testing"
func TestLanguageDetector_Detect_EN(t *testing.T) {
d := NewLanguageDetector()
lang, conf, err := d.Detect(Text{Body: " the and is in to of that for the "})
lang, err := d.DetectLanguage(" the and is in to of that for the ")
if err != nil {
t.Fatalf("Detect returned error: %v", err)
t.Fatalf("DetectLanguage returned error: %v", err)
}
if lang != "en" {
t.Fatalf("expected language 'en', got %q", lang)
}
if conf <= 0 {
t.Errorf("expected positive confidence, got %f", conf)
}
}
func TestLanguageDetector_Detect_ES(t *testing.T) {
d := NewLanguageDetector()
lang, _, _ := d.Detect(Text{Body: " el la es en de que por para el "})
lang, err := d.DetectLanguage(" el la es en de que por para el ")
if err != nil {
t.Fatalf("DetectLanguage returned error: %v", err)
}
if lang != "es" {
t.Fatalf("expected language 'es', got %q", lang)
}
@ -26,7 +26,10 @@ func TestLanguageDetector_Detect_ES(t *testing.T) {
func TestLanguageDetector_Detect_FR(t *testing.T) {
d := NewLanguageDetector()
lang, _, _ := d.Detect(Text{Body: " le la est en de que pour dans le "})
lang, err := d.DetectLanguage(" le la est en de que pour dans le ")
if err != nil {
t.Fatalf("DetectLanguage returned error: %v", err)
}
if lang != "fr" {
t.Fatalf("expected language 'fr', got %q", lang)
}
@ -35,14 +38,11 @@ func TestLanguageDetector_Detect_FR(t *testing.T) {
func TestLanguageDetector_Detect_DefaultEnglish(t *testing.T) {
d := NewLanguageDetector()
// Balanced/unknown should default to English per implementation
lang, conf, err := d.Detect(Text{Body: " lorem ipsum dolor sit amet "})
lang, err := d.DetectLanguage(" lorem ipsum dolor sit amet ")
if err != nil {
t.Fatalf("Detect returned error: %v", err)
t.Fatalf("DetectLanguage returned error: %v", err)
}
if lang != "en" {
t.Fatalf("expected default language 'en', got %q", lang)
}
if conf != 0.5 {
t.Errorf("expected default confidence 0.5, got %f", conf)
}
}

View File

@ -2,7 +2,7 @@ package linguistics
// Registry holds all the text analysis services
type Registry struct {
Lang *LanguageDetector
Lang LanguageDetector
Tok *Tokenizer
Pos *POSTagger
Lem *Lemmatizer

View File

@ -52,7 +52,7 @@ func (a *BasicTextAnalyzer) AnalyzeText(ctx context.Context, text string, langua
// Auto-detect language if not provided and a detector exists
if language == "" && a.langDetector != nil {
if detected, ok := a.langDetector.DetectLanguage(text); ok {
if detected, err := a.langDetector.DetectLanguage(text); err == nil {
language = detected
}
}
@ -114,7 +114,7 @@ func (a *BasicTextAnalyzer) AnalyzeTextConcurrently(ctx context.Context, text st
// Auto-detect language if not provided and a detector exists
if language == "" && a.langDetector != nil {
if detected, ok := a.langDetector.DetectLanguage(text); ok {
if detected, err := a.langDetector.DetectLanguage(text); err == nil {
language = detected
}
}

View File

@ -11,8 +11,8 @@ import (
// Mocks for provider interfaces
type mockLangDetector struct{ lang string; ok bool }
func (m mockLangDetector) DetectLanguage(text string) (string, bool) { return m.lang, m.ok }
type mockLangDetector struct{ lang string; err error }
func (m mockLangDetector) DetectLanguage(text string) (string, error) { return m.lang, m.err }
type mockSentimentProvider struct{ score float64; err error }
func (m mockSentimentProvider) Score(text string, language string) (float64, error) { return m.score, m.err }
@ -34,7 +34,7 @@ func TestAnalyzeText_Empty(t *testing.T) {
func TestAnalyzeText_ProvidersAndLangDetection(t *testing.T) {
// Arrange
a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}).
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.75}).
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "golang", Relevance: 0.42}}})
@ -84,7 +84,7 @@ func TestAnalyzeTextConcurrently_AggregatesWithProviders(t *testing.T) {
// Providers return consistent values regardless of input
kw := []Keyword{{Text: "constant", Relevance: 0.3}}
a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}).
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.5}).
WithKeywordProvider(mockKeywordProvider{kws: kw})
@ -119,7 +119,7 @@ func TestAnalyzeTextConcurrently_AggregatesWithProviders(t *testing.T) {
func TestAnalyzeTextConcurrently_ContextCanceled(t *testing.T) {
a := NewBasicTextAnalyzer().
WithLanguageDetector(mockLangDetector{lang: "en", ok: true}).
WithLanguageDetector(mockLangDetector{lang: "en", err: nil}).
WithSentimentProvider(mockSentimentProvider{score: 0.9}).
WithKeywordProvider(mockKeywordProvider{kws: []Keyword{{Text: "x", Relevance: 0.1}}})

View File

@ -16,7 +16,7 @@ var (
"do": {}, "does": {}, "did": {}, "will": {}, "would": {}, "could": {},
"should": {}, "may": {}, "might": {}, "can": {}, "this": {}, "that": {},
"these": {}, "those": {}, "i": {}, "you": {}, "he": {}, "she": {},
"it": {}, "we": {}, "they": {}, "me": {}, "him": {}, "hers": {},
"it": {}, "we": {}, "they": {}, "me": {}, "him": {}, "hers": {}, "over": {},
"us": {}, "them": {}, "my": {}, "your": {}, "his": {}, "its": {},
"our": {}, "their": {},
}

View File

@ -2,11 +2,9 @@ package testutil
import (
"context"
"fmt"
"log"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/suite"
@ -16,6 +14,7 @@ import (
graph "tercul/internal/adapters/graphql"
"tercul/internal/app/auth"
auth_platform "tercul/internal/platform/auth"
"tercul/internal/app/localization"
"tercul/internal/app/work"
"tercul/internal/data/sql"
@ -41,7 +40,8 @@ type IntegrationTestSuite struct {
WorkCommands *work.WorkCommands
WorkQueries *work.WorkQueries
Localization localization.Service
AuthService auth.Service
AuthCommands *auth.AuthCommands
AuthQueries *auth.AuthQueries
// Test data
TestWorks []*domain.Work
@ -189,7 +189,9 @@ func (s *IntegrationTestSuite) setupServices() {
s.WorkCommands = work.NewWorkCommands(s.WorkRepo, mockAnalyzer)
s.WorkQueries = work.NewWorkQueries(s.WorkRepo)
s.Localization = localization.NewService(s.TranslationRepo)
s.AuthService = auth.NewService(s.UserRepo, "test-secret-key")
jwtManager := auth_platform.NewJWTManager()
s.AuthCommands = auth.NewAuthCommands(s.UserRepo, jwtManager)
s.AuthQueries = auth.NewAuthQueries(s.UserRepo, jwtManager)
}
// setupTestData creates initial test data
@ -208,8 +210,8 @@ func (s *IntegrationTestSuite) setupTestData() {
// Create test authors
s.TestAuthors = []*domain.Author{
{Name: "Test Author 1", Language: "en"},
{Name: "Test Author 2", Language: "fr"},
{Name: "Test Author 1", TranslatableModel: domain.TranslatableModel{Language: "en"}},
{Name: "Test Author 2", TranslatableModel: domain.TranslatableModel{Language: "fr"}},
}
for _, author := range s.TestAuthors {
@ -220,9 +222,9 @@ func (s *IntegrationTestSuite) setupTestData() {
// Create test works
s.TestWorks = []*domain.Work{
{Title: "Test Work 1", Language: "en"},
{Title: "Test Work 2", Language: "en"},
{Title: "Test Work 3", Language: "fr"},
{Title: "Test Work 1", TranslatableModel: domain.TranslatableModel{Language: "en"}},
{Title: "Test Work 2", TranslatableModel: domain.TranslatableModel{Language: "en"}},
{Title: "Test Work 3", TranslatableModel: domain.TranslatableModel{Language: "fr"}},
}
for _, work := range s.TestWorks {
@ -304,8 +306,8 @@ func (s *IntegrationTestSuite) GetResolver() *graph.Resolver {
// CreateTestWork creates a test work with optional content
func (s *IntegrationTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
work := &domain.Work{
Title: title,
Language: language,
Title: title,
TranslatableModel: domain.TranslatableModel{Language: language},
}
if err := s.WorkRepo.Create(context.Background(), work); err != nil {

View File

@ -2,7 +2,6 @@ package testutil
import (
"context"
"errors"
"fmt"
"tercul/internal/domain"
"gorm.io/gorm"

View File

@ -35,7 +35,7 @@ func (m *MockTranslationRepository) GetByID(ctx context.Context, id uint) (*doma
return &cp, nil
}
}
return nil, domain.ErrEntityNotFound
return nil, ErrEntityNotFound
}
func (m *MockTranslationRepository) Update(ctx context.Context, t *domain.Translation) error {
@ -45,7 +45,7 @@ func (m *MockTranslationRepository) Update(ctx context.Context, t *domain.Transl
return nil
}
}
return domain.ErrEntityNotFound
return ErrEntityNotFound
}
func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error {
@ -55,7 +55,7 @@ func (m *MockTranslationRepository) Delete(ctx context.Context, id uint) error {
return nil
}
}
return domain.ErrEntityNotFound
return ErrEntityNotFound
}
func (m *MockTranslationRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Translation], error) {

View File

@ -19,9 +19,6 @@ func NewUnifiedMockWorkRepository() *UnifiedMockWorkRepository {
func (m *UnifiedMockWorkRepository) AddWork(work *domain.Work) {
work.ID = uint(len(m.Works) + 1)
if work.Language == "" {
work.Language = "en" // default for tests, can be set by caller
}
m.Works = append(m.Works, work)
}
@ -37,7 +34,7 @@ func (m *UnifiedMockWorkRepository) GetByID(ctx context.Context, id uint) (*doma
return w, nil
}
}
return nil, domain.ErrEntityNotFound
return nil, ErrEntityNotFound
}
func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *domain.Work) error {
@ -47,7 +44,7 @@ func (m *UnifiedMockWorkRepository) Update(ctx context.Context, entity *domain.W
return nil
}
}
return domain.ErrEntityNotFound
return ErrEntityNotFound
}
func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error {
@ -57,7 +54,7 @@ func (m *UnifiedMockWorkRepository) Delete(ctx context.Context, id uint) error {
return nil
}
}
return domain.ErrEntityNotFound
return ErrEntityNotFound
}
func (m *UnifiedMockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {
@ -99,7 +96,7 @@ func (m *UnifiedMockWorkRepository) FindWithPreload(ctx context.Context, preload
return w, nil
}
}
return nil, domain.ErrEntityNotFound
return nil, ErrEntityNotFound
}
func (m *UnifiedMockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Work, error) {
@ -214,7 +211,7 @@ func (m *UnifiedMockWorkRepository) GetWithTranslations(ctx context.Context, id
return w, nil
}
}
return nil, domain.ErrEntityNotFound
return nil, ErrEntityNotFound
}
func (m *UnifiedMockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Work], error) {

View File

@ -3,6 +3,7 @@ package testutil
import (
"context"
graph "tercul/internal/adapters/graphql"
"tercul/internal/app"
"tercul/internal/app/work"
"tercul/internal/domain"
@ -41,19 +42,30 @@ func (s *SimpleTestSuite) SetupTest() {
// GetResolver returns a minimal GraphQL resolver for testing
func (s *SimpleTestSuite) GetResolver() *graph.Resolver {
// This needs to be updated to reflect the new resolver structure
// For now, we'll return a resolver with the work commands and queries
return &graph.Resolver{
// WorkRepo: s.WorkRepo, // This should be removed from resolver
// WorkService: s.WorkService, // This is replaced by commands/queries
App: &app.Application{
WorkCommands: s.WorkCommands,
WorkQueries: s.WorkQueries,
Localization: &MockLocalization{},
},
}
}
type MockLocalization struct{}
func (m *MockLocalization) GetWorkContent(ctx context.Context, workID uint, preferredLanguage string) (string, error) {
return "Test content for work", nil
}
func (m *MockLocalization) GetAuthorBiography(ctx context.Context, authorID uint, preferredLanguage string) (string, error) {
return "Test biography", nil
}
// CreateTestWork creates a test work with optional content
func (s *SimpleTestSuite) CreateTestWork(title, language string, content string) *domain.Work {
work := &domain.Work{
Title: title,
Language: language,
Title: title,
TranslatableModel: domain.TranslatableModel{Language: language},
}
// Add work to the mock repository

View File

@ -2,6 +2,7 @@ package testutil
import (
"database/sql"
"errors"
"fmt"
"log"
"os"
@ -15,6 +16,8 @@ import (
"tercul/internal/platform/config"
)
var ErrEntityNotFound = errors.New("entity not found")
// TestDB holds the test database connection
var TestDB *gorm.DB