Repository: Senth/minecraft-mod-manager (python)

Issue:
`update --pretend` throws errors
### Describe the bug 
When there are updates and you run `--pretend` it gets the version from the file. But since there's no file to read it can't get the updated version.

### Steps to reproduce
Steps to reproduce the behavior:
1. Ran `mcman install -v 1.16.5 carpet`
2. Then ran `mcman update --pretend`

### Expected Behavior
Should display the updated version and not throw an error

### Possible solution
Display the version from the `VersionInfo` if `--pretend`. And it should not try to load the file info during `--pretend` 

### Info
 - OS: Ubuntu
 - Python --version: 3.8.6
 - minecraft-mod-manager --version: 1.2.4


## From #179 
### Describe the bug 
Tried updating all mods and it showed errors on several mods

```
Failed to parse mod file mods\CraftTweaker-fabric-1.18.2-9.1.127.jar
Traceback (most recent call last):
  File "C:\Users\redacted\AppData\Roaming\Python\Python310\site-packages\minecraft_mod_manager\gateways\jar_parser.py", line 50, in _get_mod_info
    with ZipFile(file, "r") as zip:
  File "C:\Users\redacted\AppData\Local\Programs\Python\Python310\lib\zipfile.py", line 1240, in __init__
    self.fp = io.open(file, filemode)
FileNotFoundError: [Errno 2] No such file or directory: 'mods\\CraftTweaker-fabric-1.18.2-9.1.127.jar'

!!! Please report this and paste the above message !!!
    ❌ Corrupted file.
```

### Steps to reproduce
Steps to reproduce the behavior:
1. Ran `minecraft-mod-manager.exe --pretend -d mods -v 1.18.2 --mod-loader=fabric update`

### Expected Behavior
Will show mods to be updated

### Info
 - OS: win10
 - Python --version: 3.10
 - minecraft-mod-manager --version: 1.27


Pull request:
Fix `--pretend` to show a valid number
When updating from Curse with --pretend, there is no version number in the return value. For now, it gets the version number from the filename

### What changed?
- Get the version_number from the API requests

### Testing
- [X] Changed unit tests
- [ ] Added integration tests
- [ ] ...Your own tests...

### Checklist
- [x] I have reviewed my own code

### Related Issues
Fixes #93


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78510a4..4325209 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## Upcoming
+## [1.3.0] - 2022-05-07: Installation Improvements
 
 ### Added
 
@@ -15,10 +15,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Changed
 
