diff options
author | 2018-08-16 22:44:24 +0200 | |
---|---|---|
committer | 2018-08-16 22:44:24 +0200 | |
commit | a804d855e649a8148cd215bbbe3a47f9d7e9164a (patch) | |
tree | 79cd0fdc83b2d9231d8f5f3030011ea3952febb9 | |
parent | Make test user superuser. (diff) |
Add support for `create` & `destroy`.
-rw-r--r-- | api/tests/test_documentation_links.py | 89 | ||||
-rw-r--r-- | api/viewsets.py | 55 |
2 files changed, 133 insertions, 11 deletions
diff --git a/api/tests/test_documentation_links.py b/api/tests/test_documentation_links.py index c3ffe5bb..e560a2fd 100644 --- a/api/tests/test_documentation_links.py +++ b/api/tests/test_documentation_links.py @@ -21,6 +21,19 @@ class UnauthedDocumentationLinkAPITests(APISubdomainTestCase): self.assertEqual(response.status_code, 401) + def test_create_returns_401(self): + url = reverse('bot:documentationlink-list', host='api') + response = self.client.post(url, data={'hi': 'there'}) + + self.assertEqual(response.status_code, 401) + + def test_delete_returns_401(self): + url = reverse('bot:documentationlink-detail', args=('whatever',), host='api') + response = self.client.delete(url) + + self.assertEqual(response.status_code, 401) + + class EmptyDatabaseDocumentationLinkAPITests(APISubdomainTestCase): def test_detail_lookup_returns_404(self): url = reverse('bot:documentationlink-detail', args=('whatever',), host='api') @@ -35,6 +48,12 @@ class EmptyDatabaseDocumentationLinkAPITests(APISubdomainTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), []) + def test_delete_returns_404(self): + url = reverse('bot:documentationlink-detail', args=('whatever',), host='api') + response = self.client.delete(url) + + self.assertEqual(response.status_code, 404) + class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase): @classmethod @@ -70,3 +89,73 @@ class DetailLookupDocumentationLinkAPITests(APISubdomainTestCase): self.assertEqual(response.status_code, 200) self.assertEqual(response.json(), [self.doc_json]) + + def test_create_invalid_body_returns_400(self): + url = reverse('bot:documentationlink-list', host='api') + response = self.client.post(url, data={'i': 'am', 'totally': 'valid'}) + + self.assertEqual(response.status_code, 400) + + def test_create_invalid_url_returns_400(self): + body = { + 'package': 'example', + 'base_url': 'https://example.com', + 'inventory_url': 'totally an url' + } + + url = reverse('bot:documentationlink-list', host='api') + response = self.client.post(url, data=body) + + self.assertEqual(response.status_code, 400) + + +class DocumentationLinkCreationTests(APISubdomainTestCase): + def setUp(self): + super().setUp() + + self.body = { + 'package': 'example', + 'base_url': 'https://example.com', + 'inventory_url': 'https://docs.example.com' + } + + url = reverse('bot:documentationlink-list', host='api') + response = self.client.post(url, data=self.body) + + self.assertEqual(response.status_code, 201) + + def test_package_in_full_list(self): + url = reverse('bot:documentationlink-list', host='api') + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), [self.body]) + + def test_detail_lookup_works_with_package(self): + url = reverse('bot:documentationlink-detail', args=(self.body['package'],), host='api') + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + self.assertEqual(response.json(), self.body) + + +class DocumentationLinkDeletionTests(APISubdomainTestCase): + @classmethod + def setUpTestData(cls): + cls.doc_link = DocumentationLink.objects.create( + package='example', + base_url='https://example.com', + inventory_url='https://docs.example.com' + ) + + def test_unknown_package_returns_404(self): + url = reverse('bot:documentationlink-detail', args=('whatever',), host='api') + response = self.client.delete(url) + + self.assertEqual(response.status_code, 404) + + def test_delete_known_package_returns_204(self): + url = reverse('bot:documentationlink-detail', args=(self.doc_link.package,), host='api') + response = self.client.delete(url) + + self.assertEqual(response.status_code, 204) diff --git a/api/viewsets.py b/api/viewsets.py index 48b2f226..39721847 100644 --- a/api/viewsets.py +++ b/api/viewsets.py @@ -1,4 +1,4 @@ -from rest_framework.mixins import ListModelMixin, RetrieveModelMixin +from rest_framework.mixins import CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet, ViewSet @@ -6,14 +6,15 @@ from .models import DocumentationLink, SnakeName from .serializers import DocumentationLinkSerializer, SnakeNameSerializer -class DocumentationLinkViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): +class DocumentationLinkViewSet(CreateModelMixin, DestroyModelMixin, ListModelMixin, RetrieveModelMixin, GenericViewSet): """ - View providing documentation links used in the bot's `Doc` cog. + View providing management of documentation links used in the bot's `Doc` cog. ## Routes ### GET /bot/documentation-links - Return all documentation links in the database in the following format: + Retrieve all currently stored entries from the database. + #### Response format >>> [ ... { ... 'package': 'flask', @@ -23,17 +24,43 @@ class DocumentationLinkViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSe ... # ... ... ] + #### Status codes + - 200: returned on success + ### GET /bot/documentation-links/<package:str> Look up the documentation object for the given `package`. - If an entry exists, return data in the following format: + #### Response format >>> { ... 'package': 'flask', ... 'base_url': 'https://flask.pocoo.org/docs/dev', ... 'inventory_url': 'https://flask.pocoo.org/docs/objects.inv' ... } - Otherwise, if no entry for the given `package` exists, returns 404. + #### Status codes + - 200: returned on success + - 404: if no entry for the given `package` exists + + ### POST /bot/documentation-links + Create a new documentation link object. + + #### Body schema + >>> { + ... 'package': str, + ... 'base_url': URL, + ... 'inventory_url': URL + ... } + + #### Status codes + - 201: returned on success + - 400: if the request body has invalid fields, see the response for details + + ### DELETE /bot/documentation-links/<package:str> + Delete the entry for the given `package`. + + #### Status codes + - 204: returned on success + - 404: if the given `package` could not be found """ queryset = DocumentationLink.objects.all() @@ -47,23 +74,29 @@ class SnakeNameViewSet(ViewSet): ## Routes ### GET /bot/snake-names - By default, return a single random snake name as JSON in the following format: + By default, return a single random snake name along with its name and scientific name. + If the `get_all` query parameter is given, for example using... + $ curl api.pythondiscord.local:8000/bot/snake-names?get_all=yes + ...then the API will return all snake names and scientific names in the database. + #### Response format + Without `get_all` query parameter: >>> { ... 'name': "Python", ... 'scientific': "Langus greatus" ... } - If the `get_all` query parameter is given, for example using... - $ curl api.pythondiscord.local:8000/bot/snake-names?get_all=yes - ...then the API will return all snake names and scientific names in the database, - for example: + If the database is empty for whatever reason, this will return an empty dictionary. + With `get_all` query parameter: >>> [ ... {'name': "Python 3", 'scientific': "Langus greatus"}, ... {'name': "Python 2", 'scientific': "Langus decentus"} ... ] + #### Status codes + - 200: returned on success + ## Authentication Requires a API token. """ |