mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-10-31 02:27:11 -04:00 
			
		
		
		
	Fix & test GET /videos
				
					
				
			This commit is contained in:
		
							parent
							
								
									45e769828b
								
							
						
					
					
						commit
						e26bc931f5
					
				| @ -14,6 +14,7 @@ import { KError } from "~/models/error"; | |||||||
| import { bubbleVideo } from "~/models/examples"; | import { bubbleVideo } from "~/models/examples"; | ||||||
| import { | import { | ||||||
| 	Page, | 	Page, | ||||||
|  | 	type Resource, | ||||||
| 	Sort, | 	Sort, | ||||||
| 	createPage, | 	createPage, | ||||||
| 	isUuid, | 	isUuid, | ||||||
| @ -54,8 +55,9 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) | |||||||
| 						slug: shows.slug, | 						slug: shows.slug, | ||||||
| 					}) | 					}) | ||||||
| 					.from(videos) | 					.from(videos) | ||||||
| 					.crossJoin( | 					.leftJoin( | ||||||
| 						sql`jsonb_array_elements_text(${videos.guess}->'year') as year`, | 						sql`jsonb_array_elements_text(${videos.guess}->'year') as year`, | ||||||
|  | 						sql`true`, | ||||||
| 					) | 					) | ||||||
| 					.innerJoin(entryVideoJoin, eq(entryVideoJoin.videoPk, videos.pk)) | 					.innerJoin(entryVideoJoin, eq(entryVideoJoin.videoPk, videos.pk)) | ||||||
| 					.innerJoin(entries, eq(entries.pk, entryVideoJoin.entryPk)) | 					.innerJoin(entries, eq(entries.pk, entryVideoJoin.entryPk)) | ||||||
| @ -78,7 +80,10 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) | |||||||
| 			const [{ guesses }] = await db | 			const [{ guesses }] = await db | ||||||
| 				.with(years, guess) | 				.with(years, guess) | ||||||
| 				.select({ | 				.select({ | ||||||
| 					guesses: jsonbObjectAgg<Guesses["guesses"]>(guess.guess, guess.years), | 					guesses: jsonbObjectAgg<Record<string, Resource>>( | ||||||
|  | 						guess.guess, | ||||||
|  | 						guess.years, | ||||||
|  | 					), | ||||||
| 				}) | 				}) | ||||||
| 				.from(guess); | 				.from(guess); | ||||||
| 
 | 
 | ||||||
| @ -98,7 +103,7 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) | |||||||
| 
 | 
 | ||||||
