Compare commits
2 Commits
b6e5a0fa4d
...
06c9038858
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
06c9038858 | ||
|
|
b90e478f4b |
140
.gitlab-ci.yml
140
.gitlab-ci.yml
@@ -7,13 +7,148 @@ variables:
|
||||
# renovate: datasource=gitlab-tags depName=esv/bsf/pypi/prosper versioning=semver registryUrl=https://gitlab.global.bsf.tools
|
||||
PROSPER_VERSION: v0.64.1
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
- deploy
|
||||
- integration # Post-deployment integration tests
|
||||
|
||||
kics:
|
||||
allow_failure: true
|
||||
|
||||
hadolint:
|
||||
allow_failure: true
|
||||
|
||||
# secrets job is a blocking check - real credential leaks should fail the pipeline
|
||||
# secrets job is a blocking check
|
||||
|
||||
# Post-deployment integration tests template
|
||||
.integration_test_template: &integration_test_template
|
||||
stage: integration
|
||||
image: deps.global.bsf.tools/docker/python:3.12-slim
|
||||
timeout: 10m
|
||||
before_script:
|
||||
- pip install httpx
|
||||
script:
|
||||
- |
|
||||
python - <<'PYTEST_SCRIPT'
|
||||
import httpx
|
||||
import os
|
||||
import sys
|
||||
import uuid
|
||||
import hashlib
|
||||
|
||||
BASE_URL = os.environ.get("BASE_URL")
|
||||
if not BASE_URL:
|
||||
print("ERROR: BASE_URL not set")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Running integration tests against {BASE_URL}")
|
||||
client = httpx.Client(base_url=BASE_URL, timeout=30.0)
|
||||
|
||||
# Generate unique names for this test run
|
||||
test_id = uuid.uuid4().hex[:8]
|
||||
project_name = f"ci-test-{test_id}"
|
||||
package_name = f"test-package-{test_id}"
|
||||
test_content = f"Test content from CI pipeline {test_id}"
|
||||
expected_hash = hashlib.sha256(test_content.encode()).hexdigest()
|
||||
|
||||
errors = []
|
||||
|
||||
try:
|
||||
# Test 1: Health endpoint
|
||||
print("\n=== Test 1: Health endpoint ===")
|
||||
r = client.get("/health")
|
||||
if r.status_code == 200:
|
||||
print("PASS: Health check passed")
|
||||
else:
|
||||
errors.append(f"Health check failed: {r.status_code}")
|
||||
|
||||
# Test 2: Create project
|
||||
print("\n=== Test 2: Create project ===")
|
||||
r = client.post("/api/v1/projects", json={"name": project_name})
|
||||
if r.status_code == 201:
|
||||
print(f"PASS: Created project: {project_name}")
|
||||
else:
|
||||
errors.append(f"Failed to create project: {r.status_code} - {r.text}")
|
||||
|
||||
# Test 3: Create package
|
||||
print("\n=== Test 3: Create package ===")
|
||||
r = client.post(f"/api/v1/project/{project_name}/packages", json={"name": package_name})
|
||||
if r.status_code == 201:
|
||||
print(f"PASS: Created package: {package_name}")
|
||||
else:
|
||||
errors.append(f"Failed to create package: {r.status_code} - {r.text}")
|
||||
|
||||
# Test 4: Upload artifact
|
||||
print("\n=== Test 4: Upload artifact ===")
|
||||
files = {"file": ("test.txt", test_content.encode(), "text/plain")}
|
||||
r = client.post(f"/api/v1/project/{project_name}/{package_name}/upload", files=files)
|
||||
if r.status_code == 201:
|
||||
upload_data = r.json()
|
||||
print(f"PASS: Uploaded artifact: {upload_data.get('artifact_id', 'unknown')[:16]}...")
|
||||
else:
|
||||
errors.append(f"Failed to upload: {r.status_code} - {r.text}")
|
||||
|
||||
# Test 5: Download artifact by hash
|
||||
print("\n=== Test 5: Download artifact ===")
|
||||
r = client.get(f"/api/v1/project/{project_name}/{package_name}/+/artifact:{expected_hash}", follow_redirects=True)
|
||||
if r.status_code == 200:
|
||||
if r.content.decode() == test_content:
|
||||
print("PASS: Downloaded content matches uploaded content")
|
||||
else:
|
||||
errors.append(f"Content mismatch: got '{r.content.decode()}'")
|
||||
else:
|
||||
errors.append(f"Failed to download: {r.status_code}")
|
||||
|
||||
# Test 6: List artifacts
|
||||
print("\n=== Test 6: List artifacts ===")
|
||||
r = client.get(f"/api/v1/project/{project_name}/{package_name}/artifacts")
|
||||
if r.status_code == 200:
|
||||
artifacts = r.json()
|
||||
print(f"PASS: Found {len(artifacts)} artifact(s)")
|
||||
else:
|
||||
errors.append(f"Failed to list artifacts: {r.status_code}")
|
||||
|
||||
finally:
|
||||
# Cleanup: Delete the test project
|
||||
print("\n=== Cleanup ===")
|
||||
r = client.delete(f"/api/v1/project/{project_name}")
|
||||
if r.status_code in (200, 204):
|
||||
print(f"PASS: Cleaned up project: {project_name}")
|
||||
else:
|
||||
print(f"Warning: Failed to cleanup project: {r.status_code}")
|
||||
|
||||
# Report results
|
||||
print("\n" + "=" * 50)
|
||||
if errors:
|
||||
print(f"FAILED: {len(errors)} error(s)")
|
||||
for e in errors:
|
||||
print(f" FAIL: {e}")
|
||||
sys.exit(1)
|
||||
else:
|
||||
print("SUCCESS: All integration tests passed!")
|
||||
sys.exit(0)
|
||||
PYTEST_SCRIPT
|
||||
|
||||
# Integration tests for stage deployment
|
||||
integration_test_stage:
|
||||
<<: *integration_test_template
|
||||
needs: [deploy_stage]
|
||||
variables:
|
||||
BASE_URL: https://orchard-stage.common.global.bsf.tools
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main"'
|
||||
when: always
|
||||
|
||||
# Integration tests for feature deployment
|
||||
integration_test_feature:
|
||||
<<: *integration_test_template
|
||||
needs: [deploy_feature]
|
||||
variables:
|
||||
BASE_URL: https://orchard-$CI_COMMIT_REF_SLUG.common.global.bsf.tools
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH && $CI_COMMIT_BRANCH != "main"'
|
||||
when: always
|
||||
|
||||
# Run Python backend tests
|
||||
python_tests:
|
||||
@@ -33,7 +168,8 @@ python_tests:
|
||||
- pip install pytest pytest-asyncio pytest-cov httpx
|
||||
script:
|
||||
- cd backend
|
||||
- python -m pytest -v --cov=app --cov-report=term --cov-report=xml:coverage.xml --cov-report=html:coverage_html --junitxml=pytest-report.xml
|
||||
# Only run unit tests - integration tests require Docker Compose services
|
||||
- python -m pytest tests/unit/ -v --cov=app --cov-report=term --cov-report=xml:coverage.xml --cov-report=html:coverage_html --junitxml=pytest-report.xml
|
||||
artifacts:
|
||||
when: always
|
||||
expire_in: 1 week
|
||||
|
||||
Reference in New Issue
Block a user