Build and Push Docker Container / build-and-push (push) Successful in 3m58s
- Switch local Python and the Docker base image to Python 3.15. - Refresh locked dependencies with Python-version-specific resolution. - Replace Pickle caching with native Redis strings, lists, and typed JSON. - Validate cache TTLs and apply expiry atomically on Redis writes. - Treat legacy binary cache values as misses and clear keys with SCAN. - Ignore trailing prose after decoded spaces in share-link paths. - Reject empty or slash-containing share IDs before database lookup. - Add regression tests for plain and encoded spaces in share links. - Update helper submodule for config lookup and tabular storage fixes. - Update quart_common with JSON error response detection. - Verify all 150 tests pass on CPython 3.15.0.
67 lines
2.2 KiB
Python
67 lines
2.2 KiB
Python
import asyncio
|
|
import importlib.util
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
from quart import Quart
|
|
|
|
def load_share_module(monkeypatch):
|
|
stubs = {
|
|
'my_modules.decoratory.header': {'login_required': lambda fn: fn},
|
|
'my_modules.functions': {'get_ip': lambda: '192.0.2.1'},
|
|
'my_modules.app.setup': {
|
|
'LIMITER': types.SimpleNamespace(limit=lambda *args, **kwargs: lambda fn: fn),
|
|
},
|
|
'my_modules.app.logger': {'logger': None},
|
|
'my_modules.expiry': {'parse_expires': lambda value: value},
|
|
'quart_common.web.wide_event': {'add_wide_event_context': lambda **kwargs: None},
|
|
}
|
|
for name, values in stubs.items():
|
|
module = types.ModuleType(name)
|
|
module.__dict__.update(values)
|
|
monkeypatch.setitem(sys.modules, name, module)
|
|
|
|
path = Path(__file__).resolve().parents[1] / 'routes/side/main.py'
|
|
spec = importlib.util.spec_from_file_location('share_route_under_test', path)
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
@pytest.mark.parametrize('suffix', [
|
|
'',
|
|
' in female shape like https:/example.com/-kzmIlsf7uUe',
|
|
'%20in%20female%20shape%20like%20https:/example.com/-kzmIlsf7uUe',
|
|
])
|
|
def test_share_link_looks_up_only_id(monkeypatch, suffix):
|
|
async def run_test():
|
|
module = load_share_module(monkeypatch)
|
|
app = Quart(__name__)
|
|
app.secret_key = 'test-secret'
|
|
app.convex = types.SimpleNamespace(get_file=AsyncMock(return_value=None))
|
|
app.register_blueprint(module.side_main_bp)
|
|
|
|
response = await app.test_client().get('/-eluTduc-npP' + suffix)
|
|
|
|
assert response.status_code == 404
|
|
app.convex.get_file.assert_awaited_once_with(file_id='eluTduc-npP')
|
|
|
|
asyncio.run(run_test())
|
|
|
|
@pytest.mark.parametrize('path', ['/-/other', '/-%20text', '/-file/other'])
|
|
def test_invalid_share_id_does_not_query_storage(monkeypatch, path):
|
|
async def run_test():
|
|
module = load_share_module(monkeypatch)
|
|
app = Quart(__name__)
|
|
app.convex = types.SimpleNamespace(get_file=AsyncMock())
|
|
app.register_blueprint(module.side_main_bp)
|
|
|
|
response = await app.test_client().get(path)
|
|
|
|
assert response.status_code == 404
|
|
app.convex.get_file.assert_not_awaited()
|
|
|
|
asyncio.run(run_test())
|