this repo has no description
at main 205 lines 5.8 kB view raw
1import { test, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test"; 2import { setupTestDb, teardownTestDb } from "db/test-utils"; 3import * as dbschema from "db/schema"; 4import type { BlobRef } from "db/schema"; 5import { createRouter } from "../index"; 6import { faker } from "@faker-js/faker"; 7import { fakeDid, fakeCid, fakeTid, fakeBlobRef } from "shared/factories"; 8import type { XRPCRouter } from "@atcute/xrpc-server"; 9 10let db: Awaited<ReturnType<typeof setupTestDb>>; 11let txDb: typeof db; 12let rollback: () => void; 13 14beforeAll(async () => { 15 db = await setupTestDb(); 16}); 17 18afterAll(async () => { 19 await teardownTestDb(); 20}); 21 22beforeEach(async () => { 23 await new Promise<void>((resolveSetup) => { 24 let rejectTx: (err: Error) => void; 25 db.transaction(async (tx) => { 26 txDb = tx as unknown as typeof db; 27 resolveSetup(); 28 await new Promise<void>((_, reject) => { 29 rejectTx = reject; 30 }); 31 }).catch(() => {}); 32 rollback = () => rejectTx(new Error("rollback")); 33 }); 34}); 35 36afterEach(() => { 37 rollback(); 38}); 39 40const COLLECTION = "ca.ansxor.catnip.track"; 41 42async function insertTrack( 43 db: typeof txDb, 44 overrides?: { 45 did?: `did:${string}:${string}`; 46 rkey?: string; 47 cid?: string; 48 title?: string; 49 audio?: BlobRef; 50 artists?: { did?: string; name?: string }[]; 51 }, 52) { 53 const did = overrides?.did ?? fakeDid(); 54 const rkey = overrides?.rkey ?? fakeTid(); 55 const uri = `at://${did}/${COLLECTION}/${rkey}`; 56 const cid = overrides?.cid ?? fakeCid(); 57 const title = overrides?.title ?? faker.music.songName(); 58 const audio = overrides?.audio ?? fakeBlobRef(); 59 60 await db.insert(dbschema.tracks).values({ 61 uri, 62 did, 63 rkey, 64 cid, 65 title, 66 createdAt: new Date(), 67 audio, 68 }); 69 70 const artists = overrides?.artists ?? [{ did, name: faker.person.fullName() }]; 71 for (let i = 0; i < artists.length; i++) { 72 const a = artists[i]!; 73 const [inserted] = await db 74 .insert(dbschema.artists) 75 .values({ did: a.did ?? null, name: a.name ?? null }) 76 .onConflictDoNothing() 77 .returning(); 78 79 const artistId = 80 inserted?.id ?? 81 (await db.query.artists.findFirst({ 82 where: (art, { eq }) => eq(art.did, a.did!), 83 }))!.id; 84 85 await db.insert(dbschema.trackArtists).values({ 86 trackUri: uri, 87 artistId, 88 position: i, 89 }); 90 } 91 92 return { uri, did, rkey, cid, title, audio }; 93} 94 95function makeRouter() { 96 return createRouter(txDb); 97} 98 99async function fetchTracks(router: XRPCRouter, uris: string[]) { 100 const url = new URL("http://localhost/xrpc/ca.ansxor.catnip.getTracks"); 101 for (const uri of uris) { 102 url.searchParams.append("uris", uri); 103 } 104 return router.fetch(new Request(url, { method: "GET" })); 105} 106 107async function fetchTracksByDid(router: XRPCRouter, did: string) { 108 const url = new URL("http://localhost/xrpc/ca.ansxor.catnip.getTracksByDid"); 109 url.searchParams.set("did", did); 110 return router.fetch(new Request(url, { method: "GET" })); 111} 112 113test("returns a track by URI", async () => { 114 const title = faker.music.songName(); 115 const artistName = faker.person.fullName(); 116 const did = fakeDid(); 117 const { uri } = await insertTrack(txDb, { 118 did, 119 title, 120 artists: [{ did, name: artistName }], 121 }); 122 123 const router = makeRouter(); 124 const response = await fetchTracks(router, [uri]); 125 expect(response.status).toBe(200); 126 127 const data = await response.json(); 128 expect(data.tracks).toHaveLength(1); 129 130 const track = data.tracks[0]; 131 expect(track.title).toBe(title); 132 expect(track.$type).toBe("ca.ansxor.catnip.track"); 133 expect(track.artists).toHaveLength(1); 134 expect(track.artists[0].did).toBe(did); 135 expect(track.artists[0].name).toBe(artistName); 136 expect(track.audio).toBeDefined(); 137}); 138 139test("returns empty array for unknown URIs", async () => { 140 const router = makeRouter(); 141 const response = await fetchTracks(router, [ 142 `at://${fakeDid()}/${COLLECTION}/${fakeTid()}`, 143 ]); 144 expect(response.status).toBe(200); 145 146 const data = await response.json(); 147 expect(data.tracks).toHaveLength(0); 148}); 149 150test("returns multiple tracks", async () => { 151 const { uri: uri1 } = await insertTrack(txDb, { title: "Track One" }); 152 const { uri: uri2 } = await insertTrack(txDb, { title: "Track Two" }); 153 154 const router = makeRouter(); 155 const response = await fetchTracks(router, [uri1, uri2]); 156 expect(response.status).toBe(200); 157 158 const data = await response.json(); 159 expect(data.tracks).toHaveLength(2); 160 161 const titles = data.tracks.map((t: any) => t.title).sort(); 162 expect(titles).toEqual(["Track One", "Track Two"]); 163}); 164 165// getTracksByDid tests 166 167test("getTracksByDid returns tracks for a given DID", async () => { 168 const did = fakeDid(); 169 await insertTrack(txDb, { did, title: "DID Track 1" }); 170 await insertTrack(txDb, { did, title: "DID Track 2" }); 171 172 const router = makeRouter(); 173 const response = await fetchTracksByDid(router, did); 174 expect(response.status).toBe(200); 175 176 const data = await response.json(); 177 expect(data.tracks).toHaveLength(2); 178 179 const titles = data.tracks.map((t: any) => t.title).sort(); 180 expect(titles).toEqual(["DID Track 1", "DID Track 2"]); 181}); 182 183test("getTracksByDid returns empty array for unknown DID", async () => { 184 const router = makeRouter(); 185 const response = await fetchTracksByDid(router, fakeDid()); 186 expect(response.status).toBe(200); 187 188 const data = await response.json(); 189 expect(data.tracks).toHaveLength(0); 190}); 191 192test("getTracksByDid only returns tracks from the specified DID", async () => { 193 const did1 = fakeDid(); 194 const did2 = fakeDid(); 195 await insertTrack(txDb, { did: did1, title: "User 1 Track" }); 196 await insertTrack(txDb, { did: did2, title: "User 2 Track" }); 197 198 const router = makeRouter(); 199 const response = await fetchTracksByDid(router, did1); 200 expect(response.status).toBe(200); 201 202 const data = await response.json(); 203 expect(data.tracks).toHaveLength(1); 204 expect(data.tracks[0].title).toBe("User 1 Track"); 205});