import { describe , expect , it , beforeEach } from 'bun:test' import { Elysia } from 'elysia' import { treaty } from '@elysiajs/eden' import app from '../app.ts' import Docker from 'dockerode' import { mkdir } from "node:fs/promises"; import path from 'node:path' if (!process.env.TRACKER_HELPER_USERNAME) throw new Error("TRACKER_HELPER_USERNAME is missing in env."); if (!process.env.TRACKER_HELPER_PASSWORD) throw new Error("TRACKER_HELPER_PASSWORD is missing in env."); const accesslistFilePath = process.env.TRACKER_HELPER_ACCESSLIST_PATH || "/var/lib/aquatic/accesslist" const fixture = "3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01" // ubuntustudio-22.04.5-dvd-amd64.iso const fixture2 = "b22ea43e4c0a7f73fc706b5faf1c35bb078d3722" // Solus-GNOME-Release-2025-01-26.iso const username = process.env.TRACKER_HELPER_USERNAME! const password = process.env.TRACKER_HELPER_PASSWORD! const opts = { headers: { authorization: "Basic " + btoa(username + ':' + password) } } const api = treaty(app) describe ('tracker-helper', () => { beforeEach(async () => { console.log(`Asserting existence of accesslist at ${accesslistFilePath}`); const assertAccesslistExists = async function (accesslistFilePath: string) { const wlFile = Bun.file(accesslistFilePath); const exists = await wlFile.exists(); if (!exists) { console.log(`Creating accesslist file at ${accesslistFilePath}`); // Ensure the parent directory exists await mkdir(path.dirname(accesslistFilePath), { recursive: true }); await wlFile.write(`${fixture2}\n${fixture}\n`); } }; await assertAccesslistExists(accesslistFilePath); }); it('return a health response', async () => { const { data, status } = await api.health.get() expect(status).toBe(200) expect(data).toBe("OK") }) it('return a version', async () => { const { data, status } = await api.version.get(opts) expect(status).toBe(200) expect(data).toContain("version") }) it('return a accesslist', async () => { const { data, status } = await api.accesslist.get(opts) expect(status).toBe(200) expect(data).toContain(fixture) }) it('expects the accesslist to already exist', async () => { const accesslist = Bun.file(accesslistFilePath) const accesslistExists = await accesslist.exists() expect(accesslistExists).toBe(true) }) it('appends a new info_hash to the accesslist file', async () => { // make an api call which is supposed to add an entry to the accesslist const { data, status } = await api.accesslist.post(fixture, opts) // assert that the entry has been added to the accesslist const w = Bun.file(accesslistFilePath) const accesslistAfter = await w.text() console.log('accesslistAfter as follows') console.log(accesslistAfter) expect(status).toBe(201) expect(data).toMatch(fixture) expect(accesslistAfter).toMatch(fixture) }) // it('sends a SIGHUP to opentracker', async () => { // const { data, status } = await api.accesslist.post(fixture, opts) // const containerId = "act-ci-Tests-Checks-6e6f12196682961041a41a25b9d0dcf00e4d0f8e58f-7cb37eebfe9e1670328d58ad1f7c7bdf0fa078298ca6dd299e67d0141a4b9579" // // await docker.getContainer(containerId).kill({ signal: 'SIGHUP' }) // let container = await docker.getContainer(containerId) // container.inspect // }) // // This is skipped because I couldn't figure out opentracker's accesslist add/delete via FIFO functionality. // // I got as far as writing to the FIFO, and seeing opentracker acknowledge the line in it's logs. // // Despite this, requests from qbittorrent to opentracker responded with, // // "Requested download is not authorized for use with this tracker" // // About a week on this problem, and I give up! Using the accesslist reloading strat instead. // it.skip('writes a new info_hash to a fifo', async () => { // const fifoFilePath = process.env.TRACKER_HELPER_FIFO_PATH! // const fifo = Bun.file(fifoFilePath) // const fifoExists = await fifo.exists(); // // create fifo if it doesn't exist // if (!fifoExists) { // await Bun.spawn(["mkfifo", fifoFilePath]).exited; // } // // Start a process to read from the FIFO // const reader = Bun.spawn(["cat", fifoFilePath], { stdout: "pipe" }); // const { data, status } = await api.accesslist.post("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01", opts) // const text = await new Response(reader.stdout).text(); // expect(text).toBe("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01\n") // expect(status).toBe(200) // expect(data).toBe("3aa5ad5e62eaffd148cff3dbe93ff2e1e9cbcf01") // }) it('returns 401 when username/password is missing from GET /accesslist ', async () => { const { status } = await api.accesslist.get() expect(status).toBe(401) }) it('returns 401 when username/password is missing from POST /accesslist ', async () => { const { status } = await api.accesslist.post() expect(status).toBe(401) }) })