mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-08 02:11:35 +00:00
prevent lossy postgres migration edge cases
This commit is contained in:
parent
8593721c35
commit
cb31fd34d5
5 changed files with 372 additions and 23 deletions
131
test/server/scripts/migrateSqliteToPostgres.test.js
Normal file
131
test/server/scripts/migrateSqliteToPostgres.test.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
const { expect } = require('chai')
|
||||
const sqlite3 = require('sqlite3')
|
||||
|
||||
const {
|
||||
normalizeJson,
|
||||
isIntegerCompatible,
|
||||
findOverlongVarcharValues,
|
||||
findIntegerTypeIssues
|
||||
} = require('../../../server/scripts/migrateSqliteToPostgres')
|
||||
|
||||
function openMemoryDb() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const db = new sqlite3.Database(':memory:', (error) => {
|
||||
if (error) return reject(error)
|
||||
resolve(db)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function run(db, sql, params = []) {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.run(sql, params, (error) => {
|
||||
if (error) return reject(error)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function close(db) {
|
||||
return new Promise((resolve, reject) => {
|
||||
db.close((error) => {
|
||||
if (error) return reject(error)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe('migrateSqliteToPostgres script helpers', () => {
|
||||
it('should keep malformed JSON payloads insertable by returning valid JSON text', () => {
|
||||
const malformed = '"{"x",1}"'
|
||||
const normalized = normalizeJson(malformed)
|
||||
|
||||
expect(() => JSON.parse(normalized)).to.not.throw()
|
||||
expect(JSON.parse(normalized)).to.equal('"{"x",1}"')
|
||||
})
|
||||
|
||||
it('should detect overlong varchar values before migration', async () => {
|
||||
const db = await openMemoryDb()
|
||||
|
||||
try {
|
||||
await run(db, 'CREATE TABLE books (subtitle TEXT)')
|
||||
await run(db, 'INSERT INTO books (subtitle) VALUES (?)', ['x'.repeat(300)])
|
||||
await run(db, 'INSERT INTO books (subtitle) VALUES (?)', ['ok'])
|
||||
|
||||
const tablesToMigrate = [{ sqliteTable: 'books', postgresTable: 'books' }]
|
||||
const pgColumnsByTable = new Map([
|
||||
[
|
||||
'books',
|
||||
new Map([
|
||||
[
|
||||
'subtitle',
|
||||
{
|
||||
column_name: 'subtitle',
|
||||
data_type: 'character varying',
|
||||
character_maximum_length: 255
|
||||
}
|
||||
]
|
||||
])
|
||||
]
|
||||
])
|
||||
|
||||
const issues = await findOverlongVarcharValues(db, tablesToMigrate, pgColumnsByTable)
|
||||
|
||||
expect(issues).to.have.length(1)
|
||||
expect(issues[0]).to.include({
|
||||
sqliteTable: 'books',
|
||||
sqliteColumn: 'subtitle',
|
||||
postgresTable: 'books',
|
||||
postgresColumn: 'subtitle',
|
||||
maxLength: 255,
|
||||
actualMaxLength: 300,
|
||||
overCount: 1
|
||||
})
|
||||
} finally {
|
||||
await close(db)
|
||||
}
|
||||
})
|
||||
|
||||
it('should detect non-integer values for integer target columns', async () => {
|
||||
const db = await openMemoryDb()
|
||||
|
||||
try {
|
||||
await run(db, 'CREATE TABLE playbackSessions (timeListening REAL)')
|
||||
await run(db, 'INSERT INTO playbackSessions (timeListening) VALUES (?)', [25.802536999999997])
|
||||
await run(db, 'INSERT INTO playbackSessions (timeListening) VALUES (?)', [42])
|
||||
|
||||
const tablesToMigrate = [{ sqliteTable: 'playbackSessions', postgresTable: 'playbackSessions' }]
|
||||
const pgColumnsByTable = new Map([
|
||||
[
|
||||
'playbackSessions',
|
||||
new Map([
|
||||
[
|
||||
'timelistening',
|
||||
{
|
||||
column_name: 'timeListening',
|
||||
data_type: 'integer'
|
||||
}
|
||||
]
|
||||
])
|
||||
]
|
||||
])
|
||||
|
||||
const issues = await findIntegerTypeIssues(db, tablesToMigrate, pgColumnsByTable)
|
||||
|
||||
expect(issues).to.have.length(1)
|
||||
expect(issues[0].sqliteTable).to.equal('playbackSessions')
|
||||
expect(issues[0].sqliteColumn).to.equal('timeListening')
|
||||
expect(issues[0].postgresType).to.equal('integer')
|
||||
expect(issues[0].badCount).to.equal(1)
|
||||
} finally {
|
||||
await close(db)
|
||||
}
|
||||
})
|
||||
|
||||
it('should only accept integer-compatible values for integer columns', () => {
|
||||
expect(isIntegerCompatible(10)).to.equal(true)
|
||||
expect(isIntegerCompatible('10')).to.equal(true)
|
||||
expect(isIntegerCompatible(10.5)).to.equal(false)
|
||||
expect(isIntegerCompatible('10.5')).to.equal(false)
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue