Update v2.17.3 migration file to first check if constraints need to be updated, add unit test

This commit is contained in:
advplyr 2024-11-30 15:48:20 -06:00
parent 70f466d03c
commit 4b52f31d58
2 changed files with 308 additions and 38 deletions

View File

@ -31,19 +31,28 @@ async function up({ context: { queryInterface, logger } }) {
{ field: 'libraryId', onDelete: 'SET NULL', onUpdate: 'CASCADE' },
{ field: 'libraryFolderId', onDelete: 'SET NULL', onUpdate: 'CASCADE' }
]
await changeConstraints(queryInterface, 'libraryItems', libraryItemsConstraints)
logger.info('[2.17.3 migration] Finished updating libraryItems constraints')
if (await changeConstraints(queryInterface, 'libraryItems', libraryItemsConstraints)) {
logger.info('[2.17.3 migration] Finished updating libraryItems constraints')
} else {
logger.info('[2.17.3 migration] No changes needed for libraryItems constraints')
}
logger.info('[2.17.3 migration] Updating feeds constraints')
const feedsConstraints = [{ field: 'userId', onDelete: 'SET NULL', onUpdate: 'CASCADE' }]
await changeConstraints(queryInterface, 'feeds', feedsConstraints)
logger.info('[2.17.3 migration] Finished updating feeds constraints')
if (await changeConstraints(queryInterface, 'feeds', feedsConstraints)) {
logger.info('[2.17.3 migration] Finished updating feeds constraints')
} else {
logger.info('[2.17.3 migration] No changes needed for feeds constraints')
}
if (await queryInterface.tableExists('mediaItemShares')) {
logger.info('[2.17.3 migration] Updating mediaItemShares constraints')
const mediaItemSharesConstraints = [{ field: 'userId', onDelete: 'SET NULL', onUpdate: 'CASCADE' }]
await changeConstraints(queryInterface, 'mediaItemShares', mediaItemSharesConstraints)
logger.info('[2.17.3 migration] Finished updating mediaItemShares constraints')
if (await changeConstraints(queryInterface, 'mediaItemShares', mediaItemSharesConstraints)) {
logger.info('[2.17.3 migration] Finished updating mediaItemShares constraints')
} else {
logger.info('[2.17.3 migration] No changes needed for mediaItemShares constraints')
}
} else {
logger.info('[2.17.3 migration] mediaItemShares table does not exist, skipping column change')
}
@ -54,18 +63,27 @@ async function up({ context: { queryInterface, logger } }) {
{ field: 'libraryId', onDelete: 'SET NULL', onUpdate: 'CASCADE' },
{ field: 'userId', onDelete: 'SET NULL', onUpdate: 'CASCADE' }
]
await changeConstraints(queryInterface, 'playbackSessions', playbackSessionsConstraints)
logger.info('[2.17.3 migration] Finished updating playbackSessions constraints')
if (await changeConstraints(queryInterface, 'playbackSessions', playbackSessionsConstraints)) {
logger.info('[2.17.3 migration] Finished updating playbackSessions constraints')
} else {
logger.info('[2.17.3 migration] No changes needed for playbackSessions constraints')
}
logger.info('[2.17.3 migration] Updating playlistMediaItems constraints')
const playlistMediaItemsConstraints = [{ field: 'playlistId', onDelete: 'CASCADE', onUpdate: 'CASCADE' }]
await changeConstraints(queryInterface, 'playlistMediaItems', playlistMediaItemsConstraints)
logger.info('[2.17.3 migration] Finished updating playlistMediaItems constraints')
if (await changeConstraints(queryInterface, 'playlistMediaItems', playlistMediaItemsConstraints)) {
logger.info('[2.17.3 migration] Finished updating playlistMediaItems constraints')
} else {
logger.info('[2.17.3 migration] No changes needed for playlistMediaItems constraints')
}
logger.info('[2.17.3 migration] Updating mediaProgresses constraints')
const mediaProgressesConstraints = [{ field: 'userId', onDelete: 'CASCADE', onUpdate: 'CASCADE' }]
await changeConstraints(queryInterface, 'mediaProgresses', mediaProgressesConstraints)
logger.info('[2.17.3 migration] Finished updating mediaProgresses constraints')
if (await changeConstraints(queryInterface, 'mediaProgresses', mediaProgressesConstraints)) {
logger.info('[2.17.3 migration] Finished updating mediaProgresses constraints')
} else {
logger.info('[2.17.3 migration] No changes needed for mediaProgresses constraints')
}
await execQuery(`COMMIT;`)
} catch (error) {
@ -103,59 +121,75 @@ async function down({ context: { queryInterface, logger } }) {
* @property {string} onUpdate - The onUpdate constraint
*/
/**
* @typedef SequelizeFKObj
* @property {{ model: string, key: string }} references
* @property {string} onDelete
* @property {string} onUpdate
*/
/**
* @param {Object} fk - The foreign key object from PRAGMA foreign_key_list
* @returns {SequelizeFKObj} - The foreign key object formatted for Sequelize
*/
const formatFKsPragmaToSequelizeFK = (fk) => {
let onDelete = fk['on_delete']
let onUpdate = fk['on_update']
if (fk.from === 'userId' || fk.from === 'libraryId' || fk.from === 'deviceId') {
onDelete = 'SET NULL'
onUpdate = 'CASCADE'
}
return {
references: {
model: fk.table,
key: fk.to
},
constraints: {
onDelete,
onUpdate
}
onDelete: fk['on_delete'],
onUpdate: fk['on_update']
}
}
/**
* Extends the Sequelize describeTable function to include the foreign keys constraints in sqlite dbs
*
* @param {import('sequelize').QueryInterface} queryInterface
* @param {String} tableName - The table name
* @param {ConstraintUpdateObj[]} constraints - constraints to update
* @param {string} tableName
* @param {ConstraintUpdateObj[]} constraints
* @returns {Promise<Record<string, SequelizeFKObj>|null>}
*/
async function describeTableWithFKs(queryInterface, tableName, constraints) {
async function getUpdatedForeignKeys(queryInterface, tableName, constraints) {
const execQuery = queryInterface.sequelize.query.bind(queryInterface.sequelize)
const quotedTableName = queryInterface.quoteIdentifier(tableName)
const foreignKeys = await execQuery(`PRAGMA foreign_key_list(${quotedTableName});`)
let hasUpdates = false
const foreignKeysByColName = foreignKeys.reduce((prev, curr) => {
const fk = formatFKsPragmaToSequelizeFK(curr)
const constraint = constraints.find((c) => c.field === curr.from)
if (constraint && (constraint.onDelete !== fk.onDelete || constraint.onUpdate !== fk.onUpdate)) {
fk.onDelete = constraint.onDelete
fk.onUpdate = constraint.onUpdate
hasUpdates = true
}
return { ...prev, [curr.from]: fk }
}, {})
return hasUpdates ? foreignKeysByColName : null
}
/**
* Extends the Sequelize describeTable function to include the updated foreign key constraints
*
* @param {import('sequelize').QueryInterface} queryInterface
* @param {String} tableName
* @param {Record<string, SequelizeFKObj>} updatedForeignKeys
*/
async function describeTableWithFKs(queryInterface, tableName, updatedForeignKeys) {
const tableDescription = await queryInterface.describeTable(tableName)
const tableDescriptionWithFks = Object.entries(tableDescription).reduce((prev, [col, attributes]) => {
let extendedAttributes = attributes
if (foreignKeysByColName[col]) {
// Use the constraints from the constraints array if they exist, otherwise use the existing constraints
const onDelete = constraints.find((c) => c.field === col)?.onDelete || foreignKeysByColName[col].constraints.onDelete
const onUpdate = constraints.find((c) => c.field === col)?.onUpdate || foreignKeysByColName[col].constraints.onUpdate
if (updatedForeignKeys[col]) {
extendedAttributes = {
...extendedAttributes,
references: foreignKeysByColName[col].references,
onDelete,
onUpdate
...updatedForeignKeys[col]
}
}
return { ...prev, [col]: extendedAttributes }
@ -171,8 +205,14 @@ async function describeTableWithFKs(queryInterface, tableName, constraints) {
* @param {import('sequelize').QueryInterface} queryInterface
* @param {string} tableName
* @param {ConstraintUpdateObj[]} constraints
* @returns {Promise<boolean>} - Return false if no changes are needed, true otherwise
*/
async function changeConstraints(queryInterface, tableName, constraints) {
const updatedForeignKeys = await getUpdatedForeignKeys(queryInterface, tableName, constraints)
if (!updatedForeignKeys) {
return false
}
const execQuery = queryInterface.sequelize.query.bind(queryInterface.sequelize)
const quotedTableName = queryInterface.quoteIdentifier(tableName)
@ -180,7 +220,7 @@ async function changeConstraints(queryInterface, tableName, constraints) {
const quotedBackupTableName = queryInterface.quoteIdentifier(backupTableName)
try {
const tableDescriptionWithFks = await describeTableWithFKs(queryInterface, tableName, constraints)
const tableDescriptionWithFks = await describeTableWithFKs(queryInterface, tableName, updatedForeignKeys)
const attributes = queryInterface.queryGenerator.attributesToSQL(tableDescriptionWithFks)
@ -210,7 +250,7 @@ async function changeConstraints(queryInterface, tableName, constraints) {
return Promise.reject(`Foreign key violations detected: ${JSON.stringify(result, null, 2)}`)
}
return Promise.resolve()
return true
} catch (error) {
return Promise.reject(error)
}

View File

@ -0,0 +1,230 @@
const { expect } = require('chai')
const sinon = require('sinon')
const { up } = require('../../../server/migrations/v2.17.3-fk-constraints')
const { Sequelize, QueryInterface } = require('sequelize')
const Logger = require('../../../server/Logger')
describe('migration-v2.17.3-fk-constraints', () => {
let sequelize
/** @type {QueryInterface} */
let queryInterface
let loggerInfoStub
beforeEach(() => {
sequelize = new Sequelize({ dialect: 'sqlite', storage: ':memory:', logging: false })
queryInterface = sequelize.getQueryInterface()
loggerInfoStub = sinon.stub(Logger, 'info')
})
afterEach(() => {
sinon.restore()
})
describe('up', () => {
beforeEach(async () => {
// Create associated tables: Users, libraries, libraryFolders, playlists, devices
await queryInterface.sequelize.query('CREATE TABLE `users` (`id` UUID PRIMARY KEY);')
await queryInterface.sequelize.query('CREATE TABLE `libraries` (`id` UUID PRIMARY KEY);')
await queryInterface.sequelize.query('CREATE TABLE `libraryFolders` (`id` UUID PRIMARY KEY);')
await queryInterface.sequelize.query('CREATE TABLE `playlists` (`id` UUID PRIMARY KEY);')
await queryInterface.sequelize.query('CREATE TABLE `devices` (`id` UUID PRIMARY KEY);')
})
afterEach(async () => {
await queryInterface.dropAllTables()
})
it('should fix table foreign key constraints', async () => {
// Create tables with missing foreign key constraints: libraryItems, feeds, mediaItemShares, playbackSessions, playlistMediaItems, mediaProgresses
await queryInterface.sequelize.query('CREATE TABLE `libraryItems` (`id` UUID UNIQUE PRIMARY KEY, `libraryId` UUID REFERENCES `libraries` (`id`), `libraryFolderId` UUID REFERENCES `libraryFolders` (`id`));')
await queryInterface.sequelize.query('CREATE TABLE `feeds` (`id` UUID UNIQUE PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`));')
await queryInterface.sequelize.query('CREATE TABLE `mediaItemShares` (`id` UUID UNIQUE PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`));')
await queryInterface.sequelize.query('CREATE TABLE `playbackSessions` (`id` UUID UNIQUE PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`), `deviceId` UUID REFERENCES `devices` (`id`), `libraryId` UUID REFERENCES `libraries` (`id`));')
await queryInterface.sequelize.query('CREATE TABLE `playlistMediaItems` (`id` UUID UNIQUE PRIMARY KEY, `playlistId` UUID REFERENCES `playlists` (`id`));')
await queryInterface.sequelize.query('CREATE TABLE `mediaProgresses` (`id` UUID UNIQUE PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`));')
//
// Validate that foreign key constraints are missing
//
let libraryItemsForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(libraryItems);`)
expect(libraryItemsForeignKeys).to.have.deep.members([
{ id: 0, seq: 0, table: 'libraryFolders', from: 'libraryFolderId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' },
{ id: 1, seq: 0, table: 'libraries', from: 'libraryId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' }
])
let feedsForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(feeds);`)
expect(feedsForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' }])
let mediaItemSharesForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(mediaItemShares);`)
expect(mediaItemSharesForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' }])
let playbackSessionForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(playbackSessions);`)
expect(playbackSessionForeignKeys).to.deep.equal([
{ id: 0, seq: 0, table: 'libraries', from: 'libraryId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' },
{ id: 1, seq: 0, table: 'devices', from: 'deviceId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' },
{ id: 2, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' }
])
let playlistMediaItemsForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(playlistMediaItems);`)
expect(playlistMediaItemsForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'playlists', from: 'playlistId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' }])
let mediaProgressesForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(mediaProgresses);`)
expect(mediaProgressesForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'NO ACTION', on_delete: 'NO ACTION', match: 'NONE' }])
//
// Insert test data into tables
//
await queryInterface.bulkInsert('users', [{ id: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
await queryInterface.bulkInsert('libraries', [{ id: 'a41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('libraryFolders', [{ id: 'b41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('playlists', [{ id: 'f41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('devices', [{ id: 'g41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('libraryItems', [{ id: 'c1a96857-48a8-43b6-8966-abc909c55b0f', libraryId: 'a41a40e3-f516-40f5-810d-757ab668ebba', libraryFolderId: 'b41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('feeds', [{ id: 'd1a96857-48a8-43b6-8966-abc909c55b0f', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
await queryInterface.bulkInsert('mediaItemShares', [{ id: 'h1a96857-48a8-43b6-8966-abc909c55b0f', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
await queryInterface.bulkInsert('playbackSessions', [{ id: 'f1a96857-48a8-43b6-8966-abc909c55b0x', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f', deviceId: 'g41a40e3-f516-40f5-810d-757ab668ebba', libraryId: 'a41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('playlistMediaItems', [{ id: 'i1a96857-48a8-43b6-8966-abc909c55b0f', playlistId: 'f41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('mediaProgresses', [{ id: 'j1a96857-48a8-43b6-8966-abc909c55b0f', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
//
// Query data before migration
//
const libraryItems = await queryInterface.sequelize.query('SELECT * FROM libraryItems;')
const feeds = await queryInterface.sequelize.query('SELECT * FROM feeds;')
const mediaItemShares = await queryInterface.sequelize.query('SELECT * FROM mediaItemShares;')
const playbackSessions = await queryInterface.sequelize.query('SELECT * FROM playbackSessions;')
const playlistMediaItems = await queryInterface.sequelize.query('SELECT * FROM playlistMediaItems;')
const mediaProgresses = await queryInterface.sequelize.query('SELECT * FROM mediaProgresses;')
//
// Run migration
//
await up({ context: { queryInterface, logger: Logger } })
//
// Validate that foreign key constraints are updated
//
libraryItemsForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(libraryItems);`)
expect(libraryItemsForeignKeys).to.have.deep.members([
{ id: 0, seq: 0, table: 'libraryFolders', from: 'libraryFolderId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' },
{ id: 1, seq: 0, table: 'libraries', from: 'libraryId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' }
])
feedsForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(feeds);`)
expect(feedsForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' }])
mediaItemSharesForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(mediaItemShares);`)
expect(mediaItemSharesForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' }])
playbackSessionForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(playbackSessions);`)
expect(playbackSessionForeignKeys).to.deep.equal([
{ id: 0, seq: 0, table: 'libraries', from: 'libraryId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' },
{ id: 1, seq: 0, table: 'devices', from: 'deviceId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' },
{ id: 2, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'CASCADE', on_delete: 'SET NULL', match: 'NONE' }
])
playlistMediaItemsForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(playlistMediaItems);`)
expect(playlistMediaItemsForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'playlists', from: 'playlistId', to: 'id', on_update: 'CASCADE', on_delete: 'CASCADE', match: 'NONE' }])
mediaProgressesForeignKeys = await queryInterface.sequelize.query(`PRAGMA foreign_key_list(mediaProgresses);`)
expect(mediaProgressesForeignKeys).to.deep.equal([{ id: 0, seq: 0, table: 'users', from: 'userId', to: 'id', on_update: 'CASCADE', on_delete: 'CASCADE', match: 'NONE' }])
//
// Validate that data is not changed
//
const libraryItemsAfter = await queryInterface.sequelize.query('SELECT * FROM libraryItems;')
expect(libraryItemsAfter).to.deep.equal(libraryItems)
const feedsAfter = await queryInterface.sequelize.query('SELECT * FROM feeds;')
expect(feedsAfter).to.deep.equal(feeds)
const mediaItemSharesAfter = await queryInterface.sequelize.query('SELECT * FROM mediaItemShares;')
expect(mediaItemSharesAfter).to.deep.equal(mediaItemShares)
const playbackSessionsAfter = await queryInterface.sequelize.query('SELECT * FROM playbackSessions;')
expect(playbackSessionsAfter).to.deep.equal(playbackSessions)
const playlistMediaItemsAfter = await queryInterface.sequelize.query('SELECT * FROM playlistMediaItems;')
expect(playlistMediaItemsAfter).to.deep.equal(playlistMediaItems)
const mediaProgressesAfter = await queryInterface.sequelize.query('SELECT * FROM mediaProgresses;')
expect(mediaProgressesAfter).to.deep.equal(mediaProgresses)
})
it('should keep correct table foreign key constraints', async () => {
// Create tables with correct foreign key constraints: libraryItems, feeds, mediaItemShares, playbackSessions, playlistMediaItems, mediaProgresses
await queryInterface.sequelize.query('CREATE TABLE `libraryItems` (`id` UUID PRIMARY KEY, `libraryId` UUID REFERENCES `libraries` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, `libraryFolderId` UUID REFERENCES `libraryFolders` (`id`) ON DELETE SET NULL ON UPDATE CASCADE);')
await queryInterface.sequelize.query('CREATE TABLE `feeds` (`id` UUID PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE);')
await queryInterface.sequelize.query('CREATE TABLE `mediaItemShares` (`id` UUID PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE);')
await queryInterface.sequelize.query('CREATE TABLE `playbackSessions` (`id` UUID PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, `deviceId` UUID REFERENCES `devices` (`id`) ON DELETE SET NULL ON UPDATE CASCADE, `libraryId` UUID REFERENCES `libraries` (`id`) ON DELETE SET NULL ON UPDATE CASCADE);')
await queryInterface.sequelize.query('CREATE TABLE `playlistMediaItems` (`id` UUID PRIMARY KEY, `playlistId` UUID REFERENCES `playlists` (`id`) ON DELETE CASCADE ON UPDATE CASCADE);')
await queryInterface.sequelize.query('CREATE TABLE `mediaProgresses` (`id` UUID PRIMARY KEY, `userId` UUID REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE);')
//
// Insert test data into tables
//
await queryInterface.bulkInsert('users', [{ id: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
await queryInterface.bulkInsert('libraries', [{ id: 'a41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('libraryFolders', [{ id: 'b41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('playlists', [{ id: 'f41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('devices', [{ id: 'g41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('libraryItems', [{ id: 'c1a96857-48a8-43b6-8966-abc909c55b0f', libraryId: 'a41a40e3-f516-40f5-810d-757ab668ebba', libraryFolderId: 'b41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('feeds', [{ id: 'd1a96857-48a8-43b6-8966-abc909c55b0f', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
await queryInterface.bulkInsert('mediaItemShares', [{ id: 'h1a96857-48a8-43b6-8966-abc909c55b0f', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
await queryInterface.bulkInsert('playbackSessions', [{ id: 'f1a96857-48a8-43b6-8966-abc909c55b0x', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f', deviceId: 'g41a40e3-f516-40f5-810d-757ab668ebba', libraryId: 'a41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('playlistMediaItems', [{ id: 'i1a96857-48a8-43b6-8966-abc909c55b0f', playlistId: 'f41a40e3-f516-40f5-810d-757ab668ebba' }])
await queryInterface.bulkInsert('mediaProgresses', [{ id: 'j1a96857-48a8-43b6-8966-abc909c55b0f', userId: 'e1a96857-48a8-43b6-8966-abc909c55b0f' }])
//
// Query data before migration
//
const libraryItems = await queryInterface.sequelize.query('SELECT * FROM libraryItems;')
const feeds = await queryInterface.sequelize.query('SELECT * FROM feeds;')
const mediaItemShares = await queryInterface.sequelize.query('SELECT * FROM mediaItemShares;')
const playbackSessions = await queryInterface.sequelize.query('SELECT * FROM playbackSessions;')
const playlistMediaItems = await queryInterface.sequelize.query('SELECT * FROM playlistMediaItems;')
const mediaProgresses = await queryInterface.sequelize.query('SELECT * FROM mediaProgresses;')
await up({ context: { queryInterface, logger: Logger } })
expect(loggerInfoStub.callCount).to.equal(14)
expect(loggerInfoStub.getCall(0).calledWith(sinon.match('[2.17.3 migration] UPGRADE BEGIN: 2.17.3-fk-constraints'))).to.be.true
expect(loggerInfoStub.getCall(1).calledWith(sinon.match('[2.17.3 migration] Updating libraryItems constraints'))).to.be.true
expect(loggerInfoStub.getCall(2).calledWith(sinon.match('[2.17.3 migration] No changes needed for libraryItems constraints'))).to.be.true
expect(loggerInfoStub.getCall(3).calledWith(sinon.match('[2.17.3 migration] Updating feeds constraints'))).to.be.true
expect(loggerInfoStub.getCall(4).calledWith(sinon.match('[2.17.3 migration] No changes needed for feeds constraints'))).to.be.true
expect(loggerInfoStub.getCall(5).calledWith(sinon.match('[2.17.3 migration] Updating mediaItemShares constraints'))).to.be.true
expect(loggerInfoStub.getCall(6).calledWith(sinon.match('[2.17.3 migration] No changes needed for mediaItemShares constraints'))).to.be.true
expect(loggerInfoStub.getCall(7).calledWith(sinon.match('[2.17.3 migration] Updating playbackSessions constraints'))).to.be.true
expect(loggerInfoStub.getCall(8).calledWith(sinon.match('[2.17.3 migration] No changes needed for playbackSessions constraints'))).to.be.true
expect(loggerInfoStub.getCall(9).calledWith(sinon.match('[2.17.3 migration] Updating playlistMediaItems constraints'))).to.be.true
expect(loggerInfoStub.getCall(10).calledWith(sinon.match('[2.17.3 migration] No changes needed for playlistMediaItems constraints'))).to.be.true
expect(loggerInfoStub.getCall(11).calledWith(sinon.match('[2.17.3 migration] Updating mediaProgresses constraints'))).to.be.true
expect(loggerInfoStub.getCall(12).calledWith(sinon.match('[2.17.3 migration] No changes needed for mediaProgresses constraints'))).to.be.true
expect(loggerInfoStub.getCall(13).calledWith(sinon.match('[2.17.3 migration] UPGRADE END: 2.17.3-fk-constraints'))).to.be.true
//
// Validate that data is not changed
//
const libraryItemsAfter = await queryInterface.sequelize.query('SELECT * FROM libraryItems;')
expect(libraryItemsAfter).to.deep.equal(libraryItems)
const feedsAfter = await queryInterface.sequelize.query('SELECT * FROM feeds;')
expect(feedsAfter).to.deep.equal(feeds)
const mediaItemSharesAfter = await queryInterface.sequelize.query('SELECT * FROM mediaItemShares;')
expect(mediaItemSharesAfter).to.deep.equal(mediaItemShares)
const playbackSessionsAfter = await queryInterface.sequelize.query('SELECT * FROM playbackSessions;')
expect(playbackSessionsAfter).to.deep.equal(playbackSessions)
const playlistMediaItemsAfter = await queryInterface.sequelize.query('SELECT * FROM playlistMediaItems;')
expect(playlistMediaItemsAfter).to.deep.equal(playlistMediaItems)
const mediaProgressesAfter = await queryInterface.sequelize.query('SELECT * FROM mediaProgresses;')
expect(mediaProgressesAfter).to.deep.equal(mediaProgresses)
})
})
})