Files
flaskpaste/tests/test_scheduled_cleanup.py
Username bfc238b5cf
Some checks failed
CI / Lint & Format (push) Successful in 16s
CI / Security Scan (push) Failing after 19s
CI / Tests (push) Successful in 34s
add CLI enhancements and scheduled cleanup
CLI commands:
- list: show user's pastes with pagination
- search: filter by type (glob), after/before timestamps
- update: modify content, password, or extend expiry
- export: save pastes to directory with optional decryption

API changes:
- PUT /<id>: update paste content and metadata
- GET /pastes: add type, after, before query params

Scheduled tasks:
- Thread-safe cleanup with per-task intervals
- Activate cleanup_expired_hashes (15min)
- Activate cleanup_rate_limits (5min)

Tests: 205 passing
2025-12-20 20:13:00 +01:00

170 lines
5.2 KiB
Python

"""Tests for scheduled cleanup functionality."""
import time
class TestScheduledCleanup:
"""Tests for scheduled cleanup in before_request hook."""
def test_cleanup_times_reset(self, app, client):
"""Verify cleanup times can be reset for testing."""
from app.api import _cleanup_times, reset_cleanup_times
# Set some cleanup times
with app.app_context():
reset_cleanup_times()
for key in _cleanup_times:
assert _cleanup_times[key] == 0
def test_cleanup_runs_on_request(self, app, client, auth_header):
"""Verify cleanup runs during request handling."""
from app.api import _cleanup_times, reset_cleanup_times
with app.app_context():
reset_cleanup_times()
# Make a request to trigger cleanup
client.get("/health")
# Cleanup times should be set
with app.app_context():
for key in _cleanup_times:
assert _cleanup_times[key] > 0
def test_cleanup_respects_intervals(self, app, client):
"""Verify cleanup respects configured intervals."""
from app.api import _cleanup_times, reset_cleanup_times
with app.app_context():
reset_cleanup_times()
# First request triggers cleanup
client.get("/health")
with app.app_context():
first_times = {k: v for k, v in _cleanup_times.items()}
# Immediate second request should not reset times
client.get("/health")
with app.app_context():
for key in _cleanup_times:
assert _cleanup_times[key] == first_times[key]
def test_expired_paste_cleanup(self, app, client, auth_header):
"""Test that expired pastes are cleaned up."""
import json
from app.database import get_db
# Create paste with short expiry
response = client.post(
"/",
data="test content",
content_type="text/plain",
headers={**auth_header, "X-Expiry": "1"}, # 1 second
)
assert response.status_code == 201
paste_id = json.loads(response.data)["id"]
# Wait for expiry
time.sleep(1.5)
# Trigger cleanup via database function
with app.app_context():
from app.database import cleanup_expired_pastes
count = cleanup_expired_pastes()
assert count >= 1
# Verify paste is gone
db = get_db()
row = db.execute("SELECT id FROM pastes WHERE id = ?", (paste_id,)).fetchone()
assert row is None
def test_rate_limit_cleanup(self, app, client):
"""Test that rate limit entries are cleaned up."""
from app.api.routes import (
_rate_limit_requests,
check_rate_limit,
cleanup_rate_limits,
reset_rate_limits,
)
with app.app_context():
reset_rate_limits()
# Add some rate limit entries
check_rate_limit("192.168.1.1", authenticated=False)
check_rate_limit("192.168.1.2", authenticated=False)
assert len(_rate_limit_requests) == 2
# Cleanup with very large window (nothing should be removed)
count = cleanup_rate_limits(window=3600)
assert count == 0
# Wait a bit and cleanup with tiny window
time.sleep(0.1)
count = cleanup_rate_limits(window=0) # Immediate cleanup
assert count == 2
assert len(_rate_limit_requests) == 0
class TestCleanupThreadSafety:
"""Tests for thread-safety of cleanup operations."""
def test_cleanup_lock_exists(self, app):
"""Verify cleanup lock exists."""
import threading
from app.api import _cleanup_lock
assert isinstance(_cleanup_lock, type(threading.Lock()))
def test_concurrent_cleanup_access(self, app, client):
"""Test that concurrent requests don't corrupt cleanup state."""
import threading
from app.api import reset_cleanup_times
with app.app_context():
reset_cleanup_times()
errors = []
results = []
def make_request():
try:
resp = client.get("/health")
results.append(resp.status_code)
except Exception as e:
errors.append(str(e))
# Simulate concurrent requests
threads = [threading.Thread(target=make_request) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
assert not errors
assert all(r == 200 for r in results)
class TestCleanupConfiguration:
"""Tests for cleanup configuration."""
def test_cleanup_intervals_configured(self, app):
"""Verify cleanup intervals are properly configured."""
from app.api import _CLEANUP_INTERVALS
assert "pastes" in _CLEANUP_INTERVALS
assert "hashes" in _CLEANUP_INTERVALS
assert "rate_limits" in _CLEANUP_INTERVALS
# Verify reasonable intervals
assert _CLEANUP_INTERVALS["pastes"] >= 60 # At least 1 minute
assert _CLEANUP_INTERVALS["hashes"] >= 60
assert _CLEANUP_INTERVALS["rate_limits"] >= 60