"""Integration tests for audit logs and history endpoints.""" import pytest from datetime import datetime, timedelta from tests.conftest import upload_test_file class TestAuditLogsEndpoint: """Tests for /api/v1/audit-logs endpoint.""" @pytest.mark.integration def test_list_audit_logs_returns_valid_response(self, integration_client): """Test that audit logs endpoint returns valid paginated response.""" response = integration_client.get("/api/v1/audit-logs") assert response.status_code == 200 data = response.json() assert "items" in data assert "pagination" in data assert isinstance(data["items"], list) pagination = data["pagination"] assert "page" in pagination assert "limit" in pagination assert "total" in pagination assert "total_pages" in pagination @pytest.mark.integration def test_audit_logs_respects_pagination(self, integration_client): """Test that audit logs endpoint respects limit parameter.""" response = integration_client.get("/api/v1/audit-logs?limit=5") assert response.status_code == 200 data = response.json() assert len(data["items"]) <= 5 assert data["pagination"]["limit"] == 5 @pytest.mark.integration def test_audit_logs_filter_by_action(self, integration_client, test_package): """Test filtering audit logs by action type.""" # Create an action that will be logged project_name, package_name = test_package response = integration_client.get("/api/v1/audit-logs?action=project.create") assert response.status_code == 200 data = response.json() # All items should have the filtered action for item in data["items"]: assert item["action"] == "project.create" @pytest.mark.integration def test_audit_log_entry_has_required_fields( self, integration_client, test_project ): """Test that audit log entries have all required fields.""" # Force some audit logs by operations on test_project response = integration_client.get("/api/v1/audit-logs?limit=10") assert response.status_code == 200 data = response.json() if data["items"]: item = data["items"][0] assert "id" in item assert "action" in item assert "resource" in item assert "user_id" in item assert "timestamp" in item class TestProjectAuditLogs: """Tests for /api/v1/projects/{project}/audit-logs endpoint.""" @pytest.mark.integration def test_project_audit_logs_returns_200(self, integration_client, test_project): """Test that project audit logs endpoint returns 200.""" response = integration_client.get(f"/api/v1/projects/{test_project}/audit-logs") assert response.status_code == 200 data = response.json() assert "items" in data assert "pagination" in data @pytest.mark.integration def test_project_audit_logs_not_found(self, integration_client): """Test that non-existent project returns 404.""" response = integration_client.get( "/api/v1/projects/nonexistent-project/audit-logs" ) assert response.status_code == 404 class TestPackageAuditLogs: """Tests for /api/v1/project/{project}/{package}/audit-logs endpoint.""" @pytest.mark.integration def test_package_audit_logs_returns_200(self, integration_client, test_package): """Test that package audit logs endpoint returns 200.""" project_name, package_name = test_package response = integration_client.get( f"/api/v1/project/{project_name}/{package_name}/audit-logs" ) assert response.status_code == 200 data = response.json() assert "items" in data assert "pagination" in data @pytest.mark.integration def test_package_audit_logs_project_not_found(self, integration_client): """Test that non-existent project returns 404.""" response = integration_client.get( "/api/v1/project/nonexistent/nonexistent/audit-logs" ) assert response.status_code == 404 @pytest.mark.integration def test_package_audit_logs_package_not_found( self, integration_client, test_project ): """Test that non-existent package returns 404.""" response = integration_client.get( f"/api/v1/project/{test_project}/nonexistent-package/audit-logs" ) assert response.status_code == 404 class TestPackageUploads: """Tests for /api/v1/project/{project}/{package}/uploads endpoint.""" @pytest.mark.integration def test_package_uploads_returns_200(self, integration_client, test_package): """Test that package uploads endpoint returns 200.""" project_name, package_name = test_package response = integration_client.get( f"/api/v1/project/{project_name}/{package_name}/uploads" ) assert response.status_code == 200 data = response.json() assert "items" in data assert "pagination" in data @pytest.mark.integration def test_package_uploads_after_upload(self, integration_client, test_package): """Test that uploads are recorded after file upload.""" project_name, package_name = test_package # Upload a file upload_result = upload_test_file( integration_client, project_name, package_name, b"test upload content", "test.txt", ) assert upload_result["artifact_id"] # Check uploads endpoint response = integration_client.get( f"/api/v1/project/{project_name}/{package_name}/uploads" ) assert response.status_code == 200 data = response.json() assert len(data["items"]) >= 1 # Verify upload record fields upload = data["items"][0] assert "artifact_id" in upload assert "package_name" in upload assert "project_name" in upload assert "uploaded_at" in upload assert "uploaded_by" in upload @pytest.mark.integration def test_package_uploads_project_not_found(self, integration_client): """Test that non-existent project returns 404.""" response = integration_client.get( "/api/v1/project/nonexistent/nonexistent/uploads" ) assert response.status_code == 404 class TestArtifactUploads: """Tests for /api/v1/artifact/{id}/uploads endpoint.""" @pytest.mark.integration def test_artifact_uploads_returns_200(self, integration_client, test_package): """Test that artifact uploads endpoint returns 200.""" project_name, package_name = test_package # Upload a file upload_result = upload_test_file( integration_client, project_name, package_name, b"artifact upload test", "artifact.txt", ) artifact_id = upload_result["artifact_id"] response = integration_client.get(f"/api/v1/artifact/{artifact_id}/uploads") assert response.status_code == 200 data = response.json() assert "items" in data assert "pagination" in data assert len(data["items"]) >= 1 @pytest.mark.integration def test_artifact_uploads_not_found(self, integration_client): """Test that non-existent artifact returns 404.""" fake_hash = "a" * 64 response = integration_client.get(f"/api/v1/artifact/{fake_hash}/uploads") assert response.status_code == 404 class TestArtifactProvenance: """Tests for /api/v1/artifact/{id}/history endpoint.""" @pytest.mark.integration def test_artifact_history_returns_200(self, integration_client, test_package): """Test that artifact history endpoint returns 200.""" project_name, package_name = test_package # Upload a file upload_result = upload_test_file( integration_client, project_name, package_name, b"provenance test content", "prov.txt", ) artifact_id = upload_result["artifact_id"] response = integration_client.get(f"/api/v1/artifact/{artifact_id}/history") assert response.status_code == 200 @pytest.mark.integration def test_artifact_history_has_required_fields( self, integration_client, test_package ): """Test that artifact history has all required fields.""" project_name, package_name = test_package # Upload a file upload_result = upload_test_file( integration_client, project_name, package_name, b"provenance fields test", "fields.txt", ) artifact_id = upload_result["artifact_id"] response = integration_client.get(f"/api/v1/artifact/{artifact_id}/history") assert response.status_code == 200 data = response.json() assert "artifact_id" in data assert "sha256" in data assert "size" in data assert "created_at" in data assert "created_by" in data assert "ref_count" in data assert "first_uploaded_at" in data assert "first_uploaded_by" in data assert "upload_count" in data assert "packages" in data assert "tags" in data assert "uploads" in data @pytest.mark.integration def test_artifact_history_not_found(self, integration_client): """Test that non-existent artifact returns 404.""" fake_hash = "b" * 64 response = integration_client.get(f"/api/v1/artifact/{fake_hash}/history") assert response.status_code == 404 @pytest.mark.integration def test_artifact_history_with_tag(self, integration_client, test_package): """Test artifact history includes tag information when tagged.""" project_name, package_name = test_package # Upload a file with a tag upload_result = upload_test_file( integration_client, project_name, package_name, b"tagged provenance test", "tagged.txt", tag="v1.0.0", ) artifact_id = upload_result["artifact_id"] response = integration_client.get(f"/api/v1/artifact/{artifact_id}/history") assert response.status_code == 200 data = response.json() # Should have at least one tag assert len(data["tags"]) >= 1 # Tag should have required fields tag = data["tags"][0] assert "project_name" in tag assert "package_name" in tag assert "tag_name" in tag