+- `update` now skips downloading files if we already have the latest version [#139](https://github.com/Senth/minecraft-mod-manager/issues/139)
+- Improved the searching algorithm where the search term `entityculling` would previously return 0 results [#56](https://github.com/Senth/minecraft-mod-manager/issues/56)
 - Now uses tealprint library which will automatically disable color and unicode
   when trying to output Unicode characters [#141](https://github.com/Senth/minecraft-mod-manager/issues/141)
-- Improved the searching algorithm where the search term `entityculling` would previously return 0 results [#56](https://github.com/Senth/minecraft-mod-manager/issues/56)
-- `update` now skips downloading files if we already have the latest version [#139](https://github.com/Senth/minecraft-mod-manager/issues/139)
 
 ### Fixed
 
@@ -29,9 +29,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   It can still break the program in an invalid state [#140](https://github.com/Senth/minecraft-mod-manager/issues/140)
 - Now retries to fetch/download file 5 times before skipping [#169](https://github.com/Senth/minecraft-mod-manager/issues/169)
 - Now identifies installed mods correctly by slug as well [#149](https://github.com/Senth/minecraft-mod-manager/issues/149)
-- Can now search by slug on Modrinth [[#135](https://github.com/Senth/minecraft-mod-manager/issues/135)].
+- Can now search by slug on Modrinth [#135](https://github.com/Senth/minecraft-mod-manager/issues/135).
   When searching on Modrinth include on their site modrinth.com/mods, searching by slug can return an empty result,
   so we now get the mod directly by slug.
+- Fixed `--pretend` not printing error and actually showing a version number the mod would upgrade to [#93](https://github.com/Senth/minecraft-mod-manager/issues/93)
 
 ## [1.2.7] - 2022-03-06
 
diff --git a/minecraft_mod_manager/app/download/download.py b/minecraft_mod_manager/app/download/download.py
index 5f028de..d7e0e6b 100644
--- a/minecraft_mod_manager/app/download/download.py
+++ b/minecraft_mod_manager/app/download/download.py
@@ -2,6 +2,7 @@ from typing import List, Sequence
 
 from tealprint import TealPrint
 
+from ...config import config
 from ...core.entities.mod import Mod, ModArg
 from ...core.entities.version_info import VersionInfo
 from ...core.errors.download_failed import DownloadFailed
@@ -133,7 +134,7 @@ class Download:
         raise NotImplementedError("Not implemented in subclass")
 
     def _update_mod_from_file(self, mod: Mod) -> None:
-        if mod.file:
+        if not config.pretend and mod.file:
             installed_mod = self._repo.get_mod_from_file(mod.file)
             if installed_mod:
                 mod.id = installed_mod.id
@@ -152,6 +153,7 @@ class Download:
             sites=sites,
             file=downloaded_file.name,
             upload_time=latest_version.upload_time,
+            version=latest_version.number,
         )
 
         return add_mod
diff --git a/minecraft_mod_manager/core/entities/version_info.py b/minecraft_mod_manager/core/entities/version_info.py
index e9fe955..afb98b9 100644
--- a/minecraft_mod_manager/core/entities/version_info.py
+++ b/minecraft_mod_manager/core/entities/version_info.py
@@ -30,8 +30,9 @@ class VersionInfo:
         upload_time: int,
         minecraft_versions: List[str],
         download_url: str,
+        number: str,
         filename: str = "",
-        name: str = "",
+        mod_name: str = "",
         dependencies: Dict[Sites, List[str]] = {},
     ) -> None:
         self.stability = stability
@@ -40,8 +41,9 @@ class VersionInfo:
         self.upload_time = upload_time
         self.minecraft_versions = minecraft_versions
         self.download_url = download_url
+        self.number = number
         self.filename = filename
-        self.name = name
+        self.name = mod_name
         self.dependencies = dependencies
 
     def __str__(self) -> str:
@@ -58,6 +60,7 @@ class VersionInfo:
             self.upload_time,
             self.minecraft_versions,
             self.download_url,
+            self.number,
             self.filename,
             self.name,
             self.dependencies,
diff --git a/minecraft_mod_manager/gateways/api/curse_api.py b/minecraft_mod_manager/gateways/api/curse_api.py
index 448f664..0efa4b5 100644
--- a/minecraft_mod_manager/gateways/api/curse_api.py
+++ b/minecraft_mod_manager/gateways/api/curse_api.py
@@ -1,3 +1,4 @@
+import re
 from enum import Enum
 from typing import Any, Dict, List
 
@@ -16,6 +17,8 @@ class DependencyTypes(Enum):
 
 
 class CurseApi(Api):
+    _filename_to_version_regex = re.compile(r"((\d{2}w\d{2}[a-f]-)?\d+\.\d+(\.\d+)?.*).*\.jar")
+
     def __init__(self, http: Http) -> None:
         super().__init__(http, Sites.curse)
 
@@ -68,7 +71,7 @@ class CurseApi(Api):
         # Find required dependencies
         dependencyList: List[str] = []
         for dependency in file_data["dependencies"]:
-            if "addonId" in dependency and "type" in dependency:
+            if {"addonId", "type"}.issubset(dependency):
                 t = dependency["type"]
                 if t == DependencyTypes.required.value:
                     dependencyList.append(str(dependency["addonId"]))
@@ -85,6 +88,7 @@ class CurseApi(Api):
             upload_time=Api._to_epoch_time(file_data["fileDate"]),
             minecraft_versions=file_data["gameVersion"],
             download_url=file_data["downloadUrl"],
+            number=CurseApi._get_version_from_filename(file_data["fileName"]),
             filename=file_data["fileName"],
             dependencies=dependencyMap,
         )
@@ -98,3 +102,10 @@ class CurseApi(Api):
         elif value == 3:
             return Stabilities.alpha
         return Stabilities.unknown
+
+    @staticmethod
+    def _get_version_from_filename(filename: str) -> str:
+        match = CurseApi._filename_to_version_regex.search(filename)
+        if match:
+            return match.group(1)
+        return "Unknown"
diff --git a/minecraft_mod_manager/gateways/api/modrinth_api.py b/minecraft_mod_manager/gateways/api/modrinth_api.py
index 3f24dcb..13e2aeb 100644
--- a/minecraft_mod_manager/gateways/api/modrinth_api.py
+++ b/minecraft_mod_manager/gateways/api/modrinth_api.py
@@ -108,6 +108,7 @@ class ModrinthApi(Api):
             site=Sites.modrinth,
             upload_time=Api._to_epoch_time(data["date_published"]),
             minecraft_versions=data["game_versions"],
+            number=data["version_number"],
             download_url=data["files"][0]["url"],
             filename=data["files"][0]["filename"],
         )


Test patch:
diff --git a/minecraft_mod_manager/adapters/repo_impl_test.py b/minecraft_mod_manager/adapters/repo_impl_test.py
index 5291d1e..974c7b3 100644
--- a/minecraft_mod_manager/adapters/repo_impl_test.py
+++ b/minecraft_mod_manager/adapters/repo_impl_test.py
@@ -74,6 +74,7 @@ def version(site: Sites) -> VersionInfo:
         minecraft_versions=[],
         upload_time=0,
         download_url="",
+        number="",
     )
 
 
diff --git a/minecraft_mod_manager/app/download/download_test.py b/minecraft_mod_manager/app/download/download_test.py
index bc00752..df94836 100644
--- a/minecraft_mod_manager/app/download/download_test.py
+++ b/minecraft_mod_manager/app/download/download_test.py
@@ -28,6 +28,7 @@ class T:
         upload_time=100,
         minecraft_versions=[],
         download_url="",
+        number="1.0.0",
     )
 
     @staticmethod
@@ -93,6 +94,7 @@ class T:
                             download_url="",
                             dependencies={Sites.curse: ["123", "456"]},
                             filename="parent.jar",
+                            number="1.0.1",
                         )
                     ]
                 ),
diff --git a/minecraft_mod_manager/app/update/update_test.py b/minecraft_mod_manager/app/update/update_test.py
index 430cb08..6336ca9 100644
--- a/minecraft_mod_manager/app/update/update_test.py
+++ b/minecraft_mod_manager/app/update/update_test.py
@@ -54,6 +54,7 @@ def version_info(filename: str) -> VersionInfo:
         minecraft_versions=[],
         download_url="",
         filename=filename,
+        number="1.0.0",
     )
 
 
diff --git a/minecraft_mod_manager/core/utils/latest_version_finder_test.py b/minecraft_mod_manager/core/utils/latest_version_finder_test.py
index ec5c7eb..02af3c0 100644
--- a/minecraft_mod_manager/core/utils/latest_version_finder_test.py
+++ b/minecraft_mod_manager/core/utils/latest_version_finder_test.py
@@ -44,6 +44,7 @@ def version_info(
         minecraft_versions=versions,
         upload_time=uploaded,
         download_url="",
+        number="",
     )
 
 
diff --git a/minecraft_mod_manager/gateways/api/curse_api_test.py b/minecraft_mod_manager/gateways/api/curse_api_test.py
index 03f8af2..3f71c1e 100644
--- a/minecraft_mod_manager/gateways/api/curse_api_test.py
+++ b/minecraft_mod_manager/gateways/api/curse_api_test.py
@@ -114,42 +114,46 @@ def test_get_all_versions(api: CurseApi, carpet_files):
             stability=Stabilities.beta,
             mod_loaders=set([ModLoaders.forge]),
             site=Sites.curse,
-            name="Carpet",
+            mod_name="Carpet",
             upload_time=1585794423,
             minecraft_versions=["1.16-Snapshot", "Forge"],
             download_url="https://edge.forgecdn.net/files/2918/924/fabric-carpet-20w13b-1.3.17+v200401.jar",
             filename="fabric-carpet-20w13b-1.3.17+v200401.jar",
             dependencies={Sites.curse: ["1337", "1338"]},
+            number="20w13b-1.3.17+v200401",
         ),
         VersionInfo(
             stability=Stabilities.alpha,
             mod_loaders=set([]),
             site=Sites.curse,
-            name="Carpet",
+            mod_name="Carpet",
             upload_time=1571975688,
             minecraft_versions=["1.14.4"],
             download_url="https://edge.forgecdn.net/files/2815/968/fabric-carpet-1.14.4-1.2.0+v191024.jar",
             filename="fabric-carpet-1.14.4-1.2.0+v191024.jar",
+            number="1.14.4-1.2.0+v191024",
         ),
         VersionInfo(
             stability=Stabilities.release,
             mod_loaders=set([ModLoaders.fabric]),
             site=Sites.curse,
-            name="Carpet",
+            mod_name="Carpet",
             upload_time=1618425238,
             minecraft_versions=["Fabric", "1.16.5", "1.16.4"],
             download_url="https://edge.forgecdn.net/files/3276/129/fabric-carpet-1.16.5-1.4.32+v210414.jar",
             filename="fabric-carpet-1.16.5-1.4.32+v210414.jar",
+            number="1.16.5-1.4.32+v210414",
         ),
         VersionInfo(
             stability=Stabilities.release,
             mod_loaders=set([ModLoaders.fabric, ModLoaders.forge]),
             site=Sites.curse,
-            name="Carpet",
+            mod_name="Carpet",
             upload_time=1618425279,
             minecraft_versions=["1.17", "Fabric", "Forge"],
             download_url="https://edge.forgecdn.net/files/3276/130/fabric-carpet-21w15a-1.4.32+v210414.jar",
             filename="fabric-carpet-21w15a-1.4.32+v210414.jar",
+            number="21w15a-1.4.32+v210414",
         ),
     ]
 
