From 38b810bc2e995227b92478a45e9d891696eb7c8d Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Wed, 13 May 2026 14:54:30 -0700 Subject: [PATCH] Initial author and filter tests created --- test/server/models/Author.test.js | 76 +++++++++++++++ .../utils/queries/authorFilters.test.js | 96 +++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 test/server/models/Author.test.js create mode 100644 test/server/utils/queries/authorFilters.test.js diff --git a/test/server/models/Author.test.js b/test/server/models/Author.test.js new file mode 100644 index 000000000..0a3c4435d --- /dev/null +++ b/test/server/models/Author.test.js @@ -0,0 +1,76 @@ +const chai = require('chai') +const sinon = require('sinon') +const { expect } = chai + +const { Sequelize } = require('sequelize') +const Database = require('../../../server/Database') +const Author = require('../../../server/models/Author') +const Library = require('../../../server/models/Library') + +describe('Author model', () => { + let sequelize + + beforeEach(async () => { + sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false }) + + Library.init(sequelize) + Author.init(sequelize) + + await sequelize.sync({ force: true }) + await Library.create({ + id: '00000000-0000-0000-0000-000000000001', + name: 'Test Library', + displayOrder: 1, + mediaType: 'book' + }) + }) + + afterEach(async () => { + sinon.restore() + Database.sequelize = null + await sequelize?.close() + }) + + describe('findOrCreateByNameAndLibrary', () => { + it('returns an error when the normalized author name is empty', async () => { + const result = await Author.findOrCreateByNameAndLibrary(' ', '00000000-0000-0000-0000-000000000001') + + expect(result.author).to.equal(null) + expect(result.created).to.equal(false) + expect(result.error).to.equal(undefined) + + const count = await Author.count() + expect(count).to.equal(0) + }) + }) + + describe('Database.rebuildAuthorRows', () => { + it('rebuilds stale derived author fields during initialization', async () => { + const db = Database + db.sequelize = sequelize + + await sequelize.getQueryInterface().bulkInsert('authors', [ + { + id: '00000000-0000-0000-0000-000000000010', + name: 'Gabriel García Márquez', + lastFirst: 'wrong', + searchName: 'wrong', + libraryId: '00000000-0000-0000-0000-000000000001', + createdAt: '2025-01-01 00:00:00.000 +00:00', + updatedAt: '2025-01-01 00:00:00.000 +00:00' + } + ]) + + await db.rebuildAuthorRows() + + const [authors] = await sequelize.query('SELECT name, lastFirst, searchName FROM authors') + expect(authors).to.deep.equal([ + { + name: 'Gabriel García Márquez', + lastFirst: 'Márquez, Gabriel García', + searchName: 'gabrielgarciamarquez' + } + ]) + }) + }) +}) diff --git a/test/server/utils/queries/authorFilters.test.js b/test/server/utils/queries/authorFilters.test.js new file mode 100644 index 000000000..7e2b518ad --- /dev/null +++ b/test/server/utils/queries/authorFilters.test.js @@ -0,0 +1,96 @@ +const chai = require('chai') +const { expect } = chai +const { Sequelize } = require('sequelize') + +const Database = require('../../../../server/Database') +const Author = require('../../../../server/models/Author') +const Library = require('../../../../server/models/Library') +const authorFilters = require('../../../../server/utils/queries/authorFilters') + +describe('authorFilters', () => { + let sequelize + + beforeEach(async () => { + sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false }) + + Library.init(sequelize) + Author.init(sequelize) + + await sequelize.sync({ force: true }) + await sequelize.getQueryInterface().createTable('bookAuthors', { + id: { type: Sequelize.DataTypes.INTEGER, allowNull: false, primaryKey: true, unique: true }, + bookId: { type: Sequelize.DataTypes.INTEGER, allowNull: false }, + authorId: { type: Sequelize.DataTypes.INTEGER, allowNull: false }, + createdAt: { type: Sequelize.DataTypes.DATE, allowNull: true } + }) + + Database.sequelize = sequelize + Database.authorModel = Author + + await Library.create({ + id: '00000000-0000-0000-0000-000000000001', + name: 'Test Library', + displayOrder: 1, + mediaType: 'book' + }) + + await Author.bulkCreate([ + { + id: '00000000-0000-0000-0000-000000000010', + name: 'J.R.R. Tolkein', + lastFirst: 'Tolkein, J. R. R.', + searchName: 'jrrtolkein', + libraryId: '00000000-0000-0000-0000-000000000001', + createdAt: '2025-01-01 00:00:00.000 +00:00', + updatedAt: '2025-01-01 00:00:00.000 +00:00' + }, + { + id: '00000000-0000-0000-0000-000000000011', + name: 'Agatha Christie', + lastFirst: 'Christie, Agatha', + searchName: 'agathachristie', + libraryId: '00000000-0000-0000-0000-000000000001', + createdAt: '2025-01-01 00:00:00.000 +00:00', + updatedAt: '2025-01-01 00:00:00.000 +00:00' + } + ]) + + await sequelize.getQueryInterface().bulkInsert('bookAuthors', [ + { + id: 1, + bookId: 1, + authorId: '00000000-0000-0000-0000-000000000010', + createdAt: '2025-01-01 00:00:00.000 +00:00' + }, + { + id: 2, + bookId: 2, + authorId: '00000000-0000-0000-0000-000000000011', + createdAt: '2025-01-01 00:00:00.000 +00:00' + } + ]) + }) + + afterEach(async () => { + await sequelize?.close() + }) + + it('matches authors by normalized searchName as well as display name', async () => { + const query = await Database.createTextSearchQuery('jrr') + const results = await authorFilters.search('00000000-0000-0000-0000-000000000001', query, 10, 0) + + expect(results).to.deep.equal([ + { + id: '00000000-0000-0000-0000-000000000010', + asin: null, + name: 'J.R.R. Tolkein', + description: null, + imagePath: null, + libraryId: '00000000-0000-0000-0000-000000000001', + addedAt: 1735689600000, + updatedAt: 1735689600000, + numBooks: 1 + } + ]) + }) +})