package bookstore_test import ( "context" "io" "testing" "time" "github.com/libnovel/backend/internal/bookstore" "github.com/libnovel/backend/internal/domain" ) // ── Mock that satisfies all bookstore interfaces ────────────────────────────── type mockStore struct{} // BookWriter func (m *mockStore) WriteMetadata(_ context.Context, _ domain.BookMeta) error { return nil } func (m *mockStore) WriteChapter(_ context.Context, _ string, _ domain.Chapter) error { return nil } func (m *mockStore) WriteChapterRefs(_ context.Context, _ string, _ []domain.ChapterRef) error { return nil } func (m *mockStore) ChapterExists(_ context.Context, _ string, _ domain.ChapterRef) bool { return false } // BookReader func (m *mockStore) ReadMetadata(_ context.Context, _ string) (domain.BookMeta, bool, error) { return domain.BookMeta{}, false, nil } func (m *mockStore) ListBooks(_ context.Context) ([]domain.BookMeta, error) { return nil, nil } func (m *mockStore) LocalSlugs(_ context.Context) (map[string]bool, error) { return map[string]bool{}, nil } func (m *mockStore) MetadataMtime(_ context.Context, _ string) int64 { return 0 } func (m *mockStore) ReadChapter(_ context.Context, _ string, _ int) (string, error) { return "", nil } func (m *mockStore) ListChapters(_ context.Context, _ string) ([]domain.ChapterInfo, error) { return nil, nil } func (m *mockStore) CountChapters(_ context.Context, _ string) int { return 0 } func (m *mockStore) ReindexChapters(_ context.Context, _ string) (int, error) { return 0, nil } // RankingStore func (m *mockStore) WriteRankingItem(_ context.Context, _ domain.RankingItem) error { return nil } func (m *mockStore) ReadRankingItems(_ context.Context) ([]domain.RankingItem, error) { return nil, nil } func (m *mockStore) RankingFreshEnough(_ context.Context, _ time.Duration) (bool, error) { return false, nil } // AudioStore func (m *mockStore) AudioObjectKey(_ string, _ int, _ string) string { return "" } func (m *mockStore) AudioObjectKeyExt(_ string, _ int, _, _ string) string { return "" } func (m *mockStore) AudioExists(_ context.Context, _ string) bool { return false } func (m *mockStore) PutAudio(_ context.Context, _ string, _ []byte) error { return nil } func (m *mockStore) PutAudioStream(_ context.Context, _ string, _ io.Reader, _ int64, _ string) error { return nil } // PresignStore func (m *mockStore) PresignChapter(_ context.Context, _ string, _ int, _ time.Duration) (string, error) { return "", nil } func (m *mockStore) PresignAudio(_ context.Context, _ string, _ time.Duration) (string, error) { return "", nil } func (m *mockStore) PresignAvatarUpload(_ context.Context, _, _ string) (string, string, error) { return "", "", nil } func (m *mockStore) PresignAvatarURL(_ context.Context, _ string) (string, bool, error) { return "", false, nil } func (m *mockStore) PutAvatar(_ context.Context, _, _, _ string, _ []byte) (string, error) { return "", nil } func (m *mockStore) DeleteAvatar(_ context.Context, _ string) error { return nil } // ProgressStore func (m *mockStore) GetProgress(_ context.Context, _, _ string) (domain.ReadingProgress, bool) { return domain.ReadingProgress{}, false } func (m *mockStore) SetProgress(_ context.Context, _ string, _ domain.ReadingProgress) error { return nil } func (m *mockStore) AllProgress(_ context.Context, _ string) ([]domain.ReadingProgress, error) { return nil, nil } func (m *mockStore) DeleteProgress(_ context.Context, _, _ string) error { return nil } // ── Compile-time interface satisfaction ─────────────────────────────────────── var _ bookstore.BookWriter = (*mockStore)(nil) var _ bookstore.BookReader = (*mockStore)(nil) var _ bookstore.RankingStore = (*mockStore)(nil) var _ bookstore.AudioStore = (*mockStore)(nil) var _ bookstore.PresignStore = (*mockStore)(nil) var _ bookstore.ProgressStore = (*mockStore)(nil) // ── Behavioural tests ───────────────────────────────────────────────────────── func TestBookWriter_WriteMetadata_ReturnsNilError(t *testing.T) { var w bookstore.BookWriter = &mockStore{} if err := w.WriteMetadata(context.Background(), domain.BookMeta{Slug: "test"}); err != nil { t.Errorf("unexpected error: %v", err) } } func TestBookReader_ReadMetadata_NotFound(t *testing.T) { var r bookstore.BookReader = &mockStore{} _, found, err := r.ReadMetadata(context.Background(), "unknown") if err != nil { t.Fatalf("unexpected error: %v", err) } if found { t.Error("expected not found") } } func TestRankingStore_RankingFreshEnough_ReturnsFalse(t *testing.T) { var s bookstore.RankingStore = &mockStore{} fresh, err := s.RankingFreshEnough(context.Background(), time.Hour) if err != nil { t.Fatalf("unexpected error: %v", err) } if fresh { t.Error("expected false") } } func TestAudioStore_AudioExists_ReturnsFalse(t *testing.T) { var s bookstore.AudioStore = &mockStore{} if s.AudioExists(context.Background(), "audio/slug/1/af_bella.mp3") { t.Error("expected false") } } func TestProgressStore_GetProgress_NotFound(t *testing.T) { var s bookstore.ProgressStore = &mockStore{} _, found := s.GetProgress(context.Background(), "session-1", "slug") if found { t.Error("expected not found") } }