test_validate_format.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. # -*- coding: utf-8 -*-
  2. import unittest
  3. from validate.format import error_message
  4. from validate.format import get_categories_content
  5. from validate.format import check_alphabetical_order
  6. from validate.format import check_title
  7. from validate.format import check_description, max_description_length
  8. from validate.format import check_auth, auth_keys
  9. from validate.format import check_https, https_keys
  10. from validate.format import check_cors, cors_keys
  11. from validate.format import check_entry
  12. from validate.format import check_file_format, min_entries_per_category, num_segments
  13. class TestValidadeFormat(unittest.TestCase):
  14. def test_error_message_return_and_return_type(self):
  15. line_num_unity = 1
  16. line_num_ten = 10
  17. line_num_hundred = 100
  18. line_num_thousand = 1000
  19. msg = 'This is a unit test'
  20. err_msg_unity = error_message(line_num_unity, msg)
  21. err_msg_ten = error_message(line_num_ten, msg)
  22. err_msg_hundred = error_message(line_num_hundred, msg)
  23. err_msg_thousand = error_message(line_num_thousand, msg)
  24. self.assertIsInstance(err_msg_unity, str)
  25. self.assertIsInstance(err_msg_ten, str)
  26. self.assertIsInstance(err_msg_hundred, str)
  27. self.assertIsInstance(err_msg_thousand, str)
  28. self.assertEqual(err_msg_unity, '(L002) This is a unit test')
  29. self.assertEqual(err_msg_ten, '(L011) This is a unit test')
  30. self.assertEqual(err_msg_hundred, '(L101) This is a unit test')
  31. self.assertEqual(err_msg_thousand, '(L1001) This is a unit test')
  32. def test_if_get_categories_content_return_correct_data_of_categories(self):
  33. fake_contents = [
  34. '### A',
  35. 'API | Description | Auth | HTTPS | CORS |',
  36. '|---|---|---|---|---|',
  37. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  38. '| [AB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  39. '',
  40. '### B',
  41. 'API | Description | Auth | HTTPS | CORS |',
  42. '|---|---|---|---|---|',
  43. '| [BA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  44. '| [BB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |'
  45. ]
  46. result = get_categories_content(fake_contents)
  47. self.assertIsInstance(result, tuple)
  48. categories, category_line_num = result
  49. self.assertIsInstance(categories, dict)
  50. self.assertIsInstance(category_line_num, dict)
  51. expected_result = ({'A': ['AA', 'AB'], 'B': ['BA', 'BB']}, {'A': 0, 'B': 6})
  52. for res, ex_res in zip(result, expected_result):
  53. with self.subTest():
  54. self.assertEqual(res, ex_res)
  55. def test_if_check_alphabetical_order_return_correct_msg_error(self):
  56. correct_lines = [
  57. '### A',
  58. 'API | Description | Auth | HTTPS | CORS |',
  59. '|---|---|---|---|---|',
  60. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  61. '| [AB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  62. '',
  63. '### B',
  64. 'API | Description | Auth | HTTPS | CORS |',
  65. '|---|---|---|---|---|',
  66. '| [BA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  67. '| [BB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |'
  68. ]
  69. incorrect_lines = [
  70. '### A',
  71. 'API | Description | Auth | HTTPS | CORS |',
  72. '|---|---|---|---|---|',
  73. '| [AB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  74. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  75. '',
  76. '### B',
  77. 'API | Description | Auth | HTTPS | CORS |',
  78. '|---|---|---|---|---|',
  79. '| [BB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  80. '| [BA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |'
  81. ]
  82. err_msgs_1 = check_alphabetical_order(correct_lines)
  83. err_msgs_2 = check_alphabetical_order(incorrect_lines)
  84. self.assertIsInstance(err_msgs_1, list)
  85. self.assertIsInstance(err_msgs_2, list)
  86. self.assertEqual(len(err_msgs_1), 0)
  87. self.assertEqual(len(err_msgs_2), 2)
  88. expected_err_msgs = [
  89. '(L001) A category is not alphabetical order',
  90. '(L007) B category is not alphabetical order'
  91. ]
  92. for err_msg, ex_err_msg in zip(err_msgs_2, expected_err_msgs):
  93. with self.subTest():
  94. self.assertEqual(err_msg, ex_err_msg)
  95. def test_check_title_with_correct_title(self):
  96. raw_title = '[A](https://www.ex.com)'
  97. err_msgs = check_title(0, raw_title)
  98. self.assertIsInstance(err_msgs, list)
  99. self.assertEqual(len(err_msgs), 0)
  100. self.assertEqual(err_msgs, [])
  101. def test_check_title_with_markdown_syntax_incorrect(self):
  102. raw_title = '[A(https://www.ex.com)'
  103. err_msgs = check_title(0, raw_title)
  104. self.assertIsInstance(err_msgs, list)
  105. self.assertEqual(len(err_msgs), 1)
  106. err_msg = err_msgs[0]
  107. expected_err_msg = '(L001) Title syntax should be "[TITLE](LINK)"'
  108. self.assertEqual(err_msg, expected_err_msg)
  109. def test_check_title_with_api_at_the_end_of_the_title(self):
  110. raw_title = '[A API](https://www.ex.com)'
  111. err_msgs = check_title(0, raw_title)
  112. self.assertIsInstance(err_msgs, list)
  113. self.assertEqual(len(err_msgs), 1)
  114. err_msg = err_msgs[0]
  115. expected_err_msg = '(L001) Title should not end with "... API". Every entry is an API here!'
  116. self.assertEqual(err_msg, expected_err_msg)
  117. def test_check_description_with_correct_description(self):
  118. desc = 'This is a fake description'
  119. err_msgs = check_description(0, desc)
  120. self.assertIsInstance(err_msgs, list)
  121. self.assertEqual(len(err_msgs), 0)
  122. self.assertEqual(err_msgs, [])
  123. def test_check_description_with_first_char_is_not_capitalized(self):
  124. desc = 'this is a fake description'
  125. err_msgs = check_description(0, desc)
  126. self.assertIsInstance(err_msgs, list)
  127. self.assertEqual(len(err_msgs), 1)
  128. err_msg = err_msgs[0]
  129. expected_err_msg = '(L001) first character of description is not capitalized'
  130. self.assertIsInstance(err_msg, str)
  131. self.assertEqual(err_msg, expected_err_msg)
  132. def test_check_description_with_punctuation_in_the_end(self):
  133. base_desc = 'This is a fake description'
  134. punctuation = r"""!"#$%&'*+,-./:;<=>?@[\]^_`{|}~"""
  135. desc_with_punc = [base_desc + punc for punc in punctuation]
  136. for desc in desc_with_punc:
  137. with self.subTest():
  138. err_msgs = check_description(0, desc)
  139. self.assertIsInstance(err_msgs, list)
  140. self.assertEqual(len(err_msgs), 1)
  141. err_msg = err_msgs[0]
  142. expected_err_msg = f'(L001) description should not end with {desc[-1]}'
  143. self.assertIsInstance(err_msg, str)
  144. self.assertEqual(err_msg, expected_err_msg)
  145. def test_check_description_that_exceeds_the_character_limit(self):
  146. long_desc = 'Desc' * max_description_length
  147. long_desc_length = len(long_desc)
  148. err_msgs = check_description(0, long_desc)
  149. self.assertIsInstance(err_msgs, list)
  150. self.assertEqual(len(err_msgs), 1)
  151. err_msg = err_msgs[0]
  152. expected_err_msg = f'(L001) description should not exceed {max_description_length} characters (currently {long_desc_length})'
  153. self.assertIsInstance(err_msg, str)
  154. self.assertEqual(err_msg, expected_err_msg)
  155. def test_check_auth_with_valid_auth(self):
  156. auth_valid = [f'`{auth}`' for auth in auth_keys if auth != 'No']
  157. auth_valid.append('No')
  158. for auth in auth_valid:
  159. with self.subTest():
  160. err_msgs = check_auth(0, auth)
  161. self.assertIsInstance(err_msgs, list)
  162. self.assertEqual(len(err_msgs), 0)
  163. self.assertEqual(err_msgs, [])
  164. def test_check_auth_without_backtick(self):
  165. auth_without_backtick = [auth for auth in auth_keys if auth != 'No']
  166. for auth in auth_without_backtick:
  167. with self.subTest():
  168. err_msgs = check_auth(0, auth)
  169. self.assertIsInstance(err_msgs, list)
  170. self.assertEqual(len(err_msgs), 1)
  171. err_msg = err_msgs[0]
  172. expected_err_msg = '(L001) auth value is not enclosed with `backticks`'
  173. self.assertIsInstance(err_msg, str)
  174. self.assertEqual(err_msg, expected_err_msg)
  175. def test_check_auth_with_invalid_auth(self):
  176. auth_invalid_without_backtick = ['Yes', 'yes', 'no', 'random', 'Unknown']
  177. auth_invalid_with_backtick = ['`Yes`', '`yes`', '`no`', '`random`', '`Unknown`']
  178. for auth in auth_invalid_without_backtick:
  179. with self.subTest():
  180. err_msgs = check_auth(0, auth)
  181. self.assertIsInstance(err_msgs, list)
  182. self.assertEqual(len(err_msgs), 2)
  183. err_msg_1 = err_msgs[0]
  184. err_msg_2 = err_msgs[1]
  185. expected_err_msg_1 = f'(L001) auth value is not enclosed with `backticks`'
  186. expected_err_msg_2 = f'(L001) {auth} is not a valid Auth option'
  187. self.assertIsInstance(err_msg_1, str)
  188. self.assertIsInstance(err_msg_2, str)
  189. self.assertEqual(err_msg_1, expected_err_msg_1)
  190. self.assertEqual(err_msg_2, expected_err_msg_2)
  191. for auth in auth_invalid_with_backtick:
  192. with self.subTest():
  193. err_msgs = check_auth(0, auth)
  194. self.assertIsInstance(err_msgs, list)
  195. self.assertEqual(len(err_msgs), 1)
  196. err_msg = err_msgs[0]
  197. expected_err_msg = f'(L001) {auth} is not a valid Auth option'
  198. self.assertIsInstance(err_msg, str)
  199. self.assertEqual(err_msg, expected_err_msg)
  200. def test_check_https_with_valid_https(self):
  201. for https in https_keys:
  202. with self.subTest():
  203. err_msgs = check_https(0, https)
  204. self.assertIsInstance(err_msgs, list)
  205. self.assertEqual(len(err_msgs), 0)
  206. self.assertEqual(err_msgs, [])
  207. def test_check_https_with_invalid_https(self):
  208. invalid_https_keys = ['yes', 'no', 'Unknown', 'https', 'http']
  209. for https in invalid_https_keys:
  210. with self.subTest():
  211. err_msgs = check_https(0, https)
  212. self.assertIsInstance(err_msgs, list)
  213. self.assertEqual(len(err_msgs), 1)
  214. err_msg = err_msgs[0]
  215. expected_err_msg = f'(L001) {https} is not a valid HTTPS option'
  216. self.assertIsInstance(err_msg, str)
  217. self.assertEqual(err_msg, expected_err_msg)
  218. def test_check_cors_with_valid_cors(self):
  219. for cors in cors_keys:
  220. with self.subTest():
  221. err_msgs = check_cors(0, cors)
  222. self.assertIsInstance(err_msgs, list)
  223. self.assertEqual(len(err_msgs), 0)
  224. self.assertEqual(err_msgs, [])
  225. def test_check_cors_with_invalid_cors(self):
  226. invalid_cors_keys = ['yes', 'no', 'unknown', 'cors']
  227. for cors in invalid_cors_keys:
  228. with self.subTest():
  229. err_msgs = check_cors(0, cors)
  230. self.assertIsInstance(err_msgs, list)
  231. self.assertEqual(len(err_msgs), 1)
  232. err_msg = err_msgs[0]
  233. expected_err_msg = f'(L001) {cors} is not a valid CORS option'
  234. self.assertIsInstance(err_msg, str)
  235. self.assertEqual(err_msg, expected_err_msg)
  236. def test_check_entry_with_correct_segments(self):
  237. correct_segments = ['[A](https://www.ex.com)', 'Desc', '`apiKey`', 'Yes', 'Yes']
  238. err_msgs = check_entry(0, correct_segments)
  239. self.assertIsInstance(err_msgs, list)
  240. self.assertEqual(len(err_msgs), 0)
  241. self.assertEqual(err_msgs, [])
  242. def test_check_entry_with_incorrect_segments(self):
  243. incorrect_segments = ['[A API](https://www.ex.com)', 'desc.', 'yes', 'yes', 'yes']
  244. err_msgs = check_entry(0, incorrect_segments)
  245. expected_err_msgs = [
  246. '(L001) Title should not end with "... API". Every entry is an API here!',
  247. '(L001) first character of description is not capitalized',
  248. '(L001) description should not end with .',
  249. '(L001) auth value is not enclosed with `backticks`',
  250. '(L001) yes is not a valid Auth option',
  251. '(L001) yes is not a valid HTTPS option',
  252. '(L001) yes is not a valid CORS option'
  253. ]
  254. self.assertIsInstance(err_msgs, list)
  255. self.assertEqual(len(err_msgs), 7)
  256. for err_msg in err_msgs:
  257. with self.subTest():
  258. self.assertIsInstance(err_msg, str)
  259. self.assertEqual(err_msgs, expected_err_msgs)
  260. def test_check_file_format_with_correct_format(self):
  261. correct_format = [
  262. '## Index',
  263. '* [A](#a)',
  264. '* [B](#b)',
  265. '',
  266. '### A',
  267. 'API | Description | Auth | HTTPS | CORS |',
  268. '|---|---|---|---|---|',
  269. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  270. '| [AB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  271. '| [AC](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  272. '',
  273. '### B',
  274. 'API | Description | Auth | HTTPS | CORS |',
  275. '|---|---|---|---|---|',
  276. '| [BA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  277. '| [BB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  278. '| [BC](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |'
  279. ]
  280. err_msgs = check_file_format(lines=correct_format)
  281. self.assertIsInstance(err_msgs, list)
  282. self.assertEqual(len(err_msgs), 0)
  283. self.assertEqual(err_msgs, [])
  284. def test_check_file_format_with_category_header_not_added_to_index(self):
  285. incorrect_format = [
  286. '## Index',
  287. '',
  288. '### A',
  289. 'API | Description | Auth | HTTPS | CORS |',
  290. '|---|---|---|---|---|',
  291. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  292. '| [AB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  293. '| [AC](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  294. ]
  295. err_msgs = check_file_format(lines=incorrect_format)
  296. expected_err_msg = '(L003) category header (A) not added to Index section'
  297. self.assertIsInstance(err_msgs, list)
  298. self.assertEqual(len(err_msgs), 1)
  299. err_msg = err_msgs[0]
  300. self.assertEqual(err_msg, expected_err_msg)
  301. def test_check_file_format_with_category_without_min_entries(self):
  302. incorrect_format = [
  303. '## Index',
  304. '* [A](#a)',
  305. '* [B](#b)',
  306. '',
  307. '### A',
  308. 'API | Description | Auth | HTTPS | CORS |',
  309. '|---|---|---|---|---|',
  310. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  311. '',
  312. '### B',
  313. 'API | Description | Auth | HTTPS | CORS |',
  314. '|---|---|---|---|---|',
  315. '| [BA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  316. '| [BB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  317. '| [BC](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |'
  318. ]
  319. category_with_err = 'A'
  320. num_in_category = 1
  321. err_msgs = check_file_format(lines=incorrect_format)
  322. expected_err_msg = f'(L005) {category_with_err} category does not have the minimum {min_entries_per_category} entries (only has {num_in_category})'
  323. self.assertIsInstance(err_msgs, list)
  324. self.assertEqual(len(err_msgs), 1)
  325. err_msg = err_msgs[0]
  326. self.assertEqual(err_msg, expected_err_msg)
  327. def test_check_file_format_entry_without_all_necessary_columns(self):
  328. incorrect_format = [
  329. '## Index',
  330. '* [A](#a)',
  331. '',
  332. '### A',
  333. 'API | Description | Auth | HTTPS | CORS |',
  334. '|---|---|---|---|---|',
  335. '| [AA](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  336. '| [AB](https://www.ex.com) | Desc | `apiKey` |', # missing https and cors
  337. '| [AC](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  338. ]
  339. current_segments_num = 3
  340. err_msgs = check_file_format(lines=incorrect_format)
  341. expected_err_msg = f'(L008) entry does not have all the required columns (have {current_segments_num}, need {num_segments})'
  342. self.assertIsInstance(err_msgs, list)
  343. self.assertEqual(len(err_msgs), 1)
  344. err_msg = err_msgs[0]
  345. self.assertEqual(err_msg, expected_err_msg)
  346. def test_check_file_format_without_1_space_between_the_segments(self):
  347. incorrect_format = [
  348. '## Index',
  349. '* [A](#a)',
  350. '',
  351. '### A',
  352. 'API | Description | Auth | HTTPS | CORS |',
  353. '|---|---|---|---|---|',
  354. '| [AA](https://www.ex.com) | Desc |`apiKey`| Yes | Yes |', # space between segment of auth column missing
  355. '| [AB](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  356. '| [AC](https://www.ex.com) | Desc | `apiKey` | Yes | Yes |',
  357. ]
  358. err_msgs = check_file_format(lines=incorrect_format)
  359. expected_err_msg = f'(L007) each segment must start and end with exactly 1 space'
  360. self.assertIsInstance(err_msgs, list)
  361. self.assertEqual(len(err_msgs), 1)
  362. err_msg = err_msgs[0]
  363. self.assertEqual(err_msg, expected_err_msg)