- Remove Tag/TagHistory model tests from unit tests - Update CacheSettings tests to remove allow_public_internet field - Replace tag= with version= in upload_test_file calls - Update test assertions to use versions instead of tags - Remove tests for tag: prefix downloads (now uses version:) - Update dependency tests for version-only schema
306 lines
11 KiB
Python
306 lines
11 KiB
Python
"""
|
|
Integration tests for package version API endpoints.
|
|
|
|
Tests cover:
|
|
- Version creation via upload
|
|
- Version auto-detection from filename
|
|
- Version listing and retrieval
|
|
- Download by version prefix
|
|
- Version deletion
|
|
"""
|
|
|
|
import pytest
|
|
import io
|
|
from tests.factories import (
|
|
compute_sha256,
|
|
upload_test_file,
|
|
)
|
|
|
|
|
|
class TestVersionCreation:
|
|
"""Tests for creating versions via upload."""
|
|
|
|
@pytest.mark.integration
|
|
def test_upload_with_explicit_version(self, integration_client, test_package):
|
|
"""Test upload with explicit version parameter creates version record."""
|
|
project, package = test_package
|
|
content = b"version creation test"
|
|
expected_hash = compute_sha256(content)
|
|
|
|
files = {"file": ("app.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
response = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
data={"version": "1.0.0"},
|
|
)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
assert result["artifact_id"] == expected_hash
|
|
assert result.get("version") == "1.0.0"
|
|
assert result.get("version_source") == "explicit"
|
|
|
|
@pytest.mark.integration
|
|
def test_duplicate_version_same_content_succeeds(self, integration_client, test_package):
|
|
"""Test uploading same version with same content succeeds (deduplication)."""
|
|
project, package = test_package
|
|
content = b"version dedup test"
|
|
|
|
# First upload with version
|
|
files1 = {"file": ("app1.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
response1 = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files1,
|
|
data={"version": "3.0.0"},
|
|
)
|
|
assert response1.status_code == 200
|
|
|
|
# Second upload with same version and same content succeeds
|
|
files2 = {"file": ("app2.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
response2 = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files2,
|
|
data={"version": "3.0.0"},
|
|
)
|
|
# This succeeds because it's the same artifact (deduplication)
|
|
assert response2.status_code == 200
|
|
|
|
|
|
class TestVersionAutoDetection:
|
|
"""Tests for automatic version detection from filename."""
|
|
|
|
@pytest.mark.integration
|
|
def test_version_detected_from_filename_tarball(self, integration_client, test_package):
|
|
"""Test version is auto-detected from tarball filename or metadata."""
|
|
project, package = test_package
|
|
content = b"auto detect version tarball"
|
|
|
|
files = {"file": ("myapp-1.2.3.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
response = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
assert result.get("version") == "1.2.3"
|
|
# Version source can be 'filename' or 'metadata' depending on detection order
|
|
assert result.get("version_source") in ["filename", "metadata"]
|
|
|
|
@pytest.mark.integration
|
|
def test_version_detected_from_filename_zip(self, integration_client, test_package):
|
|
"""Test version is auto-detected from zip filename."""
|
|
project, package = test_package
|
|
content = b"auto detect version zip"
|
|
|
|
files = {"file": ("package-2.0.0.zip", io.BytesIO(content), "application/octet-stream")}
|
|
response = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
assert result.get("version") == "2.0.0"
|
|
assert result.get("version_source") == "filename"
|
|
|
|
@pytest.mark.integration
|
|
def test_explicit_version_overrides_filename(self, integration_client, test_package):
|
|
"""Test explicit version parameter overrides filename detection."""
|
|
project, package = test_package
|
|
content = b"explicit override test"
|
|
|
|
files = {"file": ("myapp-1.0.0.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
response = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
data={"version": "9.9.9"},
|
|
)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
assert result.get("version") == "9.9.9"
|
|
assert result.get("version_source") == "explicit"
|
|
|
|
@pytest.mark.integration
|
|
def test_no_version_detected_from_plain_filename(self, integration_client, test_package):
|
|
"""Test no version is created for filenames without version pattern."""
|
|
project, package = test_package
|
|
content = b"no version in filename"
|
|
|
|
files = {"file": ("plain-file.bin", io.BytesIO(content), "application/octet-stream")}
|
|
response = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
)
|
|
assert response.status_code == 200
|
|
result = response.json()
|
|
# Version should be None or not present
|
|
assert result.get("version") is None
|
|
|
|
|
|
class TestVersionListing:
|
|
"""Tests for listing and retrieving versions."""
|
|
|
|
@pytest.mark.integration
|
|
def test_list_versions(self, integration_client, test_package):
|
|
"""Test listing all versions for a package."""
|
|
project, package = test_package
|
|
|
|
# Create multiple versions
|
|
for ver in ["1.0.0", "1.1.0", "2.0.0"]:
|
|
content = f"version {ver} content".encode()
|
|
files = {"file": (f"app-{ver}.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
response = integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
data={"version": ver},
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
# List versions
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/versions"
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
versions = [v["version"] for v in data.get("items", data)]
|
|
assert "1.0.0" in versions
|
|
assert "1.1.0" in versions
|
|
assert "2.0.0" in versions
|
|
|
|
@pytest.mark.integration
|
|
def test_get_specific_version(self, integration_client, test_package):
|
|
"""Test getting details for a specific version."""
|
|
project, package = test_package
|
|
content = b"specific version test"
|
|
expected_hash = compute_sha256(content)
|
|
|
|
# Create version
|
|
files = {"file": ("app-4.0.0.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
data={"version": "4.0.0"},
|
|
)
|
|
|
|
# Get version details
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/versions/4.0.0"
|
|
)
|
|
assert response.status_code == 200
|
|
data = response.json()
|
|
assert data["version"] == "4.0.0"
|
|
assert data["artifact_id"] == expected_hash
|
|
|
|
@pytest.mark.integration
|
|
def test_get_nonexistent_version_returns_404(self, integration_client, test_package):
|
|
"""Test getting nonexistent version returns 404."""
|
|
project, package = test_package
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/versions/99.99.99"
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
class TestDownloadByVersion:
|
|
"""Tests for downloading artifacts by version."""
|
|
|
|
@pytest.mark.integration
|
|
def test_download_by_version_prefix(self, integration_client, test_package):
|
|
"""Test downloading artifact using version: prefix."""
|
|
project, package = test_package
|
|
content = b"download by version test"
|
|
expected_hash = compute_sha256(content)
|
|
|
|
# Upload with version
|
|
files = {"file": ("app.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
data={"version": "5.0.0"},
|
|
)
|
|
|
|
# Download by version prefix
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/+/version:5.0.0",
|
|
params={"mode": "proxy"},
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.content == content
|
|
|
|
@pytest.mark.integration
|
|
def test_download_nonexistent_version_returns_404(self, integration_client, test_package):
|
|
"""Test downloading nonexistent version returns 404."""
|
|
project, package = test_package
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/+/version:99.0.0"
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
@pytest.mark.integration
|
|
def test_version_resolution_with_prefix(self, integration_client, test_package):
|
|
"""Test that version: prefix explicitly resolves to version."""
|
|
project, package = test_package
|
|
version_content = b"this is the version content"
|
|
|
|
# Create a version 6.0.0
|
|
files1 = {"file": ("app-v.tar.gz", io.BytesIO(version_content), "application/octet-stream")}
|
|
integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files1,
|
|
data={"version": "6.0.0"},
|
|
)
|
|
|
|
# Download with version: prefix should get version content
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/+/version:6.0.0",
|
|
params={"mode": "proxy"},
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.content == version_content
|
|
|
|
|
|
class TestVersionDeletion:
|
|
"""Tests for deleting versions."""
|
|
|
|
@pytest.mark.integration
|
|
def test_delete_version(self, integration_client, test_package):
|
|
"""Test deleting a version."""
|
|
project, package = test_package
|
|
content = b"delete version test"
|
|
|
|
# Create version
|
|
files = {"file": ("app.tar.gz", io.BytesIO(content), "application/octet-stream")}
|
|
integration_client.post(
|
|
f"/api/v1/project/{project}/{package}/upload",
|
|
files=files,
|
|
data={"version": "7.0.0"},
|
|
)
|
|
|
|
# Verify version exists
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/versions/7.0.0"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
# Delete version - returns 204 No Content on success
|
|
delete_response = integration_client.delete(
|
|
f"/api/v1/project/{project}/{package}/versions/7.0.0"
|
|
)
|
|
assert delete_response.status_code == 204
|
|
|
|
# Verify version no longer exists
|
|
response2 = integration_client.get(
|
|
f"/api/v1/project/{project}/{package}/versions/7.0.0"
|
|
)
|
|
assert response2.status_code == 404
|
|
|
|
@pytest.mark.integration
|
|
def test_delete_nonexistent_version_returns_404(self, integration_client, test_package):
|
|
"""Test deleting nonexistent version returns 404."""
|
|
project, package = test_package
|
|
|
|
response = integration_client.delete(
|
|
f"/api/v1/project/{project}/{package}/versions/99.0.0"
|
|
)
|
|
assert response.status_code == 404
|