${opts.title}${opts.extra ?? ''}
`); + return zip.generateAsync({ type: 'nodebuffer' }); +} + +before(async () => { + root = await mkdtemp(join(tmpdir(), 'reader-test-')); + booksDir = join(root, 'books'); + await mkdir(booksDir, { recursive: true }); + process.env.BOOKS_DIR = booksDir; + process.env.DATA_DIR = join(root, 'data'); + process.env.SCAN_INTERVAL = '0'; + process.env.WATCH_INTERVAL = '0'; + process.env.READER_TOKEN_SECRET = 'test-secret-that-is-long-enough'; + + const config = loadConfig(); + const db = openDatabase(config); + ctx = { + config, + db, + scanner: undefined as never, + users: undefined as never, + shelf: undefined as never, + sync: undefined as never, + log: undefined as never, + }; + const silent = Fastify({ logger: false }); + ctx.log = silent.log; + ctx.scanner = new Scanner(db, config, { info: () => {}, warn: () => {} }); + ctx.users = new UserService(db, config); + ctx.shelf = new ShelfService(db); + ctx.sync = new SyncService(db); + app = buildApp(ctx); + await app.ready(); +}); + +after(async () => { + await app.close(); + await rm(root, { recursive: true, force: true }); +}); + +interface Session { + token: string; + userId: string; +} + +async function register(username: string, password = 'password123'): Promise