413 lines
13 KiB
Python
413 lines
13 KiB
Python
"""
|
|
Integration tests for version API endpoints.
|
|
|
|
Tests cover:
|
|
- Version creation via upload
|
|
- Version auto-detection from filename
|
|
- Version listing with pagination
|
|
- Version deletion
|
|
- Download by version ref
|
|
- ref_count behavior with version operations
|
|
"""
|
|
|
|
import pytest
|
|
from tests.factories import upload_test_file
|
|
|
|
|
|
class TestVersionCreation:
|
|
"""Tests for version creation during upload."""
|
|
|
|
@pytest.mark.integration
|
|
def test_upload_with_explicit_version(self, integration_client, test_package):
|
|
"""Test creating a version via explicit version parameter."""
|
|
project_name, package_name = test_package
|
|
|
|
result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"version create test",
|
|
tag="latest",
|
|
version="1.0.0",
|
|
)
|
|
|
|
assert result["tag"] == "latest"
|
|
assert result["version"] == "1.0.0"
|
|
assert result["version_source"] == "explicit"
|
|
assert result["artifact_id"]
|
|
|
|
@pytest.mark.integration
|
|
def test_upload_with_version_auto_detect_from_tarball(
|
|
self, integration_client, test_package
|
|
):
|
|
"""Test version auto-detection from tarball filename pattern."""
|
|
project_name, package_name = test_package
|
|
|
|
result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"auto version test",
|
|
filename="myapp-2.1.0.tar.gz",
|
|
)
|
|
|
|
assert result["version"] == "2.1.0"
|
|
# Tarball metadata extractor parses version from filename
|
|
assert result["version_source"] == "metadata"
|
|
|
|
@pytest.mark.integration
|
|
def test_upload_with_version_auto_detect_v_prefix(
|
|
self, integration_client, test_package
|
|
):
|
|
"""Test version auto-detection strips 'v' prefix from tarball filename."""
|
|
project_name, package_name = test_package
|
|
|
|
result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"v prefix test",
|
|
filename="package-v3.0.0.tar.gz",
|
|
)
|
|
|
|
assert result["version"] == "3.0.0"
|
|
# Tarball metadata extractor parses version from filename
|
|
assert result["version_source"] == "metadata"
|
|
|
|
@pytest.mark.integration
|
|
def test_upload_duplicate_version_warning(self, integration_client, test_package):
|
|
"""Test that duplicate version during upload returns response without error."""
|
|
project_name, package_name = test_package
|
|
|
|
# Upload with version 1.0.0
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"first upload",
|
|
version="1.0.0",
|
|
)
|
|
|
|
# Upload different content with same version - should succeed but no new version
|
|
result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"second upload different content",
|
|
version="1.0.0",
|
|
)
|
|
|
|
# Upload succeeds but version may not be set (duplicate)
|
|
assert result["artifact_id"]
|
|
|
|
|
|
class TestVersionCRUD:
|
|
"""Tests for version list, get, delete operations."""
|
|
|
|
@pytest.mark.integration
|
|
def test_list_versions(self, integration_client, test_package):
|
|
"""Test listing versions for a package."""
|
|
project_name, package_name = test_package
|
|
|
|
# Create some versions
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"v1 content",
|
|
version="1.0.0",
|
|
)
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"v2 content",
|
|
version="2.0.0",
|
|
)
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
assert "items" in data
|
|
assert "pagination" in data
|
|
|
|
versions = [v["version"] for v in data["items"]]
|
|
assert "1.0.0" in versions
|
|
assert "2.0.0" in versions
|
|
|
|
@pytest.mark.integration
|
|
def test_list_versions_with_artifact_info(self, integration_client, test_package):
|
|
"""Test that version list includes artifact metadata."""
|
|
project_name, package_name = test_package
|
|
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"version with info",
|
|
version="1.0.0",
|
|
tag="release",
|
|
)
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
assert len(data["items"]) >= 1
|
|
|
|
version_item = next(
|
|
(v for v in data["items"] if v["version"] == "1.0.0"), None
|
|
)
|
|
assert version_item is not None
|
|
assert "size" in version_item
|
|
assert "artifact_id" in version_item
|
|
assert "tags" in version_item
|
|
assert "release" in version_item["tags"]
|
|
|
|
@pytest.mark.integration
|
|
def test_get_version(self, integration_client, test_package):
|
|
"""Test getting a specific version."""
|
|
project_name, package_name = test_package
|
|
|
|
upload_result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"get version test",
|
|
version="3.0.0",
|
|
)
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions/3.0.0"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
assert data["version"] == "3.0.0"
|
|
assert data["artifact_id"] == upload_result["artifact_id"]
|
|
assert data["version_source"] == "explicit"
|
|
|
|
@pytest.mark.integration
|
|
def test_get_version_not_found(self, integration_client, test_package):
|
|
"""Test getting a non-existent version returns 404."""
|
|
project_name, package_name = test_package
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions/99.99.99"
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
@pytest.mark.integration
|
|
def test_delete_version(self, integration_client, test_package):
|
|
"""Test deleting a version."""
|
|
project_name, package_name = test_package
|
|
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"delete version test",
|
|
version="4.0.0",
|
|
)
|
|
|
|
# Delete version
|
|
response = integration_client.delete(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions/4.0.0"
|
|
)
|
|
assert response.status_code == 204
|
|
|
|
# Verify deleted
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions/4.0.0"
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
class TestVersionDownload:
|
|
"""Tests for downloading artifacts by version reference."""
|
|
|
|
@pytest.mark.integration
|
|
def test_download_by_version_prefix(self, integration_client, test_package):
|
|
"""Test downloading an artifact using version: prefix."""
|
|
project_name, package_name = test_package
|
|
content = b"download by version test"
|
|
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
content,
|
|
version="5.0.0",
|
|
)
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/+/version:5.0.0",
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should either redirect or return content
|
|
assert response.status_code in [200, 302, 307]
|
|
|
|
@pytest.mark.integration
|
|
def test_download_by_implicit_version(self, integration_client, test_package):
|
|
"""Test downloading an artifact using version number directly (no prefix)."""
|
|
project_name, package_name = test_package
|
|
content = b"implicit version download test"
|
|
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
content,
|
|
version="6.0.0",
|
|
)
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/+/6.0.0",
|
|
follow_redirects=False,
|
|
)
|
|
|
|
# Should resolve version first (before tag)
|
|
assert response.status_code in [200, 302, 307]
|
|
|
|
@pytest.mark.integration
|
|
def test_version_takes_precedence_over_tag(self, integration_client, test_package):
|
|
"""Test that version is checked before tag when resolving refs."""
|
|
project_name, package_name = test_package
|
|
|
|
# Upload with version "1.0"
|
|
version_result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"version content",
|
|
version="1.0",
|
|
)
|
|
|
|
# Create a tag with the same name "1.0" pointing to different artifact
|
|
tag_result = upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"tag content different",
|
|
tag="1.0",
|
|
)
|
|
|
|
# Download by "1.0" should resolve to version, not tag
|
|
# Since version:1.0 artifact was uploaded first
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/+/1.0",
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert response.status_code in [200, 302, 307]
|
|
|
|
|
|
class TestTagVersionEnrichment:
|
|
"""Tests for tag responses including version information."""
|
|
|
|
@pytest.mark.integration
|
|
def test_tag_response_includes_version(self, integration_client, test_package):
|
|
"""Test that tag responses include version of the artifact."""
|
|
project_name, package_name = test_package
|
|
|
|
# Upload with both version and tag
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"enriched tag test",
|
|
version="7.0.0",
|
|
tag="stable",
|
|
)
|
|
|
|
# Get tag and check version field
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/tags/stable"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
assert data["name"] == "stable"
|
|
assert data["version"] == "7.0.0"
|
|
|
|
@pytest.mark.integration
|
|
def test_tag_list_includes_versions(self, integration_client, test_package):
|
|
"""Test that tag list responses include version for each tag."""
|
|
project_name, package_name = test_package
|
|
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"list version test",
|
|
version="8.0.0",
|
|
tag="latest",
|
|
)
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/tags"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
tag_item = next((t for t in data["items"] if t["name"] == "latest"), None)
|
|
assert tag_item is not None
|
|
assert tag_item.get("version") == "8.0.0"
|
|
|
|
|
|
class TestVersionPagination:
|
|
"""Tests for version listing pagination and sorting."""
|
|
|
|
@pytest.mark.integration
|
|
def test_versions_pagination(self, integration_client, test_package):
|
|
"""Test version listing respects pagination."""
|
|
project_name, package_name = test_package
|
|
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions?limit=5"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
assert "pagination" in data
|
|
assert data["pagination"]["limit"] == 5
|
|
|
|
@pytest.mark.integration
|
|
def test_versions_sorting(self, integration_client, test_package):
|
|
"""Test version listing can be sorted."""
|
|
project_name, package_name = test_package
|
|
|
|
# Create versions with different timestamps
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"sort test 1",
|
|
version="1.0.0",
|
|
)
|
|
upload_test_file(
|
|
integration_client,
|
|
project_name,
|
|
package_name,
|
|
b"sort test 2",
|
|
version="2.0.0",
|
|
)
|
|
|
|
# Test ascending sort
|
|
response = integration_client.get(
|
|
f"/api/v1/project/{project_name}/{package_name}/versions?sort=version&order=asc"
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
data = response.json()
|
|
versions = [v["version"] for v in data["items"]]
|
|
# First version should be 1.0.0 when sorted ascending
|
|
if len(versions) >= 2:
|
|
assert versions.index("1.0.0") < versions.index("2.0.0")
|