diff --git a/minecraft_mod_manager/gateways/api/modrinth_api_test.py b/minecraft_mod_manager/gateways/api/modrinth_api_test.py
index 5880495..bd679f7 100644
--- a/minecraft_mod_manager/gateways/api/modrinth_api_test.py
+++ b/minecraft_mod_manager/gateways/api/modrinth_api_test.py
@@ -153,42 +153,46 @@ def test_get_all_versions_directly_when_we_have_mod_id(api: ModrinthApi, version
             stability=Stabilities.beta,
             mod_loaders=set([ModLoaders.fabric]),
             site=Sites.modrinth,
-            name="Fabric API",
+            mod_name="Fabric API",
             upload_time=1618769767,
             minecraft_versions=["21w16a"],
             download_url="https://cdn.modrinth.com/data/P7dR8mSH/versions/0.33.0+1.17/fabric-api-0.33.0+1.17.jar",
             filename="fabric-api-0.33.0+1.17.jar",
             dependencies={Sites.modrinth: ["1338", "1337"]},
+            number="0.33.0+1.17",
         ),
         VersionInfo(
             stability=Stabilities.release,
             mod_loaders=set([]),
             site=Sites.modrinth,
-            name="Fabric API",
+            mod_name="Fabric API",
             upload_time=1618768763,
             minecraft_versions=["1.16.5"],
             download_url="https://cdn.modrinth.com/data/P7dR8mSH/versions/0.33.0+1.16/fabric-api-0.33.0+1.16.jar",
             filename="fabric-api-0.33.0+1.16.jar",
+            number="0.33.0+1.16",
         ),
         VersionInfo(
             stability=Stabilities.alpha,
             mod_loaders=set([ModLoaders.forge]),
             site=Sites.modrinth,
-            name="Fabric API",
+            mod_name="Fabric API",
             upload_time=1618429021,
             minecraft_versions=["21w15a"],
             download_url="https://cdn.modrinth.com/data/P7dR8mSH/versions/0.32.9+1.17/fabric-api-0.32.9+1.17.jar",
             filename="fabric-api-0.32.9+1.17.jar",
+            number="0.32.9+1.17",
         ),
         VersionInfo(
             stability=Stabilities.release,
             mod_loaders=set([ModLoaders.fabric, ModLoaders.forge]),
             site=Sites.modrinth,
-            name="Fabric API",
+            mod_name="Fabric API",
             upload_time=1618427403,
             minecraft_versions=["1.16.5"],
             download_url="https://cdn.modrinth.com/data/P7dR8mSH/versions/0.32.9+1.16/fabric-api-0.32.9+1.16.jar",
             filename="fabric-api-0.32.9+1.16.jar",
+            number="0.32.9+1.16",
         ),
     ]
 
