From f8343090b1b32da068d8d4d6b638f7c6174ea770 Mon Sep 17 00:00:00 2001 From: Kir_Antipov Date: Tue, 5 Jul 2022 20:44:29 +0300 Subject: [PATCH] Refactoring --- .eslintrc | 1 + src/index.ts | 14 +-- .../curseforge/curseforge-publisher.ts | 4 +- src/publishing/github/github-publisher.ts | 10 +-- src/publishing/mod-publisher.ts | 16 ++-- src/publishing/modrinth/modrinth-publisher.ts | 8 +- src/publishing/publisher-factory.ts | 2 +- src/publishing/publisher.ts | 6 +- .../{input-utils.ts => actions/input.ts} | 0 .../index.ts} | 14 +-- src/utils/{ => diagnostics}/stopwatch.ts | 0 src/utils/file-utils.ts | 36 -------- src/utils/file.ts | 33 ------- src/utils/io/file.ts | 68 ++++++++++++++ src/utils/logger.ts | 7 -- .../{logger-utils.ts => logging/logger.ts} | 9 +- src/utils/{ => logging}/logging-stopwatch.ts | 2 +- .../index.ts} | 2 +- .../minecraft-version-resolver.ts | 6 +- .../{modrinth-utils.ts => modrinth/index.ts} | 10 +-- src/utils/{function-utils.ts => retry.ts} | 2 +- src/utils/version-utils.ts | 20 ----- .../{ => versioning}/game-version-resolver.ts | 0 src/utils/versioning/version-type.ts | 19 ++++ src/utils/{ => versioning}/version.ts | 5 ++ test/file-utils.test.ts | 28 ------ .../metadata}/dependency-kind.test.ts | 2 +- .../mod-metadata-reader-factory.test.ts | 4 +- .../metadata}/mod-metadata-reader.test.ts | 88 +++++++++---------- .../publishing}/publisher-factory.test.ts | 10 +-- .../utils/actions/input.test.ts} | 10 +-- .../utils/curseforge/index.test.ts} | 4 +- .../utils/diagnostics}/stopwatch.test.ts | 4 +- test/unit-tests/utils/io/file.test.ts | 30 +++++++ .../utils/logging}/logging-stopwatch.test.ts | 6 +- .../utils/minecraft/index.test.ts} | 4 +- .../minecraft-version-resolver.test.ts | 2 +- .../utils/modrinth/modrinth.test.ts} | 10 +-- .../utils/retry.test.ts} | 4 +- test/unit-tests/utils/sleep.test.ts | 14 +++ .../utils/versioning/version-type.test.ts | 18 ++++ .../utils/versioning/version.test.ts | 17 ++++ test/version-utils.test.ts | 29 ------ 43 files changed, 302 insertions(+), 276 deletions(-) rename src/utils/{input-utils.ts => actions/input.ts} (100%) rename src/utils/{curseforge-utils.ts => curseforge/index.ts} (92%) rename src/utils/{ => diagnostics}/stopwatch.ts (100%) delete mode 100644 src/utils/file-utils.ts delete mode 100644 src/utils/file.ts create mode 100644 src/utils/io/file.ts delete mode 100644 src/utils/logger.ts rename src/utils/{logger-utils.ts => logging/logger.ts} (74%) rename src/utils/{ => logging}/logging-stopwatch.ts (96%) rename src/utils/{minecraft-utils.ts => minecraft/index.ts} (99%) rename src/utils/{ => minecraft}/minecraft-version-resolver.ts (87%) rename src/utils/{modrinth-utils.ts => modrinth/index.ts} (92%) rename src/utils/{function-utils.ts => retry.ts} (61%) delete mode 100644 src/utils/version-utils.ts rename src/utils/{ => versioning}/game-version-resolver.ts (100%) create mode 100644 src/utils/versioning/version-type.ts rename src/utils/{ => versioning}/version.ts (85%) delete mode 100644 test/file-utils.test.ts rename test/{ => unit-tests/metadata}/dependency-kind.test.ts (95%) rename test/{ => unit-tests/metadata}/mod-metadata-reader-factory.test.ts (79%) rename test/{ => unit-tests/metadata}/mod-metadata-reader.test.ts (81%) rename test/{ => unit-tests/publishing}/publisher-factory.test.ts (73%) rename test/{input-utils.test.ts => unit-tests/utils/actions/input.test.ts} (94%) rename test/{curseforge-utils.test.ts => unit-tests/utils/curseforge/index.test.ts} (98%) rename test/{ => unit-tests/utils/diagnostics}/stopwatch.test.ts (94%) create mode 100644 test/unit-tests/utils/io/file.test.ts rename test/{ => unit-tests/utils/logging}/logging-stopwatch.test.ts (93%) rename test/{minecraft-utils.test.ts => unit-tests/utils/minecraft/index.test.ts} (98%) rename test/{ => unit-tests/utils/minecraft}/minecraft-version-resolver.test.ts (98%) rename test/{modrinth-utils.test.ts => unit-tests/utils/modrinth/modrinth.test.ts} (87%) rename test/{function-utils.test.ts => unit-tests/utils/retry.test.ts} (96%) create mode 100644 test/unit-tests/utils/sleep.test.ts create mode 100644 test/unit-tests/utils/versioning/version-type.test.ts create mode 100644 test/unit-tests/utils/versioning/version.test.ts delete mode 100644 test/version-utils.test.ts diff --git a/.eslintrc b/.eslintrc index aadfdd5..03e8fe5 100644 --- a/.eslintrc +++ b/.eslintrc @@ -32,6 +32,7 @@ "@typescript-eslint/no-empty-function": ["error", { "allow": ["arrowFunctions"] }], "@typescript-eslint/no-namespace": "off", "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off", "@typescript-eslint/no-unused-vars": ["error", { "varsIgnorePattern": "^_", "argsIgnorePattern": "^_" diff --git a/src/index.ts b/src/index.ts index 879496c..6db5195 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,10 @@ -import { getRequiredFiles, gradleOutputSelector } from "./utils/file-utils"; +import File, { gradleOutputSelector } from "./utils/io/file"; import PublisherFactory from "./publishing/publisher-factory"; import PublisherTarget from "./publishing/publisher-target"; -import { getInputAsObject, mapEnumInput, mapNumberInput } from "./utils/input-utils"; -import { getDefaultLogger } from "./utils/logger-utils"; -import { retry } from "./utils/function-utils"; -import LoggingStopwatch from "./utils/logging-stopwatch"; +import { getInputAsObject, mapEnumInput, mapNumberInput } from "./utils/actions/input"; +import { getDefaultLogger } from "./utils/logging/logger"; +import retry from "./utils/retry"; +import LoggingStopwatch from "./utils/logging/logging-stopwatch"; import AggregateError from "aggregate-error"; enum FailMode { @@ -28,8 +28,8 @@ async function main() { } const options = { ...commonOptions, ...publisherOptions }; - const fileSelector = typeof options.filesPrimary === "string" ? { primary: options.filesPrimary, secondary: typeof options.filesSecondary === "string" ? options.filesSecondary : gradleOutputSelector["secondary"] } : typeof options.files === "string" ? options.files : gradleOutputSelector; - const files = await getRequiredFiles(fileSelector); + const fileSelector = typeof options.filesPrimary === "string" ? { primary: options.filesPrimary, secondary: typeof options.filesSecondary === "string" ? options.filesSecondary : gradleOutputSelector.secondary } : typeof options.files === "string" ? options.files : gradleOutputSelector; + const files = await File.getRequiredFiles(fileSelector); const retryAttempts = mapNumberInput(options.retryAttempts); const retryDelay = mapNumberInput(options.retryDelay); const failMode = mapEnumInput(options.failMode, FailMode, FailMode.Fail as FailMode); diff --git a/src/publishing/curseforge/curseforge-publisher.ts b/src/publishing/curseforge/curseforge-publisher.ts index 4eabd46..02c1ea5 100644 --- a/src/publishing/curseforge/curseforge-publisher.ts +++ b/src/publishing/curseforge/curseforge-publisher.ts @@ -1,7 +1,7 @@ -import { File } from "../../utils/file"; +import File from "../../utils/io/file"; import ModPublisher from "../mod-publisher"; import PublisherTarget from "../publisher-target"; -import { convertToCurseForgeVersions, uploadFile } from "../../utils/curseforge-utils"; +import { convertToCurseForgeVersions, uploadFile } from "../../utils/curseforge"; import Dependency from "../../metadata/dependency"; import DependencyKind from "../../metadata/dependency-kind"; diff --git a/src/publishing/github/github-publisher.ts b/src/publishing/github/github-publisher.ts index 2d80a4c..cf3802a 100644 --- a/src/publishing/github/github-publisher.ts +++ b/src/publishing/github/github-publisher.ts @@ -1,10 +1,10 @@ import PublisherTarget from "../publisher-target"; import * as github from "@actions/github"; -import { File } from "../../utils/file"; +import File from "../../utils/io/file"; import ModPublisher from "../../publishing/mod-publisher"; import Dependency from "../../metadata/dependency"; -import { mapStringInput, mapBooleanInput } from "../../utils/input-utils"; -import { VersionType } from "../../utils/version-utils"; +import { mapStringInput, mapBooleanInput } from "../../utils/actions/input"; +import VersionType from "../../utils/versioning/version-type"; import { env } from "process"; function getEnvironmentTag(): string | undefined { @@ -49,7 +49,7 @@ export default class GitHubPublisher extends ModPublisher { } const generated = !releaseId; - if (generated && (tag ??= environmentTag ?? version)) { + if (!releaseId && (tag ??= environmentTag ?? version)) { const generateChangelog = mapBooleanInput(options.generateChangelog, !changelog); const draft = mapBooleanInput(options.draft, false); const prerelease = mapBooleanInput(options.prerelease, channel !== VersionType.Release); @@ -58,7 +58,7 @@ export default class GitHubPublisher extends ModPublisher { releaseId = await this.createRelease(tag, name, changelog, generateChangelog, draft, prerelease, commitish, discussion, token); } if (!releaseId) { - throw new Error(`Cannot find or create release ${tag || `#${releaseId}`}`); + throw new Error(`Cannot find or create release ${tag}`); } const existingAssets = generated ? [] : (await octokit.rest.repos.listReleaseAssets({ ...repo, release_id: releaseId })).data; diff --git a/src/publishing/mod-publisher.ts b/src/publishing/mod-publisher.ts index 79ede3d..b2b895a 100644 --- a/src/publishing/mod-publisher.ts +++ b/src/publishing/mod-publisher.ts @@ -1,13 +1,13 @@ import { context } from "@actions/github"; -import { parseVersionName, parseVersionNameFromFileVersion } from "../utils/minecraft-utils"; -import { File } from "../utils/file"; -import { getFiles } from "../utils/file-utils"; +import { parseVersionName, parseVersionNameFromFileVersion } from "../utils/minecraft"; +import File from "../utils/io/file"; import Publisher from "./publisher"; import PublisherTarget from "./publisher-target"; -import MinecraftVersionResolver from "../utils/minecraft-version-resolver"; +import MinecraftVersionResolver from "../utils/minecraft/minecraft-version-resolver"; import ModMetadataReader from "../metadata/mod-metadata-reader"; import Dependency from "../metadata/dependency"; -import { parseVersionFromName, parseVersionTypeFromName } from "../utils/version-utils"; +import Version from "../utils/versioning/version"; +import VersionType from "../utils/versioning/version-type"; import DependencyKind from "../metadata/dependency-kind"; import path from "path"; @@ -46,7 +46,7 @@ function processDependenciesInput(input: string | string[], inputSplitter?: RegE } async function readChangelog(changelogPath: string): Promise { - const file = (await getFiles(changelogPath))[0]; + const file = (await File.getFiles(changelogPath))[0]; if (!file) { throw new Error("Changelog file was not found"); } @@ -87,8 +87,8 @@ export default abstract class ModPublisher extends PublisherreleaseInfo?.tag_name || metadata?.version || parseVersionFromName(filename); - const versionType = options.versionType?.toLowerCase() || parseVersionTypeFromName(metadata?.version || filename); + const version = (typeof options.version === "string" && options.version) || releaseInfo?.tag_name || metadata?.version || Version.fromName(filename); + const versionType = options.versionType?.toLowerCase() || VersionType.fromName(metadata?.version || filename); const name = typeof options.name === "string" ? options.name : (releaseInfo?.name || version); const changelog = typeof options.changelog === "string" ? options.changelog diff --git a/src/publishing/modrinth/modrinth-publisher.ts b/src/publishing/modrinth/modrinth-publisher.ts index 6cf9632..5d6f475 100644 --- a/src/publishing/modrinth/modrinth-publisher.ts +++ b/src/publishing/modrinth/modrinth-publisher.ts @@ -1,11 +1,11 @@ -import { createVersion, getProject, getVersions, modifyVersion } from "../../utils/modrinth-utils"; -import { File } from "../../utils/file"; +import { createVersion, getProject, getVersions, modifyVersion } from "../../utils/modrinth"; +import File from "../../utils/io/file"; import ModPublisher from "../mod-publisher"; import PublisherTarget from "../publisher-target"; import Dependency from "../../metadata/dependency"; import DependencyKind from "../../metadata/dependency-kind"; -import { mapBooleanInput, mapEnumInput } from "../../utils/input-utils"; -import LoggingStopwatch from "../../utils/logging-stopwatch"; +import { mapBooleanInput, mapEnumInput } from "../../utils/actions/input"; +import LoggingStopwatch from "../../utils/logging/logging-stopwatch"; enum UnfeatureMode { None = 0, diff --git a/src/publishing/publisher-factory.ts b/src/publishing/publisher-factory.ts index dea946e..e36047d 100644 --- a/src/publishing/publisher-factory.ts +++ b/src/publishing/publisher-factory.ts @@ -3,7 +3,7 @@ import PublisherTarget from "./publisher-target"; import GitHubPublisher from "./github/github-publisher"; import ModrinthPublisher from "./modrinth/modrinth-publisher"; import CurseForgePublisher from "./curseforge/curseforge-publisher"; -import Logger from "../utils/logger"; +import Logger from "../utils/logging/logger"; export default class PublisherFactory { public create(target: PublisherTarget, logger?: Logger): Publisher { diff --git a/src/publishing/publisher.ts b/src/publishing/publisher.ts index 1f58aba..a1fb620 100644 --- a/src/publishing/publisher.ts +++ b/src/publishing/publisher.ts @@ -1,6 +1,6 @@ -import { File } from "../utils/file"; -import Logger from "../utils/logger"; -import { getEmptyLogger } from "../utils/logger-utils"; +import File from "../utils/io/file"; +import Logger from "../utils/logging/logger"; +import { getEmptyLogger } from "../utils/logging/logger"; import PublisherTarget from "./publisher-target"; export default abstract class Publisher { diff --git a/src/utils/input-utils.ts b/src/utils/actions/input.ts similarity index 100% rename from src/utils/input-utils.ts rename to src/utils/actions/input.ts diff --git a/src/utils/curseforge-utils.ts b/src/utils/curseforge/index.ts similarity index 92% rename from src/utils/curseforge-utils.ts rename to src/utils/curseforge/index.ts index b2c76bb..60025ff 100644 --- a/src/utils/curseforge-utils.ts +++ b/src/utils/curseforge/index.ts @@ -1,8 +1,8 @@ import fetch from "node-fetch"; import FormData from "form-data"; -import { File } from "./file"; -import { findVersionByName } from "./minecraft-utils"; -import SoftError from "./soft-error"; +import File from "../io/file"; +import { findVersionByName } from "../minecraft"; +import SoftError from "../soft-error"; const baseUrl = "https://minecraft.curseforge.com/api"; @@ -36,8 +36,8 @@ class CurseForgeUploadError extends SoftError { async function fetchJsonArray(url: string): Promise { const response = await fetch(url); if (!response.ok) { - const isServerError = response.status >= 500; - throw new SoftError(isServerError, `${response.status} (${response.statusText})`); + const isSoft = response.status === 429 || response.status >= 500; + throw new SoftError(isSoft, `${response.status} (${response.statusText})`); } let array: T[]; @@ -145,8 +145,8 @@ export async function uploadFile(id: string, data: Record, file: Fi info = await response.json(); errorText += `, ${JSON.stringify(info)}`; } catch { } - const isServerError = response.status >= 500; - throw new CurseForgeUploadError(isServerError, `Failed to upload file: ${response.status} (${errorText})`, info); + const isSoftError = response.status === 429 || response.status >= 500; + throw new CurseForgeUploadError(isSoftError, `Failed to upload file: ${response.status} (${errorText})`, info); } return (<{ id: number }>await response.json()).id; diff --git a/src/utils/stopwatch.ts b/src/utils/diagnostics/stopwatch.ts similarity index 100% rename from src/utils/stopwatch.ts rename to src/utils/diagnostics/stopwatch.ts diff --git a/src/utils/file-utils.ts b/src/utils/file-utils.ts deleted file mode 100644 index c8ddef9..0000000 --- a/src/utils/file-utils.ts +++ /dev/null @@ -1,36 +0,0 @@ -import glob from "fast-glob"; -import { File } from "./file"; - -export type FileSelector = string | { primary?: string, secondary?: string }; - -export const gradleOutputSelector: FileSelector = { - primary: "build/libs/!(*-@(dev|sources)).jar", - secondary: "build/libs/*-@(dev|sources).jar" -}; - -export async function getRequiredFiles(files: FileSelector): Promise { - const foundFiles = await getFiles(files); - if (foundFiles && foundFiles.length) { - return foundFiles; - } - throw new Error(`Specified files ('${typeof files === "string" ? files : [files.primary, files.secondary].filter(x => x).join(", ")}') were not found`); -} - -export async function getFiles(files: FileSelector): Promise { - if (!files || typeof files !== "string" && !files.primary && !files.secondary) { - return []; - } - - if (typeof files === "string") { - return (await glob(files)).map(x => new File(x)); - } - - let results = []; - if (files.primary) { - results = (await glob(files.primary)).map(x => new File(x)); - } - if (files.secondary) { - results = results.concat((await glob(files.secondary)).map(x => new File(x))); - } - return results.filter((x, i, self) => self.findIndex(y => x.equals(y)) === i); -} diff --git a/src/utils/file.ts b/src/utils/file.ts deleted file mode 100644 index ee936f4..0000000 --- a/src/utils/file.ts +++ /dev/null @@ -1,33 +0,0 @@ -import fs from "fs"; -import path from "path"; - -export class File { - public name: string; - public path: string; - - public constructor(filePath: string) { - this.name = path.basename(filePath); - this.path = filePath; - Object.freeze(this); - } - - public getStream(): fs.ReadStream { - return fs.createReadStream(this.path); - } - - public async getBuffer(): Promise { - return new Promise((resolve, reject) => { - fs.readFile(this.path, (error, data) => { - if (error) { - reject(error); - } else { - resolve(data); - } - }) - }); - } - - public equals(file: unknown): boolean { - return file instanceof File && file.path === this.path; - } -} diff --git a/src/utils/io/file.ts b/src/utils/io/file.ts new file mode 100644 index 0000000..b722d80 --- /dev/null +++ b/src/utils/io/file.ts @@ -0,0 +1,68 @@ +import fs from "fs"; +import path from "path"; +import glob from "fast-glob"; + +export type FileSelector = string | { primary?: string, secondary?: string }; + +export const gradleOutputSelector = { + primary: "build/libs/!(*-@(dev|sources)).jar", + secondary: "build/libs/*-@(dev|sources).jar" +}; + +export default class File { + public name: string; + public path: string; + + public constructor(filePath: string) { + this.name = path.basename(filePath); + this.path = filePath; + Object.freeze(this); + } + + public getStream(): fs.ReadStream { + return fs.createReadStream(this.path); + } + + public async getBuffer(): Promise { + return new Promise((resolve, reject) => { + fs.readFile(this.path, (error, data) => { + if (error) { + reject(error); + } else { + resolve(data); + } + }) + }); + } + + public equals(file: unknown): boolean { + return file instanceof File && file.path === this.path; + } + + public static async getFiles(files: FileSelector): Promise { + if (!files || typeof files !== "string" && !files.primary && !files.secondary) { + return []; + } + + if (typeof files === "string") { + return (await glob(files)).map(x => new File(x)); + } + + let results = []; + if (files.primary) { + results = (await glob(files.primary)).map(x => new File(x)); + } + if (files.secondary) { + results = results.concat((await glob(files.secondary)).map(x => new File(x))); + } + return results.filter((x, i, self) => self.findIndex(y => x.equals(y)) === i); + } + + public static async getRequiredFiles(files: FileSelector): Promise { + const foundFiles = await File.getFiles(files); + if (foundFiles && foundFiles.length) { + return foundFiles; + } + throw new Error(`Specified files ('${typeof files === "string" ? files : [files.primary, files.secondary].filter(x => x).join(", ")}') were not found`); + } +} diff --git a/src/utils/logger.ts b/src/utils/logger.ts deleted file mode 100644 index 7ea82db..0000000 --- a/src/utils/logger.ts +++ /dev/null @@ -1,7 +0,0 @@ -export default interface Logger { - fatal(message: string | Error): void; - error(message: string | Error): void; - warn(message: string | Error): void; - info(message: string | Error): void; - debug(message: string | Error): void; -} diff --git a/src/utils/logger-utils.ts b/src/utils/logging/logger.ts similarity index 74% rename from src/utils/logger-utils.ts rename to src/utils/logging/logger.ts index a755b22..c7c65fd 100644 --- a/src/utils/logger-utils.ts +++ b/src/utils/logging/logger.ts @@ -1,6 +1,13 @@ import * as actions from "@actions/core"; import * as console from "console"; -import Logger from "./logger"; + +export default interface Logger { + fatal(message: string | Error): void; + error(message: string | Error): void; + warn(message: string | Error): void; + info(message: string | Error): void; + debug(message: string | Error): void; +} export function getDefaultLogger(): Logger { return { diff --git a/src/utils/logging-stopwatch.ts b/src/utils/logging/logging-stopwatch.ts similarity index 96% rename from src/utils/logging-stopwatch.ts rename to src/utils/logging/logging-stopwatch.ts index 3a653a2..cf79457 100644 --- a/src/utils/logging-stopwatch.ts +++ b/src/utils/logging/logging-stopwatch.ts @@ -1,5 +1,5 @@ import Logger from "./logger"; -import Stopwatch from "./stopwatch"; +import Stopwatch from "../diagnostics/stopwatch"; interface StartCallback { (currentDate: Date, stopwatch: Stopwatch): string; diff --git a/src/utils/minecraft-utils.ts b/src/utils/minecraft/index.ts similarity index 99% rename from src/utils/minecraft-utils.ts rename to src/utils/minecraft/index.ts index a807c25..68eabad 100644 --- a/src/utils/minecraft-utils.ts +++ b/src/utils/minecraft/index.ts @@ -1,5 +1,5 @@ import fetch from "node-fetch"; -import Version from "./version"; +import Version from "../versioning/version"; export enum MinecraftVersionType { Release = "release", diff --git a/src/utils/minecraft-version-resolver.ts b/src/utils/minecraft/minecraft-version-resolver.ts similarity index 87% rename from src/utils/minecraft-version-resolver.ts rename to src/utils/minecraft/minecraft-version-resolver.ts index d0d3b83..2875a2f 100644 --- a/src/utils/minecraft-version-resolver.ts +++ b/src/utils/minecraft/minecraft-version-resolver.ts @@ -1,6 +1,6 @@ -import GameVersionResolver from "./game-version-resolver"; -import { getCompatibleBuilds, MinecraftVersion } from "./minecraft-utils"; -import Version from "./version"; +import GameVersionResolver from "../versioning/game-version-resolver"; +import { getCompatibleBuilds, MinecraftVersion } from "."; +import Version from "../versioning/version"; export default class MinecraftVersionResolver extends GameVersionResolver { public static readonly exact = new MinecraftVersionResolver((n, v) => [v.find(x => x.version.equals(n))].filter(x => x)); diff --git a/src/utils/modrinth-utils.ts b/src/utils/modrinth/index.ts similarity index 92% rename from src/utils/modrinth-utils.ts rename to src/utils/modrinth/index.ts index bee07c1..9200d3c 100644 --- a/src/utils/modrinth-utils.ts +++ b/src/utils/modrinth/index.ts @@ -1,8 +1,8 @@ import FormData from "form-data"; import fetch, { Response } from "node-fetch"; import { URLSearchParams } from "url"; -import { File } from "./file"; -import SoftError from "./soft-error"; +import File from "../io/file"; +import SoftError from "../soft-error"; const baseUrl = "https://api.modrinth.com/v2"; @@ -100,10 +100,10 @@ async function processResponse(response: Response | Promise, mapper errorText += `, ${await response.text()}`; } catch { } errorText = `${response.status} (${errorText})`; - const isServerError = response.status >= 500; + const isSoftError = response.status === 429 || response.status >= 500; if (errorFactory) { - throw errorFactory(isServerError, errorText, response); + throw errorFactory(isSoftError, errorText, response); } else { - throw new SoftError(isServerError, errorText); + throw new SoftError(isSoftError, errorText); } } diff --git a/src/utils/function-utils.ts b/src/utils/retry.ts similarity index 61% rename from src/utils/function-utils.ts rename to src/utils/retry.ts index bf90622..f920ed7 100644 --- a/src/utils/function-utils.ts +++ b/src/utils/retry.ts @@ -1,6 +1,6 @@ import sleep from "./sleep"; -export async function retry({ func, delay = 0, maxAttempts = -1, softErrorPredicate, errorCallback }: { func: () => T | Promise, delay?: number, maxAttempts?: number, softErrorPredicate?: (error: unknown) => boolean, errorCallback?: (error: unknown) => void }): Promise { +export default async function retry({ func, delay = 0, maxAttempts = -1, softErrorPredicate, errorCallback }: { func: () => T | Promise, delay?: number, maxAttempts?: number, softErrorPredicate?: (error: unknown) => boolean, errorCallback?: (error: unknown) => void }): Promise { let attempts = 0; while (true) { try { diff --git a/src/utils/version-utils.ts b/src/utils/version-utils.ts deleted file mode 100644 index 1f71da4..0000000 --- a/src/utils/version-utils.ts +++ /dev/null @@ -1,20 +0,0 @@ -export enum VersionType { - Alpha = "alpha", - Beta = "beta", - Release = "release", -} - -export function parseVersionFromName(name: string): string { - const match = name.match(/[a-z]{0,2}\d+\.\d+.*/i); - return match ? match[0] : name; -} - -export function parseVersionTypeFromName(name: string): VersionType { - if (name.match(/[+-_]alpha/i)) { - return VersionType.Alpha; - } else if (name.match(/[+-_]beta/i)) { - return VersionType.Beta; - } else { - return VersionType.Release; - } -} diff --git a/src/utils/game-version-resolver.ts b/src/utils/versioning/game-version-resolver.ts similarity index 100% rename from src/utils/game-version-resolver.ts rename to src/utils/versioning/game-version-resolver.ts diff --git a/src/utils/versioning/version-type.ts b/src/utils/versioning/version-type.ts new file mode 100644 index 0000000..37addf0 --- /dev/null +++ b/src/utils/versioning/version-type.ts @@ -0,0 +1,19 @@ +enum VersionType { + Alpha = "alpha", + Beta = "beta", + Release = "release" +} + +namespace VersionType { + export function fromName(name: string): VersionType { + if (name.match(/[+-_]alpha/i)) { + return VersionType.Alpha; + } else if (name.match(/[+-_]beta/i)) { + return VersionType.Beta; + } else { + return VersionType.Release; + } + } +} + +export default VersionType; diff --git a/src/utils/version.ts b/src/utils/versioning/version.ts similarity index 85% rename from src/utils/version.ts rename to src/utils/versioning/version.ts index d37782e..0fdb07a 100644 --- a/src/utils/version.ts +++ b/src/utils/versioning/version.ts @@ -23,4 +23,9 @@ export default class Version { } return typeof version === "string" && this.equals(new Version(version)); } + + public static fromName(name: string): string { + const match = name.match(/[a-z]{0,2}\d+\.\d+.*/i); + return match ? match[0] : name; + } } diff --git a/test/file-utils.test.ts b/test/file-utils.test.ts deleted file mode 100644 index 3223b26..0000000 --- a/test/file-utils.test.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { describe, test, expect } from "@jest/globals"; -import { getFiles, getRequiredFiles } from "../src/utils/file-utils"; - -describe("getFiles", () => { - test("all files matching the given pattern are returned", async () => { - expect(await getFiles("(README|LICENSE|FOO).md")).toHaveLength(2); - }); - - test("files matching the primary pattern are returned first", async () => { - const files = await getFiles({ primary: "README.md", secondary: "(README|LICENSE|FOO).md" }); - expect(files).toHaveLength(2); - expect(files[0]).toHaveProperty("name", "README.md"); - - const inversedFiles = await getFiles({ primary: "LICENSE.md", secondary: "(README|LICENSE|FOO).md" }); - expect(inversedFiles).toHaveLength(2); - expect(inversedFiles[0]).toHaveProperty("name", "LICENSE.md"); - }); -}); - -describe("getRequiredFiles", () => { - test("all files matching the given pattern are returned", async () => { - expect(await getRequiredFiles("(README|LICENSE|FOO).md")).toHaveLength(2); - }); - - test("an error is thrown if no files are found", async () => { - await expect(getRequiredFiles("FOO.md")).rejects.toBeInstanceOf(Error); - }); -}); diff --git a/test/dependency-kind.test.ts b/test/unit-tests/metadata/dependency-kind.test.ts similarity index 95% rename from test/dependency-kind.test.ts rename to test/unit-tests/metadata/dependency-kind.test.ts index 30980e2..b1d1ad6 100644 --- a/test/dependency-kind.test.ts +++ b/test/unit-tests/metadata/dependency-kind.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "@jest/globals"; -import DependencyKind from "../src/metadata/dependency-kind"; +import DependencyKind from "../../../src/metadata/dependency-kind"; describe("DependencyKind.getValues", () => { test("all DependencyKind values are returned", () => { diff --git a/test/mod-metadata-reader-factory.test.ts b/test/unit-tests/metadata/mod-metadata-reader-factory.test.ts similarity index 79% rename from test/mod-metadata-reader-factory.test.ts rename to test/unit-tests/metadata/mod-metadata-reader-factory.test.ts index 14de1d4..a13e457 100644 --- a/test/mod-metadata-reader-factory.test.ts +++ b/test/unit-tests/metadata/mod-metadata-reader-factory.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from "@jest/globals"; -import ModLoaderType from "../src/metadata/mod-loader-type"; -import ModMetadataReaderFactory from "../src/metadata/mod-metadata-reader-factory"; +import ModLoaderType from "../../../src/metadata/mod-loader-type"; +import ModMetadataReaderFactory from "../../../src/metadata/mod-metadata-reader-factory"; describe("ModMetadataReaderFactory.create", () => { test("factory can create metadata reader for every ModLoaderType value", () => { diff --git a/test/mod-metadata-reader.test.ts b/test/unit-tests/metadata/mod-metadata-reader.test.ts similarity index 81% rename from test/mod-metadata-reader.test.ts rename to test/unit-tests/metadata/mod-metadata-reader.test.ts index fab477b..ab61ebd 100644 --- a/test/mod-metadata-reader.test.ts +++ b/test/unit-tests/metadata/mod-metadata-reader.test.ts @@ -1,8 +1,8 @@ import { describe, test, expect, beforeAll, afterAll } from "@jest/globals"; -import Dependency from "../src/metadata/dependency"; -import DependencyKind from "../src/metadata/dependency-kind"; -import ModMetadataReader from "../src/metadata/mod-metadata-reader"; -import PublisherTarget from "../src/publishing/publisher-target"; +import Dependency from "../../../src/metadata/dependency"; +import DependencyKind from "../../../src/metadata/dependency-kind"; +import ModMetadataReader from "../../../src/metadata/mod-metadata-reader"; +import PublisherTarget from "../../../src/publishing/publisher-target"; import { ZipFile } from "yazl"; import fs from "fs"; @@ -23,22 +23,22 @@ describe("ModMetadataReader.readMetadata", () => { }); test("mod info can be read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; expect(metadata.id).toBe("example-mod"); expect(metadata.name).toBe("Example Mod"); expect(metadata.version).toBe("0.1.0"); - expect(metadata.loaders).toMatchObject(["fabric"]); + expect(metadata.loaders).toMatchObject(["fabric"] as any); }); test("project ids can be specified in the config file", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; expect(metadata.getProjectId(PublisherTarget.Modrinth)).toBe("AANobbMI"); expect(metadata.getProjectId(PublisherTarget.CurseForge)).toBe("394468"); expect(metadata.getProjectId(PublisherTarget.GitHub)).toBe("mc1.18-0.4.0-alpha5"); }); test("all dependencies are read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; expect(metadata.dependencies).toHaveLength(9); const dependencies = metadata.dependencies.reduce((agg, x) => { agg[x.id] = x; return agg; }, >{}); expect(dependencies["fabricloader"]?.kind).toBe(DependencyKind.Depends); @@ -53,8 +53,8 @@ describe("ModMetadataReader.readMetadata", () => { }); test("dependency info can be read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); - const conflicting = metadata.dependencies.find(x => x.id === "conflicting-mod"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; + const conflicting = metadata.dependencies.find(x => x.id === "conflicting-mod")!; expect(conflicting).toBeTruthy(); expect(conflicting.id).toBe("conflicting-mod"); expect(conflicting.kind).toBe(DependencyKind.Conflicts); @@ -66,8 +66,8 @@ describe("ModMetadataReader.readMetadata", () => { }); test("custom metadata can be attached to dependency entry", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); - const recommended = metadata.dependencies.find(x => x.id === "recommended-mod"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; + const recommended = metadata.dependencies.find(x => x.id === "recommended-mod")!; expect(recommended).toBeTruthy(); expect(recommended.id).toBe("recommended-mod"); expect(recommended.kind).toBe(DependencyKind.Recommends); @@ -79,15 +79,15 @@ describe("ModMetadataReader.readMetadata", () => { }); test("special case dependencies (minecraft, java and fabricloader) are ignored by default", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); - expect(metadata.dependencies.find(x => x.id === "minecraft").ignore).toBe(true); - expect(metadata.dependencies.find(x => x.id === "java").ignore).toBe(true); - expect(metadata.dependencies.find(x => x.id === "fabricloader").ignore).toBe(true); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; + expect(metadata.dependencies.find(x => x.id === "minecraft")!.ignore).toBe(true); + expect(metadata.dependencies.find(x => x.id === "java")!.ignore).toBe(true); + expect(metadata.dependencies.find(x => x.id === "fabricloader")!.ignore).toBe(true); }); test("special case dependencies (fabric) are replaced with their aliases", async() => { - const metadata = await ModMetadataReader.readMetadata("example-mod.fabric.jar"); - const fabric = metadata.dependencies.find(x => x.id === "fabric"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.fabric.jar"))!; + const fabric = metadata.dependencies.find(x => x.id === "fabric")!; for (const target of PublisherTarget.getValues()) { expect(fabric.getProjectSlug(target) === "fabric-api"); } @@ -110,22 +110,22 @@ describe("ModMetadataReader.readMetadata", () => { }); test("mod info can be read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.forge.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.forge.jar"))!; expect(metadata.id).toBe("example-mod"); expect(metadata.name).toBe("Example Mod"); expect(metadata.version).toBe("0.1.0"); - expect(metadata.loaders).toMatchObject(["forge"]); + expect(metadata.loaders).toMatchObject(["forge"] as any); }); test("project ids can be specified in the config file", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.forge.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.forge.jar"))!; expect(metadata.getProjectId(PublisherTarget.Modrinth)).toBe("AANobbMI"); expect(metadata.getProjectId(PublisherTarget.CurseForge)).toBe("394468"); expect(metadata.getProjectId(PublisherTarget.GitHub)).toBe("mc1.18-0.4.0-alpha5"); }); test("all dependencies are read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.forge.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.forge.jar"))!; expect(metadata.dependencies).toHaveLength(6); const dependencies = metadata.dependencies.reduce((agg, x) => { agg[x.id] = x; return agg; }, >{}); expect(dependencies["forge"]?.kind).toBe(DependencyKind.Depends); @@ -137,8 +137,8 @@ describe("ModMetadataReader.readMetadata", () => { }); test("dependency info can be read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.forge.jar"); - const included = metadata.dependencies.find(x => x.id === "included-mod"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.forge.jar"))!; + const included = metadata.dependencies.find(x => x.id === "included-mod")!; expect(included).toBeTruthy(); expect(included.id).toBe("included-mod"); expect(included.kind).toBe(DependencyKind.Includes); @@ -150,8 +150,8 @@ describe("ModMetadataReader.readMetadata", () => { }); test("custom metadata can be attached to dependency entry", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.forge.jar"); - const recommended = metadata.dependencies.find(x => x.id === "recommended-mod"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.forge.jar"))!; + const recommended = metadata.dependencies.find(x => x.id === "recommended-mod")!; expect(recommended).toBeTruthy(); expect(recommended.id).toBe("recommended-mod"); expect(recommended.kind).toBe(DependencyKind.Recommends); @@ -163,10 +163,10 @@ describe("ModMetadataReader.readMetadata", () => { }); test("special case dependencies (minecraft, java and forge) are ignored by default", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.forge.jar"); - expect(metadata.dependencies.find(x => x.id === "minecraft").ignore).toBe(true); - expect(metadata.dependencies.find(x => x.id === "java").ignore).toBe(true); - expect(metadata.dependencies.find(x => x.id === "forge").ignore).toBe(true); + const metadata = (await ModMetadataReader.readMetadata("example-mod.forge.jar"))!; + expect(metadata.dependencies.find(x => x.id === "minecraft")!.ignore).toBe(true); + expect(metadata.dependencies.find(x => x.id === "java")!.ignore).toBe(true); + expect(metadata.dependencies.find(x => x.id === "forge")!.ignore).toBe(true); }); }); @@ -186,22 +186,22 @@ describe("ModMetadataReader.readMetadata", () => { }); test("mod info can be read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; expect(metadata.id).toBe("example-mod"); expect(metadata.name).toBe("Example Mod"); expect(metadata.version).toBe("0.1.0"); - expect(metadata.loaders).toMatchObject(["quilt"]); + expect(metadata.loaders).toMatchObject(["quilt"] as any); }); test("project ids can be specified in the config file", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; expect(metadata.getProjectId(PublisherTarget.Modrinth)).toBe("AANobbMI"); expect(metadata.getProjectId(PublisherTarget.CurseForge)).toBe("394468"); expect(metadata.getProjectId(PublisherTarget.GitHub)).toBe("mc1.18-0.4.0-alpha5"); }); test("all dependencies are read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; expect(metadata.dependencies).toHaveLength(8); const dependencies = metadata.dependencies.reduce((agg, x) => { agg[x.id] = x; return agg; }, >{}); expect(dependencies["quilt_loader"]?.kind).toBe(DependencyKind.Depends); @@ -215,8 +215,8 @@ describe("ModMetadataReader.readMetadata", () => { }); test("dependency info can be read", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); - const conflicting = metadata.dependencies.find(x => x.id === "conflicting-mod"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; + const conflicting = metadata.dependencies.find(x => x.id === "conflicting-mod")!; expect(conflicting).toBeTruthy(); expect(conflicting.id).toBe("conflicting-mod"); expect(conflicting.kind).toBe(DependencyKind.Conflicts); @@ -228,8 +228,8 @@ describe("ModMetadataReader.readMetadata", () => { }); test("custom metadata can be attached to dependency entry", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); - const recommended = metadata.dependencies.find(x => x.id === "recommended-mod"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; + const recommended = metadata.dependencies.find(x => x.id === "recommended-mod")!; expect(recommended).toBeTruthy(); expect(recommended.id).toBe("recommended-mod"); expect(recommended.kind).toBe(DependencyKind.Recommends); @@ -241,15 +241,15 @@ describe("ModMetadataReader.readMetadata", () => { }); test("special case dependencies (minecraft, java and quilt_loader) are ignored by default", async () => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); - expect(metadata.dependencies.find(x => x.id === "minecraft").ignore).toBe(true); - expect(metadata.dependencies.find(x => x.id === "java").ignore).toBe(true); - expect(metadata.dependencies.find(x => x.id === "quilt_loader").ignore).toBe(true); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; + expect(metadata.dependencies.find(x => x.id === "minecraft")!.ignore).toBe(true); + expect(metadata.dependencies.find(x => x.id === "java")!.ignore).toBe(true); + expect(metadata.dependencies.find(x => x.id === "quilt_loader")!.ignore).toBe(true); }); test("special case dependencies (quilted_quilt_api) are replaced with their aliases", async() => { - const metadata = await ModMetadataReader.readMetadata("example-mod.quilt.jar"); - const quilt = metadata.dependencies.find(x => x.id === "quilt_base"); + const metadata = (await ModMetadataReader.readMetadata("example-mod.quilt.jar"))!; + const quilt = metadata.dependencies.find(x => x.id === "quilt_base")!; for (const target of PublisherTarget.getValues()) { expect(quilt.getProjectSlug(target) === "qsl"); } diff --git a/test/publisher-factory.test.ts b/test/unit-tests/publishing/publisher-factory.test.ts similarity index 73% rename from test/publisher-factory.test.ts rename to test/unit-tests/publishing/publisher-factory.test.ts index d7f322b..b56c964 100644 --- a/test/publisher-factory.test.ts +++ b/test/unit-tests/publishing/publisher-factory.test.ts @@ -1,7 +1,7 @@ import { describe, test, expect } from "@jest/globals"; -import PublisherFactory from "../src/publishing/publisher-factory"; -import PublisherTarget from "../src/publishing/publisher-target"; -import { getConsoleLogger } from "../src/utils/logger-utils"; +import PublisherFactory from "../../../src/publishing/publisher-factory"; +import PublisherTarget from "../../../src/publishing/publisher-target"; +import { getConsoleLogger } from "../../../src/utils/logging/logger"; describe("PublisherFactory.create", () => { test("factory can create publisher for every PublisherTarget value", () => { @@ -10,7 +10,7 @@ describe("PublisherFactory.create", () => { const logger = getConsoleLogger(); const publisher = factory.create(target, logger); expect(publisher.target).toStrictEqual(target); - expect((publisher).logger).toStrictEqual(logger); + expect((publisher as any).logger).toStrictEqual(logger); } }); @@ -19,7 +19,7 @@ describe("PublisherFactory.create", () => { for (const target of PublisherTarget.getValues()) { const publisher = factory.create(target); expect(publisher.target).toStrictEqual(target); - expect((publisher).logger).toBeTruthy(); + expect((publisher as any).logger).toBeTruthy(); } }); diff --git a/test/input-utils.test.ts b/test/unit-tests/utils/actions/input.test.ts similarity index 94% rename from test/input-utils.test.ts rename to test/unit-tests/utils/actions/input.test.ts index ce66fbc..9aacb6a 100644 --- a/test/input-utils.test.ts +++ b/test/unit-tests/utils/actions/input.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect, beforeAll, afterAll } from "@jest/globals"; -import { setupInput, unsetInput } from "./utils/input-utils"; -import { getInputAsObject, mapStringInput, mapObjectInput, mapNumberInput, mapBooleanInput, mapEnumInput } from "../src/utils/input-utils"; +import { setupInput, unsetInput } from "../../../utils/input-utils"; +import { getInputAsObject, mapStringInput, mapObjectInput, mapNumberInput, mapBooleanInput, mapEnumInput } from "../../../../src/utils/actions/input"; const defaultInput = { "boolean": true, @@ -101,10 +101,10 @@ describe("mapObjectInput", () => { const input = getInputAsObject(); expect(input["boolean"]).not.toBeUndefined(); - expect(mapObjectInput(input["boolean"], null)).toBeNull(); + expect(mapObjectInput(input["boolean"], null!)).toBeNull(); expect(input["number"]).not.toBeUndefined(); - expect(mapObjectInput(input["number"], null)).toBeNull() + expect(mapObjectInput(input["number"], null!)).toBeNull() expect(input["array"]).not.toBeUndefined(); expect(mapObjectInput(input["array"])).toBeNull() @@ -115,7 +115,7 @@ describe("mapObjectInput", () => { test("maps object values to object", () => { const input = getInputAsObject(); - expect(mapObjectInput(input["modrinth"], null)).toStrictEqual({ id: "42", token: "1234", filesPrimary: "primaryPath", filesSecondary: "secondaryPath", files: { primary: "primaryPath", secondary: "secondaryPath" } }); + expect(mapObjectInput(input["modrinth"], null!)).toStrictEqual({ id: "42", token: "1234", filesPrimary: "primaryPath", filesSecondary: "secondaryPath", files: { primary: "primaryPath", secondary: "secondaryPath" } }); }); }); diff --git a/test/curseforge-utils.test.ts b/test/unit-tests/utils/curseforge/index.test.ts similarity index 98% rename from test/curseforge-utils.test.ts rename to test/unit-tests/utils/curseforge/index.test.ts index 1c211a7..00a6968 100644 --- a/test/curseforge-utils.test.ts +++ b/test/unit-tests/utils/curseforge/index.test.ts @@ -1,6 +1,6 @@ import process from "process"; import { describe, test, expect } from "@jest/globals"; -import { unifyGameVersion, unifyJava, convertToCurseForgeVersions } from "../src/utils/curseforge-utils"; +import { unifyGameVersion, unifyJava, convertToCurseForgeVersions } from "../../../../src/utils/curseforge"; describe("unifyGameVersion", () => { test("versions in the unified format are not changed", async () => { @@ -94,7 +94,7 @@ describe("convertToCurseForgeVersions", () => { } }; - const curseForgeVersions = await convertToCurseForgeVersions(Object.keys(versions.gameVersions), Object.keys(versions.loaders), Object.keys(versions.java), process.env.CURSEFORGE_TOKEN); + const curseForgeVersions = await convertToCurseForgeVersions(Object.keys(versions.gameVersions), Object.keys(versions.loaders), Object.keys(versions.java), process.env.CURSEFORGE_TOKEN as string); const expectedIds = new Set([...Object.values(versions.gameVersions), ...Object.values(versions.loaders), ...Object.values(versions.java)]); expect(curseForgeVersions).toHaveLength(expectedIds.size); diff --git a/test/stopwatch.test.ts b/test/unit-tests/utils/diagnostics/stopwatch.test.ts similarity index 94% rename from test/stopwatch.test.ts rename to test/unit-tests/utils/diagnostics/stopwatch.test.ts index e80a1b9..53c042e 100644 --- a/test/stopwatch.test.ts +++ b/test/unit-tests/utils/diagnostics/stopwatch.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from "@jest/globals"; -import sleep from "../src/utils/sleep"; -import Stopwatch from "../src/utils/stopwatch"; +import sleep from "../../../../src/utils/sleep"; +import Stopwatch from "../../../../src/utils/diagnostics/stopwatch"; describe("Stopwatch", () => { test("base functionality of Stopwatch works", async () => { diff --git a/test/unit-tests/utils/io/file.test.ts b/test/unit-tests/utils/io/file.test.ts new file mode 100644 index 0000000..8385fde --- /dev/null +++ b/test/unit-tests/utils/io/file.test.ts @@ -0,0 +1,30 @@ +import { describe, test, expect } from "@jest/globals"; +import File from "../../../../src/utils/io/file"; + +describe("File", () => { + describe("getFiles", () => { + test("all files matching the given pattern are returned", async () => { + expect(await File.getFiles("(README|LICENSE|FOO).md")).toHaveLength(2); + }); + + test("files matching the primary pattern are returned first", async () => { + const files = await File.getFiles({ primary: "README.md", secondary: "(README|LICENSE|FOO).md" }); + expect(files).toHaveLength(2); + expect(files[0]).toHaveProperty("name", "README.md"); + + const inversedFiles = await File.getFiles({ primary: "LICENSE.md", secondary: "(README|LICENSE|FOO).md" }); + expect(inversedFiles).toHaveLength(2); + expect(inversedFiles[0]).toHaveProperty("name", "LICENSE.md"); + }); + }); + + describe("getRequiredFiles", () => { + test("all files matching the given pattern are returned", async () => { + expect(await File.getRequiredFiles("(README|LICENSE|FOO).md")).toHaveLength(2); + }); + + test("an error is thrown if no files are found", async () => { + await expect(File.getRequiredFiles("FOO.md")).rejects.toBeInstanceOf(Error); + }); + }); +}); diff --git a/test/logging-stopwatch.test.ts b/test/unit-tests/utils/logging/logging-stopwatch.test.ts similarity index 93% rename from test/logging-stopwatch.test.ts rename to test/unit-tests/utils/logging/logging-stopwatch.test.ts index 549aa93..489d468 100644 --- a/test/logging-stopwatch.test.ts +++ b/test/unit-tests/utils/logging/logging-stopwatch.test.ts @@ -1,7 +1,7 @@ import { describe, test, expect } from "@jest/globals"; -import sleep from "../src/utils/sleep"; -import Logger from "../src/utils/logger"; -import LogginStopwatch from "../src/utils/logging-stopwatch"; +import sleep from "../../../../src/utils/sleep"; +import Logger from "../../../../src/utils/logging/logger"; +import LogginStopwatch from "../../../../src/utils/logging/logging-stopwatch"; function createLogger(info?: (msg: string) => void): Logger { const notImplementedLogger = () => { diff --git a/test/minecraft-utils.test.ts b/test/unit-tests/utils/minecraft/index.test.ts similarity index 98% rename from test/minecraft-utils.test.ts rename to test/unit-tests/utils/minecraft/index.test.ts index 2559e82..66f833b 100644 --- a/test/minecraft-utils.test.ts +++ b/test/unit-tests/utils/minecraft/index.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from "@jest/globals"; -import { getVersionById, findVersionByName, isSnapshot, parseVersionName, parseVersionNameFromFileVersion, getVersions, getLatestRelease, getCompatibleBuilds } from "../src/utils/minecraft-utils"; -import Version from "../src/utils/version"; +import { getVersionById, findVersionByName, isSnapshot, parseVersionName, parseVersionNameFromFileVersion, getVersions, getLatestRelease, getCompatibleBuilds } from "../../../../src/utils/minecraft"; +import Version from "../../../../src/utils/versioning/version"; describe("getVersionById", () => { test("returned versions have the same id as the given one", async () => { diff --git a/test/minecraft-version-resolver.test.ts b/test/unit-tests/utils/minecraft/minecraft-version-resolver.test.ts similarity index 98% rename from test/minecraft-version-resolver.test.ts rename to test/unit-tests/utils/minecraft/minecraft-version-resolver.test.ts index 234024c..cf59c3f 100644 --- a/test/minecraft-version-resolver.test.ts +++ b/test/unit-tests/utils/minecraft/minecraft-version-resolver.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "@jest/globals"; -import MinecraftVersionResolver from "../src/utils/minecraft-version-resolver"; +import MinecraftVersionResolver from "../../../../src/utils/minecraft/minecraft-version-resolver"; describe("MinecraftVersionResolver.byName", () => { test("every predefined MinecraftVersionResolver can be resolved", () => { diff --git a/test/modrinth-utils.test.ts b/test/unit-tests/utils/modrinth/modrinth.test.ts similarity index 87% rename from test/modrinth-utils.test.ts rename to test/unit-tests/utils/modrinth/modrinth.test.ts index 70c6f9f..b7d0440 100644 --- a/test/modrinth-utils.test.ts +++ b/test/unit-tests/utils/modrinth/modrinth.test.ts @@ -1,5 +1,5 @@ import { describe, test, expect } from "@jest/globals"; -import { getProject, getVersions } from "../src/utils/modrinth-utils"; +import { getProject, getVersions } from "../../../../src/utils/modrinth"; const timeout = 15000; @@ -48,12 +48,12 @@ describe("getVersions", () => { }, timeout); test("returns only featured versions with featured === true", async () => { - const versions = await getVersions("terra", null, null, true); + const versions = await getVersions("terra", null!, null!, true); expect(versions.every(x => x.featured)).toBe(true); }, timeout); test("returns only unfeatured versions with featured === false", async () => { - const versions = await getVersions("terra", null, null, false); + const versions = await getVersions("terra", null!, null!, false); expect(versions.every(x => !x.featured)).toBe(true); }, timeout); @@ -66,10 +66,10 @@ describe("getVersions", () => { }, timeout); test("returns only versions that support given mc versions", async () => { - const versions_1_18_2 = await getVersions("terra", null, ["1.18.2"]); + const versions_1_18_2 = await getVersions("terra", null!, ["1.18.2"]); expect(versions_1_18_2.every(x => x.game_versions.includes("1.18.2"))).toBe(true); - const versions_1_16_5 = await getVersions("terra", null, ["1.16.5"]); + const versions_1_16_5 = await getVersions("terra", null!, ["1.16.5"]); expect(versions_1_16_5.every(x => x.game_versions.includes("1.16.5"))).toBe(true); }, timeout); }); diff --git a/test/function-utils.test.ts b/test/unit-tests/utils/retry.test.ts similarity index 96% rename from test/function-utils.test.ts rename to test/unit-tests/utils/retry.test.ts index 37308fc..5eef984 100644 --- a/test/function-utils.test.ts +++ b/test/unit-tests/utils/retry.test.ts @@ -1,6 +1,6 @@ import { describe, test, expect } from "@jest/globals"; -import { retry } from "../src/utils/function-utils"; -import SoftError from "../src/utils/soft-error"; +import retry from "../../../src/utils/retry"; +import SoftError from "../../../src/utils/soft-error"; function createThrowingFunc(attempts: number): () => true { let counter = 0; diff --git a/test/unit-tests/utils/sleep.test.ts b/test/unit-tests/utils/sleep.test.ts new file mode 100644 index 0000000..c425c38 --- /dev/null +++ b/test/unit-tests/utils/sleep.test.ts @@ -0,0 +1,14 @@ +import { describe, test, expect } from "@jest/globals"; +import sleep from "../../../src/utils/sleep"; + +describe("sleep", () => { + test("execution continues after the specified delay", async () => { + const start = new Date(); + await sleep(100); + const end = new Date(); + const duration = end.getTime() - start.getTime(); + expect(duration).toBeGreaterThan(50); + expect(duration).toBeLessThan(200); + }); +}); + diff --git a/test/unit-tests/utils/versioning/version-type.test.ts b/test/unit-tests/utils/versioning/version-type.test.ts new file mode 100644 index 0000000..554df1f --- /dev/null +++ b/test/unit-tests/utils/versioning/version-type.test.ts @@ -0,0 +1,18 @@ +import { describe, test, expect } from "@jest/globals"; +import VersionType from "../../../../src/utils/versioning/version-type"; + +describe("VersionType", () => { + describe("fromName", () => { + test("version type is correctly extracted from the filename", () => { + expect(VersionType.fromName("sodium-fabric-mc1.17.1-0.3.2+build.7")).toStrictEqual("release"); + expect(VersionType.fromName("fabric-api-0.40.1+1.18_experimental")).toStrictEqual("release"); + expect(VersionType.fromName("TechReborn-5.0.8-beta+build.111")).toStrictEqual("beta"); + expect(VersionType.fromName("TechReborn-1.17-5.0.1-beta+build.29")).toStrictEqual("beta"); + expect(VersionType.fromName("Terra-forge-5.3.3-BETA+ec3b0e5d")).toStrictEqual("beta"); + expect(VersionType.fromName("Terra-forge-5.3.3-alpha+ec3b0e5d")).toStrictEqual("alpha"); + expect(VersionType.fromName("modmenu-2.0.12")).toStrictEqual("release"); + expect(VersionType.fromName("enhancedblockentities-0.5+1.17")).toStrictEqual("release"); + expect(VersionType.fromName("sync-mc1.17.x-1.2")).toStrictEqual("release"); + }); + }); +}); diff --git a/test/unit-tests/utils/versioning/version.test.ts b/test/unit-tests/utils/versioning/version.test.ts new file mode 100644 index 0000000..e847d2c --- /dev/null +++ b/test/unit-tests/utils/versioning/version.test.ts @@ -0,0 +1,17 @@ +import { describe, test, expect } from "@jest/globals"; +import Version from "../../../../src/utils/versioning/version"; + +describe("Version", () => { + describe("fromName", () => { + test("file version is correctly extracted from the filename", () => { + expect(Version.fromName("sodium-fabric-mc1.17.1-0.3.2+build.7")).toStrictEqual("mc1.17.1-0.3.2+build.7"); + expect(Version.fromName("fabric-api-0.40.1+1.18_experimental")).toStrictEqual("0.40.1+1.18_experimental"); + expect(Version.fromName("TechReborn-5.0.8-beta+build.111")).toStrictEqual("5.0.8-beta+build.111"); + expect(Version.fromName("TechReborn-1.17-5.0.1-beta+build.29")).toStrictEqual("1.17-5.0.1-beta+build.29"); + expect(Version.fromName("Terra-forge-5.3.3-BETA+ec3b0e5d")).toStrictEqual("5.3.3-BETA+ec3b0e5d"); + expect(Version.fromName("modmenu-2.0.12")).toStrictEqual("2.0.12"); + expect(Version.fromName("enhancedblockentities-0.5+1.17")).toStrictEqual("0.5+1.17"); + expect(Version.fromName("sync-mc1.17.x-1.2")).toStrictEqual("mc1.17.x-1.2"); + }); + }); +}); diff --git a/test/version-utils.test.ts b/test/version-utils.test.ts deleted file mode 100644 index 53881c6..0000000 --- a/test/version-utils.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, test, expect } from "@jest/globals"; -import { parseVersionFromName, parseVersionTypeFromName } from "../src/utils/version-utils"; - -describe("parseVersionFromName", () => { - test("file version is correctly extracted from the filename", () => { - expect(parseVersionFromName("sodium-fabric-mc1.17.1-0.3.2+build.7")).toStrictEqual("mc1.17.1-0.3.2+build.7"); - expect(parseVersionFromName("fabric-api-0.40.1+1.18_experimental")).toStrictEqual("0.40.1+1.18_experimental"); - expect(parseVersionFromName("TechReborn-5.0.8-beta+build.111")).toStrictEqual("5.0.8-beta+build.111"); - expect(parseVersionFromName("TechReborn-1.17-5.0.1-beta+build.29")).toStrictEqual("1.17-5.0.1-beta+build.29"); - expect(parseVersionFromName("Terra-forge-5.3.3-BETA+ec3b0e5d")).toStrictEqual("5.3.3-BETA+ec3b0e5d"); - expect(parseVersionFromName("modmenu-2.0.12")).toStrictEqual("2.0.12"); - expect(parseVersionFromName("enhancedblockentities-0.5+1.17")).toStrictEqual("0.5+1.17"); - expect(parseVersionFromName("sync-mc1.17.x-1.2")).toStrictEqual("mc1.17.x-1.2"); - }); -}); - -describe("parseVersionTypeFromName", () => { - test("version type is correctly extracted from the filename", () => { - expect(parseVersionTypeFromName("sodium-fabric-mc1.17.1-0.3.2+build.7")).toStrictEqual("release"); - expect(parseVersionTypeFromName("fabric-api-0.40.1+1.18_experimental")).toStrictEqual("release"); - expect(parseVersionTypeFromName("TechReborn-5.0.8-beta+build.111")).toStrictEqual("beta"); - expect(parseVersionTypeFromName("TechReborn-1.17-5.0.1-beta+build.29")).toStrictEqual("beta"); - expect(parseVersionTypeFromName("Terra-forge-5.3.3-BETA+ec3b0e5d")).toStrictEqual("beta"); - expect(parseVersionTypeFromName("Terra-forge-5.3.3-alpha+ec3b0e5d")).toStrictEqual("alpha"); - expect(parseVersionTypeFromName("modmenu-2.0.12")).toStrictEqual("release"); - expect(parseVersionTypeFromName("enhancedblockentities-0.5+1.17")).toStrictEqual("release"); - expect(parseVersionTypeFromName("sync-mc1.17.x-1.2")).toStrictEqual("release"); - }); -});