|
|
|
|
@@ -46,27 +46,27 @@ class TestArtifactRetrieval:
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_artifact_includes_tags(self, integration_client, test_package):
|
|
|
|
|
"""Test artifact response includes tags pointing to it."""
|
|
|
|
|
def test_artifact_includes_versions(self, integration_client, test_package):
|
|
|
|
|
"""Test artifact response includes versions pointing to it."""
|
|
|
|
|
project_name, package_name = test_package
|
|
|
|
|
content = b"artifact with tags test"
|
|
|
|
|
content = b"artifact with versions test"
|
|
|
|
|
expected_hash = compute_sha256(content)
|
|
|
|
|
|
|
|
|
|
upload_test_file(
|
|
|
|
|
integration_client, project_name, package_name, content, version="tagged-v1"
|
|
|
|
|
integration_client, project_name, package_name, content, version="1.0.0"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
response = integration_client.get(f"/api/v1/artifact/{expected_hash}")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
assert "tags" in data
|
|
|
|
|
assert len(data["tags"]) >= 1
|
|
|
|
|
assert "versions" in data
|
|
|
|
|
assert len(data["versions"]) >= 1
|
|
|
|
|
|
|
|
|
|
tag = data["tags"][0]
|
|
|
|
|
assert "name" in tag
|
|
|
|
|
assert "package_name" in tag
|
|
|
|
|
assert "project_name" in tag
|
|
|
|
|
version = data["versions"][0]
|
|
|
|
|
assert "version" in version
|
|
|
|
|
assert "package_name" in version
|
|
|
|
|
assert "project_name" in version
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestArtifactStats:
|
|
|
|
|
@@ -94,7 +94,7 @@ class TestArtifactStats:
|
|
|
|
|
assert "size" in data
|
|
|
|
|
assert "ref_count" in data
|
|
|
|
|
assert "storage_savings" in data
|
|
|
|
|
assert "tags" in data
|
|
|
|
|
assert "versions" in data
|
|
|
|
|
assert "projects" in data
|
|
|
|
|
assert "packages" in data
|
|
|
|
|
|
|
|
|
|
@@ -203,7 +203,7 @@ class TestArtifactProvenance:
|
|
|
|
|
assert "first_uploaded_by" in data
|
|
|
|
|
assert "upload_count" in data
|
|
|
|
|
assert "packages" in data
|
|
|
|
|
assert "tags" in data
|
|
|
|
|
assert "versions" in data
|
|
|
|
|
assert "uploads" in data
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
@@ -214,17 +214,17 @@ class TestArtifactProvenance:
|
|
|
|
|
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."""
|
|
|
|
|
def test_artifact_history_with_version(self, integration_client, test_package):
|
|
|
|
|
"""Test artifact history includes version information when versioned."""
|
|
|
|
|
project_name, package_name = test_package
|
|
|
|
|
|
|
|
|
|
upload_result = upload_test_file(
|
|
|
|
|
integration_client,
|
|
|
|
|
project_name,
|
|
|
|
|
package_name,
|
|
|
|
|
b"tagged provenance test",
|
|
|
|
|
"tagged.txt",
|
|
|
|
|
tag="v1.0.0",
|
|
|
|
|
b"versioned provenance test",
|
|
|
|
|
"versioned.txt",
|
|
|
|
|
version="v1.0.0",
|
|
|
|
|
)
|
|
|
|
|
artifact_id = upload_result["artifact_id"]
|
|
|
|
|
|
|
|
|
|
@@ -232,12 +232,12 @@ class TestArtifactProvenance:
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
assert len(data["tags"]) >= 1
|
|
|
|
|
assert len(data["versions"]) >= 1
|
|
|
|
|
|
|
|
|
|
tag = data["tags"][0]
|
|
|
|
|
assert "project_name" in tag
|
|
|
|
|
assert "package_name" in tag
|
|
|
|
|
assert "tag_name" in tag
|
|
|
|
|
version = data["versions"][0]
|
|
|
|
|
assert "project_name" in version
|
|
|
|
|
assert "package_name" in version
|
|
|
|
|
assert "version" in version
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestArtifactUploads:
|
|
|
|
|
@@ -306,24 +306,24 @@ class TestOrphanedArtifacts:
|
|
|
|
|
assert len(response.json()) <= 5
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_artifact_becomes_orphaned_when_tag_deleted(
|
|
|
|
|
def test_artifact_becomes_orphaned_when_version_deleted(
|
|
|
|
|
self, integration_client, test_package, unique_test_id
|
|
|
|
|
):
|
|
|
|
|
"""Test artifact appears in orphaned list after tag is deleted."""
|
|
|
|
|
"""Test artifact appears in orphaned list after version is deleted."""
|
|
|
|
|
project, package = test_package
|
|
|
|
|
content = f"orphan test {unique_test_id}".encode()
|
|
|
|
|
expected_hash = compute_sha256(content)
|
|
|
|
|
|
|
|
|
|
# Upload with tag
|
|
|
|
|
upload_test_file(integration_client, project, package, content, version="temp-tag")
|
|
|
|
|
# Upload with version
|
|
|
|
|
upload_test_file(integration_client, project, package, content, version="1.0.0-temp")
|
|
|
|
|
|
|
|
|
|
# Verify not in orphaned list
|
|
|
|
|
response = integration_client.get("/api/v1/admin/orphaned-artifacts?limit=1000")
|
|
|
|
|
orphaned_ids = [a["id"] for a in response.json()]
|
|
|
|
|
assert expected_hash not in orphaned_ids
|
|
|
|
|
|
|
|
|
|
# Delete the tag
|
|
|
|
|
integration_client.delete(f"/api/v1/project/{project}/{package}/tags/temp-tag")
|
|
|
|
|
# Delete the version
|
|
|
|
|
integration_client.delete(f"/api/v1/project/{project}/{package}/versions/1.0.0-temp")
|
|
|
|
|
|
|
|
|
|
# Verify now in orphaned list
|
|
|
|
|
response = integration_client.get("/api/v1/admin/orphaned-artifacts?limit=1000")
|
|
|
|
|
@@ -356,9 +356,9 @@ class TestGarbageCollection:
|
|
|
|
|
content = f"dry run test {unique_test_id}".encode()
|
|
|
|
|
expected_hash = compute_sha256(content)
|
|
|
|
|
|
|
|
|
|
# Upload and delete tag to create orphan
|
|
|
|
|
upload_test_file(integration_client, project, package, content, version="dry-run")
|
|
|
|
|
integration_client.delete(f"/api/v1/project/{project}/{package}/tags/dry-run")
|
|
|
|
|
# Upload and delete version to create orphan
|
|
|
|
|
upload_test_file(integration_client, project, package, content, version="1.0.0-dryrun")
|
|
|
|
|
integration_client.delete(f"/api/v1/project/{project}/{package}/versions/1.0.0-dryrun")
|
|
|
|
|
|
|
|
|
|
# Verify artifact exists
|
|
|
|
|
response = integration_client.get(f"/api/v1/artifact/{expected_hash}")
|
|
|
|
|
@@ -534,50 +534,6 @@ class TestGlobalArtifacts:
|
|
|
|
|
assert response.status_code == 400
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestGlobalTags:
|
|
|
|
|
"""Tests for global tags endpoint."""
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_global_tags_returns_200(self, integration_client):
|
|
|
|
|
"""Test global tags endpoint returns 200."""
|
|
|
|
|
response = integration_client.get("/api/v1/tags")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
assert "items" in data
|
|
|
|
|
assert "pagination" in data
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_global_tags_pagination(self, integration_client):
|
|
|
|
|
"""Test global tags endpoint respects pagination."""
|
|
|
|
|
response = integration_client.get("/api/v1/tags?limit=5&page=1")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
assert len(data["items"]) <= 5
|
|
|
|
|
assert data["pagination"]["limit"] == 5
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_global_tags_has_project_context(self, integration_client):
|
|
|
|
|
"""Test global tags response includes project/package context."""
|
|
|
|
|
response = integration_client.get("/api/v1/tags?limit=1")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
|
|
|
|
|
data = response.json()
|
|
|
|
|
if len(data["items"]) > 0:
|
|
|
|
|
item = data["items"][0]
|
|
|
|
|
assert "project_name" in item
|
|
|
|
|
assert "package_name" in item
|
|
|
|
|
assert "artifact_id" in item
|
|
|
|
|
|
|
|
|
|
@pytest.mark.integration
|
|
|
|
|
def test_global_tags_search_with_wildcard(self, integration_client):
|
|
|
|
|
"""Test global tags search supports wildcards."""
|
|
|
|
|
response = integration_client.get("/api/v1/tags?search=v*")
|
|
|
|
|
assert response.status_code == 200
|
|
|
|
|
# Just verify it doesn't error; results may vary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestAuditLogs:
|
|
|
|
|
"""Tests for global audit logs endpoint."""
|
|
|
|
|
|
|
|
|
|
|