This commit addresses several outstanding tasks from TASKS.md.

- Fixes a potential panic in the background job queue by changing `log.Fatalf` to `log.Printf`, allowing for more graceful error handling.
- Implements all `panic("not implemented")` methods in the mock repositories for `Like`, `Work`, and `User`, enabling more robust testing.
- Consolidates duplicated `WorkAnalytics` and `TranslationAnalytics` structs into a central `internal/domain/analytics` package to reduce code duplication and improve maintainability.
- Corrects build errors that arose during testing, including an unused import and an incorrect struct field name in a mock repository.
This commit is contained in:
google-labs-jules[bot] 2025-10-05 13:03:31 +00:00
parent 1bb3e23c47
commit 20da2199ba
7 changed files with 168 additions and 79 deletions

View File

@ -29,16 +29,32 @@ func (m *mockLikeRepository) Delete(ctx context.Context, id uint) error {
return args.Error(0)
}
func (m *mockLikeRepository) ListByUserID(ctx context.Context, userID uint) ([]domain.Like, error) {
panic("not implemented")
args := m.Called(ctx, userID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
func (m *mockLikeRepository) ListByWorkID(ctx context.Context, workID uint) ([]domain.Like, error) {
panic("not implemented")
args := m.Called(ctx, workID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
func (m *mockLikeRepository) ListByTranslationID(ctx context.Context, translationID uint) ([]domain.Like, error) {
panic("not implemented")
args := m.Called(ctx, translationID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
func (m *mockLikeRepository) ListByCommentID(ctx context.Context, commentID uint) ([]domain.Like, error) {
panic("not implemented")
args := m.Called(ctx, commentID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
// Implement the rest of the BaseRepository methods as needed, or panic if they are not expected to be called.
@ -59,26 +75,47 @@ func (m *mockLikeRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uin
return m.Delete(ctx, id)
}
func (m *mockLikeRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.Like], error) {
panic("not implemented")
args := m.Called(ctx, page, pageSize)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.PaginatedResult[domain.Like]), args.Error(1)
}
func (m *mockLikeRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.Like, error) {
panic("not implemented")
args := m.Called(ctx, options)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
func (m *mockLikeRepository) ListAll(ctx context.Context) ([]domain.Like, error) {
args := m.Called(ctx)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
func (m *mockLikeRepository) ListAll(ctx context.Context) ([]domain.Like, error) { panic("not implemented") }
func (m *mockLikeRepository) Count(ctx context.Context) (int64, error) {
panic("not implemented")
args := m.Called(ctx)
return args.Get(0).(int64), args.Error(1)
}
func (m *mockLikeRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
panic("not implemented")
args := m.Called(ctx, options)
return args.Get(0).(int64), args.Error(1)
}
func (m *mockLikeRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.Like, error) {
return m.GetByID(ctx, id)
}
func (m *mockLikeRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.Like, error) {
panic("not implemented")
args := m.Called(ctx, batchSize, offset)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]domain.Like), args.Error(1)
}
func (m *mockLikeRepository) Exists(ctx context.Context, id uint) (bool, error) {
panic("not implemented")
args := m.Called(ctx, id)
return args.Bool(0), args.Error(1)
}
func (m *mockLikeRepository) BeginTx(ctx context.Context) (*gorm.DB, error) { return nil, nil }
func (m *mockLikeRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) error) error {

View File

@ -46,24 +46,43 @@ func (m *mockWorkRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uin
return m.Delete(ctx, id)
}
func (m *mockWorkRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
panic("not implemented")
args := m.Called(ctx, page, pageSize)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.PaginatedResult[work.Work]), args.Error(1)
}
func (m *mockWorkRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]work.Work, error) {
panic("not implemented")
args := m.Called(ctx, options)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]work.Work), args.Error(1)
}
func (m *mockWorkRepository) ListAll(ctx context.Context) ([]work.Work, error) {
args := m.Called(ctx)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]work.Work), args.Error(1)
}
func (m *mockWorkRepository) ListAll(ctx context.Context) ([]work.Work, error) { panic("not implemented") }
func (m *mockWorkRepository) Count(ctx context.Context) (int64, error) {
args := m.Called(ctx)
return args.Get(0).(int64), args.Error(1)
}
func (m *mockWorkRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
panic("not implemented")
args := m.Called(ctx, options)
return args.Get(0).(int64), args.Error(1)
}
func (m *mockWorkRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*work.Work, error) {
return m.GetByID(ctx, id)
}
func (m *mockWorkRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]work.Work, error) {
panic("not implemented")
args := m.Called(ctx, batchSize, offset)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]work.Work), args.Error(1)
}
func (m *mockWorkRepository) Exists(ctx context.Context, id uint) (bool, error) {
args := m.Called(ctx, id)
@ -74,16 +93,32 @@ func (m *mockWorkRepository) WithTx(ctx context.Context, fn func(tx *gorm.DB) er
return fn(nil)
}
func (m *mockWorkRepository) FindByTitle(ctx context.Context, title string) ([]work.Work, error) {
panic("not implemented")
args := m.Called(ctx, title)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]work.Work), args.Error(1)
}
func (m *mockWorkRepository) FindByAuthor(ctx context.Context, authorID uint) ([]work.Work, error) {
panic("not implemented")
args := m.Called(ctx, authorID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]work.Work), args.Error(1)
}
func (m *mockWorkRepository) FindByCategory(ctx context.Context, categoryID uint) ([]work.Work, error) {
panic("not implemented")
args := m.Called(ctx, categoryID)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).([]work.Work), args.Error(1)
}
func (m *mockWorkRepository) FindByLanguage(ctx context.Context, language string, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
panic("not implemented")
args := m.Called(ctx, language, page, pageSize)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.PaginatedResult[work.Work]), args.Error(1)
}
func (m *mockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (*work.Work, error) {
args := m.Called(ctx, id)
@ -93,7 +128,11 @@ func (m *mockWorkRepository) GetWithTranslations(ctx context.Context, id uint) (
return args.Get(0).(*work.Work), args.Error(1)
}
func (m *mockWorkRepository) ListWithTranslations(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[work.Work], error) {
panic("not implemented")
args := m.Called(ctx, page, pageSize)
if args.Get(0) == nil {
return nil, args.Error(1)
}
return args.Get(0).(*domain.PaginatedResult[work.Work]), args.Error(1)
}
func (m *mockWorkRepository) IsAuthor(ctx context.Context, workID uint, authorID uint) (bool, error) {
args := m.Called(ctx, workID, authorID)

View File

@ -10,28 +10,6 @@ import (
"go.opentelemetry.io/otel/trace"
)
// WorkAnalytics contains analytics data for a work
type WorkAnalytics struct {
WorkID uint
ViewCount int64
LikeCount int64
CommentCount int64
BookmarkCount int64
TranslationCount int64
ReadabilityScore float64
SentimentScore float64
TopKeywords []string
PopularTranslations []TranslationAnalytics
}
// TranslationAnalytics contains analytics data for a translation
type TranslationAnalytics struct {
TranslationID uint
Language string
ViewCount int64
LikeCount int64
}
// WorkQueries contains the query handlers for the work aggregate.
type WorkQueries struct {
repo work.WorkRepository

View File

@ -0,0 +1,23 @@
package analytics
// WorkAnalytics contains analytics data for a work
type WorkAnalytics struct {
WorkID uint
ViewCount int64
LikeCount int64
CommentCount int64
BookmarkCount int64
TranslationCount int64
ReadabilityScore float64
SentimentScore float64
TopKeywords []string
PopularTranslations []TranslationAnalytics
}
// TranslationAnalytics contains analytics data for a translation
type TranslationAnalytics struct {
TranslationID uint
Language string
ViewCount int64
LikeCount int64
}

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"tercul/internal/domain"
"tercul/internal/domain/analytics"
"time"
"tercul/internal/platform/log"
@ -15,29 +16,7 @@ type WorkAnalysisService interface {
AnalyzeWork(ctx context.Context, workID uint) error
// GetWorkAnalytics retrieves analytics data for a work
GetWorkAnalytics(ctx context.Context, workID uint) (*WorkAnalytics, error)
}
// WorkAnalytics contains analytics data for a work
type WorkAnalytics struct {
WorkID uint
ViewCount int64
LikeCount int64
CommentCount int64
BookmarkCount int64
TranslationCount int64
ReadabilityScore float64
SentimentScore float64
TopKeywords []string
PopularTranslations []TranslationAnalytics
}
// TranslationAnalytics contains analytics data for a translation
type TranslationAnalytics struct {
TranslationID uint
Language string
ViewCount int64
LikeCount int64
GetWorkAnalytics(ctx context.Context, workID uint) (*analytics.WorkAnalytics, error)
}
// workAnalysisService implements the WorkAnalysisService interface
@ -150,7 +129,7 @@ func (s *workAnalysisService) AnalyzeWork(ctx context.Context, workID uint) erro
}
// GetWorkAnalytics retrieves analytics data for a work
func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint) (*WorkAnalytics, error) {
func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint) (*analytics.WorkAnalytics, error) {
if workID == 0 {
return nil, fmt.Errorf("invalid work ID")
}
@ -179,7 +158,7 @@ func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint)
}
// For now, return placeholder analytics with actual analysis data
return &WorkAnalytics{
return &analytics.WorkAnalytics{
WorkID: work.ID,
ViewCount: 0, // TODO: Implement view counting
LikeCount: 0, // TODO: Implement like counting
@ -189,7 +168,7 @@ func (s *workAnalysisService) GetWorkAnalytics(ctx context.Context, workID uint)
ReadabilityScore: readabilityScore.Score,
SentimentScore: extractSentimentFromAnalysis(languageAnalysis.Analysis),
TopKeywords: keywords,
PopularTranslations: []TranslationAnalytics{}, // TODO: Implement translation analytics
PopularTranslations: []analytics.TranslationAnalytics{}, // TODO: Implement translation analytics
}, nil
}
@ -202,4 +181,4 @@ func extractSentimentFromAnalysis(analysis domain.JSONB) float64 {
return sentiment
}
return 0.0
}
}

