"""Tests for FlaskPaste API endpoints.""" import json from app.config import VERSION class TestIndex: """Tests for GET / endpoint.""" def test_get_api_info(self, client): """GET / returns API information.""" response = client.get("/") assert response.status_code == 200 data = json.loads(response.data) assert data["version"] == VERSION assert "endpoints" in data assert "usage" in data def test_api_info_contains_endpoints(self, client): """API info lists all endpoints.""" response = client.get("/") data = json.loads(response.data) endpoints = data["endpoints"] assert "GET /" in endpoints assert "POST /" in endpoints assert "GET /" in endpoints assert "GET //raw" in endpoints assert "DELETE /" in endpoints class TestHealth: """Tests for GET /health endpoint.""" def test_health_endpoint_returns_ok(self, client): """Health endpoint returns healthy status.""" response = client.get("/health") assert response.status_code == 200 data = json.loads(response.data) assert data["status"] == "healthy" assert data["database"] == "ok" def test_health_endpoint_json_response(self, client): """Health endpoint returns JSON content type.""" response = client.get("/health") assert "application/json" in response.content_type class TestCreatePaste: """Tests for POST / endpoint.""" def test_create_paste_raw(self, client, sample_text): """Create paste with raw text body.""" response = client.post( "/", data=sample_text, content_type="text/plain", ) assert response.status_code == 201 data = json.loads(response.data) assert "id" in data assert len(data["id"]) == 12 assert data["mime_type"] == "text/plain" assert "url" in data assert "raw" in data def test_create_paste_json(self, client, sample_json): """Create paste with JSON body.""" response = client.post( "/", data=json.dumps(sample_json), content_type="application/json", ) assert response.status_code == 201 data = json.loads(response.data) assert "id" in data assert data["mime_type"] == "text/plain" def test_create_paste_binary(self, client, png_bytes): """Create paste with binary content returns octet-stream (magic detection disabled).""" response = client.post( "/", data=png_bytes, content_type="application/octet-stream", ) assert response.status_code == 201 data = json.loads(response.data) # Magic byte detection disabled - binary content is octet-stream assert data["mime_type"] == "application/octet-stream" def test_create_paste_empty_fails(self, client): """Create paste with empty content fails.""" response = client.post("/", data="") assert response.status_code == 400 data = json.loads(response.data) assert "error" in data def test_create_paste_with_auth(self, client, sample_text, auth_header): """Create paste with authentication includes owner.""" response = client.post( "/", data=sample_text, content_type="text/plain", headers=auth_header, ) assert response.status_code == 201 data = json.loads(response.data) assert "owner" in data assert data["owner"] == "a" * 40 def test_create_paste_invalid_auth_ignored(self, client, sample_text): """Invalid auth header is ignored (treated as anonymous).""" response = client.post( "/", data=sample_text, content_type="text/plain", headers={"X-SSL-Client-SHA1": "invalid"}, ) assert response.status_code == 201 data = json.loads(response.data) assert "owner" not in data class TestGetPaste: """Tests for GET / endpoint.""" def test_get_paste_metadata(self, client, sample_text): """Get paste returns metadata.""" create = client.post("/", data=sample_text, content_type="text/plain") paste_id = json.loads(create.data)["id"] response = client.get(f"/{paste_id}") assert response.status_code == 200 data = json.loads(response.data) assert data["id"] == paste_id assert data["mime_type"] == "text/plain" assert data["size"] == len(sample_text) assert "created_at" in data assert "raw" in data def test_get_paste_not_found(self, client): """Get nonexistent paste returns 404.""" response = client.get("/abcd12345678") assert response.status_code == 404 data = json.loads(response.data) assert "error" in data def test_get_paste_invalid_id(self, client): """Get paste with invalid ID returns 400.""" response = client.get("/invalid!") assert response.status_code == 400 def test_get_paste_wrong_length_id(self, client): """Get paste with wrong length ID returns 400.""" response = client.get("/abc") assert response.status_code == 400 def test_head_paste_metadata(self, client, sample_text): """HEAD returns headers without body.""" create = client.post("/", data=sample_text, content_type="text/plain") paste_id = json.loads(create.data)["id"] response = client.head(f"/{paste_id}") assert response.status_code == 200 assert response.content_type == "application/json" assert response.data == b"" # No body for HEAD def test_head_paste_not_found(self, client): """HEAD on nonexistent paste returns 404.""" response = client.head("/abcd12345678") assert response.status_code == 404 class TestGetPasteRaw: """Tests for GET //raw endpoint.""" def test_get_paste_raw_text(self, client, sample_text): """Get raw paste returns content with correct MIME type.""" 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.status_code == 200 assert response.data.decode("utf-8") == sample_text assert response.content_type == "text/plain; charset=utf-8" def test_get_paste_raw_binary(self, client, png_bytes): """Get raw binary paste returns correct content.""" create = client.post( "/", data=png_bytes, content_type="application/octet-stream", ) paste_id = json.loads(create.data)["id"] response = client.get(f"/{paste_id}/raw") assert response.status_code == 200 assert response.data == png_bytes # Magic byte detection disabled - binary served as octet-stream assert response.content_type == "application/octet-stream" def test_get_paste_raw_not_found(self, client): """Get raw nonexistent paste returns 404.""" response = client.get("/abcd12345678/raw") assert response.status_code == 404 def test_get_paste_raw_inline_disposition(self, client, sample_text): """Text and image pastes have inline disposition.""" 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.headers.get("Content-Disposition") == "inline" def test_head_paste_raw(self, client, sample_text): """HEAD on raw returns content-type without body.""" create = client.post("/", data=sample_text, content_type="text/plain") paste_id = json.loads(create.data)["id"] response = client.head(f"/{paste_id}/raw") assert response.status_code == 200 assert response.content_type == "text/plain; charset=utf-8" assert response.data == b"" # No body for HEAD class TestDeletePaste: """Tests for DELETE / endpoint.""" def test_delete_paste_success(self, client, sample_text, auth_header): """Delete owned paste succeeds.""" create = client.post( "/", data=sample_text, content_type="text/plain", headers=auth_header, ) paste_id = json.loads(create.data)["id"] response = client.delete(f"/{paste_id}", headers=auth_header) assert response.status_code == 200 data = json.loads(response.data) assert "message" in data # Verify deletion get_response = client.get(f"/{paste_id}") assert get_response.status_code == 404 def test_delete_paste_no_auth(self, client, sample_text): """Delete without authentication fails.""" create = client.post("/", data=sample_text, content_type="text/plain") paste_id = json.loads(create.data)["id"] response = client.delete(f"/{paste_id}") assert response.status_code == 401 data = json.loads(response.data) assert "error" in data def test_delete_paste_wrong_owner(self, client, sample_text, auth_header, other_auth_header): """Delete paste owned by another user fails.""" create = client.post( "/", data=sample_text, content_type="text/plain", headers=auth_header, ) paste_id = json.loads(create.data)["id"] response = client.delete(f"/{paste_id}", headers=other_auth_header) assert response.status_code == 403 data = json.loads(response.data) assert "error" in data def test_delete_anonymous_paste_fails(self, client, sample_text, auth_header): """Cannot delete anonymous paste (no owner).""" create = client.post("/", data=sample_text, content_type="text/plain") paste_id = json.loads(create.data)["id"] response = client.delete(f"/{paste_id}", headers=auth_header) assert response.status_code == 403 def test_delete_paste_not_found(self, client, auth_header): """Delete nonexistent paste returns 404.""" response = client.delete("/abcd12345678", headers=auth_header) assert response.status_code == 404 def test_delete_paste_invalid_id(self, client, auth_header): """Delete with invalid ID returns 400.""" response = client.delete("/invalid!", headers=auth_header) assert response.status_code == 400