Matthieu Sieben 371e04aad2
Account management page (#3659)
---------

Co-authored-by: Eric Bailey <git@esb.lol>
2025-04-15 17:15:27 +02:00

166 lines
4.0 KiB
TypeScript

import { TestNetworkNoAppView } from '@atproto/dev-env'
// Importing from `dist` to circumvent circular dependency typing issues
import { AccountDb } from '../dist/account-manager/db'
describe('db', () => {
let network: TestNetworkNoAppView
let db: AccountDb
beforeAll(async () => {
network = await TestNetworkNoAppView.create({
dbPostgresSchema: 'db',
})
db = network.pds.ctx.accountManager.db
})
afterAll(async () => {
await network.close()
})
it('commits changes', async () => {
const result = await db.transaction(async (dbTxn) => {
return await dbTxn.db
.insertInto('repo_root')
.values({
did: 'x',
cid: 'x',
rev: 'x',
indexedAt: 'bad-date',
})
.returning('did')
.executeTakeFirst()
})
if (!result) {
return expect(result).toBeTruthy()
}
expect(result.did).toEqual('x')
const row = await db.db
.selectFrom('repo_root')
.selectAll()
.where('did', '=', 'x')
.executeTakeFirst()
expect(row).toEqual({
did: 'x',
cid: 'x',
rev: 'x',
indexedAt: 'bad-date',
})
})
it('rolls-back changes on failure', async () => {
const promise = db.transaction(async (dbTxn) => {
await dbTxn.db
.insertInto('repo_root')
.values({
did: 'y',
cid: 'y',
rev: 'y',
indexedAt: 'bad-date',
})
.returning('did')
.executeTakeFirst()
throw new Error('Oops!')
})
await expect(promise).rejects.toThrow('Oops!')
const row = await db.db
.selectFrom('repo_root')
.selectAll()
.where('did', '=', 'y')
.executeTakeFirst()
expect(row).toBeUndefined()
})
it('indicates isTransaction', async () => {
expect(db.isTransaction).toEqual(false)
await db.transaction(async (dbTxn) => {
expect(db.isTransaction).toEqual(false)
expect(dbTxn.isTransaction).toEqual(true)
})
expect(db.isTransaction).toEqual(false)
})
it('asserts transaction', async () => {
expect(() => db.assertTransaction()).toThrow('Transaction required')
await db.transaction(async (dbTxn) => {
expect(() => dbTxn.assertTransaction()).not.toThrow()
})
})
it('does not allow leaky transactions', async () => {
let leakedTx: AccountDb | undefined
const tx = db.transaction(async (dbTxn) => {
leakedTx = dbTxn
await dbTxn.db
.insertInto('repo_root')
.values({ cid: 'a', did: 'a', rev: 'a', indexedAt: 'bad-date' })
.execute()
throw new Error('test tx failed')
})
await expect(tx).rejects.toThrow('test tx failed')
const attempt = leakedTx?.db
.insertInto('repo_root')
.values({ cid: 'b', did: 'b', rev: 'b', indexedAt: 'bad-date' })
.execute()
await expect(attempt).rejects.toThrow('tx already failed')
const res = await db.db
.selectFrom('repo_root')
.selectAll()
.where('did', 'in', ['a', 'b'])
.execute()
expect(res.length).toBe(0)
})
it('ensures all inflight queries are rolled back', async () => {
let promise: Promise<unknown> | undefined = undefined
const names: string[] = []
try {
await db.transaction(async (dbTxn) => {
const queries: Promise<unknown>[] = []
for (let i = 0; i < 20; i++) {
const name = `user${i}`
const query = dbTxn.db
.insertInto('repo_root')
.values({
cid: name,
did: name,
rev: name,
indexedAt: 'bad-date',
})
.execute()
names.push(name)
queries.push(query)
}
promise = Promise.allSettled(queries)
throw new Error()
})
} catch (err) {
expect(err).toBeDefined()
}
if (promise) {
await promise
}
const res = await db.db
.selectFrom('repo_root')
.selectAll()
.where('did', 'in', names)
.execute()
expect(res.length).toBe(0)
})
})