View File

@ -64,6 +64,6 @@ func RegisterQueueHandlers(srv *asynq.Server, syncJob *SyncJob) {
mux.HandleFunc(TaskEntitySync, syncJob.HandleEntitySync)
mux.HandleFunc(TaskEdgeSync, syncJob.HandleEdgeSync)
if err := srv.Run(mux); err != nil {
log.Fatalf("Failed to start asynq server: %v", err)
log.Printf("Failed to start asynq server: %v", err)
}
}

View File

@ -98,29 +98,62 @@ func (m *MockUserRepository) DeleteInTx(ctx context.Context, tx *gorm.DB, id uin
return m.Delete(ctx, id)
}
func (m *MockUserRepository) List(ctx context.Context, page, pageSize int) (*domain.PaginatedResult[domain.User], error) {
panic("not implemented")
start := (page - 1) * pageSize
end := start + pageSize
if start > len(m.Users) {
start = len(m.Users)
}
if end > len(m.Users) {
end = len(m.Users)
}
users := m.Users[start:end]
var resultUsers []domain.User
for _, u := range users {
resultUsers = append(resultUsers, *u)
}
return &domain.PaginatedResult[domain.User]{
Items: resultUsers,
TotalCount: int64(len(m.Users)),
Page: page,
PageSize: pageSize,
}, nil
}
func (m *MockUserRepository) ListWithOptions(ctx context.Context, options *domain.QueryOptions) ([]domain.User, error) {
panic("not implemented")
// This is a mock implementation, so we'll just return all users for now.
return m.ListAll(ctx)
}
func (m *MockUserRepository) ListAll(ctx context.Context) ([]domain.User, error) {
var users []domain.User
for _, u := range m.Users {
users = append(users, *u)
}
return users, nil
for _, u := range m.Users {
users = append(users, *u)
}
return users, nil
}
func (m *MockUserRepository) Count(ctx context.Context) (int64, error) {
return int64(len(m.Users)), nil
}
func (m *MockUserRepository) CountWithOptions(ctx context.Context, options *domain.QueryOptions) (int64, error) {
panic("not implemented")
// This is a mock implementation, so we'll just return the total count for now.
return m.Count(ctx)
}
func (m *MockUserRepository) FindWithPreload(ctx context.Context, preloads []string, id uint) (*domain.User, error) {
return m.GetByID(ctx, id)
}
func (m *MockUserRepository) GetAllForSync(ctx context.Context, batchSize, offset int) ([]domain.User, error) {
panic("not implemented")
start := offset
end := start + batchSize
if start > len(m.Users) {
return []domain.User{}, nil
}
if end > len(m.Users) {
end = len(m.Users)
}
users := m.Users[start:end]
var resultUsers []domain.User
for _, u := range users {
resultUsers = append(resultUsers, *u)
}
return resultUsers, nil
}
func (m *MockUserRepository) Exists(ctx context.Context, id uint) (bool, error) {
_, err := m.GetByID(ctx, id)