mc-publish/tests/unit/platforms/curseforge/curseforge-upload-api-client.spec.ts
2024-01-12 17:54:31 +03:00

414 lines
14 KiB
TypeScript

import { readFileSync } from "node:fs";
import { resolve } from "node:path";
import mockFs from "mock-fs";
import { createFakeFetch } from "../../../utils/fetch-utils";
import { FormData } from "@/utils/net/form-data";
import { HttpResponse } from "@/utils/net/http-response";
import { CurseForgeDependencyType } from "@/platforms/curseforge/curseforge-dependency-type";
import { CurseForgeVersionInitMetadata } from "@/platforms/curseforge/curseforge-version";
import { CURSEFORGE_UPLOAD_API_URL, CurseForgeUploadApiClient } from "@/platforms/curseforge/curseforge-upload-api-client";
const FILES = Object.freeze([
"fabric.mod.json",
"mods.toml",
"quilt.mod.json",
]) as string[];
const DB = Object.freeze({
versionTypes: Object.freeze(JSON.parse(
readFileSync(resolve(__dirname, "../../../content/curseforge/version-types.json"), "utf8")
)),
versions: Object.freeze(JSON.parse(
readFileSync(resolve(__dirname, "../../../content/curseforge/versions.json"), "utf8")
)),
projects: Object.freeze([
{
id: 1,
slug: "mod",
// 1.19, Fabric, Java 17
game_versions: [9186, 7499, 8326],
},
{
id: 2,
slug: "plugin",
// 1.19+Bukkit
game_versions: [9190],
},
{
id: 4,
slug: "resource-pack",
// 1.19
game_versions: [9186],
},
{
id: 5,
slug: "addon",
// 1.19+Addon
game_versions: [9189],
},
]),
files: Object.freeze([
{
id: 1,
name: "Mod v1.0.0",
project_id: 1,
url: "https://www.curseforge.com/api/v1/mods/1/files/1/download",
version_id: 1,
},
{
id: 2,
name: "mods.toml",
project_id: 1,
url: "https://www.curseforge.com/api/v1/mods/1/files/2/download",
version_id: 1,
},
{
id: 3,
name: "quilt.mod.json",
project_id: 1,
url: "https://www.curseforge.com/api/v1/mods/1/files/3/download",
version_id: 1,
},
{
id: 4,
name: "Plugin v1.0.0",
project_id: 2,
url: "https://www.curseforge.com/api/v1/mods/2/files/4/download",
version_id: 4,
},
{
id: 5,
name: "Resource Pack v1.0.0",
project_id: 4,
url: "https://www.curseforge.com/api/v1/mods/4/files/5/download",
version_id: 5,
},
{
id: 6,
name: "Addon v1.0.0",
project_id: 5,
url: "https://www.curseforge.com/api/v1/mods/5/files/6/download",
version_id: 6,
},
]),
});
const CURSEFORGE_FETCH = createFakeFetch({
baseUrl: CURSEFORGE_UPLOAD_API_URL,
requiredHeaders: ["X-Api-Token"],
GET: {
"^\\/game\\/version-types": () => DB.versionTypes,
"^\\/game\\/versions": () => DB.versions,
},
POST: {
"^\\/projects\\/(\\d+)\\/upload-file": ([id], { body }) => {
const project = DB.projects.find(x => x.id === +id);
if (!project) {
return HttpResponse.json({ errorCode: 404, errorMessage: `Project not found: '${id}'` }, { status: 404 });
}
const formData = body as FormData;
const fileName = formData.get("file")?.["name"] as string;
const metadata = JSON.parse(formData.get("metadata") as string) as CurseForgeVersionInitMetadata;
const dependencies = metadata.relations?.projects?.map(x => x.slug) || [];
const unknownDependency = dependencies.find(x => !DB.projects.find(y => x === y.slug));
const unknownGameVersion = metadata.gameVersions?.find(x => !project.game_versions.includes(x));
if (metadata.relations && !dependencies.length || metadata.parentFileID && dependencies.length) {
return HttpResponse.json({ errorCode: 400, errorMessage: "We don't know how to parse an empty array, so just like fuck you, lmao" }, { status: 400 });
}
if (!!metadata.gameVersions?.length === !!metadata.parentFileID) {
return HttpResponse.json({ errorCode: 400, errorMessage: "At least one game version is required" }, { status: 400 });
}
if (unknownGameVersion) {
return HttpResponse.json({ errorCode: 1009, errorMessage: `Invalid game version: '${unknownGameVersion}'` }, { status: 400 });
}
if (unknownDependency) {
return HttpResponse.json({ errorCode: 1018, errorMessage: `Invalid slug in project relations: '${unknownDependency}'` }, { status: 400 });
}
const uploadedFile = DB.files.find(x => x.project_id === project.id && [fileName, metadata.displayName].includes(x.name));
expect(uploadedFile).toBeDefined();
return HttpResponse.json(uploadedFile, { status: 200 });
},
},
});
beforeEach(() => {
const fakeFiles = FILES.reduce((a, b) => ({ ...a, [b]: "" }), {});
mockFs(fakeFiles);
});
afterEach(() => {
mockFs.restore();
});
describe("CurseForgeUploadApiClient", () => {
describe("getGameVersionTypes", () => {
test("returns game version types", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const versionTypes = await api.getGameVersionTypes();
expect(versionTypes?.length).toBeGreaterThan(0);
for (const versionType of versionTypes) {
expect(versionType).toHaveProperty("id");
expect(versionType).toHaveProperty("name");
expect(versionType).toHaveProperty("slug");
}
});
test("returned game version types contain one representing Bukkit plugins", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const versionTypes = await api.getGameVersionTypes();
expect(versionTypes).toContainEqual({
id: 1,
slug: "bukkit",
name: "Bukkit",
});
});
});
describe("getGameVersions", () => {
test("returns game versions", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const versions = await api.getGameVersions();
expect(versions?.length).toBeGreaterThan(0);
for (const version of versions) {
expect(version).toHaveProperty("id");
expect(version).toHaveProperty("gameVersionTypeID");
expect(version).toHaveProperty("name");
expect(version).toHaveProperty("slug");
}
});
});
describe("getGameVersionMap", () => {
test("returns game version map", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const map = await api.getGameVersionMap();
expect(map.environments?.length).toBeGreaterThan(0);
expect(map.game_versions?.length).toBeGreaterThan(0);
expect(map.game_versions_for_addons?.length).toBeGreaterThan(0);
expect(map.game_versions_for_plugins?.length).toBeGreaterThan(0);
expect(map.java_versions?.length).toBeGreaterThan(0);
expect(map.loaders?.length).toBeGreaterThan(0);
});
});
describe("createVersion", () => {
test("creates a new mod version", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 1;
const projectId = 1;
const name = "Mod v1.0.0";
const fileCount = 1;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
test("creates a new plugin version", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 4;
const projectId = 2;
const name = "Plugin v1.0.0";
const fileCount = 1;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
test("creates a new resource pack version", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 5;
const projectId = 4;
const name = "Resource Pack v1.0.0";
const fileCount = 1;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
test("creates a new addon version", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 6;
const projectId = 5;
const name = "Addon v1.0.0";
const fileCount = 1;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
test("creates a new version with multiple files", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 1;
const projectId = 1;
const name = "Mod v1.0.0";
const fileCount = 3;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
test("creates a new version with dependencies", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 1;
const projectId = 1;
const name = "Mod v1.0.0";
const fileCount = 3;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
dependencies: [
{ slug: "addon", type: CurseForgeDependencyType.OPTIONAL_DEPENDENCY },
{ slug: "resource-pack", type: CurseForgeDependencyType.REQUIRED_DEPENDENCY },
{ slug: "plugin", type: CurseForgeDependencyType.EMBEDDED_LIBRARY },
],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
test("creates a new version while filtering out invalid dependencies", async () => {
const api = new CurseForgeUploadApiClient({ fetch: CURSEFORGE_FETCH, token: "token" });
const expectedVersionId = 1;
const projectId = 1;
const name = "Mod v1.0.0";
const fileCount = 3;
const version = await api.createVersion({
project_id: projectId,
files: FILES.slice(0, fileCount),
name,
game_versions: ["1.19"],
loaders: ["fabric"],
java_versions: ["Java 17"],
dependencies: [
{ slug: "addon", type: CurseForgeDependencyType.OPTIONAL_DEPENDENCY },
{ slug: "addon-1", type: CurseForgeDependencyType.OPTIONAL_DEPENDENCY },
{ slug: "resource-pack", type: CurseForgeDependencyType.REQUIRED_DEPENDENCY },
{ slug: "plugin", type: CurseForgeDependencyType.EMBEDDED_LIBRARY },
{ slug: "plugin-1", type: CurseForgeDependencyType.EMBEDDED_LIBRARY },
],
});
expect(version).toEqual({
id: expectedVersionId,
project_id: projectId,
name,
files: DB.files.filter(x => x.project_id === projectId).slice(0, fileCount),
});
});
});
});