You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

171 lines
5.4 KiB

  1. import unittest
  2. from random import choice
  3. import requests
  4. from semantic_version import Version
  5. import frappe
  6. from frappe.utils import get_site_url
  7. def maintain_state(f):
  8. def wrapper(*args, **kwargs):
  9. frappe.db.rollback()
  10. r = f(*args, **kwargs)
  11. frappe.db.commit()
  12. return r
  13. return wrapper
  14. class TestResourceAPI(unittest.TestCase):
  15. SITE_URL = get_site_url(frappe.local.site)
  16. RESOURCE_URL = f"{SITE_URL}/api/resource"
  17. DOCTYPE = "ToDo"
  18. GENERATED_DOCUMENTS = []
  19. @classmethod
  20. @maintain_state
  21. def setUpClass(self):
  22. for _ in range(10):
  23. doc = frappe.get_doc(
  24. {"doctype": "ToDo", "description": frappe.mock("paragraph")}
  25. ).insert()
  26. self.GENERATED_DOCUMENTS.append(doc.name)
  27. @classmethod
  28. @maintain_state
  29. def tearDownClass(self):
  30. for name in self.GENERATED_DOCUMENTS:
  31. frappe.delete_doc_if_exists(self.DOCTYPE, name)
  32. @property
  33. def sid(self):
  34. if not getattr(self, "_sid", None):
  35. self._sid = requests.post(
  36. f"{self.SITE_URL}/api/method/login",
  37. data={
  38. "usr": "Administrator",
  39. "pwd": frappe.conf.admin_password or "admin",
  40. },
  41. ).cookies.get("sid")
  42. return self._sid
  43. def get(self, path, params=""):
  44. return requests.get(f"{self.RESOURCE_URL}/{path}?sid={self.sid}{params}")
  45. def post(self, path, data):
  46. return requests.post(
  47. f"{self.RESOURCE_URL}/{path}?sid={self.sid}", data=frappe.as_json(data)
  48. )
  49. def put(self, path, data):
  50. return requests.put(
  51. f"{self.RESOURCE_URL}/{path}?sid={self.sid}", data=frappe.as_json(data)
  52. )
  53. def delete(self, path):
  54. return requests.delete(f"{self.RESOURCE_URL}/{path}?sid={self.sid}")
  55. def test_unauthorized_call(self):
  56. # test 1: fetch documents without auth
  57. response = requests.get(f"{self.RESOURCE_URL}/{self.DOCTYPE}")
  58. self.assertEqual(response.status_code, 403)
  59. def test_get_list(self):
  60. # test 2: fetch documents without params
  61. response = self.get(self.DOCTYPE)
  62. self.assertEqual(response.status_code, 200)
  63. self.assertIsInstance(response.json(), dict)
  64. self.assertIn("data", response.json())
  65. def test_get_list_limit(self):
  66. # test 3: fetch data with limit
  67. response = self.get(self.DOCTYPE, "&limit=2")
  68. self.assertEqual(response.status_code, 200)
  69. self.assertEqual(len(response.json()["data"]), 2)
  70. def test_get_list_dict(self):
  71. # test 4: fetch response as (not) dict
  72. response = self.get(self.DOCTYPE, "&as_dict=True")
  73. json = frappe._dict(response.json())
  74. self.assertEqual(response.status_code, 200)
  75. self.assertIsInstance(json.data, list)
  76. self.assertIsInstance(json.data[0], dict)
  77. response = self.get(self.DOCTYPE, "&as_dict=False")
  78. json = frappe._dict(response.json())
  79. self.assertEqual(response.status_code, 200)
  80. self.assertIsInstance(json.data, list)
  81. self.assertIsInstance(json.data[0], list)
  82. def test_get_list_debug(self):
  83. # test 5: fetch response with debug
  84. response = self.get(self.DOCTYPE, "&debug=true")
  85. self.assertEqual(response.status_code, 200)
  86. self.assertIn("exc", response.json())
  87. self.assertIsInstance(response.json()["exc"], str)
  88. self.assertIsInstance(eval(response.json()["exc"]), list)
  89. def test_get_list_fields(self):
  90. # test 6: fetch response with fields
  91. response = self.get(self.DOCTYPE, r'&fields=["description"]')
  92. self.assertEqual(response.status_code, 200)
  93. json = frappe._dict(response.json())
  94. self.assertIn("description", json.data[0])
  95. def test_create_document(self):
  96. # test 7: POST method on /api/resource to create doc
  97. data = {"description": frappe.mock("paragraph")}
  98. response = self.post(self.DOCTYPE, data)
  99. self.assertEqual(response.status_code, 200)
  100. docname = response.json()["data"]["name"]
  101. self.assertIsInstance(docname, str)
  102. self.GENERATED_DOCUMENTS.append(docname)
  103. def test_update_document(self):
  104. # test 8: PUT method on /api/resource to update doc
  105. generated_desc = frappe.mock("paragraph")
  106. data = {"description": generated_desc}
  107. random_doc = choice(self.GENERATED_DOCUMENTS)
  108. desc_before_update = frappe.db.get_value(self.DOCTYPE, random_doc, "description")
  109. response = self.put(f"{self.DOCTYPE}/{random_doc}", data=data)
  110. self.assertEqual(response.status_code, 200)
  111. self.assertNotEqual(response.json()["data"]["description"], desc_before_update)
  112. self.assertEqual(response.json()["data"]["description"], generated_desc)
  113. def test_delete_document(self):
  114. # test 9: DELETE method on /api/resource
  115. doc_to_delete = choice(self.GENERATED_DOCUMENTS)
  116. response = self.delete(f"{self.DOCTYPE}/{doc_to_delete}")
  117. self.assertEqual(response.status_code, 202)
  118. self.assertDictEqual(response.json(), {"message": "ok"})
  119. non_existent_doc = frappe.generate_hash(length=12)
  120. response = self.delete(f"{self.DOCTYPE}/{non_existent_doc}")
  121. self.assertEqual(response.status_code, 404)
  122. self.assertDictEqual(response.json(), {})
  123. class TestMethodAPI(unittest.TestCase):
  124. METHOD_URL = f"{get_site_url(frappe.local.site)}/api/method"
  125. def test_version(self):
  126. # test 1: test for /api/method/version
  127. response = requests.get(f"{self.METHOD_URL}/version")
  128. json = frappe._dict(response.json())
  129. self.assertEqual(response.status_code, 200)
  130. self.assertIsInstance(json, dict)
  131. self.assertIsInstance(json.message, str)
  132. self.assertEqual(Version(json.message), Version(frappe.__version__))
  133. def test_ping(self):
  134. # test 2: test for /api/method/ping
  135. response = requests.get(f"{self.METHOD_URL}/ping")
  136. self.assertEqual(response.status_code, 200)
  137. self.assertIsInstance(response.json(), dict)
  138. self.assertEqual(response.json()['message'], "pong")