Files
flaskpaste/tests/test_database.py
Username 8f9868f0d9 flaskpaste: initial commit with security hardening
Features:
- REST API for text/binary pastes with MIME detection
- Client certificate auth via X-SSL-Client-SHA1 header
- SQLite with WAL mode for concurrent access
- Automatic paste expiry with LRU cleanup

Security:
- HSTS, CSP, X-Frame-Options, X-Content-Type-Options
- Cache-Control: no-store for sensitive responses
- X-Request-ID tracing for log correlation
- X-Proxy-Secret validation for defense-in-depth
- Parameterized queries, input validation
- Size limits (3 MiB anon, 50 MiB auth)

Includes /health endpoint, container support, and 70 tests.
2025-12-16 04:42:18 +01:00

91 lines
3.0 KiB
Python

"""Tests for database operations."""
import json
import time
from unittest.mock import patch
class TestDatabaseOperations:
"""Tests for database functionality."""
def test_paste_persists(self, client, sample_text):
"""Paste persists in database and can be retrieved."""
create = client.post("/", data=sample_text, content_type="text/plain")
paste_id = json.loads(create.data)["id"]
response = client.get(f"/{paste_id}/raw")
assert response.data.decode("utf-8") == sample_text
def test_multiple_pastes_independent(self, client):
"""Multiple pastes have unique IDs and content."""
create1 = client.post("/", data="paste one", content_type="text/plain")
create2 = client.post("/", data="paste two", content_type="text/plain")
id1 = json.loads(create1.data)["id"]
id2 = json.loads(create2.data)["id"]
assert id1 != id2
raw1 = client.get(f"/{id1}/raw")
raw2 = client.get(f"/{id2}/raw")
assert raw1.data.decode("utf-8") == "paste one"
assert raw2.data.decode("utf-8") == "paste two"
def test_last_accessed_updated_on_get(self, client, sample_text):
"""Last accessed timestamp updates on retrieval."""
create = client.post("/", data=sample_text, content_type="text/plain")
paste_id = json.loads(create.data)["id"]
# Get the paste twice with a small delay
client.get(f"/{paste_id}")
first_access = time.time()
time.sleep(0.1)
client.get(f"/{paste_id}")
second_access = time.time()
# Timestamps should be close to current time (within 2 seconds)
assert second_access > first_access
class TestCleanupExpiredPastes:
"""Tests for paste expiry and cleanup."""
def test_expired_paste_cleaned_up(self, app, client, sample_text):
"""Expired pastes are removed by cleanup."""
from app.database import cleanup_expired_pastes
# Create a paste
create = client.post("/", data=sample_text, content_type="text/plain")
paste_id = json.loads(create.data)["id"]
# Verify it exists
assert client.get(f"/{paste_id}").status_code == 200
# Mock time to simulate expiry (paste expiry + 1 second)
future_time = time.time() + app.config["PASTE_EXPIRY_SECONDS"] + 1
with patch("time.time", return_value=future_time):
with app.app_context():
deleted = cleanup_expired_pastes()
assert deleted >= 1
# Paste should now be gone
assert client.get(f"/{paste_id}").status_code == 404
def test_non_expired_paste_kept(self, app, client, sample_text):
"""Non-expired pastes are preserved by cleanup."""
from app.database import cleanup_expired_pastes
create = client.post("/", data=sample_text, content_type="text/plain")
paste_id = json.loads(create.data)["id"]
with app.app_context():
deleted = cleanup_expired_pastes()
assert deleted == 0
assert client.get(f"/{paste_id}").status_code == 200