| 			return { | 			return { | ||||||
| 				paths: paths.map((x) => x.path), | 				paths: paths.map((x) => x.path), | ||||||
| 				guesses, | 				guesses: guesses ?? {}, | ||||||
| 				unmatched: unmatched.map((x) => x.path), | 				unmatched: unmatched.map((x) => x.path), | ||||||
| 			}; | 			}; | ||||||
| 		}, | 		}, | ||||||
| @ -177,8 +182,7 @@ export const videosH = new Elysia({ prefix: "/videos", tags: ["videos"] }) | |||||||
| 							path: videos.path, | 							path: videos.path, | ||||||
| 						}); | 						}); | ||||||
| 				} catch (e) { | 				} catch (e) { | ||||||
| 					if (!isUniqueConstraint(e)) | 					if (!isUniqueConstraint(e)) throw e; | ||||||
| 						throw e; |  | ||||||
| 					return error(409, { | 					return error(409, { | ||||||
| 						status: 409, | 						status: 409, | ||||||
| 						message: comment` | 						message: comment` | ||||||
|  | |||||||
| @ -144,5 +144,7 @@ export const jsonbBuildObject = <T>(select: JsonFields) => { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export const isUniqueConstraint = (e: unknown): boolean => { | export const isUniqueConstraint = (e: unknown): boolean => { | ||||||
| 	return typeof e === "object" && e != null && "code" in e && e.code === "23505"; | 	return ( | ||||||
|  | 		typeof e === "object" && e != null && "code" in e && e.code === "23505" | ||||||
|  | 	); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ export const Resource = () => | |||||||
| 		id: t.String({ format: "uuid" }), | 		id: t.String({ format: "uuid" }), | ||||||
| 		slug: t.String({ format: "slug" }), | 		slug: t.String({ format: "slug" }), | ||||||
| 	}); | 	}); | ||||||
|  | export type Resource = ReturnType<typeof Resource>["static"]; | ||||||
| 
 | 
 | ||||||
| const checker = TypeCompiler.Compile(t.String({ format: "uuid" })); | const checker = TypeCompiler.Compile(t.String({ format: "uuid" })); | ||||||
| export const isUuid = (id: string) => checker.Check(id); | export const isUuid = (id: string) => checker.Check(id); | ||||||
|  | |||||||
| @ -167,10 +167,7 @@ export const Guesses = t.Object({ | |||||||
| 	paths: t.Array(t.String()), | 	paths: t.Array(t.String()), | ||||||
| 	guesses: t.Record( | 	guesses: t.Record( | ||||||
| 		t.String(), | 		t.String(), | ||||||
| 		t.Record( | 		t.Record(t.String({ pattern: "^([1-9][0-9]{3})|unknown$" }), Resource()), | ||||||
| 			t.Union([t.Literal("unknown"), t.String({ pattern: "[1-9][0-9]*" })]), |  | ||||||
| 			Resource(), |  | ||||||
| 		), |  | ||||||
| 	), | 	), | ||||||
| 	unmatched: t.Array(t.String()), | 	unmatched: t.Array(t.String()), | ||||||
| }); | }); | ||||||
| @ -188,7 +185,7 @@ registerExamples(Guesses, { | |||||||
| 				id: "43b742f5-9ce6-467d-ad29-74460624020a", | 				id: "43b742f5-9ce6-467d-ad29-74460624020a", | ||||||
| 				slug: "evangelion", | 				slug: "evangelion", | ||||||
| 			}, | 			}, | ||||||
| 			1995: { | 			"1995": { | ||||||
| 				id: "43b742f5-9ce6-467d-ad29-74460624020a", | 				id: "43b742f5-9ce6-467d-ad29-74460624020a", | ||||||
| 				slug: "evangelion", | 				slug: "evangelion", | ||||||
| 			}, | 			}, | ||||||
|  | |||||||
| @ -17,3 +17,29 @@ export const createVideo = async (video: SeedVideo | SeedVideo[]) => { | |||||||
| 	const body = await resp.json(); | 	const body = await resp.json(); | ||||||
| 	return [resp, body] as const; | 	return [resp, body] as const; | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
|  | export const getVideos = async () => { | ||||||
|  | 	const resp = await app.handle( | ||||||
|  | 		new Request(buildUrl("videos"), { | ||||||
|  | 			method: "GET", | ||||||
|  | 			headers: await getJwtHeaders(), | ||||||
|  | 		}), | ||||||
|  | 	); | ||||||
|  | 	const body = await resp.json(); | ||||||
|  | 	return [resp, body] as const; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | export const deleteVideo = async (paths: string[]) => { | ||||||
|  | 	const resp = await app.handle( | ||||||
|  | 		new Request(buildUrl("videos"), { | ||||||
|  | 			method: "DELETE", | ||||||
|  | 			body: JSON.stringify(paths), | ||||||
|  | 			headers: { | ||||||
|  | 				"Content-Type": "application/json", | ||||||
|  | 				...(await getJwtHeaders()), | ||||||
|  | 			}, | ||||||
|  | 		}), | ||||||
|  | 	); | ||||||
|  | 	const body = await resp.json(); | ||||||
|  | 	return [resp, body] as const; | ||||||
|  | }; | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import { db, migrate } from "~/db"; | import { db, migrate } from "~/db"; | ||||||
| import { profiles, shows } from "~/db/schema"; | import { profiles, shows } from "~/db/schema"; | ||||||
| import { madeInAbyss } from "~/models/examples"; | import { bubble, madeInAbyss } from "~/models/examples"; | ||||||
| import { createSerie, createVideo } from "./helpers"; | import { createMovie, createSerie, createVideo, getVideos } from "./helpers"; | ||||||
| 
 | 
 | ||||||
| // test file used to run manually using `bun tests/manual.ts`
 | // test file used to run manually using `bun tests/manual.ts`
 | ||||||
| // run those before running this script
 | // run those before running this script
 | ||||||
| @ -12,22 +12,42 @@ await migrate(); | |||||||
| await db.delete(shows); | await db.delete(shows); | ||||||
| await db.delete(profiles); | await db.delete(profiles); | ||||||
| 
 | 
 | ||||||
| const [__, ser] = await createSerie(madeInAbyss); | const [_, ser] = await createSerie(madeInAbyss); | ||||||
| console.log(ser); | const [__, mov] = await createMovie(bubble); | ||||||
| const [_, body] = await createVideo({ | const [resp, body] = await createVideo([ | ||||||
| 	guess: { title: "mia", season: [1], episode: [13], from: "test" }, | 	{ | ||||||
| 	part: null, | 		guess: { title: "mia", season: [1], episode: [13], from: "test" }, | ||||||
| 	path: "/video/mia s1e13.mkv", | 		part: null, | ||||||
| 	rendering: "renderingsha", | 		path: "/video/mia s1e13.mkv", | ||||||
| 	version: 1, | 		rendering: "sha2", | ||||||
| 	for: [ | 		version: 1, | ||||||
| 		{ | 		for: [{ slug: `${madeInAbyss.slug}-s1e13` }], | ||||||
| 			serie: madeInAbyss.slug, | 	}, | ||||||
| 			season: madeInAbyss.entries[0].seasonNumber!, | 	{ | ||||||
| 			episode: madeInAbyss.entries[0].episodeNumber!, | 		guess: { | ||||||
|  | 			title: "mia", | ||||||
|  | 			season: [2], | ||||||
|  | 			episode: [1], | ||||||
|  | 			year: [2017], | ||||||
|  | 			from: "test", | ||||||
| 		}, | 		}, | ||||||
| 	], | 		part: null, | ||||||
| }); | 		path: "/video/mia 2017 s2e1.mkv", | ||||||
|  | 		rendering: "sha8", | ||||||
|  | 		version: 1, | ||||||
|  | 		for: [{ slug: `${madeInAbyss.slug}-s2e1` }], | ||||||
|  | 	}, | ||||||
|  | 	{ | ||||||
|  | 		guess: { title: "bubble", from: "test" }, | ||||||
|  | 		part: null, | ||||||
|  | 		path: "/video/bubble.mkv", | ||||||
|  | 		rendering: "sha5", | ||||||
|  | 		version: 1, | ||||||
|  | 		for: [{ movie: bubble.slug }], | ||||||
|  | 	}, | ||||||
|  | ]); | ||||||
| console.log(body); | console.log(body); | ||||||
|  | const [___, ret] = await getVideos(); | ||||||
|  | console.log(JSON.stringify(ret, undefined, 4)); | ||||||
| 
 | 
 | ||||||
| process.exit(0); | process.exit(0); | ||||||
|  | |||||||
							
								
								
									
										154
									
								
								api/tests/videos/getdel.test.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								api/tests/videos/getdel.test.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | |||||||
|  | import { beforeAll, describe, expect, it } from "bun:test"; | ||||||
|  | import { | ||||||
|  | 	createMovie, | ||||||
|  | 	createSerie, | ||||||
|  | 	createVideo, | ||||||
|  | 	getVideos, | ||||||
|  | } from "tests/helpers"; | ||||||
|  | import { expectStatus } from "tests/utils"; | ||||||
|  | import { db } from "~/db"; | ||||||
|  | import { shows, videos } from "~/db/schema"; | ||||||
|  | import { bubble, madeInAbyss } from "~/models/examples"; | ||||||
|  | 
 | ||||||
|  | beforeAll(async () => { | ||||||
|  | 	await db.delete(shows); | ||||||
|  | 	await db.delete(videos); | ||||||
|  | 
 | ||||||
|  | 	let [ret, body] = await createSerie(madeInAbyss); | ||||||
|  | 	expectStatus(ret, body).toBe(201); | ||||||
|  | 	[ret, body] = await createMovie(bubble); | ||||||
|  | 	expectStatus(ret, body).toBe(201); | ||||||
|  | 
 | ||||||
|  | 	[ret, body] = await createVideo([ | ||||||
|  | 		{ | ||||||
|  | 			guess: { title: "mia", season: [1], episode: [13], from: "test" }, | ||||||
|  | 			part: null, | ||||||
|  | 			path: "/video/mia s1e13.mkv", | ||||||
|  | 			rendering: "sha2", | ||||||
|  | 			version: 1, | ||||||
|  | 			for: [{ slug: `${madeInAbyss.slug}-s1e13` }], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			guess: { | ||||||
|  | 				title: "mia", | ||||||
|  | 				season: [2], | ||||||
|  | 				episode: [1], | ||||||
|  | 				year: [2017], | ||||||
|  | 				from: "test", | ||||||
|  | 			}, | ||||||
|  | 			part: null, | ||||||
|  | 			path: "/video/mia 2017 s2e1.mkv", | ||||||
|  | 			rendering: "sha8", | ||||||
|  | 			version: 1, | ||||||
|  | 			for: [{ slug: `${madeInAbyss.slug}-s2e1` }], | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			guess: { title: "bubble", from: "test" }, | ||||||
|  | 			part: null, | ||||||
|  | 			path: "/video/bubble.mkv", | ||||||
|  | 			rendering: "sha5", | ||||||
|  | 			version: 1, | ||||||
|  | 			for: [{ movie: bubble.slug }], | ||||||
|  | 		}, | ||||||
|  | 	]); | ||||||
|  | 	expectStatus(ret, body).toBe(201); | ||||||
|  | 	expect(body).toBeArrayOfSize(3); | ||||||
|  | 	expect(body[0].entries).toBeArrayOfSize(1); | ||||||
|  | 	expect(body[1].entries).toBeArrayOfSize(1); | ||||||
|  | 	expect(body[2].entries).toBeArrayOfSize(1); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | describe("Video get/deletion", () => { | ||||||
|  | 	it("Get current state", async () => { | ||||||
|  | 		const [resp, body] = await getVideos(); | ||||||
|  | 		expectStatus(resp, body).toBe(200); | ||||||
|  | 		expect(body.guesses).toMatchObject({ | ||||||
|  | 			mia: { | ||||||
|  | 				unknown: { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "made-in-abyss", | ||||||
|  | 				}, | ||||||
|  | 				"2017": { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "made-in-abyss", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			bubble: { | ||||||
|  | 				unknown: { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "bubble", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	it("With unknown", async () => { | ||||||
|  | 		let [resp, body] = await createVideo({ | ||||||
|  | 			guess: { title: "mia", season: [1], episode: [13], from: "test" }, | ||||||
|  | 			part: null, | ||||||
|  | 			path: "/video/mia s1e13 unknown test.mkv", | ||||||
|  | 			rendering: "shanthnth", | ||||||
|  | 			version: 1, | ||||||
|  | 		}); | ||||||
|  | 		expectStatus(resp, body).toBe(201); | ||||||
|  | 
 | ||||||
|  | 		[resp, body] = await getVideos(); | ||||||
|  | 		expectStatus(resp, body).toBe(200); | ||||||
|  | 		expect(body.guesses).toMatchObject({ | ||||||
|  | 			mia: { | ||||||
|  | 				unknown: { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "made-in-abyss", | ||||||
|  | 				}, | ||||||
|  | 				"2017": { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "made-in-abyss", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			bubble: { | ||||||
|  | 				unknown: { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "bubble", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}); | ||||||
|  | 		expect(body.unmatched).toBeArrayOfSize(1); | ||||||
|  | 		expect(body.unmatched[0]).toBe("/video/mia s1e13 unknown test.mkv"); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	it("Mismatch title guess", async () => { | ||||||
|  | 		let [resp, body] = await createVideo({ | ||||||
|  | 			guess: { title: "mia", season: [1], episode: [13], from: "test" }, | ||||||
|  | 			part: null, | ||||||
|  | 			path: "/video/mia s1e13 mismatch.mkv", | ||||||
|  | 			rendering: "mismatch", | ||||||
|  | 			version: 1, | ||||||
|  | 			for: [{ movie: "bubble" }], | ||||||
|  | 		}); | ||||||
|  | 		expectStatus(resp, body).toBe(201); | ||||||
|  | 
 | ||||||
|  | 		[resp, body] = await getVideos(); | ||||||
|  | 		expectStatus(resp, body).toBe(200); | ||||||
|  | 		expect(body.guesses).toMatchObject({ | ||||||
|  | 			mia: { | ||||||
|  | 				unknown: { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					// take the latest slug
 | ||||||
|  | 					slug: "bubble", | ||||||
|  | 				}, | ||||||
|  | 				"2017": { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "made-in-abyss", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			bubble: { | ||||||
|  | 				unknown: { | ||||||
|  | 					id: expect.any(String), | ||||||
|  | 					slug: "bubble", | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	it.todo("Delete video", async () => {}); | ||||||
|  | }); | ||||||
| @ -8,7 +8,6 @@ import { bubble, madeInAbyss } from "~/models/examples"; | |||||||
| 
 | 
 | ||||||
| beforeAll(async () => { | beforeAll(async () => { | ||||||
| 	await db.delete(shows); | 	await db.delete(shows); | ||||||
| 	await db.delete(entries); |  | ||||||
| 	await db.delete(videos); | 	await db.delete(videos); | ||||||
| 	let [ret, body] = await createSerie(madeInAbyss); | 	let [ret, body] = await createSerie(madeInAbyss); | ||||||
| 	expectStatus(ret, body).toBe(201); | 	expectStatus(ret, body).toBe(201); | ||||||
| @ -358,7 +357,6 @@ describe("Video seeding", () => { | |||||||
| 		expect(body.message).toBeString(); | 		expect(body.message).toBeString(); | ||||||
| 	}); | 	}); | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| 	it("Two for the same entry", async () => { | 	it("Two for the same entry", async () => { | ||||||
| 		const [resp, body] = await createVideo({ | 		const [resp, body] = await createVideo({ | ||||||
| 			guess: { | 			guess: { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user