this repo has no description
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});