Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: replace Travis CI with GH Actions #64

Merged
merged 13 commits into from
Aug 1, 2024
44 changes: 44 additions & 0 deletions .github/workflows/docker-build-publish-drs-filer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: drs-filer

on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev

jobs:
publish_to_docker:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'

name: Build and publish to Docker
steps:
- name: Checkout repository
uses: actions/[email protected]

- name: Set up Docker Buildx
uses: docker/[email protected]

- name: Login to Docker Hub
uses: docker/[email protected]
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/[email protected]
with:
images: elixircloud/drs-filer

- name: Build and push Docker images
uses: docker/[email protected]
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
42 changes: 42 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Lint workflow

on:
push:
branches:
- main
- dev
pull_request:
branches:
- main
- dev

jobs:
linting_and_tests:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8]

name: Run linting and unit tests
steps:
- name: Checkout repository
uses: actions/[email protected]

- name: Set up Python ${{ matrix.python-version }}
uses: actions/[email protected]
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest coverage coveralls

- name: Lint with flake8
run: flake8

- name: Run unit tests with coverage
run: |
coverage run --source drs_filer -m pytest
coverage report -m
coveralls
42 changes: 42 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Test Drs-filer API Endpoints

on: [push, pull_request]

jobs:
test:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/[email protected]

- name: Build and run Docker Compose
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
run: docker-compose up --build -d

- name: Wait for the services to be ready
run: |
echo "Waiting for the services to be ready..."
for i in {1..10}; do
if curl -sSf http://localhost:8080/ga4gh/drs/v1 > /dev/null; then
echo "Service is up!"
break
fi
echo "Waiting for the service to be ready..."
sleep 3
done

- name: Set up Python ${{ matrix.python-version }}
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
uses: actions/[email protected]
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest requests

- name: Test with pytest
run: |
pytest tests/test.py -v -p no:warnings
env:
DRS_FILER_URL: http://localhost:8080/ga4gh/drs/v1
243 changes: 243 additions & 0 deletions tests/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
import os
import requests
import time
import pytest

DRS_FILER_URL = os.getenv('DRS_FILER_URL')
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

@pytest.fixture(scope="function")
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
def get_object_id():
object_id = test_create_object()
yield object_id
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

@pytest.fixture(scope="function")
def get_access_id(get_object_id):
access_id = test_get_object(get_object_id).get('access_methods')[0].get('access_id')
yield access_id
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def handle_error(response):
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
"""Helper function to handle common error cases."""
if response.status_code == 400:
print(f"400 Bad Request: {response.json().get('msg')}")
elif response.status_code == 401:
print(f"401 Unauthorized: {response.json().get('msg')}")
elif response.status_code == 403:
print(f"403 Forbidden: {response.json().get('msg')}")
elif response.status_code == 404:
print(f"404 Not Found: {response.json().get('msg')}")
elif response.status_code == 409:
print(f"409 Conflict: {response.json()}")
elif response.status_code == 500:
print(f"500 Internal Server Error: {response.json().get('msg')}")
else:
print(f"Unexpected status code {response.status_code}: {response.json().get('msg')}")

def test_create_object():
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
data = {
"access_methods": [
{
"access_url": {
"headers": [],
"url": "http://example.com/data"
},
"region": "us-east-1",
"type": "s3"
}
],
"aliases": [
"example_alias"
],
"checksums": [
{
"checksum": "abc123",
"type": "sha-256"
}
],
"contents": [],
"created_time": "2024-06-12T12:58:19Z",
"description": "An example object",
"mime_type": "application/json",
"name": "example_object",
"size": 1024,
"updated_time": "2024-06-12T12:58:19Z",
"version": "1.0"
}

response = requests.post(f"{DRS_FILER_URL}/objects", json=data)

if response.status_code == 200:
object_id = response.json()
print(f"Created object with ID: {object_id}")
return object_id
else:
handle_error(response)
return None
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_objects():
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
response = requests.get(f"{DRS_FILER_URL}/objects")
assert response.status_code == 200
assert response.json() is not None
assert isinstance(response.json(), list)
if response.status_code == 200:
print("Following are the objects: ")
print(response.json())
else:
handle_error(response)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_object(get_object_id):
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
object_id = get_object_id

