mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
fix(library): exclude missing books from library counts
Align library list totals with statistics by counting only present records while preserving empty libraries and user content filters. Fixes #639
This commit is contained in:
@@ -6,7 +6,8 @@ vi.mock('drizzle-orm', () => ({
|
||||
sql: vi.fn((strings: TemplateStringsArray, ...values: unknown[]) => ({ op: 'sql', text: strings.join(''), values })),
|
||||
}));
|
||||
|
||||
import { libraries } from '../../db/schema';
|
||||
import { books, libraries } from '../../db/schema';
|
||||
import { LIBRARY_BOOK_STATUS_PRESENT } from './library.constants';
|
||||
import { LibraryRepository } from './library.repository';
|
||||
|
||||
describe('LibraryRepository', () => {
|
||||
@@ -52,7 +53,25 @@ describe('LibraryRepository', () => {
|
||||
expect(txUpdateSet).toHaveBeenNthCalledWith(2, { displayOrder: 6 });
|
||||
});
|
||||
|
||||
it('findAllForUser includes file rename eligibility in the scoped library projection', async () => {
|
||||
it('findAll counts only present books while retaining empty libraries', async () => {
|
||||
const orderBy = vi.fn().mockResolvedValue([]);
|
||||
const groupBy = vi.fn().mockReturnValue({ orderBy });
|
||||
const leftJoin = vi.fn().mockReturnValue({ groupBy });
|
||||
const from = vi.fn().mockReturnValue({ leftJoin });
|
||||
db.select.mockReturnValue({ from });
|
||||
|
||||
await repo.findAll();
|
||||
|
||||
expect(leftJoin).toHaveBeenCalledWith(books, {
|
||||
op: 'and',
|
||||
clauses: [
|
||||
{ op: 'eq', left: books.libraryId, right: libraries.id },
|
||||
{ op: 'eq', left: books.status, right: LIBRARY_BOOK_STATUS_PRESENT },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('findAllForUser includes file rename eligibility and counts only present books', async () => {
|
||||
const orderBy = vi.fn().mockResolvedValue([]);
|
||||
const groupBy = vi.fn().mockReturnValue({ orderBy });
|
||||
const leftJoin = vi.fn().mockReturnValue({ groupBy });
|
||||
@@ -67,6 +86,13 @@ describe('LibraryRepository', () => {
|
||||
fileRenameEnabled: libraries.fileRenameEnabled,
|
||||
}),
|
||||
);
|
||||
expect(leftJoin).toHaveBeenCalledWith(books, {
|
||||
op: 'and',
|
||||
clauses: [
|
||||
{ op: 'eq', left: books.libraryId, right: libraries.id },
|
||||
{ op: 'eq', left: books.status, right: LIBRARY_BOOK_STATUS_PRESENT },
|
||||
],
|
||||
});
|
||||
expect(orderBy).toHaveBeenCalledWith(libraries.displayOrder, libraries.name);
|
||||
});
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ export class LibraryRepository {
|
||||
bookCount: sql<number>`count(${books.id})::int`,
|
||||
})
|
||||
.from(libraries)
|
||||
.leftJoin(books, eq(books.libraryId, libraries.id))
|
||||
.leftJoin(books, and(eq(books.libraryId, libraries.id), eq(books.status, LIBRARY_BOOK_STATUS_PRESENT)))
|
||||
.groupBy(libraries.id)
|
||||
.orderBy(libraries.displayOrder, libraries.name);
|
||||
}
|
||||
|
||||
findAllForUser(userId: number, contentFilters?: ContentFilterRules) {
|
||||
const filterClauses = contentFilters ? buildContentFilterClauses(contentFilters, this.db) : [];
|
||||
const bookJoinOn = filterClauses.length > 0 ? and(eq(books.libraryId, libraries.id), ...filterClauses)! : eq(books.libraryId, libraries.id);
|
||||
const bookJoinOn = and(eq(books.libraryId, libraries.id), eq(books.status, LIBRARY_BOOK_STATUS_PRESENT), ...filterClauses)!;
|
||||
|
||||
return this.db
|
||||
.select({
|
||||
|
||||
Reference in New Issue
Block a user