diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..a4bb107
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,20 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "Python: Install",
+ "type": "python",
+ "request": "launch",
+ "program": "${file}",
+ "console": "integratedTerminal",
+ "justMyCode": true,
+ "args": [
+ "install",
+ "-y"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/common/__init__.py b/src/common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/src/common/maven_coords.py b/src/common/maven_coords.py
new file mode 100644
index 0000000..f2374b9
--- /dev/null
+++ b/src/common/maven_coords.py
@@ -0,0 +1,91 @@
+# MCM-Manager: Minecraft Modpack Manager
+# Copyright (C) 2023 Tygo Everts
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from os import path
+
+
+class maven_parse:
+ def __init__(self, arg: str) -> None:
+ """
+ Parse java maven coords to a folder and a file.
+
+ Example:
+ ```txt
+ de.oceanlabs.mcp:mcp_config:1.20.1-20230612.114412@zip
+ - folder: de/oceanlabs/mcp/mcp_config/1.20.1-20230612.114412/
+ - file: mcp_config-1.20.1-20230612.114412.zip
+ ```
+
+ You can get a file path using `.maven_to_file()`
+ or an url using `.maven_to_url`.
+
+ To directly access the parsed result,
+ use the variable `.parsed`
+ """
+
+ self.parsed = self._parse(arg)
+
+ def _parse(self, arg: str) -> tuple[str, str]:
+ # Split the file extension
+ if '@' in arg:
+ arg, ext = arg.split('@', 1)
+ else:
+ ext = 'jar'
+
+ # Until the third colon, replace
+ # ':' with path.sep. Until the
+ # first, also replace '.' with it
+ colons = 0
+ folder = ''
+ for char in arg:
+ if colons == 3:
+ break
+ if char == ':':
+ colons += 1
+ char = path.sep
+ elif char == '.' and colons == 0:
+ char = path.sep
+ folder += char
+
+ if not folder[-1] == '/':
+ folder += '/'
+
+ # Select everything from the first
+ # colon and replace ':' with '-'
+ file = ''
+ for char in arg[arg.find(':')+1:]:
+ if char == ':':
+ char = '-'
+ file += char
+
+ # Add the file extension
+ file += '.' + ext
+
+ return folder, file
+
+ def to_file(self, *paths: str) -> str:
+ """
+ Create a file path from the maven coord. This calls
+ a `path.join()` with all paths plus `self.parsed`
+ """
+ return path.join(*paths, *self.parsed)
+
+ def to_url(self, base: str) -> str:
+ """Create a url from the maven coord"""
+ if not base[-1] == '/':
+ base += '/'
+
+ return ''.join((base, *self.parsed))
diff --git a/src/install/_types.py b/src/install/_types.py
index dc42d10..155883f 100644
--- a/src/install/_types.py
+++ b/src/install/_types.py
@@ -19,11 +19,11 @@
Generic, NotRequired, Optional
)
-# --- install/filesize.py --- #
+# --- filesize.py --- #
SizeSystem = list[tuple[int, str | tuple[str, str]]]
-# --- install --- #
+# --- common --- #
Client = Literal['client']
Server = Literal['server']
Side = Literal['client', 'server']
diff --git a/src/install/modloaders.py b/src/install/modloaders.py
index d0e584b..e081e31 100644
--- a/src/install/modloaders.py
+++ b/src/install/modloaders.py
@@ -27,6 +27,8 @@
from .urls import forge as forge_urls, fabric as fabric_urls
from .loadingbar import loadingbar
+from ..common.maven_coords import maven_parse
+
from ._types import (
Client, Server, Side, Modloader,
MinecraftJson, VersionJson,
@@ -169,54 +171,21 @@ def replace_arg_vars(self, arg: str, data: dict[str, str]) -> str:
# in the arg string with **data
arg = arg.format(**data)
- # Extract when paths ends with '.lzma'
- if path.normpath(arg).endswith('.lzma'):
+ # Extract when a path ends with '.lzma'
+ if arg.endswith('.lzma'):
with ZipFile(self.installer, 'r') as archive:
archive.extract(arg[1:], self.temp_dir)
return path.join(self.temp_dir, path.normpath(arg[1:]))
- # Return arg if it isn't "[something]"
+ # Return arg when it isn't "[something]"
if arg[0] != '[' and arg[-1] != ']':
return arg
# Remove the brackets
- arg = arg.replace('[', '').replace(']', '')
-
- # Split the file extension
- if '@' in arg:
- arg, file_extension = arg.split('@', 1)
- else:
- file_extension = 'jar'
-
- # Create the file and folder path
- folder = (
- # Until the first colon, replace
- # ':' and '.' with path.sep
- arg[:arg.find(':')]
- .replace(':', '.')
- .replace('.', path.sep) +
-
- # Then, do the same
- # until the third colon
- (arg[arg.find(':'):]
- if arg.count(':') != 3
- else arg[arg.find(':'):
- arg.rfind(':')]
- ).replace(':', path.sep)
- )
-
- file = (
- # Select everything from
- # the first colon and
- # replace ':' with '-'
- arg[arg.find(':')+1:]
- .replace(':', '-') +
-
- # Add the file extension
- '.' + file_extension)
+ arg = arg[1:-1]
# Return the full path
- return path.join(self.launcher_dir, 'libraries', folder, file)
+ return maven_parse(arg).to_file(self.launcher_dir, 'libraries')
def download_jar_files(self) -> None:
"""Download the jar files"""
diff --git a/src/runner.py b/src/runner.py
index f17a5e5..324e50e 100644
--- a/src/runner.py
+++ b/src/runner.py
@@ -1,3 +1,19 @@
+# MCM-Manager: Minecraft Modpack Manager
+# Copyright (C) 2023 Tygo Everts
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
from argparse import ArgumentParser
from dataclasses import dataclass, fields
from os import path, makedirs
diff --git a/tests/__init__.py b/tests/__init__.py
index 6080c6b..e69de29 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -1,50 +0,0 @@
-# MCM-Manager: Minecraft Modpack Manager
-# Copyright (C) 2023 Tygo Everts
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-
-from os import path, mkdir
-from json import dump
-from .vars import install_dir, launcher_dir
-
-# Create the install dir
-if not path.isdir(install_dir):
- mkdir(install_dir)
-
-# Create the launcher dir and example-manifest.json
-if not path.isdir(launcher_dir):
- mkdir(launcher_dir)
-
-launcher_profiles = {
- "profiles": {},
- "settings": {
- "crashAssistance": True,
- "enableAdvanced": False,
- "enableAnalytics": True,
- "enableHistorical": False,
- "enableReleases": True,
- "enableSnapshots": False,
- "keepLauncherOpen": False,
- "profileSorting": "ByLastPlayed",
- "showGameLog": False,
- "showMenu": False,
- "soundOn": False
- },
- "version": 3
-}
-
-with open(path.join(launcher_dir, 'launcher_profiles.json'), 'w') as fp:
- dump(launcher_profiles, fp, indent=4)
-
-del path, mkdir, dump, install_dir, launcher_dir, launcher_profiles, fp
diff --git a/tests/assets/launcher_profiles.json b/tests/assets/launcher_profiles.json
new file mode 100644
index 0000000..be99c01
--- /dev/null
+++ b/tests/assets/launcher_profiles.json
@@ -0,0 +1,17 @@
+{
+ "profiles": {},
+ "settings": {
+ "crashAssistance": true,
+ "enableAdvanced": false,
+ "enableAnalytics": true,
+ "enableHistorical": false,
+ "enableReleases": true,
+ "enableSnapshots": false,
+ "keepLauncherOpen": false,
+ "profileSorting": "ByLastPlayed",
+ "showGameLog": false,
+ "showMenu": false,
+ "soundOn": false
+ },
+ "version": 3
+}
\ No newline at end of file
diff --git a/tests/common/__init__.py b/tests/common/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/common/test_maven_coords.py b/tests/common/test_maven_coords.py
new file mode 100644
index 0000000..c22c313
--- /dev/null
+++ b/tests/common/test_maven_coords.py
@@ -0,0 +1,71 @@
+# MCM-Manager: Minecraft Modpack Manager
+# Copyright (C) 2023 Tygo Everts
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+import unittest
+
+from src.common.maven_coords import maven_parse
+
+
+class MavenParse(unittest.TestCase):
+ def test_maven_coords(self):
+ cases: list[tuple[str, tuple[str, str], str, str]] = [
+ ( # With three parts
+ 'net.fabricmc:fabric-loader:0.14.22', (
+ 'net/fabricmc/fabric-loader/0.14.22/',
+ 'fabric-loader-0.14.22.jar'
+ ),
+ 'libraries/net/fabricmc/fabric-loader/0.14.22/'
+ 'fabric-loader-0.14.22.jar',
+ 'https://example.com/net/fabricmc/fabric-loader'
+ '/0.14.22/fabric-loader-0.14.22.jar'
+ ),
+ ( # With four parts
+ 'net.minecraft:client:1.20.1-20230612.114412:slim', (
+ 'net/minecraft/client/1.20.1-20230612.114412/',
+ 'client-1.20.1-20230612.114412-slim.jar'
+ ),
+ 'libraries/net/minecraft/client/1.20.1-20230612.114412/'
+ 'client-1.20.1-20230612.114412-slim.jar',
+ 'https://example.com/net/minecraft/client/'
+ '1.20.1-20230612.114412/client-1.20.1-20230612.114412-slim.jar'
+ ),
+ ( # With three parts and a file extension
+ 'de.oceanlabs.mcp:mcp_config:1.20.1-20230612.114412@zip', (
+ 'de/oceanlabs/mcp/mcp_config/1.20.1-20230612.114412/',
+ 'mcp_config-1.20.1-20230612.114412.zip'
+ ),
+ 'libraries/de/oceanlabs/mcp/mcp_config/1.20.1-20230612.114412/'
+ 'mcp_config-1.20.1-20230612.114412.zip',
+ 'https://example.com/de/oceanlabs/mcp/mcp_config/'
+ '1.20.1-20230612.114412/mcp_config-1.20.1-20230612.114412.zip'
+ ),
+ ( # With four parts and a file extension
+ 'de.oceanlabs.mcp:mcp_config:1.20.1-20230612.114412:mappings@txt', (
+ 'de/oceanlabs/mcp/mcp_config/1.20.1-20230612.114412/',
+ 'mcp_config-1.20.1-20230612.114412-mappings.txt'
+ ),
+ 'libraries/de/oceanlabs/mcp/mcp_config/1.20.1-20230612.114412/'
+ 'mcp_config-1.20.1-20230612.114412-mappings.txt',
+ 'https://example.com/de/oceanlabs/mcp/mcp_config/'
+ '1.20.1-20230612.114412/mcp_config-1.20.1-20230612.114412-mappings.txt'
+ )
+ ]
+
+ for i in cases:
+ maven = maven_parse(i[0])
+ self.assertEqual(maven.parsed, i[1])
+ self.assertEqual(maven.to_file('libraries'), i[2])
+ self.assertEqual(maven.to_url('https://example.com'), i[3])
diff --git a/tests/config.py b/tests/config.py
new file mode 100644
index 0000000..b415905
--- /dev/null
+++ b/tests/config.py
@@ -0,0 +1,27 @@
+# MCM-Manager: Minecraft Modpack Manager
+# Copyright (C) 2023 Tygo Everts
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from os import path
+
+CURDIR = path.dirname(path.realpath(__file__))
+
+TMPDIR = path.join(CURDIR, 'temp')
+LAUNDIR = path.join(TMPDIR, '.minecraft')
+INSTDIR = path.join(TMPDIR, 'gamedir')
+
+ASSETDIR = path.join(CURDIR, 'assets')
+MANIFEST = path.join(ASSETDIR, 'manifest.json')
+LAUNPROF = path.join(ASSETDIR, 'launcher_profiles.json')
diff --git a/tests/vars.py b/tests/globalfuncs.py
similarity index 65%
rename from tests/vars.py
rename to tests/globalfuncs.py
index 0e688f0..e299dcd 100644
--- a/tests/vars.py
+++ b/tests/globalfuncs.py
@@ -15,10 +15,21 @@
# along with this program. If not, see .
from contextlib import redirect_stdout
-from typing import Callable, Any
-from os import path, walk, remove
+from typing import Any, Callable
+from os import path, remove, walk
from shutil import rmtree
+from .config import TMPDIR
+
+
+def empty_dir(directory: str):
+ "Empty a directory"
+ for root, dirs, files in walk(directory):
+ for f in files:
+ remove(path.join(root, f))
+ for d in dirs:
+ rmtree(path.join(root, d))
+
def quiet(func: Callable[..., Any]) -> Callable[..., Any]:
"A simple decorator function that supresses stdout"
@@ -29,19 +40,9 @@ def wrapper(*args: Any, **kwargs: Any):
return wrapper
-def empty_dir(*directories: str):
- for directory in directories:
- for root, dirs, files in walk(directory):
- for f in files:
- remove(path.join(root, f))
- for d in dirs:
- rmtree(path.join(root, d))
-
-
-launcher_dir = path.realpath(
- path.join(path.dirname(path.realpath(__file__)), '.minecraft'))
-
-install_dir = path.realpath(path.join(path.dirname(
- path.realpath(__file__)), 'gamedir'))
-
-current_dir = path.dirname(path.realpath(__file__))
+def cleanup(func: Callable[..., Any]) -> Callable[..., Any]:
+ "A function that removes the temp directory after completion"
+ def wrapper(*args: Any, **kwargs: Any):
+ func(*args, **kwargs)
+ empty_dir(TMPDIR)
+ return wrapper
diff --git a/tests/install/setup.py b/tests/install/setup.py
new file mode 100644
index 0000000..ca4de34
--- /dev/null
+++ b/tests/install/setup.py
@@ -0,0 +1,44 @@
+# MCM-Manager: Minecraft Modpack Manager
+# Copyright (C) 2023 Tygo Everts
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+from os import mkdir, path
+from shutil import copy
+from ..config import INSTDIR, LAUNDIR, LAUNPROF, TMPDIR
+
+
+def maketemp():
+ """Make a temp directory"""
+
+ if not path.isdir(TMPDIR):
+ mkdir(TMPDIR)
+
+
+def setup_dirs() -> None:
+ """Setup necesarry directories"""
+
+ # Make a temp directory
+ maketemp()
+
+ # Create the install dir
+ if not path.isdir(INSTDIR):
+ mkdir(INSTDIR)
+
+ # Create the launcher dir and example-manifest.json
+ if not path.isdir(LAUNDIR):
+ mkdir(LAUNDIR)
+
+ # Copy the launcher profiles
+ copy(LAUNPROF, path.join(LAUNDIR, 'launcher_profiles.json'))
diff --git a/tests/install/test_loadingbar.py b/tests/install/test_loadingbar.py
index 6153a2f..de38111 100644
--- a/tests/install/test_loadingbar.py
+++ b/tests/install/test_loadingbar.py
@@ -15,13 +15,13 @@
# along with this program. If not, see .
import unittest
-from ..vars import quiet
+from ..globalfuncs import quiet
from typing import Iterator
from src.install.loadingbar import loadingbar
-class LoadingBar(unittest.TestCase):
+class Loadingbar(unittest.TestCase):
@quiet
def test_bar(self):
# Iterating
diff --git a/tests/install/test_media.py b/tests/install/test_media.py
index 782e201..8b4d972 100644
--- a/tests/install/test_media.py
+++ b/tests/install/test_media.py
@@ -15,48 +15,56 @@
# along with this program. If not, see .
import unittest
-from ..vars import quiet, empty_dir, current_dir, install_dir
+from ..config import CURDIR, INSTDIR
+from ..globalfuncs import cleanup, quiet
+from .setup import setup_dirs
from src.install.media import prepare, download_files
from os import path
-unittest.TestLoader.sortTestMethodsUsing = None # type: ignore
-manifest_file = path.join(current_dir, 'assets', 'manifest.json')
+manifest_file = path.join(CURDIR, 'assets', 'manifest.json')
-class Install(unittest.TestCase):
+class Media(unittest.TestCase):
# These tests need some serious improvement
def test_media(self):
- self._test_prepare()
- self._test_download()
+ # Create the manifest
+ self.manifest = prepare.load_manifest(manifest_file)
- @quiet
- def _test_prepare(self):
- manifest = prepare.load_manifest(manifest_file)
+ # Run all sub tests in order
+ self._test_prepare_client()
+ self._test_prepare_server()
+ self._test_download_client()
+ self._test_download_server()
- empty_dir(install_dir)
+ @quiet
+ @cleanup
+ def _test_prepare_client(self):
+ setup_dirs()
self.total_size_client = prepare(
- install_dir, 'client', manifest).total_size
+ INSTDIR, 'client', self.manifest).total_size
self.assertIsInstance(self.total_size_client, int)
- empty_dir(install_dir)
+ @quiet
+ @cleanup
+ def _test_prepare_server(self):
+ setup_dirs()
self.total_size_server = prepare(
- install_dir, 'server', manifest).total_size
+ INSTDIR, 'server', self.manifest).total_size
self.assertIsInstance(self.total_size_server, int)
@quiet
- def _test_download(self):
- manifest = prepare.load_manifest(manifest_file)
-
- empty_dir(install_dir)
+ @cleanup
+ def _test_download_client(self):
+ setup_dirs()
download_files(self.total_size_client,
- install_dir, 'client', manifest)
+ INSTDIR, 'client', self.manifest)
- empty_dir(install_dir)
+ @quiet
+ @cleanup
+ def _test_download_server(self):
+ setup_dirs()
download_files(self.total_size_server,
- install_dir, 'server', manifest)
-
- def tearDown(self):
- empty_dir(install_dir)
+ INSTDIR, 'server', self.manifest)
diff --git a/tests/install/test_modloaders.py b/tests/install/test_modloaders.py
index 68bf6f5..d7cd927 100644
--- a/tests/install/test_modloaders.py
+++ b/tests/install/test_modloaders.py
@@ -15,33 +15,36 @@
# along with this program. If not, see .
import unittest
-from ..vars import quiet, empty_dir, install_dir, launcher_dir
+from ..config import INSTDIR, LAUNDIR
+from ..globalfuncs import cleanup, quiet
+from .setup import setup_dirs
from src.install.modloaders import forge, fabric
-class Install(unittest.TestCase):
+class Modloaders(unittest.TestCase):
@quiet
+ @cleanup
def test_forge_client(self):
- empty_dir(install_dir)
- forge('1.20.1', '47.1.0', 'client', install_dir, launcher_dir)
+ setup_dirs()
+ forge('1.20.1', '47.1.0', 'client', INSTDIR, LAUNDIR)
@quiet
+ @cleanup
def test_forge_server(self):
- empty_dir(install_dir)
- forge('1.20.1', '47.1.0', 'server', install_dir)
-
- def tearDown(self):
- empty_dir(install_dir, launcher_dir)
+ setup_dirs()
+ forge('1.20.1', '47.1.0', 'server', INSTDIR)
if False: # Tests of unfinished features
@quiet
+ @cleanup
def test_fabric_client(self):
- empty_dir(install_dir)
- fabric('1.20.1', '0.14.22', 'client', install_dir)
+ setup_dirs()
+ fabric('1.20.1', '0.14.22', 'client', INSTDIR)
@quiet
+ @cleanup
def test_fabric_server(self):
- empty_dir(install_dir)
- fabric('1.20.1', '0.14.22', 'server', install_dir)
+ setup_dirs()
+ fabric('1.20.1', '0.14.22', 'server', INSTDIR)
diff --git a/tests/install/test_urls.py b/tests/install/test_urls.py
index c01f21a..79ae68f 100644
--- a/tests/install/test_urls.py
+++ b/tests/install/test_urls.py
@@ -15,11 +15,12 @@
# along with this program. If not, see .
import unittest
+
from src.install.urls import media_url, forge
from src.install._types import Media
-class Install(unittest.TestCase):
+class Urls(unittest.TestCase):
def test_urls(self):
# Define different media types
cf_media: Media = {
@@ -62,7 +63,8 @@ def test_urls(self):
"info": {
"title": "Essential",
"icon": "https://static.essential.gg/icon/256x256.png",
- "description": "Essential is a quality of life mod that boosts Minecraft Java to the next level."
+ "description": "Essential is a quality of life mod that "
+ "boosts Minecraft Java to the next level."
},
"sides": [
"client",
@@ -72,10 +74,11 @@ def test_urls(self):
# Test if they assert equal
cases = {
- media_url(cf_media): 'https://mediafilez.forgecdn.net/files/4586/218/worldedit-mod-7.2.15.jar',
- media_url(pm_media): 'https://static.planetminecraft.com/files/resource_media/texture/unique-spawn-eggs-v1-5.zip',
- media_url(mr_media): 'https://cdn-raw.modrinth.com/data/AANobbMI/versions/' +
- 'OkwCNtFH/sodium-fabric-mc1.20.1-0.5.1.jar',
+ media_url(cf_media): "https://mediafilez.forgecdn.net/files/4586/218/worldedit-mod-7.2.15.jar",
+ media_url(pm_media): "https://static.planetminecraft.com/files/resource_media/"
+ "texture/unique-spawn-eggs-v1-5.zip",
+ media_url(mr_media): "https://cdn-raw.modrinth.com/data/AANobbMI/versions/"
+ "OkwCNtFH/sodium-fabric-mc1.20.1-0.5.1.jar",
media_url(url_media): url_media['url']
}
@@ -90,5 +93,6 @@ def test_urls(self):
self.assertEqual(
forge.forge_installer_url('1.20.1', '47.1.0'),
- "https://maven.minecraftforge.net/net/minecraftforge/forge/1.20.1-47.1.0/forge-1.20.1-47.1.0-installer.jar"
+ "https://maven.minecraftforge.net/net/minecraftforge/forge/"
+ "1.20.1-47.1.0/forge-1.20.1-47.1.0-installer.jar"
)