From bfabd56532f7baf34195dd2427b50ff98f111d7e Mon Sep 17 00:00:00 2001 From: Prati28 Date: Mon, 22 Jul 2024 22:47:55 +0530 Subject: [PATCH 01/13] replaced travis ci with github actions Signed-off-by: Prati28 --- .../docker-build-publish-drs-filer.yml | 44 ++++ .github/workflows/lint.yml | 42 ++++ .github/workflows/test.yml | 35 +++ tests/test.py | 237 ++++++++++++++++++ 4 files changed, 358 insertions(+) create mode 100644 .github/workflows/docker-build-publish-drs-filer.yml create mode 100644 .github/workflows/lint.yml create mode 100644 .github/workflows/test.yml create mode 100644 tests/test.py diff --git a/.github/workflows/docker-build-publish-drs-filer.yml b/.github/workflows/docker-build-publish-drs-filer.yml new file mode 100644 index 0000000..f269086 --- /dev/null +++ b/.github/workflows/docker-build-publish-drs-filer.yml @@ -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/checkout@v4.1.7 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3.3.0 + + - name: Login to Docker Hub + uses: docker/login-action@v3.2.0 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5.1 + with: + images: elixircloud/drs-filer + + - name: Build and push Docker images + uses: docker/build-push-action@v6.3.0 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..ae2fd12 --- /dev/null +++ b/.github/workflows/lint.yml @@ -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/checkout@v4.1.7 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5.1.0 + 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 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..c2dc5de --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,35 @@ +name: Test Drs-filer API Endpoints + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.7 + + - name: Build and run Docker Compose + run: docker-compose up --build -d + + - name: Wait for the services to be ready + run: | + echo "Waiting for the services to be ready..." + sleep 30 # Wait for 30 seconds to ensure the services are up + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5.1.0 + 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 \ No newline at end of file diff --git a/tests/test.py b/tests/test.py new file mode 100644 index 0000000..aa293ed --- /dev/null +++ b/tests/test.py @@ -0,0 +1,237 @@ +import os +import requests +import time +import pytest + +DRS_FILER_URL = os.getenv('DRS_FILER_URL') + +@pytest.fixture(scope="function") +def get_object_id(): + object_id = test_create_object() + yield object_id + +@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 + +def handle_error(response): + """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(): + 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 + +def test_get_objects(): + response = requests.get(f"{DRS_FILER_URL}/objects") + if response.status_code == 200: + print("Following are the objects: ") + print(response.json()) + else: + handle_error(response) + +def test_get_object(get_object_id): + object_id = get_object_id + + response = requests.get(f"{DRS_FILER_URL}/objects/{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 + +def test_get_object_access(get_object_id, get_access_id): + object_id = get_object_id + access_id = get_access_id + + 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) + +def test_update_object(get_object_id): + object_id = get_object_id + + data = { + "access_methods": [ + { + "access_url": { + "headers": ["string"], + "url": "string" + }, + "region": "us-east-1", + "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) + + if response.status_code == 200: + print(f"Updated the object with ID: {object_id}") + else: + handle_error(response) + +def test_delete_object_access(get_object_id, get_access_id): + 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) + +def test_delete_object(get_object_id): + 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) + +def test_post_service_info(): + data = { + "contactUrl": "mailto:support@example.com", + "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) + + if response.status_code == 201: + print("Service info was successfully created.") + else: + handle_error(response) + +def test_get_service_info(): + response = requests.get(f"{DRS_FILER_URL}/service-info") + if response.status_code == 200: + print("Retrieved service info:") + print(response.json()) + else: + handle_error(response) + +if __name__ == "__main__": + test_get_objects() + test_get_object() + test_get_object_access() + test_update_object() + test_delete_object_access() + test_delete_object() + test_post_service_info() + test_get_service_info() \ No newline at end of file From 85eab200b8298f2830c2e26fea155bcd6e482d25 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Mon, 22 Jul 2024 23:58:31 +0530 Subject: [PATCH 02/13] made some changes according to reviews Signed-off-by: Prati28 --- .github/workflows/test.yml | 9 ++++++++- tests/test.py | 38 ++++++++++++++++++++++---------------- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c2dc5de..2438e6c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,7 +16,14 @@ jobs: - name: Wait for the services to be ready run: | echo "Waiting for the services to be ready..." - sleep 30 # Wait for 30 seconds to ensure the services are up + 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 }} uses: actions/setup-python@v5.1.0 diff --git a/tests/test.py b/tests/test.py index aa293ed..b2f3eb2 100644 --- a/tests/test.py +++ b/tests/test.py @@ -75,6 +75,9 @@ def test_create_object(): def test_get_objects(): 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()) @@ -85,6 +88,8 @@ def test_get_object(get_object_id): 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}:") @@ -102,6 +107,8 @@ def test_get_object(get_object_id): def test_get_object_access(get_object_id, get_access_id): 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}") @@ -128,6 +135,14 @@ def test_update_object(get_object_id): }, "region": "us-east-1", "type": "s3" + }, + { + "access_url": { + "headers": ["string"], + "url": "string" + }, + "region": "us-east-2", + "type": "s3" } ], "aliases": ["string"], @@ -158,6 +173,7 @@ def test_update_object(get_object_id): } 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}") @@ -212,7 +228,8 @@ def test_post_service_info(): } 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: @@ -220,18 +237,7 @@ def test_post_service_info(): def test_get_service_info(): response = requests.get(f"{DRS_FILER_URL}/service-info") - if response.status_code == 200: - print("Retrieved service info:") - print(response.json()) - else: - handle_error(response) - -if __name__ == "__main__": - test_get_objects() - test_get_object() - test_get_object_access() - test_update_object() - test_delete_object_access() - test_delete_object() - test_post_service_info() - test_get_service_info() \ No newline at end of file + assert response.status_code == 200 + service_info = response.json() + assert "name" in service_info + assert "version" in service_info From 509c8743b3fa93cbec6e09f99c3914c9a2584ff1 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Tue, 23 Jul 2024 00:16:16 +0530 Subject: [PATCH 03/13] did some changes based on sourcery reviews Signed-off-by: Prati28 --- tests/test.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/test.py b/tests/test.py index b2f3eb2..1689ad4 100644 --- a/tests/test.py +++ b/tests/test.py @@ -3,34 +3,34 @@ import time import pytest -DRS_FILER_URL = os.getenv('DRS_FILER_URL') +# DRS_FILER_URL = os.getenv('DRS_FILER_URL') +DRS_FILER_URL= 'http://localhost:8080/ga4gh/drs/v1' @pytest.fixture(scope="function") def get_object_id(): - object_id = test_create_object() - yield object_id + yield test_create_object() @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 + yield test_get_object(get_object_id).get('access_methods')[0].get('access_id') def handle_error(response): """Helper function to handle common error cases.""" if response.status_code == 400: - print(f"400 Bad Request: {response.json().get('msg')}") + assert False, "Bad Request: The server could not understand the request." elif response.status_code == 401: - print(f"401 Unauthorized: {response.json().get('msg')}") + assert False, "Unauthorized: Access is denied due to invalid credentials." elif response.status_code == 403: - print(f"403 Forbidden: {response.json().get('msg')}") + assert False, "Forbidden: The server understood the request, but refuses to authorize it." elif response.status_code == 404: - print(f"404 Not Found: {response.json().get('msg')}") + assert False, "Not Found: The requested resource could not be found." elif response.status_code == 409: - print(f"409 Conflict: {response.json()}") + assert False, "Conflict: The request could not be completed due to a conflict with the current state of the target resource." elif response.status_code == 500: - print(f"500 Internal Server Error: {response.json().get('msg')}") + assert False, f"Internal Server Error" else: - print(f"Unexpected status code {response.status_code}: {response.json().get('msg')}") + assert False, f"Unexpected Status Code: {response.status_code} - {response.json().get('msg')}" + def test_create_object(): data = { @@ -64,7 +64,8 @@ def test_create_object(): } response = requests.post(f"{DRS_FILER_URL}/objects", json=data) - + assert response.status_code == 200 + if response.status_code == 200: object_id = response.json() print(f"Created object with ID: {object_id}") From c763bf6310a29aebf03f03d4b28bf20c75cb0fd0 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Tue, 23 Jul 2024 00:16:38 +0530 Subject: [PATCH 04/13] did some changes based on sourcery reviews Signed-off-by: Prati28 --- tests/test.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/test.py b/tests/test.py index 1689ad4..d7add61 100644 --- a/tests/test.py +++ b/tests/test.py @@ -3,8 +3,7 @@ import time import pytest -# DRS_FILER_URL = os.getenv('DRS_FILER_URL') -DRS_FILER_URL= 'http://localhost:8080/ga4gh/drs/v1' +DRS_FILER_URL = os.getenv('DRS_FILER_URL') @pytest.fixture(scope="function") def get_object_id(): From d8486b994adec91764a52ae58794cf861b82e9a4 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Tue, 23 Jul 2024 00:19:04 +0530 Subject: [PATCH 05/13] added py version Signed-off-by: Prati28 --- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index ae2fd12..a139ad9 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.6, 3.7, 3.8] + python-version: [3.7, 3.8, 3.9, 3.10] name: Run linting and unit tests steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2438e6c..b45394c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,9 @@ on: [push, pull_request] jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.7, 3.8, 3.9, 3.10] steps: - name: Checkout repository @@ -39,4 +42,7 @@ jobs: run: | pytest tests/test.py -v -p no:warnings env: - DRS_FILER_URL: http://localhost:8080/ga4gh/drs/v1 \ No newline at end of file + DRS_FILER_URL: http://localhost:8080/ga4gh/drs/v1 + + - name: Stop and remove Docker Compose services + run: docker-compose down \ No newline at end of file From 14bed4532466878641cc95323ffcc612f910cfd9 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Tue, 23 Jul 2024 07:19:34 +0530 Subject: [PATCH 06/13] made some changes based on review Signed-off-by: Prati28 --- .github/workflows/lint.yml | 2 +- .github/workflows/test.yml | 2 +- tests/test.py | 189 ++++++++++++++++++++----------------- 3 files changed, 106 insertions(+), 87 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a139ad9..e02392b 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9, 3.10] + python-version: [3.11] name: Run linting and unit tests steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b45394c..2461bd2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.7, 3.8, 3.9, 3.10] + python-version: [3.11] steps: - name: Checkout repository diff --git a/tests/test.py b/tests/test.py index d7add61..3f50f72 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,17 +1,20 @@ -import os import requests import time import pytest -DRS_FILER_URL = os.getenv('DRS_FILER_URL') +# DRS_FILER_URL = os.getenv('DRS_FILER_URL') +DRS_FILER_URL = "http://localhost:8080/ga4gh/drs/v1" + @pytest.fixture(scope="function") def get_object_id(): yield test_create_object() + @pytest.fixture(scope="function") def get_access_id(get_object_id): - yield test_get_object(get_object_id).get('access_methods')[0].get('access_id') + yield test_get_object(get_object_id).get("access_methods")[0].get("access_id") + def handle_error(response): """Helper function to handle common error cases.""" @@ -20,38 +23,50 @@ def handle_error(response): elif response.status_code == 401: assert False, "Unauthorized: Access is denied due to invalid credentials." elif response.status_code == 403: - assert False, "Forbidden: The server understood the request, but refuses to authorize it." + assert ( + False + ), "Forbidden: The server understood the request, but refuses to authorize it." elif response.status_code == 404: assert False, "Not Found: The requested resource could not be found." elif response.status_code == 409: - assert False, "Conflict: The request could not be completed due to a conflict with the current state of the target resource." + assert ( + False + ), "Conflict: The request could not be completed due to a conflict with the current state of the target resource." elif response.status_code == 500: assert False, f"Internal Server Error" else: - assert False, f"Unexpected Status Code: {response.status_code} - {response.json().get('msg')}" + assert ( + False + ), f"Unexpected Status Code: {response.status_code} - {response.json().get('msg')}" + + +def object_exists(object_id): + try: + obj = test_get_object(object_id) + return obj is not None + except Exception: + return False + + +def check_access_exists(object_id, access_id): + try: + obj = test_get_object_access(object_id, access_id) + return obj is not None + except Exception: + return False def test_create_object(): data = { "access_methods": [ { - "access_url": { - "headers": [], - "url": "http://example.com/data" - }, + "access_url": {"headers": [], "url": "http://example.com/data"}, "region": "us-east-1", - "type": "s3" - } - ], - "aliases": [ - "example_alias" - ], - "checksums": [ - { - "checksum": "abc123", - "type": "sha-256" + "type": "s3", } ], + "aliases": ["example_alias"], + "checksums": [{"checksum": "abc123", "type": "sha-256"}], "contents": [], "created_time": "2024-06-12T12:58:19Z", "description": "An example object", @@ -59,51 +74,53 @@ def test_create_object(): "name": "example_object", "size": 1024, "updated_time": "2024-06-12T12:58:19Z", - "version": "1.0" + "version": "1.0", } - + response = requests.post(f"{DRS_FILER_URL}/objects", json=data) assert response.status_code == 200 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 + def test_get_objects(): 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) + objects = response.json() + assert isinstance(objects, list) + assert len(objects) > 0 + -def test_get_object(get_object_id): +def test_get_object(get_object_id, max_retries=5, retry_count=0): 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 - + assert response.status_code == 200 or response.status_code == 202 + 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 + if retry_count < max_retries: + retry_after = int(response.headers.get("Retry-After", 5)) + print( + f"202 Accepted: Operation is delayed. Retry after {retry_after} seconds. Retry count: {retry_count + 1}" + ) + time.sleep(retry_after) + return test_get_object(object_id, max_retries, retry_count + 1) + else: + print(f"Maximum retry limit reached ({max_retries}).") + handle_error(response) + return None else: handle_error(response) return None + def test_get_object_access(get_object_id, get_access_id): object_id = get_object_id access_id = get_access_id @@ -111,9 +128,11 @@ def test_get_object_access(get_object_id, get_access_id): 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( + 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)) @@ -123,44 +142,34 @@ def test_get_object_access(get_object_id, get_access_id): else: handle_error(response) + def test_update_object(get_object_id): object_id = get_object_id - + data = { "access_methods": [ { - "access_url": { - "headers": ["string"], - "url": "string" - }, + "access_url": {"headers": ["string"], "url": "string"}, "region": "us-east-1", - "type": "s3" + "type": "s3", }, { - "access_url": { - "headers": ["string"], - "url": "string" - }, + "access_url": {"headers": ["string"], "url": "string"}, "region": "us-east-2", - "type": "s3" - } + "type": "s3", + }, ], "aliases": ["string"], - "checksums": [ - { - "checksum": "string", - "type": "sha-256" - } - ], + "checksums": [{"checksum": "string", "type": "sha-256"}], "contents": [ { "contents": [], "drs_uri": [ "drs://drs.example.org/314159", - "drs://drs.example.org/213512" + "drs://drs.example.org/213512", ], "id": "string", - "name": "string" + "name": "string", } ], "created_time": "2024-07-03T14:16:59.268Z", @@ -169,42 +178,56 @@ def test_update_object(get_object_id): "name": "string", "size": 0, "updated_time": "2024-07-03T14:16:59.268Z", - "version": "string" + "version": "string", } - + response = requests.put(f"{DRS_FILER_URL}/objects/{object_id}", json=data) assert response.status_code == 200 - + object_id = get_object_id + if response.status_code == 200: print(f"Updated the object with ID: {object_id}") else: handle_error(response) + def test_delete_object_access(get_object_id, get_access_id): - object_id=get_object_id - access_id=get_access_id - - response = requests.delete(f"{DRS_FILER_URL}/objects/{object_id}/access/{access_id}") - + object_id = get_object_id + access_id = get_access_id + + response = requests.delete( + f"{DRS_FILER_URL}/objects/{object_id}/access/{access_id}" + ) + assert response.status_code == 200 or response.status_code == 409 + assert ( + response.status_code == 200 and not check_access_exists(object_id, access_id) + ) or response.status_code == 409 + 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}.") + 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) + def test_delete_object(get_object_id): - object_id=get_object_id + object_id = get_object_id response = requests.delete(f"{DRS_FILER_URL}/objects/{object_id}") - + assert response.status_code == 200 + assert not object_exists(object_id) + if response.status_code == 200: print(f"Deleted the object with ID: {object_id}") else: handle_error(response) + def test_post_service_info(): data = { "contactUrl": "mailto:support@example.com", @@ -214,30 +237,26 @@ def test_post_service_info(): "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" - }, + "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" + "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) + def test_get_service_info(): 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 + assert "description" in service_info + assert "contactUrl" in service_info From 5e5f0fefcc08774cdfdc6ef8670a90b999c72acc Mon Sep 17 00:00:00 2001 From: Prati28 Date: Thu, 25 Jul 2024 23:37:48 +0530 Subject: [PATCH 07/13] fix linting issue and py version Signed-off-by: Prati28 --- .github/workflows/lint.yml | 7 ++----- .github/workflows/test.yml | 7 ++----- drs_filer/ga4gh/drs/server.py | 9 +++++---- setup.cfg | 3 +++ tests/test.py | 22 +++++++++++++++------- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e02392b..a90f085 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,19 +13,16 @@ on: jobs: linting_and_tests: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.11] name: Run linting and unit tests steps: - name: Checkout repository uses: actions/checkout@v4.1.7 - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python uses: actions/setup-python@v5.1.0 with: - python-version: ${{ matrix.python-version }} + python-version: 3.11 - name: Install dependencies run: | diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2461bd2..500bb0c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,9 +5,6 @@ on: [push, pull_request] jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [3.11] steps: - name: Checkout repository @@ -28,10 +25,10 @@ jobs: sleep 3 done - - name: Set up Python ${{ matrix.python-version }} + - name: Set up Python uses: actions/setup-python@v5.1.0 with: - python-version: ${{ matrix.python-version }} + python-version: 3.11 - name: Install dependencies run: | diff --git a/drs_filer/ga4gh/drs/server.py b/drs_filer/ga4gh/drs/server.py index e404112..47fea6e 100644 --- a/drs_filer/ga4gh/drs/server.py +++ b/drs_filer/ga4gh/drs/server.py @@ -22,6 +22,7 @@ logger = logging.getLogger(__name__) + @log_traffic def ListDrsObjects() -> List[Dict]: """Get all DRS objects. @@ -33,10 +34,10 @@ def ListDrsObjects() -> List[Dict]: current_app.config.foca.db.dbs['drsStore']. collections['objects'].client ) - cursor = db_collection.find({}, {'_id': 0}) - objects = [] - for obj in cursor: - objects.append(obj) + cursor = db_collection.find({}, {'_id': 0}) + objects = [] + for obj in cursor: + objects.append(obj) return objects diff --git a/setup.cfg b/setup.cfg index f655a7f..5106b62 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,3 +3,6 @@ source = drs_filer omit = drs_filer/app.py + +[flake8] +max-line-length = 88 \ No newline at end of file diff --git a/tests/test.py b/tests/test.py index 3f50f72..650d6cf 100644 --- a/tests/test.py +++ b/tests/test.py @@ -1,9 +1,9 @@ import requests import time import pytest +import os -# DRS_FILER_URL = os.getenv('DRS_FILER_URL') -DRS_FILER_URL = "http://localhost:8080/ga4gh/drs/v1" +DRS_FILER_URL = os.getenv('DRS_FILER_URL') @pytest.fixture(scope="function") @@ -31,13 +31,19 @@ def handle_error(response): elif response.status_code == 409: assert ( False - ), "Conflict: The request could not be completed due to a conflict with the current state of the target resource." + ), ( + "Conflict: The request could not be completed due to a conflict with the " + "current state of the target resource." + ) elif response.status_code == 500: - assert False, f"Internal Server Error" + assert False, "Internal Server Error" else: assert ( False - ), f"Unexpected Status Code: {response.status_code} - {response.json().get('msg')}" + ), ( + f"Unexpected Status Code: {response.status_code} - " + f"{response.json().get('msg')}" + ) def object_exists(object_id): @@ -108,7 +114,8 @@ def test_get_object(get_object_id, max_retries=5, retry_count=0): if retry_count < max_retries: retry_after = int(response.headers.get("Retry-After", 5)) print( - f"202 Accepted: Operation is delayed. Retry after {retry_after} seconds. Retry count: {retry_count + 1}" + f"Accepted: Operation is delayed. Retry after {retry_after} seconds. " + f"Retry count: {retry_count + 1}" ) time.sleep(retry_after) return test_get_object(object_id, max_retries, retry_count + 1) @@ -207,7 +214,8 @@ def test_delete_object_access(get_object_id, get_access_id): 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}." + f"Refusing to delete the last remaining access method for object " + f"{object_id}." ) elif response.status_code == 200: print(f"Deleted access method with ID {access_id} for object {object_id}.") From 827be0faafee5d902fb37b52b134afc1e506319c Mon Sep 17 00:00:00 2001 From: Prati28 Date: Thu, 25 Jul 2024 23:49:50 +0530 Subject: [PATCH 08/13] fix installation of package issue Signed-off-by: Prati28 --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index a90f085..bbcc4af 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,6 +28,7 @@ jobs: run: | python -m pip install --upgrade pip pip install flake8 pytest coverage coveralls + pip install requirements-test.txt - name: Lint with flake8 run: flake8 From 17ed5214ca48ebe5e00ad73e17a1cca536af946c Mon Sep 17 00:00:00 2001 From: Prati28 Date: Thu, 25 Jul 2024 23:50:48 +0530 Subject: [PATCH 09/13] fix command Signed-off-by: Prati28 --- .github/workflows/lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bbcc4af..34a7166 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -28,7 +28,7 @@ jobs: run: | python -m pip install --upgrade pip pip install flake8 pytest coverage coveralls - pip install requirements-test.txt + pip install -r requirements-test.txt - name: Lint with flake8 run: flake8 From 505273203464499faf67e501358e85f50a8b5567 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Thu, 25 Jul 2024 23:52:14 +0530 Subject: [PATCH 10/13] added flask to requirements Signed-off-by: Prati28 --- requirements-test.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-test.txt b/requirements-test.txt index a024d5e..c0eba27 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -1,3 +1,4 @@ +flask flake8 mongomock pytest From 1fb34c07f2543189c265cbc54b0c183dfb75c788 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Thu, 25 Jul 2024 23:54:08 +0530 Subject: [PATCH 11/13] added command for installation of packages Signed-off-by: Prati28 --- .github/workflows/lint.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 34a7166..3816b9f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -29,6 +29,7 @@ jobs: python -m pip install --upgrade pip pip install flake8 pytest coverage coveralls pip install -r requirements-test.txt + pip install -r requirements.txt - name: Lint with flake8 run: flake8 From 7689f247a78310418e268d225d2ffd77c2f68efa Mon Sep 17 00:00:00 2001 From: Prati28 Date: Thu, 25 Jul 2024 23:57:17 +0530 Subject: [PATCH 12/13] added github token Signed-off-by: Prati28 --- .github/workflows/lint.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 3816b9f..ddd8ee7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -38,4 +38,8 @@ jobs: run: | coverage run --source drs_filer -m pytest coverage report -m - coveralls \ No newline at end of file + + - name: Send coverage data to Coveralls + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: coveralls \ No newline at end of file From 8c3d86a98c3046a1fb76d25726c424c40eb84525 Mon Sep 17 00:00:00 2001 From: Prati28 Date: Sat, 27 Jul 2024 15:17:56 +0530 Subject: [PATCH 13/13] made changes Signed-off-by: Prati28 --- tests/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test.py b/tests/test.py index 650d6cf..6e14f0c 100644 --- a/tests/test.py +++ b/tests/test.py @@ -3,7 +3,7 @@ import pytest import os -DRS_FILER_URL = os.getenv('DRS_FILER_URL') +DRS_FILER_URL = os.getenv('DRS_FILER_URL', 'http://localhost:8080/ga4gh/drs/v1') @pytest.fixture(scope="function")