mirror of
https://github.com/rommapp/romm.git
synced 2025-12-22 10:27:13 +00:00
start work on code coverage
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -56,6 +56,7 @@ backend/romm_test/logs
|
||||
backend/romm_test/config
|
||||
.pytest_cache
|
||||
/romm_test
|
||||
.coverage
|
||||
|
||||
# service worker
|
||||
frontend/dev-dist
|
||||
|
||||
4
backend/.coveragerc
Normal file
4
backend/.coveragerc
Normal file
@@ -0,0 +1,4 @@
|
||||
[run]
|
||||
omit =
|
||||
alembic/*
|
||||
*/tests/*
|
||||
@@ -57,7 +57,10 @@ class ScanStats:
|
||||
|
||||
def __add__(self, other: Any) -> ScanStats:
|
||||
if not isinstance(other, ScanStats):
|
||||
return NotImplemented
|
||||
raise NotImplementedError(
|
||||
f"Addition not implemented between ScanStats and {type(other)}"
|
||||
)
|
||||
|
||||
return ScanStats(
|
||||
scanned_platforms=self.scanned_platforms + other.scanned_platforms,
|
||||
added_platforms=self.added_platforms + other.added_platforms,
|
||||
|
||||
250
backend/endpoints/sockets/tests/test_scan.py
Normal file
250
backend/endpoints/sockets/tests/test_scan.py
Normal file
@@ -0,0 +1,250 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
from handler.scan_handler import ScanType
|
||||
from models.rom import Rom
|
||||
|
||||
from ..scan import ScanStats, _should_scan_rom
|
||||
|
||||
|
||||
def test_scan_stats():
|
||||
stats = ScanStats()
|
||||
assert stats.scanned_platforms == 0
|
||||
assert stats.added_platforms == 0
|
||||
assert stats.metadata_platforms == 0
|
||||
assert stats.scanned_roms == 0
|
||||
assert stats.added_roms == 0
|
||||
assert stats.metadata_roms == 0
|
||||
assert stats.scanned_firmware == 0
|
||||
assert stats.added_firmware == 0
|
||||
|
||||
stats.scanned_platforms += 1
|
||||
stats.added_platforms += 1
|
||||
stats.metadata_platforms += 1
|
||||
stats.scanned_roms += 1
|
||||
stats.added_roms += 1
|
||||
stats.metadata_roms += 1
|
||||
stats.scanned_firmware += 1
|
||||
stats.added_firmware += 1
|
||||
|
||||
assert stats.scanned_platforms == 1
|
||||
assert stats.added_platforms == 1
|
||||
assert stats.metadata_platforms == 1
|
||||
assert stats.scanned_roms == 1
|
||||
assert stats.added_roms == 1
|
||||
assert stats.metadata_roms == 1
|
||||
assert stats.scanned_firmware == 1
|
||||
assert stats.added_firmware == 1
|
||||
|
||||
|
||||
def test_merging_scan_stats():
|
||||
stats = ScanStats(
|
||||
scanned_platforms=1,
|
||||
added_platforms=2,
|
||||
metadata_platforms=3,
|
||||
scanned_roms=4,
|
||||
added_roms=5,
|
||||
metadata_roms=6,
|
||||
scanned_firmware=7,
|
||||
added_firmware=8,
|
||||
)
|
||||
|
||||
stats2 = ScanStats(
|
||||
scanned_platforms=10,
|
||||
added_platforms=11,
|
||||
metadata_platforms=12,
|
||||
scanned_roms=13,
|
||||
added_roms=14,
|
||||
metadata_roms=15,
|
||||
scanned_firmware=16,
|
||||
added_firmware=17,
|
||||
)
|
||||
|
||||
stats += stats2
|
||||
|
||||
assert stats.scanned_platforms == 11
|
||||
assert stats.added_platforms == 13
|
||||
assert stats.metadata_platforms == 15
|
||||
assert stats.scanned_roms == 17
|
||||
assert stats.added_roms == 19
|
||||
assert stats.metadata_roms == 21
|
||||
assert stats.scanned_firmware == 23
|
||||
assert stats.added_firmware == 25
|
||||
|
||||
stats3: dict = {}
|
||||
with pytest.raises(NotImplementedError):
|
||||
stats += stats3
|
||||
|
||||
|
||||
class TestShouldScanRom:
|
||||
def test_new_platforms_scan_with_no_rom(self):
|
||||
"""NEW_PLATFORMS should scan when rom is None"""
|
||||
result = _should_scan_rom(ScanType.NEW_PLATFORMS, None, [])
|
||||
assert result is True
|
||||
|
||||
def test_new_platforms_scan_with_existing_rom(self, rom: Rom):
|
||||
"""NEW_PLATFORMS should not scan when rom exists"""
|
||||
result = _should_scan_rom(ScanType.NEW_PLATFORMS, rom, [])
|
||||
assert result is False
|
||||
|
||||
# Test QUICK scan type
|
||||
def test_quick_scan_with_no_rom(self):
|
||||
"""QUICK should scan when rom is None"""
|
||||
result = _should_scan_rom(ScanType.QUICK, None, [])
|
||||
assert result is True
|
||||
|
||||
def test_quick_scan_with_existing_rom(self, rom: Rom):
|
||||
"""QUICK should not scan when rom exists"""
|
||||
result = _should_scan_rom(ScanType.QUICK, rom, [])
|
||||
assert result is False
|
||||
|
||||
# Test COMPLETE scan type
|
||||
def test_complete_scan_always_scans(self, rom: Rom):
|
||||
"""COMPLETE should always scan regardless of rom state"""
|
||||
assert _should_scan_rom(ScanType.COMPLETE, None, []) is True
|
||||
assert _should_scan_rom(ScanType.COMPLETE, rom, []) is True
|
||||
assert _should_scan_rom(ScanType.COMPLETE, rom, ["2", "3"]) is True
|
||||
|
||||
# Test HASHES scan type
|
||||
def test_hashes_scan_always_scans(self, rom: Rom):
|
||||
"""HASHES should always scan regardless of rom state"""
|
||||
assert _should_scan_rom(ScanType.HASHES, None, []) is True
|
||||
assert _should_scan_rom(ScanType.HASHES, rom, []) is True
|
||||
assert _should_scan_rom(ScanType.HASHES, rom, ["2", "3"]) is True
|
||||
|
||||
# Test UNIDENTIFIED scan type
|
||||
def test_unidentified_scan_with_no_rom(self):
|
||||
"""UNIDENTIFIED should not scan when rom is None"""
|
||||
result = _should_scan_rom(ScanType.UNIDENTIFIED, None, [])
|
||||
assert result is False
|
||||
|
||||
def test_unidentified_scan_with_unidentified_rom(self, rom: Rom):
|
||||
"""UNIDENTIFIED should scan when rom is unidentified"""
|
||||
rom.igdb_id = None
|
||||
rom.moby_id = None
|
||||
rom.ss_id = None
|
||||
rom.ra_id = None
|
||||
rom.launchbox_id = None
|
||||
result = _should_scan_rom(ScanType.UNIDENTIFIED, rom, [])
|
||||
assert result is True
|
||||
|
||||
def test_unidentified_scan_with_identified_rom(self, rom: Rom):
|
||||
"""UNIDENTIFIED should not scan when rom is identified"""
|
||||
rom.igdb_id = 1
|
||||
result = _should_scan_rom(ScanType.UNIDENTIFIED, rom, [])
|
||||
assert result is False
|
||||
|
||||
# Test PARTIAL scan type
|
||||
def test_partial_scan_with_no_rom(self):
|
||||
"""PARTIAL should not scan when rom is None"""
|
||||
result = _should_scan_rom(ScanType.PARTIAL, None, [])
|
||||
assert result is False
|
||||
|
||||
def test_partial_scan_with_identified_rom(self, rom: Rom):
|
||||
"""PARTIAL should scan when rom is identified"""
|
||||
rom.igdb_id = 1
|
||||
result = _should_scan_rom(ScanType.PARTIAL, rom, [])
|
||||
assert result is True
|
||||
|
||||
def test_partial_scan_with_unidentified_rom(self, rom: Rom):
|
||||
"""PARTIAL should not scan when rom is not identified"""
|
||||
rom.igdb_id = None
|
||||
rom.moby_id = None
|
||||
rom.ss_id = None
|
||||
rom.ra_id = None
|
||||
rom.launchbox_id = None
|
||||
result = _should_scan_rom(ScanType.PARTIAL, rom, [])
|
||||
assert result is False
|
||||
|
||||
# Test rom_ids parameter
|
||||
def test_scan_when_rom_id_in_list(self, rom: Rom):
|
||||
"""Should scan when rom.id is in roms_ids list regardless of scan type"""
|
||||
rom.id = 1
|
||||
roms_ids = ["1", "2", "3"]
|
||||
|
||||
# Test with different scan types
|
||||
for scan_type in [
|
||||
ScanType.NEW_PLATFORMS,
|
||||
ScanType.QUICK,
|
||||
ScanType.UNIDENTIFIED,
|
||||
ScanType.PARTIAL,
|
||||
]:
|
||||
result = _should_scan_rom(scan_type, rom, roms_ids)
|
||||
assert result is True
|
||||
|
||||
def test_no_scan_when_rom_id_not_in_list(self, rom: Rom):
|
||||
"""Should follow normal rules when rom.id is not in roms_ids list"""
|
||||
rom.id = 4
|
||||
roms_ids = ["1", "2", "3"]
|
||||
|
||||
# These should not scan because rom exists and id not in list
|
||||
assert _should_scan_rom(ScanType.NEW_PLATFORMS, rom, roms_ids) is False
|
||||
assert _should_scan_rom(ScanType.QUICK, rom, roms_ids) is False
|
||||
assert _should_scan_rom(ScanType.UNIDENTIFIED, rom, roms_ids) is False
|
||||
assert _should_scan_rom(ScanType.PARTIAL, rom, roms_ids) is False
|
||||
|
||||
# Edge cases
|
||||
def test_empty_roms_ids_list(self, rom: Rom):
|
||||
"""Test behavior with empty roms_ids list"""
|
||||
rom.id = 1
|
||||
rom.igdb_id = 1
|
||||
|
||||
assert _should_scan_rom(ScanType.PARTIAL, rom, []) is True
|
||||
assert _should_scan_rom(ScanType.NEW_PLATFORMS, rom, []) is False
|
||||
|
||||
def test_rom_id_type_conversion(self, rom: Rom):
|
||||
"""Test that rom.id (int) is properly compared with roms_ids (list of strings)"""
|
||||
rom.id = 123
|
||||
roms_ids = ["123", "456"]
|
||||
|
||||
# This should scan because 123 should match "123"
|
||||
result = _should_scan_rom(ScanType.NEW_PLATFORMS, rom, roms_ids)
|
||||
assert result is True
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"scan_type,rom_exists,is_identified,rom_in_list,expected",
|
||||
[
|
||||
# Comprehensive test matrix
|
||||
(ScanType.NEW_PLATFORMS, False, None, False, True),
|
||||
(ScanType.NEW_PLATFORMS, True, True, False, False),
|
||||
(ScanType.NEW_PLATFORMS, True, True, True, True),
|
||||
(ScanType.QUICK, False, None, False, True),
|
||||
(ScanType.QUICK, True, True, False, False),
|
||||
(ScanType.COMPLETE, False, None, False, True),
|
||||
(ScanType.COMPLETE, True, False, False, True),
|
||||
(ScanType.HASHES, False, None, False, True),
|
||||
(ScanType.HASHES, True, False, False, True),
|
||||
(ScanType.UNIDENTIFIED, True, False, False, True),
|
||||
(ScanType.UNIDENTIFIED, True, True, False, False),
|
||||
(ScanType.PARTIAL, True, True, False, True),
|
||||
(ScanType.PARTIAL, True, True, False, False),
|
||||
],
|
||||
)
|
||||
def test_comprehensive_scenarios(
|
||||
self,
|
||||
scan_type,
|
||||
rom_exists,
|
||||
is_identified,
|
||||
rom_in_list,
|
||||
expected,
|
||||
):
|
||||
"""Test comprehensive scenarios with different combinations"""
|
||||
rom: Rom = Mock(spec=Rom)
|
||||
roms_ids = []
|
||||
|
||||
if rom_exists:
|
||||
rom.id = 1
|
||||
if is_identified:
|
||||
rom.igdb_id = 1
|
||||
else:
|
||||
rom.igdb_id = None
|
||||
rom.moby_id = None
|
||||
rom.ss_id = None
|
||||
rom.ra_id = None
|
||||
rom.launchbox_id = None
|
||||
|
||||
if rom_in_list:
|
||||
roms_ids = ["1"]
|
||||
|
||||
result = _should_scan_rom(scan_type, rom, roms_ids)
|
||||
assert result is expected
|
||||
@@ -19,6 +19,7 @@ dependencies = [
|
||||
"anyio ~= 4.4",
|
||||
"authlib ~= 1.3",
|
||||
"colorama ~= 0.4",
|
||||
"coverage>=7.9.2",
|
||||
"defusedxml ~= 0.7.1",
|
||||
"emoji == 2.10.1",
|
||||
"fastapi-pagination[sqlalchemy] ~= 0.12",
|
||||
|
||||
33
uv.lock
generated
33
uv.lock
generated
@@ -296,6 +296,37 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e6/75/49e5bfe642f71f272236b5b2d2691cf915a7283cc0ceda56357b61daa538/comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3", size = 7180, upload-time = "2024-03-12T16:53:39.226Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "coverage"
|
||||
version = "7.9.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/04/b7/c0465ca253df10a9e8dae0692a4ae6e9726d245390aaef92360e1d6d3832/coverage-7.9.2.tar.gz", hash = "sha256:997024fa51e3290264ffd7492ec97d0690293ccd2b45a6cd7d82d945a4a80c8b", size = 813556, upload-time = "2025-07-03T10:54:15.101Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/94/9d/7a8edf7acbcaa5e5c489a646226bed9591ee1c5e6a84733c0140e9ce1ae1/coverage-7.9.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:985abe7f242e0d7bba228ab01070fde1d6c8fa12f142e43debe9ed1dde686038", size = 212367, upload-time = "2025-07-03T10:53:25.811Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/9e/5cd6f130150712301f7e40fb5865c1bc27b97689ec57297e568d972eec3c/coverage-7.9.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:82c3939264a76d44fde7f213924021ed31f55ef28111a19649fec90c0f109e6d", size = 212632, upload-time = "2025-07-03T10:53:27.075Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a8/de/6287a2c2036f9fd991c61cefa8c64e57390e30c894ad3aa52fac4c1e14a8/coverage-7.9.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae5d563e970dbe04382f736ec214ef48103d1b875967c89d83c6e3f21706d5b3", size = 245793, upload-time = "2025-07-03T10:53:28.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/cc/9b5a9961d8160e3cb0b558c71f8051fe08aa2dd4b502ee937225da564ed1/coverage-7.9.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdd612e59baed2a93c8843c9a7cb902260f181370f1d772f4842987535071d14", size = 243006, upload-time = "2025-07-03T10:53:29.754Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/49/d9/4616b787d9f597d6443f5588619c1c9f659e1f5fc9eebf63699eb6d34b78/coverage-7.9.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:256ea87cb2a1ed992bcdfc349d8042dcea1b80436f4ddf6e246d6bee4b5d73b6", size = 244990, upload-time = "2025-07-03T10:53:31.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/83/801cdc10f137b2d02b005a761661649ffa60eb173dcdaeb77f571e4dc192/coverage-7.9.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f44ae036b63c8ea432f610534a2668b0c3aee810e7037ab9d8ff6883de480f5b", size = 245157, upload-time = "2025-07-03T10:53:32.717Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c8/a4/41911ed7e9d3ceb0ffb019e7635468df7499f5cc3edca5f7dfc078e9c5ec/coverage-7.9.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:82d76ad87c932935417a19b10cfe7abb15fd3f923cfe47dbdaa74ef4e503752d", size = 243128, upload-time = "2025-07-03T10:53:34.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/41/344543b71d31ac9cb00a664d5d0c9ef134a0fe87cb7d8430003b20fa0b7d/coverage-7.9.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:619317bb86de4193debc712b9e59d5cffd91dc1d178627ab2a77b9870deb2868", size = 244511, upload-time = "2025-07-03T10:53:35.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/81/3b68c77e4812105e2a060f6946ba9e6f898ddcdc0d2bfc8b4b152a9ae522/coverage-7.9.2-cp313-cp313-win32.whl", hash = "sha256:0a07757de9feb1dfafd16ab651e0f628fd7ce551604d1bf23e47e1ddca93f08a", size = 214765, upload-time = "2025-07-03T10:53:36.787Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/a2/7fac400f6a346bb1a4004eb2a76fbff0e242cd48926a2ce37a22a6a1d917/coverage-7.9.2-cp313-cp313-win_amd64.whl", hash = "sha256:115db3d1f4d3f35f5bb021e270edd85011934ff97c8797216b62f461dd69374b", size = 215536, upload-time = "2025-07-03T10:53:38.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/08/47/2c6c215452b4f90d87017e61ea0fd9e0486bb734cb515e3de56e2c32075f/coverage-7.9.2-cp313-cp313-win_arm64.whl", hash = "sha256:48f82f889c80af8b2a7bb6e158d95a3fbec6a3453a1004d04e4f3b5945a02694", size = 213943, upload-time = "2025-07-03T10:53:39.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/46/e211e942b22d6af5e0f323faa8a9bc7c447a1cf1923b64c47523f36ed488/coverage-7.9.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:55a28954545f9d2f96870b40f6c3386a59ba8ed50caf2d949676dac3ecab99f5", size = 213088, upload-time = "2025-07-03T10:53:40.874Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d2/2f/762551f97e124442eccd907bf8b0de54348635b8866a73567eb4e6417acf/coverage-7.9.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cdef6504637731a63c133bb2e6f0f0214e2748495ec15fe42d1e219d1b133f0b", size = 213298, upload-time = "2025-07-03T10:53:42.218Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/b7/76d2d132b7baf7360ed69be0bcab968f151fa31abe6d067f0384439d9edb/coverage-7.9.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd5ebe66c7a97273d5d2ddd4ad0ed2e706b39630ed4b53e713d360626c3dbb3", size = 256541, upload-time = "2025-07-03T10:53:43.823Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/17/392b219837d7ad47d8e5974ce5f8dc3deb9f99a53b3bd4d123602f960c81/coverage-7.9.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9303aed20872d7a3c9cb39c5d2b9bdbe44e3a9a1aecb52920f7e7495410dfab8", size = 252761, upload-time = "2025-07-03T10:53:45.19Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/77/4256d3577fe1b0daa8d3836a1ebe68eaa07dd2cbaf20cf5ab1115d6949d4/coverage-7.9.2-cp313-cp313t-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc18ea9e417a04d1920a9a76fe9ebd2f43ca505b81994598482f938d5c315f46", size = 254917, upload-time = "2025-07-03T10:53:46.931Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/99/fc1a008eef1805e1ddb123cf17af864743354479ea5129a8f838c433cc2c/coverage-7.9.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6406cff19880aaaadc932152242523e892faff224da29e241ce2fca329866584", size = 256147, upload-time = "2025-07-03T10:53:48.289Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/92/c0/f63bf667e18b7f88c2bdb3160870e277c4874ced87e21426128d70aa741f/coverage-7.9.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:2d0d4f6ecdf37fcc19c88fec3e2277d5dee740fb51ffdd69b9579b8c31e4232e", size = 254261, upload-time = "2025-07-03T10:53:49.99Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/32/37dd1c42ce3016ff8ec9e4b607650d2e34845c0585d3518b2a93b4830c1a/coverage-7.9.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c33624f50cf8de418ab2b4d6ca9eda96dc45b2c4231336bac91454520e8d1fac", size = 255099, upload-time = "2025-07-03T10:53:51.354Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/da/2e/af6b86f7c95441ce82f035b3affe1cd147f727bbd92f563be35e2d585683/coverage-7.9.2-cp313-cp313t-win32.whl", hash = "sha256:1df6b76e737c6a92210eebcb2390af59a141f9e9430210595251fbaf02d46926", size = 215440, upload-time = "2025-07-03T10:53:52.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4d/bb/8a785d91b308867f6b2e36e41c569b367c00b70c17f54b13ac29bcd2d8c8/coverage-7.9.2-cp313-cp313t-win_amd64.whl", hash = "sha256:f5fd54310b92741ebe00d9c0d1d7b2b27463952c022da6d47c175d246a98d1bd", size = 216537, upload-time = "2025-07-03T10:53:54.273Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/a0/a6bffb5e0f41a47279fd45a8f3155bf193f77990ae1c30f9c224b61cacb0/coverage-7.9.2-cp313-cp313t-win_arm64.whl", hash = "sha256:c48c2375287108c887ee87d13b4070a381c6537d30e8487b24ec721bf2a781cb", size = 214398, upload-time = "2025-07-03T10:53:56.715Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/38/bbe2e63902847cf79036ecc75550d0698af31c91c7575352eb25190d0fb3/coverage-7.9.2-py3-none-any.whl", hash = "sha256:e425cd5b00f6fc0ed7cdbd766c70be8baab4b7839e4d4fe5fac48581dd968ea4", size = 204005, upload-time = "2025-07-03T10:54:13.491Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crontab"
|
||||
version = "1.0.4"
|
||||
@@ -1657,6 +1688,7 @@ dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "authlib" },
|
||||
{ name = "colorama" },
|
||||
{ name = "coverage" },
|
||||
{ name = "defusedxml" },
|
||||
{ name = "emoji" },
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
@@ -1717,6 +1749,7 @@ requires-dist = [
|
||||
{ name = "anyio", specifier = "~=4.4" },
|
||||
{ name = "authlib", specifier = "~=1.3" },
|
||||
{ name = "colorama", specifier = "~=0.4" },
|
||||
{ name = "coverage", specifier = ">=7.9.2" },
|
||||
{ name = "defusedxml", specifier = "~=0.7.1" },
|
||||
{ name = "emoji", specifier = "==2.10.1" },
|
||||
{ name = "fakeredis", marker = "extra == 'test'", specifier = "~=2.21" },
|
||||
|
||||
Reference in New Issue
Block a user