@@ -207,21 +211,23 @@ def test_get_versions_without_files(api: ModrinthApi, versions_without_files):
             stability=Stabilities.release,
             mod_loaders=set([ModLoaders.fabric]),
             site=Sites.modrinth,
-            name="Fabric API",
+            mod_name="Fabric API",
             upload_time=1638379386,
             minecraft_versions=["1.18"],
             download_url="https://cdn.modrinth.com/data/Nz0RSWrF/versions/0.2.5/lazy-language-loader-0.2.5.jar",
             filename="lazy-language-loader-0.2.5.jar",
+            number="0.2.5",
         ),
         VersionInfo(
             stability=Stabilities.release,
             mod_loaders=set([ModLoaders.fabric]),
             site=Sites.modrinth,
-            name="Fabric API",
+            mod_name="Fabric API",
             upload_time=1638297554,
             minecraft_versions=["1.18"],
             download_url="https://cdn.modrinth.com/data/Nz0RSWrF/versions/0.2.3/lazy-language-loader-0.2.3.jar",
             filename="lazy-language-loader-0.2.3.jar",
+            number="0.2.3",
         ),
     ]
 
diff --git a/tests/update_test.py b/tests/update_test.py
index 802b151..72d2f6a 100644
--- a/tests/update_test.py
+++ b/tests/update_test.py
@@ -11,14 +11,17 @@ def helper():
 
 
 def test_update_does_not_remove_mods(helper: Helper):
