Initial commit
This commit is contained in:
0
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__init__.py
vendored
Normal file
0
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__init__.py
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/__init__.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/test_documents.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/test_documents.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/test_images.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/test_images.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/test_pages.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/test_pages.cpython-310.pyc
vendored
Normal file
Binary file not shown.
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/tests.cpython-310.pyc
vendored
Normal file
BIN
env/lib/python3.10/site-packages/wagtail/api/v2/tests/__pycache__/tests.cpython-310.pyc
vendored
Normal file
Binary file not shown.
615
env/lib/python3.10/site-packages/wagtail/api/v2/tests/test_documents.py
vendored
Normal file
615
env/lib/python3.10/site-packages/wagtail/api/v2/tests/test_documents.py
vendored
Normal file
@@ -0,0 +1,615 @@
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.api.v2 import signal_handlers
|
||||
from wagtail.documents import get_document_model
|
||||
|
||||
|
||||
class TestDocumentListing(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, **params):
|
||||
return self.client.get(reverse("wagtailapi_v2:documents:listing"), params)
|
||||
|
||||
def get_document_id_list(self, content):
|
||||
return [document["id"] for document in content["items"]]
|
||||
|
||||
# BASIC TESTS
|
||||
|
||||
def test_basic(self):
|
||||
response = self.get_response()
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# Check that the meta section is there
|
||||
self.assertIn("meta", content)
|
||||
self.assertIsInstance(content["meta"], dict)
|
||||
|
||||
# Check that the total count is there and correct
|
||||
self.assertIn("total_count", content["meta"])
|
||||
self.assertIsInstance(content["meta"]["total_count"], int)
|
||||
self.assertEqual(
|
||||
content["meta"]["total_count"], get_document_model().objects.count()
|
||||
)
|
||||
|
||||
# Check that the items section is there
|
||||
self.assertIn("items", content)
|
||||
self.assertIsInstance(content["items"], list)
|
||||
|
||||
# Check that each document has a meta section with type and detail_url attributes
|
||||
for document in content["items"]:
|
||||
self.assertIn("meta", document)
|
||||
self.assertIsInstance(document["meta"], dict)
|
||||
self.assertEqual(
|
||||
set(document["meta"].keys()),
|
||||
{"type", "detail_url", "download_url", "tags"},
|
||||
)
|
||||
|
||||
# Type should always be wagtaildocs.Document
|
||||
self.assertEqual(document["meta"]["type"], "wagtaildocs.Document")
|
||||
|
||||
# Check detail_url
|
||||
self.assertEqual(
|
||||
document["meta"]["detail_url"],
|
||||
"http://localhost/api/main/documents/%d/" % document["id"],
|
||||
)
|
||||
|
||||
# Check download_url
|
||||
self.assertTrue(
|
||||
document["meta"]["download_url"].startswith(
|
||||
"http://localhost/documents/%d/" % document["id"]
|
||||
)
|
||||
)
|
||||
|
||||
# FIELDS
|
||||
|
||||
def test_fields_default(self):
|
||||
response = self.get_response()
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(document["meta"].keys()),
|
||||
{"type", "detail_url", "download_url", "tags"},
|
||||
)
|
||||
|
||||
def test_fields(self):
|
||||
response = self.get_response(fields="title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(document["meta"].keys()),
|
||||
{"type", "detail_url", "download_url", "tags"},
|
||||
)
|
||||
|
||||
def test_remove_fields(self):
|
||||
response = self.get_response(fields="-title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "meta"})
|
||||
|
||||
def test_remove_meta_fields(self):
|
||||
response = self.get_response(fields="-download_url")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(document["meta"].keys()), {"type", "detail_url", "tags"}
|
||||
)
|
||||
|
||||
def test_remove_all_meta_fields(self):
|
||||
response = self.get_response(fields="-type,-detail_url,-tags,-download_url")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "title"})
|
||||
|
||||
def test_remove_id_field(self):
|
||||
response = self.get_response(fields="-id")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"meta", "title"})
|
||||
|
||||
def test_all_fields(self):
|
||||
response = self.get_response(fields="*")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(document["meta"].keys()),
|
||||
{"type", "detail_url", "tags", "download_url"},
|
||||
)
|
||||
|
||||
def test_all_fields_then_remove_something(self):
|
||||
response = self.get_response(fields="*,-title,-download_url")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertEqual(set(document.keys()), {"id", "meta"})
|
||||
self.assertEqual(
|
||||
set(document["meta"].keys()), {"type", "detail_url", "tags"}
|
||||
)
|
||||
|
||||
def test_fields_tags(self):
|
||||
response = self.get_response(fields="tags")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for document in content["items"]:
|
||||
self.assertIsInstance(document["meta"]["tags"], list)
|
||||
|
||||
def test_star_in_wrong_position_gives_error(self):
|
||||
response = self.get_response(fields="title,*")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "fields error: '*' must be in the first position"}
|
||||
)
|
||||
|
||||
def test_fields_which_are_not_in_api_fields_gives_error(self):
|
||||
response = self.get_response(fields="uploaded_by_user")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: uploaded_by_user"})
|
||||
|
||||
def test_fields_unknown_field_gives_error(self):
|
||||
response = self.get_response(fields="123,title,abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
def test_fields_remove_unknown_field_gives_error(self):
|
||||
response = self.get_response(fields="-123,-title,-abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
# FILTERING
|
||||
|
||||
def test_filtering_exact_filter(self):
|
||||
response = self.get_response(title="James Joyce")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list, [2])
|
||||
|
||||
def test_filtering_on_id(self):
|
||||
response = self.get_response(id=10)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list, [10])
|
||||
|
||||
def test_filtering_tags(self):
|
||||
get_document_model().objects.get(id=3).tags.add("test")
|
||||
|
||||
response = self.get_response(tags="test")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list, [3])
|
||||
|
||||
def test_filtering_unknown_field_gives_error(self):
|
||||
response = self.get_response(not_a_field="abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content,
|
||||
{
|
||||
"message": "query parameter is not an operation or a recognised field: not_a_field"
|
||||
},
|
||||
)
|
||||
|
||||
# ORDERING
|
||||
|
||||
def test_ordering_by_title(self):
|
||||
response = self.get_response(order="title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list, [3, 12, 10, 2, 7, 8, 5, 4, 1, 11, 9, 6])
|
||||
|
||||
def test_ordering_by_title_backwards(self):
|
||||
response = self.get_response(order="-title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list, [6, 9, 11, 1, 4, 5, 8, 7, 2, 10, 12, 3])
|
||||
|
||||
def test_ordering_by_random(self):
|
||||
response_1 = self.get_response(order="random")
|
||||
content_1 = json.loads(response_1.content.decode("UTF-8"))
|
||||
document_id_list_1 = self.get_document_id_list(content_1)
|
||||
|
||||
response_2 = self.get_response(order="random")
|
||||
content_2 = json.loads(response_2.content.decode("UTF-8"))
|
||||
document_id_list_2 = self.get_document_id_list(content_2)
|
||||
|
||||
self.assertNotEqual(document_id_list_1, document_id_list_2)
|
||||
|
||||
def test_ordering_by_random_backwards_gives_error(self):
|
||||
response = self.get_response(order="-random")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "cannot order by 'random' (unknown field)"}
|
||||
)
|
||||
|
||||
def test_ordering_by_random_with_offset_gives_error(self):
|
||||
response = self.get_response(order="random", offset=10)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "random ordering with offset is not supported"}
|
||||
)
|
||||
|
||||
def test_ordering_by_unknown_field_gives_error(self):
|
||||
response = self.get_response(order="not_a_field")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "cannot order by 'not_a_field' (unknown field)"}
|
||||
)
|
||||
|
||||
# LIMIT
|
||||
|
||||
def test_limit_only_two_items_returned(self):
|
||||
response = self.get_response(limit=2)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(len(content["items"]), 2)
|
||||
|
||||
def test_limit_total_count(self):
|
||||
response = self.get_response(limit=2)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# The total count must not be affected by "limit"
|
||||
self.assertEqual(
|
||||
content["meta"]["total_count"], get_document_model().objects.count()
|
||||
)
|
||||
|
||||
def test_limit_not_integer_gives_error(self):
|
||||
response = self.get_response(limit="abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "limit must be a positive integer"})
|
||||
|
||||
def test_limit_too_high_gives_error(self):
|
||||
response = self.get_response(limit=1000)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "limit cannot be higher than 20"})
|
||||
|
||||
@override_settings(WAGTAILAPI_LIMIT_MAX=None)
|
||||
def test_limit_max_none_gives_no_errors(self):
|
||||
response = self.get_response(limit=1000000)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(content["items"]), get_document_model().objects.count())
|
||||
|
||||
@override_settings(WAGTAILAPI_LIMIT_MAX=10)
|
||||
def test_limit_maximum_can_be_changed(self):
|
||||
response = self.get_response(limit=20)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "limit cannot be higher than 10"})
|
||||
|
||||
@override_settings(WAGTAILAPI_LIMIT_MAX=2)
|
||||
def test_limit_default_changes_with_max(self):
|
||||
# The default limit is 20. If WAGTAILAPI_LIMIT_MAX is less than that,
|
||||
# the default should change accordingly.
|
||||
response = self.get_response()
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(len(content["items"]), 2)
|
||||
|
||||
# OFFSET
|
||||
|
||||
def test_offset_5_usually_appears_5th_in_list(self):
|
||||
response = self.get_response()
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list.index(5), 4)
|
||||
|
||||
def test_offset_5_moves_after_offset(self):
|
||||
response = self.get_response(offset=4)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
self.assertEqual(document_id_list.index(5), 0)
|
||||
|
||||
def test_offset_total_count(self):
|
||||
response = self.get_response(offset=10)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# The total count must not be affected by "offset"
|
||||
self.assertEqual(
|
||||
content["meta"]["total_count"], get_document_model().objects.count()
|
||||
)
|
||||
|
||||
def test_offset_not_integer_gives_error(self):
|
||||
response = self.get_response(offset="abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "offset must be a positive integer"})
|
||||
|
||||
|
||||
class TestDocumentListingSearch(TransactionTestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, **params):
|
||||
return self.client.get(reverse("wagtailapi_v2:documents:listing"), params)
|
||||
|
||||
def get_document_id_list(self, content):
|
||||
return [document["id"] for document in content["items"]]
|
||||
|
||||
def test_search_for_james_joyce(self):
|
||||
response = self.get_response(search="james")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
|
||||
self.assertEqual(set(document_id_list), {2})
|
||||
|
||||
def test_search_with_order(self):
|
||||
response = self.get_response(search="james", order="title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
document_id_list = self.get_document_id_list(content)
|
||||
|
||||
self.assertEqual(document_id_list, [2])
|
||||
|
||||
@override_settings(WAGTAILAPI_SEARCH_ENABLED=False)
|
||||
def test_search_when_disabled_gives_error(self):
|
||||
response = self.get_response(search="james")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "search is disabled"})
|
||||
|
||||
def test_search_when_filtering_by_tag_gives_error(self):
|
||||
response = self.get_response(search="james", tags="wagtail")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content,
|
||||
{"message": "filtering by tag with a search query is not supported"},
|
||||
)
|
||||
|
||||
|
||||
class TestDocumentDetail(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, image_id, **params):
|
||||
return self.client.get(
|
||||
reverse("wagtailapi_v2:documents:detail", args=(image_id,)), params
|
||||
)
|
||||
|
||||
def test_basic(self):
|
||||
response = self.get_response(1)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# Check the id field
|
||||
self.assertIn("id", content)
|
||||
self.assertEqual(content["id"], 1)
|
||||
|
||||
# Check that the meta section is there
|
||||
self.assertIn("meta", content)
|
||||
self.assertIsInstance(content["meta"], dict)
|
||||
|
||||
# Check the meta type
|
||||
self.assertIn("type", content["meta"])
|
||||
self.assertEqual(content["meta"]["type"], "wagtaildocs.Document")
|
||||
|
||||
# Check the meta detail_url
|
||||
self.assertIn("detail_url", content["meta"])
|
||||
self.assertEqual(
|
||||
content["meta"]["detail_url"], "http://localhost/api/main/documents/1/"
|
||||
)
|
||||
|
||||
# Check the meta download_url
|
||||
self.assertIn("download_url", content["meta"])
|
||||
self.assertEqual(
|
||||
content["meta"]["download_url"],
|
||||
"http://localhost/documents/1/wagtail_by_markyharky.jpg",
|
||||
)
|
||||
|
||||
# Check the title field
|
||||
self.assertIn("title", content)
|
||||
self.assertEqual(content["title"], "Wagtail by mark Harkin")
|
||||
|
||||
# Check the tags field
|
||||
self.assertIn("tags", content["meta"])
|
||||
self.assertEqual(content["meta"]["tags"], [])
|
||||
|
||||
def test_tags(self):
|
||||
get_document_model().objects.get(id=1).tags.add("hello")
|
||||
get_document_model().objects.get(id=1).tags.add("world")
|
||||
|
||||
response = self.get_response(1)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("tags", content["meta"])
|
||||
self.assertEqual(content["meta"]["tags"], ["hello", "world"])
|
||||
|
||||
@override_settings(WAGTAILAPI_BASE_URL="http://api.example.com/")
|
||||
def test_download_url_with_custom_base_url(self):
|
||||
response = self.get_response(1)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("download_url", content["meta"])
|
||||
self.assertEqual(
|
||||
content["meta"]["download_url"],
|
||||
"http://api.example.com/documents/1/wagtail_by_markyharky.jpg",
|
||||
)
|
||||
|
||||
# FIELDS
|
||||
|
||||
def test_remove_fields(self):
|
||||
response = self.get_response(2, fields="-title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("id", set(content.keys()))
|
||||
self.assertNotIn("title", set(content.keys()))
|
||||
|
||||
def test_remove_meta_fields(self):
|
||||
response = self.get_response(2, fields="-download_url")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("detail_url", set(content["meta"].keys()))
|
||||
self.assertNotIn("download_url", set(content["meta"].keys()))
|
||||
|
||||
def test_remove_id_field(self):
|
||||
response = self.get_response(2, fields="-id")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("title", set(content.keys()))
|
||||
self.assertNotIn("id", set(content.keys()))
|
||||
|
||||
def test_remove_all_fields(self):
|
||||
response = self.get_response(2, fields="_,id,type")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(set(content.keys()), {"id", "meta"})
|
||||
self.assertEqual(set(content["meta"].keys()), {"type"})
|
||||
|
||||
def test_star_in_wrong_position_gives_error(self):
|
||||
response = self.get_response(2, fields="title,*")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "fields error: '*' must be in the first position"}
|
||||
)
|
||||
|
||||
def test_fields_which_are_not_in_api_fields_gives_error(self):
|
||||
response = self.get_response(2, fields="path")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: path"})
|
||||
|
||||
def test_fields_unknown_field_gives_error(self):
|
||||
response = self.get_response(2, fields="123,title,abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
def test_fields_remove_unknown_field_gives_error(self):
|
||||
response = self.get_response(2, fields="-123,-title,-abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
def test_nested_fields_on_non_relational_field_gives_error(self):
|
||||
response = self.get_response(2, fields="title(foo,bar)")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "'title' does not support nested fields"})
|
||||
|
||||
|
||||
class TestDocumentFind(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, **params):
|
||||
return self.client.get(reverse("wagtailapi_v2:documents:find"), params)
|
||||
|
||||
def test_without_parameters(self):
|
||||
response = self.get_response()
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(content, {"message": "not found"})
|
||||
|
||||
def test_find_by_id(self):
|
||||
response = self.get_response(id=5)
|
||||
|
||||
self.assertRedirects(
|
||||
response,
|
||||
"http://localhost" + reverse("wagtailapi_v2:documents:detail", args=[5]),
|
||||
fetch_redirect_response=False,
|
||||
)
|
||||
|
||||
def test_find_by_id_nonexistent(self):
|
||||
response = self.get_response(id=1234)
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(content, {"message": "not found"})
|
||||
|
||||
|
||||
@override_settings(
|
||||
WAGTAILFRONTENDCACHE={
|
||||
"varnish": {
|
||||
"BACKEND": "wagtail.contrib.frontend_cache.backends.HTTPBackend",
|
||||
"LOCATION": "http://localhost:8000",
|
||||
},
|
||||
},
|
||||
WAGTAILAPI_BASE_URL="http://api.example.com",
|
||||
)
|
||||
@mock.patch("wagtail.contrib.frontend_cache.backends.http.HTTPBackend.purge")
|
||||
class TestDocumentCacheInvalidation(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
signal_handlers.register_signal_handlers()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super().tearDownClass()
|
||||
signal_handlers.unregister_signal_handlers()
|
||||
|
||||
def test_resave_document_purges(self, purge):
|
||||
get_document_model().objects.get(id=5).save()
|
||||
|
||||
purge.assert_any_call("http://api.example.com/api/main/documents/5/")
|
||||
|
||||
def test_delete_document_purges(self, purge):
|
||||
get_document_model().objects.get(id=5).delete()
|
||||
|
||||
purge.assert_any_call("http://api.example.com/api/main/documents/5/")
|
||||
607
env/lib/python3.10/site-packages/wagtail/api/v2/tests/test_images.py
vendored
Normal file
607
env/lib/python3.10/site-packages/wagtail/api/v2/tests/test_images.py
vendored
Normal file
@@ -0,0 +1,607 @@
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
from django.test import TestCase, TransactionTestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.urls import reverse
|
||||
|
||||
from wagtail.api.v2 import signal_handlers
|
||||
from wagtail.images import get_image_model
|
||||
|
||||
|
||||
class TestImageListing(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, **params):
|
||||
return self.client.get(reverse("wagtailapi_v2:images:listing"), params)
|
||||
|
||||
def get_image_id_list(self, content):
|
||||
return [image["id"] for image in content["items"]]
|
||||
|
||||
# BASIC TESTS
|
||||
|
||||
def test_basic(self):
|
||||
response = self.get_response()
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# Check that the meta section is there
|
||||
self.assertIn("meta", content)
|
||||
self.assertIsInstance(content["meta"], dict)
|
||||
|
||||
# Check that the total count is there and correct
|
||||
self.assertIn("total_count", content["meta"])
|
||||
self.assertIsInstance(content["meta"]["total_count"], int)
|
||||
self.assertEqual(
|
||||
content["meta"]["total_count"], get_image_model().objects.count()
|
||||
)
|
||||
|
||||
# Check that the items section is there
|
||||
self.assertIn("items", content)
|
||||
self.assertIsInstance(content["items"], list)
|
||||
|
||||
# Check that each image has a meta section with type and detail_url attributes
|
||||
for image in content["items"]:
|
||||
self.assertIn("meta", image)
|
||||
self.assertIsInstance(image["meta"], dict)
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()),
|
||||
{"type", "detail_url", "tags", "download_url"},
|
||||
)
|
||||
|
||||
# Type should always be wagtailimages.Image
|
||||
self.assertEqual(image["meta"]["type"], "wagtailimages.Image")
|
||||
|
||||
# Check detail url
|
||||
self.assertEqual(
|
||||
image["meta"]["detail_url"],
|
||||
"http://localhost/api/main/images/%d/" % image["id"],
|
||||
)
|
||||
|
||||
# FIELDS
|
||||
|
||||
def test_fields_default(self):
|
||||
response = self.get_response()
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()),
|
||||
{"type", "detail_url", "tags", "download_url"},
|
||||
)
|
||||
|
||||
def test_fields(self):
|
||||
response = self.get_response(fields="width,height")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(
|
||||
set(image.keys()), {"id", "meta", "title", "width", "height"}
|
||||
)
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()),
|
||||
{"type", "detail_url", "tags", "download_url"},
|
||||
)
|
||||
|
||||
def test_remove_fields(self):
|
||||
response = self.get_response(fields="-title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"id", "meta"})
|
||||
|
||||
def test_remove_meta_fields(self):
|
||||
response = self.get_response(fields="-tags")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()), {"type", "detail_url", "download_url"}
|
||||
)
|
||||
|
||||
def test_remove_all_meta_fields(self):
|
||||
response = self.get_response(fields="-type,-detail_url,-tags")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"id", "title", "meta"})
|
||||
|
||||
def test_remove_id_field(self):
|
||||
response = self.get_response(fields="-id")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"meta", "title"})
|
||||
|
||||
def test_all_fields(self):
|
||||
response = self.get_response(fields="*")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(
|
||||
set(image.keys()), {"id", "meta", "title", "width", "height"}
|
||||
)
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()),
|
||||
{"type", "detail_url", "tags", "download_url"},
|
||||
)
|
||||
|
||||
def test_all_fields_then_remove_something(self):
|
||||
response = self.get_response(fields="*,-title,-tags")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"id", "meta", "width", "height"})
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()), {"type", "detail_url", "download_url"}
|
||||
)
|
||||
|
||||
def test_fields_tags(self):
|
||||
response = self.get_response(fields="tags")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
for image in content["items"]:
|
||||
self.assertEqual(set(image.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(set(image.keys()), {"id", "meta", "title"})
|
||||
self.assertEqual(
|
||||
set(image["meta"].keys()),
|
||||
{"type", "detail_url", "tags", "download_url"},
|
||||
)
|
||||
self.assertIsInstance(image["meta"]["tags"], list)
|
||||
|
||||
def test_star_in_wrong_position_gives_error(self):
|
||||
response = self.get_response(fields="title,*")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "fields error: '*' must be in the first position"}
|
||||
)
|
||||
|
||||
def test_fields_which_are_not_in_api_fields_gives_error(self):
|
||||
response = self.get_response(fields="uploaded_by_user")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: uploaded_by_user"})
|
||||
|
||||
def test_fields_unknown_field_gives_error(self):
|
||||
response = self.get_response(fields="123,title,abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
def test_fields_remove_unknown_field_gives_error(self):
|
||||
response = self.get_response(fields="-123,-title,-abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
# FILTERING
|
||||
|
||||
def test_filtering_exact_filter(self):
|
||||
response = self.get_response(title="James Joyce")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list, [5])
|
||||
|
||||
def test_filtering_on_id(self):
|
||||
response = self.get_response(id=10)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list, [10])
|
||||
|
||||
def test_filtering_tags(self):
|
||||
get_image_model().objects.get(id=6).tags.add("test")
|
||||
|
||||
response = self.get_response(tags="test")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list, [6])
|
||||
|
||||
def test_filtering_unknown_field_gives_error(self):
|
||||
response = self.get_response(not_a_field="abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content,
|
||||
{
|
||||
"message": "query parameter is not an operation or a recognised field: not_a_field"
|
||||
},
|
||||
)
|
||||
|
||||
# ORDERING
|
||||
|
||||
def test_ordering_by_title(self):
|
||||
response = self.get_response(order="title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list, [6, 15, 13, 5, 10, 11, 8, 7, 4, 14, 12, 9])
|
||||
|
||||
def test_ordering_by_title_backwards(self):
|
||||
response = self.get_response(order="-title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list, [9, 12, 14, 4, 7, 8, 11, 10, 5, 13, 15, 6])
|
||||
|
||||
def test_ordering_by_random(self):
|
||||
response_1 = self.get_response(order="random")
|
||||
content_1 = json.loads(response_1.content.decode("UTF-8"))
|
||||
image_id_list_1 = self.get_image_id_list(content_1)
|
||||
|
||||
response_2 = self.get_response(order="random")
|
||||
content_2 = json.loads(response_2.content.decode("UTF-8"))
|
||||
image_id_list_2 = self.get_image_id_list(content_2)
|
||||
|
||||
self.assertNotEqual(image_id_list_1, image_id_list_2)
|
||||
|
||||
def test_ordering_by_random_backwards_gives_error(self):
|
||||
response = self.get_response(order="-random")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "cannot order by 'random' (unknown field)"}
|
||||
)
|
||||
|
||||
def test_ordering_by_random_with_offset_gives_error(self):
|
||||
response = self.get_response(order="random", offset=10)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "random ordering with offset is not supported"}
|
||||
)
|
||||
|
||||
def test_ordering_by_unknown_field_gives_error(self):
|
||||
response = self.get_response(order="not_a_field")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "cannot order by 'not_a_field' (unknown field)"}
|
||||
)
|
||||
|
||||
# LIMIT
|
||||
|
||||
def test_limit_only_two_items_returned(self):
|
||||
response = self.get_response(limit=2)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(len(content["items"]), 2)
|
||||
|
||||
def test_limit_total_count(self):
|
||||
response = self.get_response(limit=2)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# The total count must not be affected by "limit"
|
||||
self.assertEqual(
|
||||
content["meta"]["total_count"], get_image_model().objects.count()
|
||||
)
|
||||
|
||||
def test_limit_not_integer_gives_error(self):
|
||||
response = self.get_response(limit="abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "limit must be a positive integer"})
|
||||
|
||||
def test_limit_too_high_gives_error(self):
|
||||
response = self.get_response(limit=1000)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "limit cannot be higher than 20"})
|
||||
|
||||
@override_settings(WAGTAILAPI_LIMIT_MAX=None)
|
||||
def test_limit_max_none_gives_no_errors(self):
|
||||
response = self.get_response(limit=1000000)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(content["items"]), get_image_model().objects.count())
|
||||
|
||||
@override_settings(WAGTAILAPI_LIMIT_MAX=10)
|
||||
def test_limit_maximum_can_be_changed(self):
|
||||
response = self.get_response(limit=20)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "limit cannot be higher than 10"})
|
||||
|
||||
@override_settings(WAGTAILAPI_LIMIT_MAX=2)
|
||||
def test_limit_default_changes_with_max(self):
|
||||
# The default limit is 20. If WAGTAILAPI_LIMIT_MAX is less than that,
|
||||
# the default should change accordingly.
|
||||
response = self.get_response()
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(len(content["items"]), 2)
|
||||
|
||||
# OFFSET
|
||||
|
||||
def test_offset_10_usually_appears_7th_in_list(self):
|
||||
response = self.get_response()
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list.index(10), 6)
|
||||
|
||||
def test_offset_10_moves_after_offset(self):
|
||||
response = self.get_response(offset=4)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
self.assertEqual(image_id_list.index(10), 2)
|
||||
|
||||
def test_offset_total_count(self):
|
||||
response = self.get_response(offset=10)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# The total count must not be affected by "offset"
|
||||
self.assertEqual(
|
||||
content["meta"]["total_count"], get_image_model().objects.count()
|
||||
)
|
||||
|
||||
def test_offset_not_integer_gives_error(self):
|
||||
response = self.get_response(offset="abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "offset must be a positive integer"})
|
||||
|
||||
|
||||
class TestImageListingSearch(TransactionTestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, **params):
|
||||
return self.client.get(reverse("wagtailapi_v2:images:listing"), params)
|
||||
|
||||
def get_image_id_list(self, content):
|
||||
return [image["id"] for image in content["items"]]
|
||||
|
||||
def test_search_for_james_joyce(self):
|
||||
response = self.get_response(search="james")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
|
||||
self.assertEqual(set(image_id_list), {5})
|
||||
|
||||
def test_search_with_order(self):
|
||||
response = self.get_response(search="james", order="title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
image_id_list = self.get_image_id_list(content)
|
||||
|
||||
self.assertEqual(image_id_list, [5])
|
||||
|
||||
@override_settings(WAGTAILAPI_SEARCH_ENABLED=False)
|
||||
def test_search_when_disabled_gives_error(self):
|
||||
response = self.get_response(search="james")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "search is disabled"})
|
||||
|
||||
def test_search_when_filtering_by_tag_gives_error(self):
|
||||
response = self.get_response(search="james", tags="wagtail")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content,
|
||||
{"message": "filtering by tag with a search query is not supported"},
|
||||
)
|
||||
|
||||
|
||||
class TestImageDetail(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, image_id, **params):
|
||||
return self.client.get(
|
||||
reverse("wagtailapi_v2:images:detail", args=(image_id,)), params
|
||||
)
|
||||
|
||||
def test_basic(self):
|
||||
response = self.get_response(5)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
# Check the id field
|
||||
self.assertIn("id", content)
|
||||
self.assertEqual(content["id"], 5)
|
||||
|
||||
# Check that the meta section is there
|
||||
self.assertIn("meta", content)
|
||||
self.assertIsInstance(content["meta"], dict)
|
||||
|
||||
# Check the meta type
|
||||
self.assertIn("type", content["meta"])
|
||||
self.assertEqual(content["meta"]["type"], "wagtailimages.Image")
|
||||
|
||||
# Check the meta detail_url
|
||||
self.assertIn("detail_url", content["meta"])
|
||||
self.assertEqual(
|
||||
content["meta"]["detail_url"], "http://localhost/api/main/images/5/"
|
||||
)
|
||||
|
||||
# Check the title field
|
||||
self.assertIn("title", content)
|
||||
self.assertEqual(content["title"], "James Joyce")
|
||||
|
||||
# Check the width and height fields
|
||||
self.assertIn("width", content)
|
||||
self.assertIn("height", content)
|
||||
self.assertEqual(content["width"], 500)
|
||||
self.assertEqual(content["height"], 392)
|
||||
|
||||
# Check the tags field
|
||||
self.assertIn("tags", content["meta"])
|
||||
self.assertEqual(content["meta"]["tags"], [])
|
||||
|
||||
def test_tags(self):
|
||||
image = get_image_model().objects.get(id=5)
|
||||
image.tags.add("hello")
|
||||
image.tags.add("world")
|
||||
|
||||
response = self.get_response(5)
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("tags", content["meta"])
|
||||
self.assertEqual(content["meta"]["tags"], ["hello", "world"])
|
||||
|
||||
# FIELDS
|
||||
|
||||
def test_remove_fields(self):
|
||||
response = self.get_response(5, fields="-title")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("id", set(content.keys()))
|
||||
self.assertNotIn("title", set(content.keys()))
|
||||
|
||||
def test_remove_meta_fields(self):
|
||||
response = self.get_response(5, fields="-type")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("detail_url", set(content["meta"].keys()))
|
||||
self.assertNotIn("type", set(content["meta"].keys()))
|
||||
|
||||
def test_remove_id_field(self):
|
||||
response = self.get_response(5, fields="-id")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertIn("title", set(content.keys()))
|
||||
self.assertNotIn("id", set(content.keys()))
|
||||
|
||||
def test_remove_all_fields(self):
|
||||
response = self.get_response(5, fields="_,id,type")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(set(content.keys()), {"id", "meta"})
|
||||
self.assertEqual(set(content["meta"].keys()), {"type"})
|
||||
|
||||
def test_star_in_wrong_position_gives_error(self):
|
||||
response = self.get_response(5, fields="title,*")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
content, {"message": "fields error: '*' must be in the first position"}
|
||||
)
|
||||
|
||||
def test_fields_which_are_not_in_api_fields_gives_error(self):
|
||||
response = self.get_response(5, fields="path")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: path"})
|
||||
|
||||
def test_fields_unknown_field_gives_error(self):
|
||||
response = self.get_response(5, fields="123,title,abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
def test_fields_remove_unknown_field_gives_error(self):
|
||||
response = self.get_response(5, fields="-123,-title,-abc")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "unknown fields: 123, abc"})
|
||||
|
||||
def test_nested_fields_on_non_relational_field_gives_error(self):
|
||||
response = self.get_response(5, fields="title(foo,bar)")
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(content, {"message": "'title' does not support nested fields"})
|
||||
|
||||
|
||||
class TestImageFind(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
def get_response(self, **params):
|
||||
return self.client.get(reverse("wagtailapi_v2:images:find"), params)
|
||||
|
||||
def test_without_parameters(self):
|
||||
response = self.get_response()
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(content, {"message": "not found"})
|
||||
|
||||
def test_find_by_id(self):
|
||||
response = self.get_response(id=5)
|
||||
|
||||
self.assertRedirects(
|
||||
response,
|
||||
"http://localhost" + reverse("wagtailapi_v2:images:detail", args=[5]),
|
||||
fetch_redirect_response=False,
|
||||
)
|
||||
|
||||
def test_find_by_id_nonexistent(self):
|
||||
response = self.get_response(id=1234)
|
||||
|
||||
self.assertEqual(response.status_code, 404)
|
||||
self.assertEqual(response["Content-type"], "application/json")
|
||||
|
||||
# Will crash if the JSON is invalid
|
||||
content = json.loads(response.content.decode("UTF-8"))
|
||||
|
||||
self.assertEqual(content, {"message": "not found"})
|
||||
|
||||
|
||||
@override_settings(
|
||||
WAGTAILFRONTENDCACHE={
|
||||
"varnish": {
|
||||
"BACKEND": "wagtail.contrib.frontend_cache.backends.HTTPBackend",
|
||||
"LOCATION": "http://localhost:8000",
|
||||
},
|
||||
},
|
||||
WAGTAILAPI_BASE_URL="http://api.example.com",
|
||||
)
|
||||
@mock.patch("wagtail.contrib.frontend_cache.backends.http.HTTPBackend.purge")
|
||||
class TestImageCacheInvalidation(TestCase):
|
||||
fixtures = ["demosite.json"]
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
signal_handlers.register_signal_handlers()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super().tearDownClass()
|
||||
signal_handlers.unregister_signal_handlers()
|
||||
|
||||
def test_resave_image_purges(self, purge):
|
||||
get_image_model().objects.get(id=5).save()
|
||||
|
||||
purge.assert_any_call("http://api.example.com/api/main/images/5/")
|
||||
|
||||
def test_delete_image_purges(self, purge):
|
||||
get_image_model().objects.get(id=5).delete()
|
||||
|
||||
purge.assert_any_call("http://api.example.com/api/main/images/5/")
|
||||
1903
env/lib/python3.10/site-packages/wagtail/api/v2/tests/test_pages.py
vendored
Normal file
1903
env/lib/python3.10/site-packages/wagtail/api/v2/tests/test_pages.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
401
env/lib/python3.10/site-packages/wagtail/api/v2/tests/tests.py
vendored
Normal file
401
env/lib/python3.10/site-packages/wagtail/api/v2/tests/tests.py
vendored
Normal file
@@ -0,0 +1,401 @@
|
||||
from django.test import RequestFactory, TestCase, override_settings
|
||||
from django.utils.encoding import force_bytes
|
||||
|
||||
from wagtail.models import Site
|
||||
|
||||
from ..utils import (
|
||||
FieldsParameterParseError,
|
||||
get_base_url,
|
||||
parse_boolean,
|
||||
parse_fields_parameter,
|
||||
)
|
||||
|
||||
|
||||
class DynamicBaseUrl:
|
||||
def __str__(self):
|
||||
return "https://www.example.com"
|
||||
|
||||
def __bytes__(self):
|
||||
return force_bytes(self.__str__())
|
||||
|
||||
def decode(self, *args, **kwargs):
|
||||
return self.__bytes__().decode(*args, **kwargs)
|
||||
|
||||
|
||||
class TestGetBaseUrl(TestCase):
|
||||
def setUp(self):
|
||||
Site.objects.all().delete()
|
||||
|
||||
def prepare_site(self):
|
||||
return Site.objects.get_or_create(
|
||||
hostname="other.example.com",
|
||||
port=8080,
|
||||
root_page_id=1,
|
||||
is_default_site=True,
|
||||
)[0]
|
||||
|
||||
def clear_cached_site(self, request):
|
||||
del request._wagtail_site
|
||||
|
||||
def test_get_base_url_unset(self):
|
||||
self.assertIsNone(get_base_url())
|
||||
|
||||
def test_get_base_url_from_request(self):
|
||||
# base url for siteless request should be None
|
||||
request = RequestFactory().get("/")
|
||||
self.assertIsNone(Site.find_for_request(request))
|
||||
self.assertIsNone(get_base_url(request))
|
||||
|
||||
# base url for request with a site should be based on the site's details
|
||||
site = self.prepare_site()
|
||||
self.clear_cached_site(request)
|
||||
self.assertEqual(site, Site.find_for_request(request))
|
||||
self.assertEqual(get_base_url(request), "http://other.example.com:8080")
|
||||
|
||||
# port 443 should indicate https without a port
|
||||
site.port = 443
|
||||
site.save()
|
||||
self.clear_cached_site(request)
|
||||
self.assertEqual(get_base_url(request), "https://other.example.com")
|
||||
|
||||
# port 80 should indicate http without a port
|
||||
site.port = 80
|
||||
site.save()
|
||||
self.clear_cached_site(request)
|
||||
self.assertEqual(get_base_url(request), "http://other.example.com")
|
||||
|
||||
@override_settings(WAGTAILAPI_BASE_URL="https://bar.example.com")
|
||||
def test_get_base_url_prefers_setting(self):
|
||||
request = RequestFactory().get("/")
|
||||
site = self.prepare_site()
|
||||
self.assertEqual(site, Site.find_for_request(request))
|
||||
self.assertEqual(get_base_url(request), "https://bar.example.com")
|
||||
with override_settings(WAGTAILAPI_BASE_URL=None):
|
||||
self.assertEqual(get_base_url(request), "http://other.example.com:8080")
|
||||
|
||||
@override_settings(WAGTAILAPI_BASE_URL="https://bar.example.com")
|
||||
def test_get_base_url_from_setting_string(self):
|
||||
self.assertEqual(get_base_url(), "https://bar.example.com")
|
||||
|
||||
@override_settings(WAGTAILAPI_BASE_URL=b"https://baz.example.com")
|
||||
def test_get_base_url_from_setting_bytes(self):
|
||||
self.assertEqual(get_base_url(), "https://baz.example.com")
|
||||
|
||||
@override_settings(WAGTAILAPI_BASE_URL=DynamicBaseUrl())
|
||||
def test_get_base_url_from_setting_object(self):
|
||||
self.assertEqual(get_base_url(), "https://www.example.com")
|
||||
|
||||
|
||||
class TestParseFieldsParameter(TestCase):
|
||||
# GOOD STUFF
|
||||
|
||||
def test_valid_single_field(self):
|
||||
parsed = parse_fields_parameter("test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("test", False, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_multiple_fields(self):
|
||||
parsed = parse_fields_parameter("test,another_test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("test", False, None),
|
||||
("another_test", False, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_negated_field(self):
|
||||
parsed = parse_fields_parameter("-test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("test", True, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_nested_fields(self):
|
||||
parsed = parse_fields_parameter("test(foo,bar)")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
(
|
||||
"test",
|
||||
False,
|
||||
[
|
||||
("foo", False, None),
|
||||
("bar", False, None),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_star_field(self):
|
||||
parsed = parse_fields_parameter("*,-test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("*", False, None),
|
||||
("test", True, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_star_with_additional_field(self):
|
||||
# Note: '*,test' is not allowed but '*,test(foo)' is
|
||||
parsed = parse_fields_parameter("*,test(foo)")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("*", False, None),
|
||||
(
|
||||
"test",
|
||||
False,
|
||||
[
|
||||
("foo", False, None),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_underscore_field(self):
|
||||
parsed = parse_fields_parameter("_,test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("_", False, None),
|
||||
("test", False, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_field_with_underscore_in_middle(self):
|
||||
parsed = parse_fields_parameter("a_test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("a_test", False, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_negated_field_with_underscore_in_middle(self):
|
||||
parsed = parse_fields_parameter("-a_test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("a_test", True, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_field_with_underscore_at_beginning(self):
|
||||
parsed = parse_fields_parameter("_test")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("_test", False, None),
|
||||
],
|
||||
)
|
||||
|
||||
def test_valid_field_with_underscore_at_end(self):
|
||||
parsed = parse_fields_parameter("test_")
|
||||
|
||||
self.assertEqual(
|
||||
parsed,
|
||||
[
|
||||
("test_", False, None),
|
||||
],
|
||||
)
|
||||
|
||||
# BAD STUFF
|
||||
|
||||
def test_invalid_char(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test#")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '#' at position 4")
|
||||
|
||||
def test_invalid_whitespace_before_identifier(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter(" test")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected whitespace at position 0")
|
||||
|
||||
def test_invalid_whitespace_after_identifier(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test ")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected whitespace at position 4")
|
||||
|
||||
def test_invalid_whitespace_after_comma(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test, test")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected whitespace at position 5")
|
||||
|
||||
def test_invalid_whitespace_before_comma(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test ,test")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected whitespace at position 4")
|
||||
|
||||
def test_invalid_unexpected_negation_operator(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test-")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '-' at position 4")
|
||||
|
||||
def test_invalid_unexpected_open_bracket(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test,(foo)")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '(' at position 5")
|
||||
|
||||
def test_invalid_unexpected_close_bracket(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test)")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char ')' at position 4")
|
||||
|
||||
def test_invalid_unexpected_comma_in_middle(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test,,foo")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char ',' at position 5")
|
||||
|
||||
def test_invalid_unexpected_comma_at_end(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test,foo,")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char ',' at position 9")
|
||||
|
||||
def test_invalid_unclosed_bracket(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test(foo")
|
||||
|
||||
self.assertEqual(
|
||||
str(e.exception),
|
||||
"unexpected end of input (did you miss out a close bracket?)",
|
||||
)
|
||||
|
||||
def test_invalid_subfields_on_negated_field(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("-test(foo)")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '(' at position 5")
|
||||
|
||||
def test_invalid_star_field_in_wrong_position(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test,*")
|
||||
|
||||
self.assertEqual(str(e.exception), "'*' must be in the first position")
|
||||
|
||||
def test_invalid_negated_star(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("-*")
|
||||
|
||||
self.assertEqual(str(e.exception), "'*' cannot be negated")
|
||||
|
||||
def test_invalid_star_with_nesting(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("*(foo,bar)")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '(' at position 1")
|
||||
|
||||
def test_invalid_star_with_chars_after(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("*foo")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char 'f' at position 1")
|
||||
|
||||
def test_invalid_star_with_chars_before(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("foo*")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '*' at position 3")
|
||||
|
||||
def test_invalid_star_with_additional_field(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("*,foo")
|
||||
|
||||
self.assertEqual(
|
||||
str(e.exception), "additional fields with '*' doesn't make sense"
|
||||
)
|
||||
|
||||
def test_invalid_underscore_in_wrong_position(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("test,_")
|
||||
|
||||
self.assertEqual(str(e.exception), "'_' must be in the first position")
|
||||
|
||||
def test_invalid_negated_underscore(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("-_")
|
||||
|
||||
self.assertEqual(str(e.exception), "'_' cannot be negated")
|
||||
|
||||
def test_invalid_underscore_with_nesting(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("_(foo,bar)")
|
||||
|
||||
self.assertEqual(str(e.exception), "unexpected char '(' at position 1")
|
||||
|
||||
def test_invalid_underscore_with_negated_field(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("_,-foo")
|
||||
|
||||
self.assertEqual(str(e.exception), "negated fields with '_' doesn't make sense")
|
||||
|
||||
def test_invalid_star_and_underscore(self):
|
||||
with self.assertRaises(FieldsParameterParseError) as e:
|
||||
parse_fields_parameter("*,_")
|
||||
|
||||
self.assertEqual(str(e.exception), "'_' must be in the first position")
|
||||
|
||||
|
||||
class TestParseBoolean(TestCase):
|
||||
# GOOD STUFF
|
||||
|
||||
def test_valid_true(self):
|
||||
parsed = parse_boolean("true")
|
||||
|
||||
self.assertIs(parsed, True)
|
||||
|
||||
def test_valid_false(self):
|
||||
parsed = parse_boolean("false")
|
||||
|
||||
self.assertIs(parsed, False)
|
||||
|
||||
def test_valid_1(self):
|
||||
parsed = parse_boolean("1")
|
||||
|
||||
self.assertIs(parsed, True)
|
||||
|
||||
def test_valid_0(self):
|
||||
parsed = parse_boolean("0")
|
||||
|
||||
self.assertIs(parsed, False)
|
||||
|
||||
# BAD STUFF
|
||||
|
||||
def test_invalid(self):
|
||||
with self.assertRaises(ValueError) as e:
|
||||
parse_boolean("foo")
|
||||
|
||||
self.assertEqual(str(e.exception), "expected 'true' or 'false', got 'foo'")
|
||||
|
||||
def test_invalid_integer(self):
|
||||
with self.assertRaises(ValueError) as e:
|
||||
parse_boolean("2")
|
||||
|
||||
self.assertEqual(str(e.exception), "expected 'true' or 'false', got '2'")
|
||||
Reference in New Issue
Block a user