response = requests.get(f"{DRS_FILER_URL}/objects/{object_id}")
assert response.status_code == 200
assert response.json()['id'] == object_id

if response.status_code == 200:
print(f"Following is the object retrieved based on {object_id}:")
print(response.json())
return response.json()
elif response.status_code == 202:
retry_after = int(response.headers.get("Retry-After", 5))
print(f"202 Accepted: Operation is delayed. Retry after {retry_after} seconds.")
time.sleep(retry_after)
return test_get_object(object_id) # Retry the request
else:
handle_error(response)
return None
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_object_access(get_object_id, get_access_id):
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
object_id = get_object_id
access_id = get_access_id
assert object_id is not None, "Object ID should not be None"
assert access_id is not None, "Access ID should not be None"

response = requests.get(f"{DRS_FILER_URL}/objects/{object_id}/access/{access_id}")

if response.status_code == 200:
print(f"Following is the object retrieved based on {object_id} and {access_id}:")
print(response.json())
elif response.status_code == 202:
retry_after = int(response.headers.get("Retry-After", 5))
print(f"202 Accepted: Operation is delayed. Retry after {retry_after} seconds.")
time.sleep(retry_after)
return test_get_object_access(object_id, access_id) # Retry the request
else:
handle_error(response)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_update_object(get_object_id):
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
object_id = get_object_id

data = {
"access_methods": [
{
"access_url": {
"headers": ["string"],
"url": "string"
},
"region": "us-east-1",
"type": "s3"
},
{
"access_url": {
"headers": ["string"],
"url": "string"
},
"region": "us-east-2",
"type": "s3"
}
],
"aliases": ["string"],
"checksums": [
{
"checksum": "string",
"type": "sha-256"
}
],
"contents": [
{
"contents": [],
"drs_uri": [
"drs://drs.example.org/314159",
"drs://drs.example.org/213512"
],
"id": "string",
"name": "string"
}
],
"created_time": "2024-07-03T14:16:59.268Z",
"description": "string",
"mime_type": "application/json",
"name": "string",
"size": 0,
"updated_time": "2024-07-03T14:16:59.268Z",
"version": "string"
}

response = requests.put(f"{DRS_FILER_URL}/objects/{object_id}", json=data)
assert response.status_code == 200

if response.status_code == 200:
print(f"Updated the object with ID: {object_id}")
else:
handle_error(response)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_delete_object_access(get_object_id, get_access_id):
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
object_id=get_object_id
access_id=get_access_id

response = requests.delete(f"{DRS_FILER_URL}/objects/{object_id}/access/{access_id}")

if response.status_code == 404:
print(f"Object with ID {object_id} or access ID {access_id} not found.")
elif response.status_code == 409:
print(f"Refusing to delete the last remaining access method for object {object_id}.")
elif response.status_code == 200:
print(f"Deleted access method with ID {access_id} for object {object_id}.")
else:
handle_error(response)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_delete_object(get_object_id):
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
object_id=get_object_id

response = requests.delete(f"{DRS_FILER_URL}/objects/{object_id}")

if response.status_code == 200:
print(f"Deleted the object with ID: {object_id}")
else:
handle_error(response)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_post_service_info():
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
data = {
"contactUrl": "mailto:[email protected]",
"createdAt": "2024-06-12T12:58:19Z",
"description": "This service provides...",
"documentationUrl": "https://docs.myservice.example.com",
"environment": "test",
"id": "org.ga4gh.myservice",
"name": "My project",
"organization": {
"name": "My organization",
"url": "https://example.com"
},
"type": {
"artifact": "beacon",
"group": "org.ga4gh",
"version": "1.0.0"
},
"updatedAt": "2024-06-12T12:58:19Z",
"version": "1.0.0"
}

response = requests.post(f"{DRS_FILER_URL}/service-info", json=data)
assert response.status_code == 201

if response.status_code == 201:
print("Service info was successfully created.")
else:
handle_error(response)
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved

def test_get_service_info():
psankhe28 marked this conversation as resolved.
Show resolved Hide resolved
response = requests.get(f"{DRS_FILER_URL}/service-info")
assert response.status_code == 200
service_info = response.json()
assert "name" in service_info
assert "version" in service_info
Loading