-    helper.run("install", "carpet", "-v", "1.16.2")
+    code = helper.run("install", "carpet", "-v", "1.16.2")
     carpet_mod = helper.get_mod_in_dir_like("*carpet*.jar")
+    assert code == 0
     assert carpet_mod is not None
 
-    helper.run("update")
+    code = helper.run("update")
     carpet_mod = helper.get_mod_in_dir_like("*carpet*.jar")
+    assert code == 0
     assert carpet_mod is not None
 
-    helper.run("update")
+    code = helper.run("update")
     carpet_mod = helper.get_mod_in_dir_like("*carpet*.jar")
+    assert code == 0
     assert carpet_mod is not None
diff --git a/tests/util/helper.py b/tests/util/helper.py
index 8549b9d..e50ad81 100644
--- a/tests/util/helper.py
+++ b/tests/util/helper.py
@@ -1,7 +1,7 @@
 import sqlite3
 from pathlib import Path
 from subprocess import run
-from typing import List, Union
+from typing import List, Optional
 
 
 class Helper:
@@ -19,7 +19,7 @@ class Helper:
         output = run(cmd)
         return output.returncode
 
-    def get_mod_in_dir_like(self, glob: str) -> Union[Path, None]:
+    def get_mod_in_dir_like(self, glob: str) -> Optional[Path]:
         for file in self.dir.glob(glob):
             return file
         return None