sendou.ink/app/features/art/ArtRepository.server.test.ts
2025-11-01 14:55:23 +02:00

400 lines
9.3 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { dbInsertUsers, dbReset } from "~/utils/Test";
import * as ArtRepository from "./ArtRepository.server";
let imageCounter = 0;
const createArt = async ({ authorId }: { authorId: number }) => {
imageCounter++;
const art = await ArtRepository.insert({
authorId,
url: `https://example.com/image-${authorId}-${imageCounter}.png`,
validatedAt: Date.now(),
description: null,
linkedUsers: [],
tags: [],
});
return art.id;
};
describe("findShowcaseArts", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(5);
});
afterEach(() => {
dbReset();
});
test("shows one art per artist", async () => {
await createArt({ authorId: 1 });
await createArt({ authorId: 2 });
await createArt({ authorId: 3 });
const result = await ArtRepository.findShowcaseArts();
expect(result).toHaveLength(3);
const authorIds = result.map((art) => art.author?.discordId);
expect(new Set(authorIds).size).toBe(3);
});
test("prioritizes showcase art over regular art for same artist", async () => {
// first create art should be showcase
const id = await createArt({ authorId: 1 });
await createArt({ authorId: 1 });
const result = await ArtRepository.findShowcaseArts();
expect(result[0].id).toBe(id);
});
test("shows only one art per artist even with multiple pieces", async () => {
await createArt({ authorId: 1 });
await createArt({ authorId: 1 });
await createArt({ authorId: 1 });
const result = await ArtRepository.findShowcaseArts();
expect(result).toHaveLength(1);
});
test("shows artist even if no showcase art exists", async () => {
const showcaseArtId = await createArt({ authorId: 1 });
const nonShowcaseArtId = await createArt({ authorId: 1 });
await ArtRepository.deleteById(showcaseArtId);
const result = await ArtRepository.findShowcaseArts();
expect(result).toHaveLength(1);
expect(result[0].id).toBe(nonShowcaseArtId);
});
test("returns empty array when no art exists", async () => {
const result = await ArtRepository.findShowcaseArts();
expect(result).toHaveLength(0);
});
});
describe("findAllTags", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(1);
});
afterEach(() => {
dbReset();
});
test("returns all art tags", async () => {
imageCounter++;
await ArtRepository.insert({
authorId: 1,
url: `https://example.com/image-1-${imageCounter}.png`,
validatedAt: Date.now(),
description: null,
linkedUsers: [],
tags: [{ name: "Character" }, { name: "Weapon" }, { name: "Landscape" }],
});
const result = await ArtRepository.findAllTags();
expect(result).toHaveLength(3);
expect(result.map((t) => t.name).sort()).toEqual([
"Character",
"Landscape",
"Weapon",
]);
});
test("returns empty array when no tags exist", async () => {
const result = await ArtRepository.findAllTags();
expect(result).toHaveLength(0);
});
});
describe("unlinkUserFromArt", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(2);
});
afterEach(() => {
dbReset();
});
test("removes user link from art", async () => {
const art = await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-1.png",
validatedAt: Date.now(),
description: null,
linkedUsers: [2],
tags: [],
});
await ArtRepository.unlinkUserFromArt({ userId: 2, artId: art.id });
const result = await ArtRepository.findArtsByUserId(2, {
includeAuthored: false,
});
expect(result).toHaveLength(0);
});
});
describe("findShowcaseArtsByTag", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(3);
});
afterEach(() => {
dbReset();
});
test("returns arts filtered by tag", async () => {
const art1 = await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-1.png",
validatedAt: Date.now(),
description: null,
linkedUsers: [],
tags: [{ name: "Character" }],
});
await ArtRepository.insert({
authorId: 2,
url: "https://example.com/image-2.png",
validatedAt: Date.now(),
description: null,
linkedUsers: [],
tags: [{ name: "Weapon" }],
});
const tags = await ArtRepository.findAllTags();
const characterTag = tags.find((t) => t.name === "Character");
const result = await ArtRepository.findShowcaseArtsByTag(
characterTag?.id ?? 0,
);
expect(result).toHaveLength(1);
expect(result[0].id).toBe(art1.id);
});
test("shows only one art per artist", async () => {
await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-1.png",
validatedAt: Date.now(),
description: null,
linkedUsers: [],
tags: [{ name: "Character" }],
});
const tags = await ArtRepository.findAllTags();
const characterTag = tags.find((t) => t.name === "Character");
await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-2.png",
validatedAt: Date.now(),
description: null,
linkedUsers: [],
tags: [{ id: characterTag?.id }],
});
const result = await ArtRepository.findShowcaseArtsByTag(
characterTag?.id ?? 0,
);
expect(result).toHaveLength(1);
});
});
describe("findRecentlyUploadedArts", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(3);
});
afterEach(() => {
dbReset();
});
test("returns recently uploaded arts", async () => {
const artId = await createArt({ authorId: 1 });
const result = await ArtRepository.findRecentlyUploadedArts();
expect(result.length).toBeGreaterThan(0);
expect(result.some((art) => art.id === artId)).toBe(true);
});
});
describe("findArtsByUserId", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(3);
});
afterEach(() => {
dbReset();
});
test("returns authored art", async () => {
const artId = await createArt({ authorId: 1 });
const result = await ArtRepository.findArtsByUserId(1);
expect(result).toHaveLength(1);
expect(result[0].id).toBe(artId);
});
test("returns tagged art", async () => {
const art = await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-1.png",
validatedAt: Date.now(),
description: null,
linkedUsers: [2],
tags: [],
});
const result = await ArtRepository.findArtsByUserId(2);
expect(result).toHaveLength(1);
expect(result[0].id).toBe(art.id);
});
});
describe("deleteById", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(1);
});
afterEach(() => {
dbReset();
});
test("deletes art by id", async () => {
const artId = await createArt({ authorId: 1 });
await ArtRepository.deleteById(artId);
const result = await ArtRepository.findArtsByUserId(1);
expect(result).toHaveLength(0);
});
test("deletes only the specified art", async () => {
const firstArtId = await createArt({ authorId: 1 });
const secondArtId = await createArt({ authorId: 1 });
await ArtRepository.deleteById(firstArtId);
const result = await ArtRepository.findArtsByUserId(1);
expect(result).toHaveLength(1);
expect(result[0].id).toBe(secondArtId);
});
});
describe("insert", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(2);
});
afterEach(() => {
dbReset();
});
test("inserts art with all metadata", async () => {
const art = await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-1.png",
validatedAt: Date.now(),
description: "Test description",
linkedUsers: [2],
tags: [{ name: "Character" }],
});
const result = await ArtRepository.findArtsByUserId(1);
expect(result).toHaveLength(1);
expect(result[0].id).toBe(art.id);
expect(result[0].description).toBe("Test description");
expect(result[0].tags).toHaveLength(1);
expect(result[0].linkedUsers).toHaveLength(1);
});
test("sets first art as showcase", async () => {
await createArt({ authorId: 1 });
const result = await ArtRepository.findArtsByUserId(1);
expect(result[0].isShowcase).toBe(true);
});
});
describe("update", () => {
beforeEach(async () => {
imageCounter = 0;
await dbInsertUsers(3);
});
afterEach(() => {
dbReset();
});
test("updates art metadata", async () => {
const art = await ArtRepository.insert({
authorId: 1,
url: "https://example.com/image-1.png",
validatedAt: Date.now(),
description: "Original",
linkedUsers: [2],
tags: [{ name: "Character" }],
});
await ArtRepository.update(art.id, {
description: "Updated",
linkedUsers: [3],
tags: [{ name: "Weapon" }],
isShowcase: 1,
});
const result = await ArtRepository.findArtsByUserId(1);
expect(result[0].description).toBe("Updated");
expect(result[0].linkedUsers).toHaveLength(1);
expect(result[0].linkedUsers?.[0].id).toBe(3);
expect(result[0].tags).toHaveLength(1);
expect(result[0].tags?.[0].name).toBe("Weapon");
});
test("unsets other showcase art when setting new showcase", async () => {
const firstArtId = await createArt({ authorId: 1 });
const secondArtId = await createArt({ authorId: 1 });
await ArtRepository.update(secondArtId, {
description: null,
linkedUsers: [],
tags: [],
isShowcase: 1,
});
const result = await ArtRepository.findArtsByUserId(1);
expect(result).toHaveLength(2);
const showcaseArt = result.find((art) => art.id === secondArtId);
const nonShowcaseArt = result.find((art) => art.id === firstArtId);
expect(showcaseArt?.isShowcase).toBe(true);
expect(nonShowcaseArt?.isShowcase).toBe(false);
});
});