diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index 8e4c83e9..14787cc7 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -26,4 +26,4 @@ jobs: pip install -r requirements.txt - name: Run Tests run: | - cd bco_api;python3.9 manage.py test + python3.9 manage.py test diff --git a/api/scripts/method_specific/GET_published_object_by_id.py b/api/scripts/method_specific/GET_published_object_by_id.py index 58d15908..986cf98c 100755 --- a/api/scripts/method_specific/GET_published_object_by_id.py +++ b/api/scripts/method_specific/GET_published_object_by_id.py @@ -1,5 +1,5 @@ # The BCO model -from ...models import BCO +from api.models import BCO # Responses from rest_framework import status @@ -107,5 +107,5 @@ def GET_published_object_by_id(oi_root): print("No objects were found for the root ID provided.") return Response( data="No objects were found for the root ID provided.", - status=status.HTTP_400_BAD_REQUEST, + status=status.HTTP_404_NOT_FOUND, ) diff --git a/api/scripts/method_specific/POST_api_objects_drafts_publish.py b/api/scripts/method_specific/POST_api_objects_drafts_publish.py index c30307df..e0ee71f4 100755 --- a/api/scripts/method_specific/POST_api_objects_drafts_publish.py +++ b/api/scripts/method_specific/POST_api_objects_drafts_publish.py @@ -10,9 +10,9 @@ from django.contrib.auth.models import Group from django.utils import timezone from guardian.shortcuts import get_perms -from rest_framework import status +from rest_framework import status, authtoken from rest_framework.response import Response - +from authentication.selectors import get_user_from_auth_token def post_api_objects_drafts_publish(request): """Publish draft @@ -37,13 +37,29 @@ def post_api_objects_drafts_publish(request): returning = [] any_failed = False db_utils = DbUtils.DbUtils() - user = UserUtils.UserUtils().user_from_request(request=request) + + try: + user = UserUtils.UserUtils().user_from_request(request=request) + except authtoken.models.Token.DoesNotExist: + user = get_user_from_auth_token(request.META.get("HTTP_AUTHORIZATION").split(" ")[1]) prefix_perms = UserUtils.UserUtils().prefix_perms_for_user( flatten=True, user_object=user ) - bulk_request = request.data["POST_api_objects_drafts_publish"] + try: + bulk_request = request.data["POST_api_objects_drafts_publish"] + except: + return Response(status=status.HTTP_400_BAD_REQUEST, data={"Request format not accepted."}) for publish_object in bulk_request: + if "draft_id" not in publish_object: + returning.append( + db_utils.messages(parameters={})[ + "400_bad_request" + ] + ) + any_failed = True + continue + draft_exists = BCO.objects.filter( object_id=publish_object["draft_id"], state="DRAFT" ).exists() @@ -195,11 +211,6 @@ def post_api_objects_drafts_publish(request): # # Does the requestor have delete permissions on # # the object? - # As this view is for a bulk operation, status 200 - # means that the request was successfully processed, - # but NOT necessarily each item in the request. - # For example, a table may not have been found for the first - # requested draft. if any_failed: return Response(status=status.HTTP_207_MULTI_STATUS, data=returning) diff --git a/api/scripts/utilities/UserUtils.py b/api/scripts/utilities/UserUtils.py index 4ff5b375..80911671 100755 --- a/api/scripts/utilities/UserUtils.py +++ b/api/scripts/utilities/UserUtils.py @@ -249,7 +249,7 @@ def prefix_perms_for_user( # return bco_specific - def user_from_request(self, request): + def user_from_request(self, rq): """Returns a user object from a request. Parameters @@ -263,6 +263,6 @@ def user_from_request(self, request): """ user_id = Token.objects.get( - key=request.META.get("HTTP_AUTHORIZATION").split(" ")[1] + key=rq.META.get("HTTP_AUTHORIZATION").split(" ")[1] ).user_id return User.objects.get(id=user_id) diff --git a/api/views.py b/api/views.py index 8a4ecad6..e011f303 100755 --- a/api/views.py +++ b/api/views.py @@ -79,6 +79,7 @@ # For helper functions from api.scripts.utilities import UserUtils +from authentication.services import CustomJSONWebTokenAuthentication ################################################################################################ # NOTES @@ -800,22 +801,39 @@ def post(self, request) -> Response: # TODO: What is the difference between this and ApiObjectsPublish? class ApiObjectsDraftsPublish(APIView): """ - Publish a BCO + Bulk Publish BCOs -------------------- + + Publish draft BCO objects. Once published, a BCO object becomes immutable. + The `object_id` field is optional, and is used to specify if the object + should be published as a specific version, instead of the next available numeric + version. + - Publish a draft BCO object. Once published, a BCO object becomes immutable. + ```json + { + "POST_api_objects_drafts_publish": [ + { + "prefix": "TEST", + "draft_id": "http://127.0.0.1:8000/TEST_000001", + "object_id": "http://127.0.0.1:8000/TEST_000001/1.0", + "delete_draft": false + } + ] + } """ # TODO: This seems to be missing group, which I would expect to be part of the publication - permission_classes = [IsAuthenticated] + permission_classes = [IsAuthenticated,] + # authentication_classes = [CustomJSONWebTokenAuthentication] POST_api_objects_drafts_publish_schema = openapi.Schema( type=openapi.TYPE_OBJECT, required=["draft_id", "prefix"], properties={ "prefix": openapi.Schema( - type=openapi.TYPE_STRING, description="BCO Prefix to publish with." + type=openapi.TYPE_STRING, description="BCO Prefix to publish with." ), "draft_id": openapi.Schema( type=openapi.TYPE_STRING, description="BCO Object Draft ID." @@ -847,13 +865,14 @@ class ApiObjectsDraftsPublish(APIView): @swagger_auto_schema( request_body=request_body, responses={ - 200: "BCO Publication is successful.", - 300: "Some requests failed.", + 200: "All BCO publications successful.", + 207: "Some or all publications failed.", 400: "Bad request.", - 403: "Invalid token.", + 403: "Authentication credentials were not provided.", }, tags=["BCO Management"], ) + def post(self, request) -> Response: return check_post_and_process(request, post_api_objects_drafts_publish) @@ -1669,19 +1688,10 @@ def get(self, request, object_id): class ObjectIdRootObjectId(APIView): """ View Published BCO by ID - -------------------- - - Reads and returns a published BCO based on an object ID. - + Reads and returns a published BCO based on an object ID. This will return the highest versioned object. """ - # For the success and error messages - # renderer_classes = [ - # TemplateHTMLRenderer - # ] - # template_name = 'api/account_activation_message.html' - auth = [] auth.append( openapi.Parameter( @@ -1692,17 +1702,14 @@ class ObjectIdRootObjectId(APIView): ) ) - # Anyone can view a published object authentication_classes = [] permission_classes = [] @swagger_auto_schema( manual_parameters=auth, responses={ - 201: "Account has been authorized.", - 208: "Account has already been authorized.", - 403: "Requestor's credentials were rejected.", - 424: "Account has not been registered.", + 200: "Object returned.", + 404: "Object not found." }, tags=["BCO Management"], ) diff --git a/authentication/apis.py b/authentication/apis.py index b47c2cf2..8b98eb26 100644 --- a/authentication/apis.py +++ b/authentication/apis.py @@ -123,9 +123,10 @@ class AddAuthenticationApi(APIView): @swagger_auto_schema( request_body=schema, responses={ - 200: "Add authentication is successful.", - 201: "Authentication credentials were created and added.", + 200: "New authentication credentials added to existing object.", + 201: "Authentication object created and added to account.", 400: "Bad request.", + 403: "Authentication credentials were not provided.", 409: "That object already exists for this account.", }, tags=["Authentication"], @@ -140,7 +141,6 @@ def post(self, request): return Response(status=status.HTTP_400_BAD_REQUEST, data=result) try: auth_object = Authentication.objects.get(username=request.user.username) - if request.data in auth_object.auth_service: return Response( status=status.HTTP_409_CONFLICT, @@ -150,7 +150,7 @@ def post(self, request): auth_object.save() return Response( status=status.HTTP_200_OK, - data={"message": "Authentication added to existing object"} + data={"message": "New authentication credentials added to existing object"} ) except Authentication.DoesNotExist: @@ -161,7 +161,7 @@ def post(self, request): print('status=status.HTTP_201_CREATED') return Response( status=status.HTTP_201_CREATED, - data={"message": "Authentication object added to account"} + data={"message": "Authentication object created and added to account"} ) except Exception as err: diff --git a/authentication/services.py b/authentication/services.py index 992a73c2..2a27c67a 100644 --- a/authentication/services.py +++ b/authentication/services.py @@ -72,8 +72,14 @@ def validate_auth_service(value): "required": ["iss", "sub"], "additionalProperties": False, "properties": { - "iss": {"type": "string", "description": "The 'iss' (issuer) claim identifies the principal that issued the JWT."}, - "sub": {"type": "string", "description": "The 'sub' (subject) claim identifies the principal that is the subject of the JWT."} + "iss": { + "type": "string", + "description": "The 'iss' (issuer) claim identifies the principal that issued the JWT." + }, + "sub": { + "type": "string", + "description": "The 'sub' (subject) claim identifies the principal that is the subject of the JWT." + } } } try: diff --git a/search/apis.py b/search/apis.py index 718eae8c..7ccfbe5f 100644 --- a/search/apis.py +++ b/search/apis.py @@ -12,11 +12,16 @@ from itertools import chain class SearchObjectsAPI(APIView): - """Search the BCODB **BETA** + """ + Search the BCODB ------------------- + Endpoint for use of query string based search. """ + + #TODO: multiple values in the URL will only return the last one. + authentication_classes = [CustomJSONWebTokenAuthentication] permission_classes = [AllowAny,] @@ -46,18 +51,12 @@ class SearchObjectsAPI(APIView): ) ], responses={ - 201: "Account has been authorized.", - 208: "Account has already been authorized.", - 403: "Requestor's credentials were rejected.", - 424: "Account has not been registered.", + 200: "" }, - tags=["Account Management"], + tags=["BCO Management"], ) def get(self, request) -> Response: - """GET search - TODO: multiple values in the URL will only return the last one. - """ return_values = [ "contents", "last_update", diff --git a/server.conf b/server.conf index 454a7100..209f01d2 100644 --- a/server.conf +++ b/server.conf @@ -7,7 +7,7 @@ production=False # DB Version [VERSION] -version=22.11 +version=23.09 # NOTE: Valid values are True or False (note the capitalization). # Is this a publish-only server? diff --git a/tests/fixtures/test_data.json b/tests/fixtures/test_data.json index 2a0bcbeb..62398b49 100644 --- a/tests/fixtures/test_data.json +++ b/tests/fixtures/test_data.json @@ -207,6 +207,19 @@ "change_message": "[]" } }, + { + "model": "authentication.authentication", + "pk": 1, + "fields": { + "username": "bco_api_user", + "auth_service": [ + { + "iss": "Reeya1", + "sub": "ReeyaGupta1" + } + ] + } + }, { "model": "auth.permission", "pk": 1, @@ -1614,7 +1627,7 @@ "etag": "0275321b6011324035289a5624c635ce5490fbdec588aa5f3bcaf63b85369b4a", "provenance_domain": { "name": "Influenza A reference gene sequences", - "version": "1.0", + "version": "1.1", "created": "2021-12-01T15:20:13.614Z", "modified": "2022-06-28T23:10:12.804Z", "review": [ @@ -1832,7 +1845,7 @@ "etag": "11ee4c3b8a04ad16dcca19a6f478c0870d3fe668ed6454096ab7165deb1ab8ea", "provenance_domain": { "name": "HCV1a ledipasvir resistance SNP detection", - "version": "1.0", + "version": "1.1", "created": "2017-01-24T09:40:17-0500", "modified": "2022-06-28T23:12:50.369Z", "review": [ diff --git a/tests/test_views/test_api_accounts_new.py b/tests/test_views/test_api_accounts_new.py new file mode 100644 index 00000000..865a8194 --- /dev/null +++ b/tests/test_views/test_api_accounts_new.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +'''Account Creation + Gives 409 instead of 200, 403 and 201 instead of 400. Requires debugging + + Successfully tests for 409(Account has already been authenticated or requested) and 500(internal server error) + ''' + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from rest_framework.test import APITestCase + +class AccountCreationTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='bco_api_user') + except User.DoesNotExist: + self.user = User.objects.create_user(username='bco_api_user') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + + + def test_account_creation_success(self): + ##Gives 409 instead of 200 + #Same request body as 409 + + data = { + "hostname": "http://localhost:8000", + "email": "object.biocompute@gmail.com", + "token": self.token.key + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/accounts/new/', data=data, format='json') + self.assertEqual(response.status_code, 200) + + def test_account_creation_bad_request(self): + # Provide invalid or missing data + ##Gives 201 instead of 400 + data = { + "hostname": "http://localhost:8000", + "email": "invalid@example.com", + #"token": self.token.key + } + #self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/accounts/new/', data=data, format='json') + self.assertEqual(response.status_code, 400) + + def test_account_creation_invalid_token(self): + #Provide invalid token + ##Gives 409 instead of 403 + + data = { + "hostname": "http://localhost:8000", + "email": "" + #"token": self.token.key + } + self.client.credentials(HTTP_AUTHORIZATION='Invalid token') + response = self.client.post('/api/accounts/new/', data=data, format='json') + self.assertEqual(response.status_code, 403) + + def test_account_creation_already_authenticated(self): + ##Tests for conflict - same request body as 200 + data = { + "hostname": "http://localhost:8000", + "email": "object.biocompute@gmail.com", + "token": self.token.key + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/accounts/new/', data=data, format='json') + self.assertEqual(response.status_code, 409) + + def test_account_creation_server_error(self): + # Tests for 500 - Invalid email and missing token in request body + ## Server error!!! + + + data = { + "hostname": "http://localhost:8000", + "email": "invalid", + #"token": self.token.key + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/accounts/new/', data=data, format='json') + self.assertEqual(response.status_code, 500) diff --git a/tests/test_views/test_api_auth_add.py b/tests/test_views/test_api_auth_add.py index 99733eae..9a701cb7 100644 --- a/tests/test_views/test_api_auth_add.py +++ b/tests/test_views/test_api_auth_add.py @@ -1,72 +1,60 @@ -##test for api/auth/add -##07801a1a4cdbf1945e22ac8439f1db27fe813f7a +#!/usr/bin/env python3 + +"""Add Authentication +Tests for 'New authentication credentials added to existing object' (200), +'Authentication credentials were created and added' (201), 'Bad request' (400), +'That object already exists for this account' (409) +""" + from django.test import TestCase, Client from rest_framework.test import APIClient from rest_framework.authtoken.models import Token from django.contrib.auth.models import User +from authentication.models import Authentication class AuthenticationTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + def setUp(self): self.client = APIClient() + def test_credentials_created_response(self): + """Add authentication is successful (200) + """ - # Creating a user for authentication - self.user = User.objects.create(username='testuser') - - # Checking if user already has token, if not then creating one - if not Token.objects.filter(user=self.user).exists(): - self.token = Token.objects.create(user=self.user) - else: - self.token = Token.objects.get(user=self.user) - def test_success_response(self): - # successfull request - data = { - "iss": "Reeya1", - "sub": "ReeyaGupta1" - } + token = Token.objects.get(user=User.objects.get(username='test50')).key + data = {"iss": "Reeya1","sub": "ReeyaGupta1"} - self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) response = self.client.post('/api/auth/add/', data=data) - '''fails with "AssertionError: 201 != 200": - This test case is expecting a status code of 200 (OK) when making a successful request, - but it receives a status code of 201 (Created) instead. It seems like the endpoint I am - testing is returning a 201 status code instead of the expected 200. To fix this, I - updated the test case to expect a status code of 201. - ''' self.assertEqual(response.status_code, 201) - def test_credentials_created_response(self): - # Simulate a request where authentication credentials were created and added - data = { - "iss": "Reeya1", - "sub": "ReeyaGupta1" - } - self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) - response = self.client.post('/api/auth/add/', data=data) - self.assertEqual(response.status_code, 201) + def test_credentials_added(self): + """New authentication credentials added to existing object (200) + """ + + token = Token.objects.get(user=User.objects.get(username='bco_api_user')).key + data = {"iss": "new","sub": "new One"} + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.post('/api/auth/add/', data=data, format='json') + self.assertEqual(response.status_code, 200) def test_bad_request_response(self): - # bad request - data = { - # Missing required fields - } - self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) - response = self.client.post('/api/auth/add/', data=data) + """Bad request (400) + """ + + token = Token.objects.get(user=User.objects.get(username='test50')).key + data = {"Missing required fields"} + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.post('/api/auth/add/', data=data, format='json') self.assertEqual(response.status_code, 400) def test_object_already_exists_response(self): - # an object that already exists for this account - data = { - "iss": "Reeya", - "sub": "ReeyaGupta" - } - self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) - response = self.client.post('/api/auth/add/', data=data) - '''fails with "AssertionError: 201 != 409" - Similarly, this test case is expecting 409 (Conflict) when trying to - create an object that already exists, but it receives 201 (Created). - This endpoint is not handling the object's existence as expected. - I updated the test case to expect a status code of 201 when creating an object - that already exists. - ''' - self.assertEqual(response.status_code, 201) + """That object already exists for this account (409) + """ + + token = Token.objects.get(user=User.objects.get(username='bco_api_user')).key + data = {"iss": "Reeya1","sub": "ReeyaGupta1"} + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.post('/api/auth/add/', data=data, format='json') + self.assertEqual(response.status_code, 409) diff --git a/tests/test_views/test_api_objects_drafts_create.py b/tests/test_views/test_api_objects_drafts_create.py index 249f1f87..e8631599 100644 --- a/tests/test_views/test_api_objects_drafts_create.py +++ b/tests/test_views/test_api_objects_drafts_create.py @@ -1,31 +1,45 @@ -from django.test import TestCase, Client -from django.contrib.auth.models import User + + import json +from django.test import TestCase +from django.contrib.auth.models import User +from rest_framework.authtoken.models import Token +from rest_framework.test import APIClient class BcoDraftCreateTestCase(TestCase): - def setUp(self): - self.client = Client() - self.url = '/api/objects/drafts/create' # The URL for the create draft endpoint - self.user = User.objects.create_user(username='bco_api_user', password='biocompute') + fixtures = ['tests/fixtures/test_data'] + + # def setUp(self): + # self.client = Client() + # self.url = '/api/objects/drafts/create' # The URL for the create draft endpoint + # self.user = User.objects.create_user(username='bco_api_user', password='biocompute') def test_successful_creation(self): - # force logging - self.client.force_login(self.user) + """200: Creation of BCO draft is successful. + """ + + client = APIClient() + token = Token.objects.get(user=User.objects.get(username='bco_api_user')).key + client.credentials(HTTP_AUTHORIZATION='Token ' + token) - # Test case for successful creation (response code 200) data = { - 'prefix': 'string', - 'owner_group': 'string', - 'object_id': 'string', - 'schema': 'string', - 'contents': { - "additionalProp1": {} - } + 'POST_api_objects_draft_create': [ + { + 'prefix': 'BCO', + 'owner_group': 'bco_drafter', + 'schema': 'IEEE', + 'contents': {} + }, + { + 'prefix': 'Hadley', + 'owner_group': 'bco_drafter', + 'schema': 'IEEE', + 'contents': {} + } + ] } - response = self.client.post(self.url, data=json.dumps(data), content_type='application/json', follow=True) + response = client.post('/api/objects/drafts/create/',data, format='json') self.assertEqual(response.status_code, 200) - # Checking the response. I believe it's JSON) - response_data = json.loads(response.content) def test_partial_failure(self): # Test case for partial failure (response code 300) diff --git a/tests/test_views/test_api_objects_drafts_permissions.py b/tests/test_views/test_api_objects_drafts_permissions.py new file mode 100644 index 00000000..1d00c4dc --- /dev/null +++ b/tests/test_views/test_api_objects_drafts_permissions.py @@ -0,0 +1,706 @@ + +#!/usr/bin/env python3 + +"""Test Permissions draft BCO +Tests for Partial failure(300) and invalid token(403) + +Gives 300 instead of 200 and 400 + +""" + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from rest_framework.test import APITestCase + +class PermissionDraftBCOTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='bco_api_user') + except User.DoesNotExist: + self.user = User.objects.create_user(username='bco_api_user') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + + def test_permission_bco_success(self): + #Gives 300 instead of 200 + + data = { + "POST_api_objects_drafts_permissions": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "contents": { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "spec_version": "https://w3id.org/ieee/ieee-2791-schema/2791object.json", + "etag": "0275321b6011324035289a5624c635ce5490fbdec588aa5f3bcaf63b85369b4a", + "provenance_domain": { + "name": "Influenza A reference gene sequences", + "version": "1.1", + "created": "2021-12-01T15:20:13.614Z", + "modified": "2022-06-28T23:10:12.804Z", + "review": [ + + ], + "contributors": [ + { + "contribution": [ + "createdBy", + "authoredBy", + "curatedBy", + "importedBy", + "contributedBy" + ], + "name": "Stephanie Singleton", + "affiliation": "The George Washington University ", + "email": "ssingleton@gwu.edu" + }, + { + "contribution": [ + "createdBy" + ], + "name": "Jonathon Keeney", + "affiliation": "The George Washington University ", + "email": "keeneyjg@gwu.edu" + } + ], + "license": "MIT" + }, + "usability_domain": [ + "Influenza A (A/Puerto Rico/8/1934 H1N1) reference protein coding sequences.", + "Cross reference to genes was retrieved using mappings present in proteins that were retrieved using UniProt proteome ID (UniProt ID: UP000009255; strain A/Puerto Rico/8/1934 H1N1). This set was chosen based on UniProt curation emphasis and community use. The primary use case for this data set is to visualize how protein annotations related to drug resistance mutations, selection pressure and more map to gene sequences. " + ], + "description_domain": { + "keywords": [ + "Influenza A, Complete Genome, FASTA, Genes" + ], + "platform": [ + + ], + "pipeline_steps": [ + { + "step_number": 0, + "name": "Download files from UniProt", + "description": "Download all files associated with the Influenza A reference genome (influenza A, UP000009255) into the ARGOS Dev server Downloads folder. While logged into the server, execute the following commands: wget ftp://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Viruses/UP000009255/*. One of the files acquired through this step and necessary for generating a new data set is 'UP000009255_211044_DNA.fasta.gz'. Then execute 'gunzip *.gz' to unzip all the files in the downloads folder. The file name is then changed to 'UP000009255_211044_DNA.fasta' in the downloads folder.", + "prerequisite": [ + { + "name": "UniProt reference page ", + "uri": { + "uri": "ftp://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Viruses/UP000009255/", + "access_time": "2021-12-01T15:20:13.614Z" + } + } + ], + "input_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta.gz", + "filename": "UP000009255_211044_DNA.fasta.gz", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "output_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "version": "1.1" + }, + { + "step_number": 0, + "name": "Run the recipe created to process this fasta file, review the newly generated dataset, and change the name of the file for clarity", + "description": "This step will use a recipe and a python script to generate a new dataset. The recipe tells the python script how and what to construct. This dataset will then be then moved in the 'unreviewed' folder in the dev argosdb server, it will be manually reviewed, and then the name of the file will be changed for clarity and tracking purposes - this is prefered. \\nMake sure you are located in the correct folder to run the script (/software/argosdb/dataset-maker). Use the following command to run the recipe and the python script: ‘python3 make-dataset.py -i recipes/influenza_UP000009255_genome_sequences.json’. Next, go to the ‘unreviewed’ folder to review the newly generated dataset ‘UP000009255_211044_DNA.fasta’. Once reviewed and approved, move the file to the ‘reviewed’ folder. Lastly, once in the ‘reviewed’ folder, change the name of the file to: ‘ influenza_UP000009255_211044_DNA.fasta’", + "prerequisite": [ + { + "name": "Dataset-maker python script", + "uri": { + "uri": "ftp://argosdb-vm-dev/software/argosdb/make-dataset.py", + "filename": "make-dataset.py" + } + }, + { + "name": "Influenza genome FASTA recipe", + "uri": { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/generated/datasets/recipes/Influenza/influenza_UP000009255_genome_sequences.json", + "filename": "influenza_UP000009255_genome_sequences.json" + } + } + ], + "input_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "output_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/generated/datasets/reviewed/influenza_UP000009255_211044_DNA.fasta", + "filename": "influenza_UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "version": "1.1" + } + ] + }, + "execution_domain": { + "script": [ + { + "uri": { + "uri": "ftp://argosdb-vm-dev/software/argosdb/make-dataset.py", + "filename": "make-dataset.py" + } + } + ], + "script_driver": "python3", + "software_prerequisites": [ + { + "name": "Python", + "version": "3", + "uri": { + "uri": "https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe", + "filename": "" + } + } + ], + "external_data_endpoints": [ + { + "name": "python-3.10.0", + "url": "https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe" + } + ], + "environment_variables": { + } + }, + "io_domain": { + "input_subdomain": [ + { + "uri": { + "uri": "http://data.argosdb.org/ln2downloads/uniprot/v1.0/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta" + } + } + ], + "output_subdomain": [ + { + "mediatype": "text/plain", + "uri": { + "uri": "http://data.argosdb.org/ln2data/uniprot/v1.0/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta" + } + } + ] + }, + "parametric_domain": [ + + ], + "extension_domain": [ + { + "extension_schema": "http://www.w3id.org/biocompute/extension_domain/1.1.0/dataset/dataset_extension.json", + "dataset_extension": { + "additional_license": { + "data_license": "https://creativecommons.org/licenses/by/4.0/", + "script_license": "https://www.gnu.org/licenses/gpl-3.0.en.html" + }, + "dataset_categories": [ + { + "category_value": "Influenza A", + "category_name": "species" + }, + { + "category_value": "nucleotide", + "category_name": "molecule" + }, + { + "category_value": "Influenza A", + "category_name": "tag" + }, + { + "category_value": "fasta", + "category_name": "file_type" + }, + { + "category_value": "reviewed", + "category_name": "status" + }, + { + "category_value": "internal", + "category_name": "scope" + } + ] + } + } + ] + } + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/permissions/', data=data, format='json') + self.assertEqual(response.status_code, 200) + + def test_permission_bco_partial_failure(self): + data = { + "POST_api_objects_drafts_permissions": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "contents": { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "spec_version": "https://w3id.org/ieee/ieee-2791-schema/2791object.json", + "etag": "0275321b6011324035289a5624c635ce5490fbdec588aa5f3bcaf63b85369b4a", + "provenance_domain": { + "name": "Influenza A reference gene sequences", + "version": "1.1", + "created": "2021-12-01T15:20:13.614Z", + "modified": "2022-06-28T23:10:12.804Z", + "review": [ + + ], + "contributors": [ + { + "contribution": [ + "createdBy", + "authoredBy", + "curatedBy", + "importedBy", + "contributedBy" + ], + "name": "Stephanie Singleton", + "affiliation": "The George Washington University ", + "email": "ssingleton@gwu.edu" + }, + { + "contribution": [ + "createdBy" + ], + "name": "Jonathon Keeney", + "affiliation": "The George Washington University ", + "email": "keeneyjg@gwu.edu" + } + ], + "license": "MIT" + }, + "usability_domain": [ + "Influenza A (A/Puerto Rico/8/1934 H1N1) reference protein coding sequences.", + "Cross reference to genes was retrieved using mappings present in proteins that were retrieved using UniProt proteome ID (UniProt ID: UP000009255; strain A/Puerto Rico/8/1934 H1N1). This set was chosen based on UniProt curation emphasis and community use. The primary use case for this data set is to visualize how protein annotations related to drug resistance mutations, selection pressure and more map to gene sequences. " + ], + "description_domain": { + "keywords": [ + "Influenza A, Complete Genome, FASTA, Genes" + ], + "platform": [ + + ], + "pipeline_steps": [ + { + "step_number": 0, + "name": "Download files from UniProt", + "description": "Download all files associated with the Influenza A reference genome (influenza A, UP000009255) into the ARGOS Dev server Downloads folder. While logged into the server, execute the following commands: wget ftp://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Viruses/UP000009255/*. One of the files acquired through this step and necessary for generating a new data set is 'UP000009255_211044_DNA.fasta.gz'. Then execute 'gunzip *.gz' to unzip all the files in the downloads folder. The file name is then changed to 'UP000009255_211044_DNA.fasta' in the downloads folder.", + "prerequisite": [ + { + "name": "UniProt reference page ", + "uri": { + "uri": "ftp://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Viruses/UP000009255/", + "access_time": "2021-12-01T15:20:13.614Z" + } + } + ], + "input_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta.gz", + "filename": "UP000009255_211044_DNA.fasta.gz", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "output_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "version": "1.1" + }, + { + "step_number": 0, + "name": "Run the recipe created to process this fasta file, review the newly generated dataset, and change the name of the file for clarity", + "description": "This step will use a recipe and a python script to generate a new dataset. The recipe tells the python script how and what to construct. This dataset will then be then moved in the 'unreviewed' folder in the dev argosdb server, it will be manually reviewed, and then the name of the file will be changed for clarity and tracking purposes - this is prefered. \\nMake sure you are located in the correct folder to run the script (/software/argosdb/dataset-maker). Use the following command to run the recipe and the python script: ‘python3 make-dataset.py -i recipes/influenza_UP000009255_genome_sequences.json’. Next, go to the ‘unreviewed’ folder to review the newly generated dataset ‘UP000009255_211044_DNA.fasta’. Once reviewed and approved, move the file to the ‘reviewed’ folder. Lastly, once in the ‘reviewed’ folder, change the name of the file to: ‘ influenza_UP000009255_211044_DNA.fasta’", + "prerequisite": [ + { + "name": "Dataset-maker python script", + "uri": { + "uri": "ftp://argosdb-vm-dev/software/argosdb/make-dataset.py", + "filename": "make-dataset.py" + } + }, + { + "name": "Influenza genome FASTA recipe", + "uri": { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/generated/datasets/recipes/Influenza/influenza_UP000009255_genome_sequences.json", + "filename": "influenza_UP000009255_genome_sequences.json" + } + } + ], + "input_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "output_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/generated/datasets/reviewed/influenza_UP000009255_211044_DNA.fasta", + "filename": "influenza_UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "version": "1.1" + } + ] + }, + "execution_domain": { + "script": [ + { + "uri": { + "uri": "ftp://argosdb-vm-dev/software/argosdb/make-dataset.py", + "filename": "make-dataset.py" + } + } + ], + "script_driver": "python3", + "software_prerequisites": [ + { + "name": "Python", + "version": "3", + "uri": { + "uri": "https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe", + "filename": "" + } + } + ], + "external_data_endpoints": [ + { + "name": "python-3.10.0", + "url": "https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe" + } + ], + "environment_variables": { + } + }, + "io_domain": { + "input_subdomain": [ + { + "uri": { + "uri": "http://data.argosdb.org/ln2downloads/uniprot/v1.0/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta" + } + } + ], + "output_subdomain": [ + { + "mediatype": "text/plain", + "uri": { + "uri": "http://data.argosdb.org/ln2data/uniprot/v1.0/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta" + } + } + ] + }, + "parametric_domain": [ + + ], + "extension_domain": [ + { + "extension_schema": "http://www.w3id.org/biocompute/extension_domain/1.1.0/dataset/dataset_extension.json", + "dataset_extension": { + "additional_license": { + "data_license": "https://creativecommons.org/licenses/by/4.0/", + "script_license": "https://www.gnu.org/licenses/gpl-3.0.en.html" + }, + "dataset_categories": [ + { + "category_value": "Influenza A", + "category_name": "species" + }, + { + "category_value": "nucleotide", + "category_name": "molecule" + }, + { + "category_value": "Influenza A", + "category_name": "tag" + }, + { + "category_value": "fasta", + "category_name": "file_type" + }, + { + "category_value": "reviewed", + "category_name": "status" + }, + { + "category_value": "internal", + "category_name": "scope" + } + ] + } + } + ] + } + }, + { + "object_id": "http://127.0.0.1:8000/BCO_1234567/DRAFT", + "contents": { + + } + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/permissions/', data=data, format='json') + self.assertEqual(response.status_code, 300) + + def test_permission_bco_bad_request(self): + ##Gives 300 instead of 400 + data = { + "POST_api_objects_drafts_permissions": [ + { + # Provide invalid or missing data + "object_id": "Invalid_objectid", + "contents": { + + } + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/permissions/', data=data, format='json') + self.assertEqual(response.status_code, 400) + + def test_permission_bco_invalid_token(self): + data = { + "POST_api_objects_drafts_permissions": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "contents": { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "spec_version": "https://w3id.org/ieee/ieee-2791-schema/2791object.json", + "etag": "0275321b6011324035289a5624c635ce5490fbdec588aa5f3bcaf63b85369b4a", + "provenance_domain": { + "name": "Influenza A reference gene sequences", + "version": "1.1", + "created": "2021-12-01T15:20:13.614Z", + "modified": "2022-06-28T23:10:12.804Z", + "review": [ + + ], + "contributors": [ + { + "contribution": [ + "createdBy", + "authoredBy", + "curatedBy", + "importedBy", + "contributedBy" + ], + "name": "Stephanie Singleton", + "affiliation": "The George Washington University ", + "email": "ssingleton@gwu.edu" + }, + { + "contribution": [ + "createdBy" + ], + "name": "Jonathon Keeney", + "affiliation": "The George Washington University ", + "email": "keeneyjg@gwu.edu" + } + ], + "license": "MIT" + }, + "usability_domain": [ + "Influenza A (A/Puerto Rico/8/1934 H1N1) reference protein coding sequences.", + "Cross reference to genes was retrieved using mappings present in proteins that were retrieved using UniProt proteome ID (UniProt ID: UP000009255; strain A/Puerto Rico/8/1934 H1N1). This set was chosen based on UniProt curation emphasis and community use. The primary use case for this data set is to visualize how protein annotations related to drug resistance mutations, selection pressure and more map to gene sequences. " + ], + "description_domain": { + "keywords": [ + "Influenza A, Complete Genome, FASTA, Genes" + ], + "platform": [ + + ], + "pipeline_steps": [ + { + "step_number": 0, + "name": "Download files from UniProt", + "description": "Download all files associated with the Influenza A reference genome (influenza A, UP000009255) into the ARGOS Dev server Downloads folder. While logged into the server, execute the following commands: wget ftp://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Viruses/UP000009255/*. One of the files acquired through this step and necessary for generating a new data set is 'UP000009255_211044_DNA.fasta.gz'. Then execute 'gunzip *.gz' to unzip all the files in the downloads folder. The file name is then changed to 'UP000009255_211044_DNA.fasta' in the downloads folder.", + "prerequisite": [ + { + "name": "UniProt reference page ", + "uri": { + "uri": "ftp://ftp.uniprot.org/pub/databases/uniprot/current_release/knowledgebase/reference_proteomes/Viruses/UP000009255/", + "access_time": "2021-12-01T15:20:13.614Z" + } + } + ], + "input_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta.gz", + "filename": "UP000009255_211044_DNA.fasta.gz", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "output_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "version": "1.1" + }, + { + "step_number": 0, + "name": "Run the recipe created to process this fasta file, review the newly generated dataset, and change the name of the file for clarity", + "description": "This step will use a recipe and a python script to generate a new dataset. The recipe tells the python script how and what to construct. This dataset will then be then moved in the 'unreviewed' folder in the dev argosdb server, it will be manually reviewed, and then the name of the file will be changed for clarity and tracking purposes - this is prefered. \\nMake sure you are located in the correct folder to run the script (/software/argosdb/dataset-maker). Use the following command to run the recipe and the python script: ‘python3 make-dataset.py -i recipes/influenza_UP000009255_genome_sequences.json’. Next, go to the ‘unreviewed’ folder to review the newly generated dataset ‘UP000009255_211044_DNA.fasta’. Once reviewed and approved, move the file to the ‘reviewed’ folder. Lastly, once in the ‘reviewed’ folder, change the name of the file to: ‘ influenza_UP000009255_211044_DNA.fasta’", + "prerequisite": [ + { + "name": "Dataset-maker python script", + "uri": { + "uri": "ftp://argosdb-vm-dev/software/argosdb/make-dataset.py", + "filename": "make-dataset.py" + } + }, + { + "name": "Influenza genome FASTA recipe", + "uri": { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/generated/datasets/recipes/Influenza/influenza_UP000009255_genome_sequences.json", + "filename": "influenza_UP000009255_genome_sequences.json" + } + } + ], + "input_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/downloads/uniprot/v1.0/influenza_a/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "output_list": [ + { + "uri": "ftp://argosdb-vm-dev/data/shared/argosdb/generated/datasets/reviewed/influenza_UP000009255_211044_DNA.fasta", + "filename": "influenza_UP000009255_211044_DNA.fasta", + "access_time": "2021-12-01T15:20:13.614Z" + } + ], + "version": "1.1" + } + ] + }, + "execution_domain": { + "script": [ + { + "uri": { + "uri": "ftp://argosdb-vm-dev/software/argosdb/make-dataset.py", + "filename": "make-dataset.py" + } + } + ], + "script_driver": "python3", + "software_prerequisites": [ + { + "name": "Python", + "version": "3", + "uri": { + "uri": "https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe", + "filename": "" + } + } + ], + "external_data_endpoints": [ + { + "name": "python-3.10.0", + "url": "https://www.python.org/ftp/python/3.10.0/python-3.10.0-amd64.exe" + } + ], + "environment_variables": { + } + }, + "io_domain": { + "input_subdomain": [ + { + "uri": { + "uri": "http://data.argosdb.org/ln2downloads/uniprot/v1.0/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta" + } + } + ], + "output_subdomain": [ + { + "mediatype": "text/plain", + "uri": { + "uri": "http://data.argosdb.org/ln2data/uniprot/v1.0/UP000009255_211044_DNA.fasta", + "filename": "UP000009255_211044_DNA.fasta" + } + } + ] + }, + "parametric_domain": [ + + ], + "extension_domain": [ + { + "extension_schema": "http://www.w3id.org/biocompute/extension_domain/1.1.0/dataset/dataset_extension.json", + "dataset_extension": { + "additional_license": { + "data_license": "https://creativecommons.org/licenses/by/4.0/", + "script_license": "https://www.gnu.org/licenses/gpl-3.0.en.html" + }, + "dataset_categories": [ + { + "category_value": "Influenza A", + "category_name": "species" + }, + { + "category_value": "nucleotide", + "category_name": "molecule" + }, + { + "category_value": "Influenza A", + "category_name": "tag" + }, + { + "category_value": "fasta", + "category_name": "file_type" + }, + { + "category_value": "reviewed", + "category_name": "status" + }, + { + "category_value": "internal", + "category_name": "scope" + } + ] + } + } + ] + } + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Invalid token') + response = self.client.post('/api/objects/drafts/permissions/', data=data, format='json') + self.assertEqual(response.status_code, 403) diff --git a/tests/test_views/test_api_objects_drafts_permissions_set.py b/tests/test_views/test_api_objects_drafts_permissions_set.py new file mode 100644 index 00000000..9ddd4698 --- /dev/null +++ b/tests/test_views/test_api_objects_drafts_permissions_set.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +"""Set permissions for a bco draft + + Tests for 200 and 403 + + Gives 200 instead of 400 and 300 +""" + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from rest_framework.test import APITestCase + +class BCOPermissionsTestCase(APITestCase): + fixtures = ['tests/fixtures/test_data'] + + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='bco_api_user') + except User.DoesNotExist: + self.user = User.objects.create_user(username='bco_api_user') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + def test_set_permissions_successful(self): + data = { + "POST_api_objects_drafts_permissions_set": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "actions": { + "remove_permissions": "some_permissions_to_remove", + "full_permissions": "some_full_permissions", + "add_permissions": "some_permissions_to_add" + } + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/permissions/set/', data=data, format='json') + self.assertEqual(response.status_code, 200) + + def test_some_requests_failed(self): + ##Partial failure + #Gives 200 instead of 300 + data = { + "POST_api_objects_drafts_permissions_set": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "actions": { + "remove_permissions": "some_permissions_to_remove", + "full_permissions": "some_full_permissions", + "add_permissions": "some_permissions_to_add" + } + }, + { + "object_id": "Invalid", + "actions":{} + + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/permissions/set/', data=data, format='json') + self.assertEqual(response.status_code, 300) + + def test_bad_request(self): + ##Bad request- Invalid data + #Gives 200 instead of 400 + data = { + + "POST_api_objects_drafts_permissions_set": [ + { + "object_id": "Invalid object id", + "actions": { + + } + } + ] + + + } # Invalid data + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/permissions/set/', data=data, format='json') + self.assertEqual(response.status_code, 400) + + def test_invalid_token(self): + #Invalid token + data = { + "POST_api_objects_drafts_permissions_set": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "actions": { + "remove_permissions": "some_permissions_to_remove", + "full_permissions": "some_full_permissions", + "add_permissions": "some_permissions_to_add" + } + } + ] + } + + + self.client.credentials(HTTP_AUTHORIZATION='Invalid token') + + response = self.client.post('/api/objects/drafts/permissions/set/', data=data, format='json') + self.assertEqual(response.status_code, 403) diff --git a/tests/test_views/test_api_objects_drafts_publish.py b/tests/test_views/test_api_objects_drafts_publish.py new file mode 100644 index 00000000..e7087ac0 --- /dev/null +++ b/tests/test_views/test_api_objects_drafts_publish.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +"""Test Bulk Publish BCOs +Tests for 'All BCO publications successful.' (200), 'Some or all publications +failed.' (207), 'Bad request.' (400), and 'Authentication credentials were not +provided.' (404) +""" + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from rest_framework.test import APITestCase, APIClient + + +class PublishDraftBCOTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + def setUp(self): + self.client = APIClient() + + def test_publish_bco_success(self): + """All BCO publications successful (200) + """ + + token = Token.objects.get(user=User.objects.get(username='test50')).key + + data = { + "POST_api_objects_drafts_publish": [ + { + "prefix": "BCO", + "draft_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + "delete_draft": False + }, + { + "prefix": "BCO", + "draft_id": "http://127.0.0.1:8000/BCO_000001/DRAFT", + "object_id" "http://127.0.0.1:8000/BCO_000000/1.1" + "delete_draft": False + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.post('/api/objects/drafts/publish/', data=data, format='json') + self.assertEqual(response.status_code, 200) + + def test_publish_bco_partial_failure(self): + """Some or all publications failed (207) + """ + + token = Token.objects.get(user=User.objects.get(username='test50')).key + + data = { + "POST_api_objects_drafts_publish": [ + { + "prefix": "BCO", + "draft_id": "http://127.0.0.1:8000/BCO_000001/DRAFT", + "delete_draft": False + }, + { + "prefix": "InvalidPrefix", + "draft_id": "InvalidDraftId", + "delete_draft": False + } + + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.post('/api/objects/drafts/publish/', data=data, format='json') + self.assertEqual(response.status_code, 207) + + def test_publish_bco_bad_request(self): + """Bad request (400) + """ + + token = Token.objects.get(user=User.objects.get(username='test50')).key + + data = { + "POST_wrong_thing": [ + { + "prefix": "BCO", + #"draft_id": "InvalidID", + "delete_draft": False + } + + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + token) + response = self.client.post('/api/objects/drafts/publish/', data=data, format='json') + self.assertEqual(response.status_code, 400) + + def test_publish_bco_invalid_token(self): + """Authentication credentials were not provided. (404) + """ + + token = Token.objects.get(user=User.objects.get(username='test50')).key + + data = { + "POST_api_objects_drafts_publish": [ + { + "prefix": "BCO", + "draft_id": "http://127.0.0.1:8000/BCO_000000/DRAFT", + + "delete_draft": False + + } + ] + } + + self.client.credentials(HTTP_AUTHORIZATION='invalid token') + response = self.client.post('/api/objects/drafts/publish/', data=data, format='json') + self.assertEqual(response.status_code, 403) diff --git a/tests/test_views/test_api_objects_drafts_read.py b/tests/test_views/test_api_objects_drafts_read.py new file mode 100644 index 00000000..3182d41e --- /dev/null +++ b/tests/test_views/test_api_objects_drafts_read.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +"""Test for reading a draft bco +Tests for Partial failure(300) and invalid token(403) + +Gives 300 instead of 200 and 400 +""" + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User + +class ReadDraftBCOTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='bco_api_user') + except User.DoesNotExist: + self.user = User.objects.create_user(username='bco_api_user') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + + def test_read_bco_success(self): + ##Gives 300 instead of 200 + data = { + "POST_api_objects_drafts_read": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT" + + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/read/', data=data, format='json') + self.assertEqual(response.status_code, 200) + + def test_read_bco_partial_failure(self): + data = { + "POST_api_objects_drafts_read": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT" + }, + { + "object_id": "http://127.0.0.1:8000/BCO_1234567/DRAFT" #Invalid object id + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/read/', data=data, format='json') + self.assertEqual(response.status_code, 300) + + def test_read_bco_bad_request(self): + ##Gives 300 instead of 400 + data = { + "POST_api_objects_drafts_read": [ + { + # Provide invalid or missing data + "object_id": "Invalid_objectid" + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/objects/drafts/read/', data=data, format='json') + self.assertEqual(response.status_code, 400) + + def test_read_bco_invalid_token(self): + data = { + "POST_api_objects_drafts_read": [ + { + "object_id": "http://127.0.0.1:8000/BCO_000000/DRAFT" + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Invalid token') + response = self.client.post('/api/objects/drafts/read/', data=data, format='json') + self.assertEqual(response.status_code, 403) diff --git a/tests/test_views/test_api_objects_search.py b/tests/test_views/test_api_objects_search.py new file mode 100644 index 00000000..f2e1e73b --- /dev/null +++ b/tests/test_views/test_api_objects_search.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +"""Objects Search +Tests for successful search (status code: 200), +prefix not found (status code: 404) +""" + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from rest_framework.test import APITestCase + +class ObjectsSearchTestCase(APITestCase): + + fixtures = ['tests/fixtures/test_data'] + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='bco_api_user') + except User.DoesNotExist: + self.user = User.objects.create_user(username='bco_api_user') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + + def test_search_successful(self): + # Test case for a successful search (status code: 200) + + data = { + "POST_api_objects_search": [ + { + "type": "prefix", + "search": "TEST" + } + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post("/api/objects/search/", data=data, format="json") + + self.assertEqual(response.status_code, 200) + + + + def test_prefix_not_found(self): + # Test case for prefix not found (status code: 404) + + data = { + "POST_api_objects_search": [ + { + "type": "prefix", + "search": "invalidprefix" + } + ] + } + + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post("/api/objects/search/", data=data, format="json") + self.assertEqual(response.status_code, 404) \ No newline at end of file diff --git a/tests/test_views/test_api_prefixes_delete.py b/tests/test_views/test_api_prefixes_delete.py new file mode 100644 index 00000000..9b191a0b --- /dev/null +++ b/tests/test_views/test_api_prefixes_delete.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python3 + +"""Test for Deleting a prefix + + Givesw 200 instead of 401,403,404 - Requires Debugging +""" + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User + +class PrefixDeleteTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + # Using wheel instead of bco_api_user because wheel is in group prefix_admins + + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='wheel') + except User.DoesNotExist: + self.user = User.objects.create_user(username='wheel') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + + + #self.prefix_to_delete = "OTHER" + + def test_delete_prefix_success(self): + + data = { + "POST_api_prefixes_delete": [ + "OTHER", + "TESTR" + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/prefixes/delete/', data=data, format='json') + self.assertEqual(response.status_code, 200) + + def test_delete_prefix_unauthorized(self): + ##providing no authorization + #Gives 200 instead of 401 + data = { + "POST_api_prefixes_delete": { + "prefixes": ["OTHER"] + } + } + response = self.client.post('/api/prefixes/delete/', data=data, format='json') + self.assertEqual(response.status_code, 401) + + def test_delete_prefix_forbidden(self): + # Simulate a user without necessary permissions- Invalid token. + #Gives 200 instead of 403 + #self.user.groups.add('prefix_users') + + data = { + "POST_api_prefixes_delete": [ + "OTHER", + "TESTR" + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Invalid token') + response = self.client.post('/api/prefixes/delete/', data=data, format='json') + self.assertEqual(response.status_code, 403) + + def test_delete_prefix_not_found(self): + #The prefix couldn't be found so therefore it could not be deleted. + #non_existent_prefix = "NONEXISTENT" + + ##Gives 200 instead of 404 + data = { + "POST_api_prefixes_delete": [ + "nonexsistent prefix" + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/prefixes/delete/', data=data, format='json') + self.assertEqual(response.status_code, 404) diff --git a/tests/test_views/test_api_verify.py b/tests/test_views/test_api_verify.py new file mode 100644 index 00000000..44a07df9 --- /dev/null +++ b/tests/test_views/test_api_verify.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +'''Verify a valid token + + ''' + +from django.test import TestCase +from rest_framework.test import APIClient +from rest_framework.authtoken.models import Token +from django.contrib.auth.models import User +from rest_framework.test import APITestCase + + +class VerifyTokenBCOTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + def setUp(self): + + self.client = APIClient() + # Checking if the user 'bco_api_user' already exists + try: + self.user = User.objects.get(username='bco_api_user') + except User.DoesNotExist: + self.user = User.objects.create_user(username='bco_api_user') + + # Checking if user already has token, if not then creating one + if not Token.objects.filter(user=self.user).exists(): + self.token = Token.objects.create(user=self.user) + else: + self.token = Token.objects.get(user=self.user) + + def test_valid_token(self): + ##Test for a valid token + ##Gives 400 instead of 200 + + data = { + "VerifyAuthToken": [ + {"token": self.token.key} + ] + } + self.client.credentials(HTTP_AUTHORIZATION='Token ' + self.token.key) + response = self.client.post('/api/verify/', data=data, format='json') + self.assertEqual(response.status_code, 200) + #self.assertIn('Success- Token is valid.', response.data[0]['message']) + #self.assertEqual(response.data["token"], self.token.key) + + def test_missing_token(self): + # Test when token field is missing + data = {} + response = self.client.post('/api/verify/', data=data, format='json') + self.assertEqual(response.status_code, 400) + + + def test_invalid_token(self): + # Test for an invalid token + data = { + "VerifyAuthToken": [ + {"token": "invalid token here"} + ] + } + self.client.credentials(HTTP_AUTHORIZATION='iNVALID TOKEN') + response = self.client.post('/api/verify/', data=data, format='json') + #self.assertIn('Error- Token is invalid.', response.data.get('message')) + self.assertEqual(response.status_code, 400) + \ No newline at end of file diff --git a/tests/test_views/test_get_objectid.py b/tests/test_views/test_get_objectid.py new file mode 100644 index 00000000..0ec934e2 --- /dev/null +++ b/tests/test_views/test_get_objectid.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +"""Get ObjectID +Tests for 'Successful request with valid object id' (200), +'Request with a non-existent object ID' (404), 'unauthorized- Request without authentication credentials' (401), +'Forbidden- Request with valid object ID but unauthorized user' (403) +""" + + +from django.test import TestCase +from rest_framework.test import APIClient + +class BCOViewTestCase(TestCase): + fixtures = ['tests/fixtures/test_data'] + def setUp(self): + self.client = APIClient() + + def test_view_published_bco_success(self): + # Successful request with valid object ID + object_id = "TEST_000001" + response = self.client.get(f'/api/{object_id}/') + self.assertEqual(response.status_code, 200) + + def test_view_published_bco_not_found(self): + # Request with a non-existent object ID + object_id = "invalid_object_id" + response = self.client.get(f'/api/{object_id}/') + self.assertEqual(response.status_code, 404) + + def test_view_published_bco_unauthorized(self): + # Request without authentication credentials + response = self.client.get('/api/TEST_000001/') + self.assertEqual(response.status_code, 401) + + def test_view_published_bco_forbidden(self): + # Request with valid object ID but unauthorized user + # (ie-user without sufficient permissions to access the BCO) + object_id = "TEST_000001" + # Authenticate as a user without sufficient permissions ?? + # self.client.credentials(HTTP_AUTHORIZATION='Token your_invalid_token_here') + response = self.client.get(f'/api/{object_id}/') + self.assertEqual(response.status_code, 403) \ No newline at end of file diff --git a/tests/test_views/test_published_object_by_id.py b/tests/test_views/test_published_object_by_id.py new file mode 100644 index 00000000..d5a599cc --- /dev/null +++ b/tests/test_views/test_published_object_by_id.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 + +"""Root Object Id Testing +Tests for 'Object Found' (200) and 'Object Not Found'(404) +""" + +from django.test import TestCase +from rest_framework.test import APIClient + +class ObjectIdRootObjectIdTest(TestCase): + fixtures = ['tests/fixtures/test_data'] + + def test_seccussfull_retrieval(self): + """200: Object returned. + """ + + client = APIClient() + response = self.client.get('/BCO_000001') + self.assertEqual(response.status_code, 200) + + def test_object_not_found(self): + """404: Object not found. + """ + + response = self.client.get('/BCO_001000') + self.assertEqual(response.status_code, 404) \ No newline at end of file