test_schema.py 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341
  1. # -*- coding: utf-8 -*-
  2. # pylint: disable=function-redefined,missing-docstring
  3. # TODO: Remove the following PyLint disable as soon as we support YAML and RST rendering
  4. # pylint: disable=abstract-method
  5. # Import python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import copy
  8. # Import Salt Testing Libs
  9. from tests.support.unit import TestCase, skipIf
  10. # Import Salt Libs
  11. import salt.utils.json
  12. import salt.utils.stringutils
  13. import salt.utils.yaml
  14. import salt.utils.schema as schema
  15. from salt.ext import six
  16. from salt.utils.versions import LooseVersion as _LooseVersion
  17. # Import 3rd-party libs
  18. try:
  19. import jsonschema
  20. import jsonschema.exceptions
  21. HAS_JSONSCHEMA = True
  22. JSONSCHEMA_VERSION = _LooseVersion(jsonschema.__version__)
  23. except ImportError:
  24. HAS_JSONSCHEMA = False
  25. JSONSCHEMA_VERSION = _LooseVersion('0')
  26. # pylint: disable=unused-import
  27. try:
  28. import rfc3987
  29. HAS_RFC3987 = True
  30. except ImportError:
  31. HAS_RFC3987 = False
  32. try:
  33. import strict_rfc3339
  34. HAS_STRICT_RFC3339 = True
  35. except ImportError:
  36. HAS_STRICT_RFC3339 = False
  37. try:
  38. import isodate
  39. HAS_ISODATE = True
  40. except ImportError:
  41. HAS_ISODATE = False
  42. # pylint: enable=unused-import
  43. class ConfigTestCase(TestCase):
  44. '''
  45. TestCase for salt.utils.config module
  46. '''
  47. def test_configuration_subclass_inherits_items(self):
  48. class BaseConfig(schema.Schema):
  49. base = schema.BooleanItem(default=True, required=True)
  50. class SubClassedConfig(BaseConfig):
  51. hungry = schema.BooleanItem(title='Hungry', description='Are you hungry?', required=True)
  52. self.assertDictEqual(
  53. SubClassedConfig.serialize(),
  54. {
  55. '$schema': 'http://json-schema.org/draft-04/schema#',
  56. 'type': 'object',
  57. 'properties': {
  58. 'base': {
  59. 'default': True,
  60. 'type': 'boolean',
  61. 'title': 'base'
  62. },
  63. 'hungry': {
  64. 'type': 'boolean',
  65. 'description': 'Are you hungry?',
  66. 'title': 'Hungry'
  67. }
  68. },
  69. 'required': ['base', 'hungry'],
  70. 'x-ordering': ['base', 'hungry'],
  71. 'additionalProperties': False,
  72. }
  73. )
  74. class MergedConfigClass(schema.Schema):
  75. thirsty = schema.BooleanItem(title='Thirsty', description='Are you thirsty?', required=True)
  76. merge_subclassed = SubClassedConfig(flatten=True)
  77. expected = {
  78. '$schema': 'http://json-schema.org/draft-04/schema#',
  79. 'type': 'object',
  80. 'properties': {
  81. 'thirsty': {
  82. 'type': 'boolean',
  83. 'description': 'Are you thirsty?',
  84. 'title': 'Thirsty'
  85. },
  86. 'base': {
  87. 'default': True,
  88. 'type': 'boolean',
  89. 'title': 'base'
  90. },
  91. 'hungry': {
  92. 'type': 'boolean',
  93. 'description': 'Are you hungry?',
  94. 'title': 'Hungry'
  95. }
  96. },
  97. 'required': ['thirsty', 'base', 'hungry'],
  98. 'x-ordering': ['thirsty', 'base', 'hungry'],
  99. 'additionalProperties': False,
  100. }
  101. self.assertDictContainsSubset(
  102. MergedConfigClass.serialize()['properties'],
  103. expected['properties']
  104. )
  105. self.assertDictContainsSubset(
  106. expected,
  107. MergedConfigClass.serialize()
  108. )
  109. def test_configuration_items_order(self):
  110. class One(schema.Schema):
  111. one = schema.BooleanItem()
  112. class Three(schema.Schema):
  113. three = schema.BooleanItem()
  114. class Final(One):
  115. two = schema.BooleanItem()
  116. three = Three(flatten=True)
  117. self.assertEqual(Final.serialize()['x-ordering'], ['one', 'two', 'three'])
  118. def test_optional_requirements_config(self):
  119. class BaseRequirements(schema.Schema):
  120. driver = schema.StringItem(default='digitalocean', format='hidden')
  121. class SSHKeyFileSchema(schema.Schema):
  122. ssh_key_file = schema.StringItem(
  123. title='SSH Private Key',
  124. description='The path to an SSH private key which will be used '
  125. 'to authenticate on the deployed VMs',
  126. )
  127. class SSHKeyNamesSchema(schema.Schema):
  128. ssh_key_names = schema.StringItem(
  129. title='SSH Key Names',
  130. description='The names of an SSH key being managed on '
  131. 'DigitalOcean account which will be used to '
  132. 'authenticate on the deployed VMs',
  133. )
  134. class Requirements(BaseRequirements):
  135. title = 'DigitalOcean'
  136. description = 'DigitalOcean Cloud VM configuration requirements.'
  137. personal_access_token = schema.StringItem(
  138. title='Personal Access Token',
  139. description='This is the API access token which can be generated '
  140. 'under the API/Application on your account',
  141. required=True)
  142. requirements_definition = schema.AnyOfItem(
  143. items=(
  144. SSHKeyFileSchema.as_requirements_item(),
  145. SSHKeyNamesSchema.as_requirements_item()
  146. ),
  147. )(flatten=True)
  148. ssh_key_file = SSHKeyFileSchema(flatten=True)
  149. ssh_key_names = SSHKeyNamesSchema(flatten=True)
  150. expected = {
  151. '$schema': 'http://json-schema.org/draft-04/schema#',
  152. 'title': 'DigitalOcean',
  153. 'description': 'DigitalOcean Cloud VM configuration requirements.',
  154. 'type': 'object',
  155. 'properties': {
  156. 'driver': {
  157. 'default': 'digitalocean',
  158. 'format': 'hidden',
  159. 'type': 'string',
  160. 'title': 'driver'
  161. },
  162. 'personal_access_token': {
  163. 'type': 'string',
  164. 'description': 'This is the API access token which can be '
  165. 'generated under the API/Application on your account',
  166. 'title': 'Personal Access Token'
  167. },
  168. 'ssh_key_file': {
  169. 'type': 'string',
  170. 'description': 'The path to an SSH private key which will '
  171. 'be used to authenticate on the deployed VMs',
  172. 'title': 'SSH Private Key'
  173. },
  174. 'ssh_key_names': {
  175. 'type': 'string',
  176. 'description': 'The names of an SSH key being managed on DigitalOcean '
  177. 'account which will be used to authenticate on the deployed VMs',
  178. 'title': 'SSH Key Names'
  179. }
  180. },
  181. 'anyOf': [
  182. {'required': ['ssh_key_file']},
  183. {'required': ['ssh_key_names']}
  184. ],
  185. 'required': [
  186. 'personal_access_token'
  187. ],
  188. 'x-ordering': [
  189. 'driver',
  190. 'personal_access_token',
  191. 'ssh_key_file',
  192. 'ssh_key_names',
  193. ],
  194. 'additionalProperties': False
  195. }
  196. self.assertDictEqual(expected, Requirements.serialize())
  197. class Requirements2(BaseRequirements):
  198. title = 'DigitalOcean'
  199. description = 'DigitalOcean Cloud VM configuration requirements.'
  200. personal_access_token = schema.StringItem(
  201. title='Personal Access Token',
  202. description='This is the API access token which can be generated '
  203. 'under the API/Application on your account',
  204. required=True)
  205. ssh_key_file = schema.StringItem(
  206. title='SSH Private Key',
  207. description='The path to an SSH private key which will be used '
  208. 'to authenticate on the deployed VMs')
  209. ssh_key_names = schema.StringItem(
  210. title='SSH Key Names',
  211. description='The names of an SSH key being managed on '
  212. 'DigitalOcean account which will be used to '
  213. 'authenticate on the deployed VMs')
  214. requirements_definition = schema.AnyOfItem(
  215. items=(
  216. schema.RequirementsItem(requirements=['ssh_key_file']),
  217. schema.RequirementsItem(requirements=['ssh_key_names'])
  218. ),
  219. )(flatten=True)
  220. expected = {
  221. '$schema': 'http://json-schema.org/draft-04/schema#',
  222. 'title': 'DigitalOcean',
  223. 'description': 'DigitalOcean Cloud VM configuration requirements.',
  224. 'type': 'object',
  225. 'properties': {
  226. 'driver': {
  227. 'default': 'digitalocean',
  228. 'format': 'hidden',
  229. 'type': 'string',
  230. 'title': 'driver'
  231. },
  232. 'personal_access_token': {
  233. 'type': 'string',
  234. 'description': 'This is the API access token which can be '
  235. 'generated under the API/Application on your account',
  236. 'title': 'Personal Access Token'
  237. },
  238. 'ssh_key_file': {
  239. 'type': 'string',
  240. 'description': 'The path to an SSH private key which will '
  241. 'be used to authenticate on the deployed VMs',
  242. 'title': 'SSH Private Key'
  243. },
  244. 'ssh_key_names': {
  245. 'type': 'string',
  246. 'description': 'The names of an SSH key being managed on DigitalOcean '
  247. 'account which will be used to authenticate on the deployed VMs',
  248. 'title': 'SSH Key Names'
  249. }
  250. },
  251. 'anyOf': [
  252. {'required': ['ssh_key_file']},
  253. {'required': ['ssh_key_names']}
  254. ],
  255. 'required': [
  256. 'personal_access_token'
  257. ],
  258. 'x-ordering': [
  259. 'driver',
  260. 'personal_access_token',
  261. 'ssh_key_file',
  262. 'ssh_key_names',
  263. ],
  264. 'additionalProperties': False
  265. }
  266. self.assertDictContainsSubset(expected, Requirements2.serialize())
  267. class Requirements3(schema.Schema):
  268. title = 'DigitalOcean'
  269. description = 'DigitalOcean Cloud VM configuration requirements.'
  270. merge_reqs = Requirements(flatten=True)
  271. expected = {
  272. '$schema': 'http://json-schema.org/draft-04/schema#',
  273. 'title': 'DigitalOcean',
  274. 'description': 'DigitalOcean Cloud VM configuration requirements.',
  275. 'type': 'object',
  276. 'properties': {
  277. 'driver': {
  278. 'default': 'digitalocean',
  279. 'format': 'hidden',
  280. 'type': 'string',
  281. 'title': 'driver'
  282. },
  283. 'personal_access_token': {
  284. 'type': 'string',
  285. 'description': 'This is the API access token which can be '
  286. 'generated under the API/Application on your account',
  287. 'title': 'Personal Access Token'
  288. },
  289. 'ssh_key_file': {
  290. 'type': 'string',
  291. 'description': 'The path to an SSH private key which will '
  292. 'be used to authenticate on the deployed VMs',
  293. 'title': 'SSH Private Key'
  294. },
  295. 'ssh_key_names': {
  296. 'type': 'string',
  297. 'description': 'The names of an SSH key being managed on DigitalOcean '
  298. 'account which will be used to authenticate on the deployed VMs',
  299. 'title': 'SSH Key Names'
  300. }
  301. },
  302. 'anyOf': [
  303. {'required': ['ssh_key_file']},
  304. {'required': ['ssh_key_names']}
  305. ],
  306. 'required': [
  307. 'personal_access_token'
  308. ],
  309. 'x-ordering': [
  310. 'driver',
  311. 'personal_access_token',
  312. 'ssh_key_file',
  313. 'ssh_key_names',
  314. ],
  315. 'additionalProperties': False
  316. }
  317. self.assertDictContainsSubset(expected, Requirements3.serialize())
  318. class Requirements4(schema.Schema):
  319. title = 'DigitalOcean'
  320. description = 'DigitalOcean Cloud VM configuration requirements.'
  321. merge_reqs = Requirements(flatten=True)
  322. ssh_key_file_2 = schema.StringItem(
  323. title='SSH Private Key',
  324. description='The path to an SSH private key which will be used '
  325. 'to authenticate on the deployed VMs')
  326. ssh_key_names_2 = schema.StringItem(
  327. title='SSH Key Names',
  328. description='The names of an SSH key being managed on '
  329. 'DigitalOcean account which will be used to '
  330. 'authenticate on the deployed VMs')
  331. requirements_definition_2 = schema.AnyOfItem(
  332. items=(
  333. schema.RequirementsItem(requirements=['ssh_key_file_2']),
  334. schema.RequirementsItem(requirements=['ssh_key_names_2'])
  335. ),
  336. )(flatten=True)
  337. expected = {
  338. '$schema': 'http://json-schema.org/draft-04/schema#',
  339. 'title': 'DigitalOcean',
  340. 'description': 'DigitalOcean Cloud VM configuration requirements.',
  341. 'type': 'object',
  342. 'properties': {
  343. 'driver': {
  344. 'default': 'digitalocean',
  345. 'format': 'hidden',
  346. 'type': 'string',
  347. 'title': 'driver'
  348. },
  349. 'personal_access_token': {
  350. 'type': 'string',
  351. 'description': 'This is the API access token which can be '
  352. 'generated under the API/Application on your account',
  353. 'title': 'Personal Access Token'
  354. },
  355. 'ssh_key_file': {
  356. 'type': 'string',
  357. 'description': 'The path to an SSH private key which will '
  358. 'be used to authenticate on the deployed VMs',
  359. 'title': 'SSH Private Key'
  360. },
  361. 'ssh_key_names': {
  362. 'type': 'string',
  363. 'description': 'The names of an SSH key being managed on DigitalOcean '
  364. 'account which will be used to authenticate on the deployed VMs',
  365. 'title': 'SSH Key Names'
  366. },
  367. 'ssh_key_file_2': {
  368. 'type': 'string',
  369. 'description': 'The path to an SSH private key which will '
  370. 'be used to authenticate on the deployed VMs',
  371. 'title': 'SSH Private Key'
  372. },
  373. 'ssh_key_names_2': {
  374. 'type': 'string',
  375. 'description': 'The names of an SSH key being managed on DigitalOcean '
  376. 'account which will be used to authenticate on the deployed VMs',
  377. 'title': 'SSH Key Names'
  378. }
  379. },
  380. 'anyOf': [
  381. {'required': ['ssh_key_file']},
  382. {'required': ['ssh_key_names']},
  383. {'required': ['ssh_key_file_2']},
  384. {'required': ['ssh_key_names_2']}
  385. ],
  386. 'required': [
  387. 'personal_access_token'
  388. ],
  389. 'x-ordering': [
  390. 'driver',
  391. 'personal_access_token',
  392. 'ssh_key_file',
  393. 'ssh_key_names',
  394. 'ssh_key_file_2',
  395. 'ssh_key_names_2',
  396. ],
  397. 'additionalProperties': False
  398. }
  399. self.assertDictContainsSubset(expected, Requirements4.serialize())
  400. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  401. def test_optional_requirements_config_validation(self):
  402. class BaseRequirements(schema.Schema):
  403. driver = schema.StringItem(default='digitalocean', format='hidden')
  404. class SSHKeyFileSchema(schema.Schema):
  405. ssh_key_file = schema.StringItem(
  406. title='SSH Private Key',
  407. description='The path to an SSH private key which will be used '
  408. 'to authenticate on the deployed VMs')
  409. class SSHKeyNamesSchema(schema.Schema):
  410. ssh_key_names = schema.StringItem(
  411. title='SSH Key Names',
  412. description='The names of an SSH key being managed on '
  413. 'Digial Ocean account which will be used to '
  414. 'authenticate on the deployed VMs')
  415. class Requirements(BaseRequirements):
  416. title = 'DigitalOcean'
  417. description = 'DigitalOcean Cloud VM configuration requirements.'
  418. personal_access_token = schema.StringItem(
  419. title='Personal Access Token',
  420. description='This is the API access token which can be generated '
  421. 'under the API/Application on your account',
  422. required=True)
  423. requirements_definition = schema.AnyOfItem(
  424. items=(
  425. SSHKeyFileSchema.as_requirements_item(),
  426. SSHKeyNamesSchema.as_requirements_item()
  427. ),
  428. )(flatten=True)
  429. ssh_key_file = SSHKeyFileSchema(flatten=True)
  430. ssh_key_names = SSHKeyNamesSchema(flatten=True)
  431. try:
  432. jsonschema.validate(
  433. {'personal_access_token': 'foo', 'ssh_key_names': 'bar', 'ssh_key_file': 'test'},
  434. Requirements.serialize()
  435. )
  436. except jsonschema.exceptions.ValidationError as exc:
  437. self.fail('ValidationError raised: {0}'.format(exc))
  438. try:
  439. jsonschema.validate(
  440. {'personal_access_token': 'foo', 'ssh_key_names': 'bar'},
  441. Requirements.serialize()
  442. )
  443. except jsonschema.exceptions.ValidationError as exc:
  444. self.fail('ValidationError raised: {0}'.format(exc))
  445. try:
  446. jsonschema.validate(
  447. {'personal_access_token': 'foo', 'ssh_key_file': 'test'},
  448. Requirements.serialize()
  449. )
  450. except jsonschema.exceptions.ValidationError as exc:
  451. self.fail('ValidationError raised: {0}'.format(exc))
  452. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  453. jsonschema.validate(
  454. {'personal_access_token': 'foo'},
  455. Requirements.serialize()
  456. )
  457. if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'):
  458. self.assertIn('\'ssh_key_file\' is a required property', excinfo.exception.message)
  459. else:
  460. self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
  461. def test_boolean_config(self):
  462. item = schema.BooleanItem(title='Hungry', description='Are you hungry?')
  463. self.assertDictEqual(
  464. item.serialize(), {
  465. 'type': 'boolean',
  466. 'title': item.title,
  467. 'description': item.description
  468. }
  469. )
  470. item = schema.BooleanItem(title='Hungry',
  471. description='Are you hungry?',
  472. default=False)
  473. self.assertDictEqual(
  474. item.serialize(), {
  475. 'type': 'boolean',
  476. 'title': item.title,
  477. 'description': item.description,
  478. 'default': item.default
  479. }
  480. )
  481. item = schema.BooleanItem(title='Hungry',
  482. description='Are you hungry?',
  483. default=schema.Null)
  484. self.assertDictEqual(
  485. item.serialize(), {
  486. 'type': 'boolean',
  487. 'title': item.title,
  488. 'description': item.description,
  489. 'default': None
  490. }
  491. )
  492. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  493. def test_boolean_config_validation(self):
  494. class TestConf(schema.Schema):
  495. item = schema.BooleanItem(title='Hungry', description='Are you hungry?')
  496. try:
  497. jsonschema.validate({'item': False}, TestConf.serialize())
  498. except jsonschema.exceptions.ValidationError as exc:
  499. self.fail('ValidationError raised: {0}'.format(exc))
  500. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  501. jsonschema.validate({'item': 1}, TestConf.serialize())
  502. self.assertIn('is not of type', excinfo.exception.message)
  503. def test_string_config(self):
  504. item = schema.StringItem(title='Foo', description='Foo Item')
  505. self.assertDictEqual(
  506. item.serialize(), {
  507. 'type': 'string',
  508. 'title': item.title,
  509. 'description': item.description
  510. }
  511. )
  512. item = schema.StringItem(title='Foo', description='Foo Item', min_length=1, max_length=3)
  513. self.assertDictEqual(
  514. item.serialize(), {
  515. 'type': 'string',
  516. 'title': item.title,
  517. 'description': item.description,
  518. 'minLength': item.min_length,
  519. 'maxLength': item.max_length
  520. }
  521. )
  522. item = schema.StringItem(title='Foo',
  523. description='Foo Item',
  524. min_length=1,
  525. max_length=3,
  526. default='foo')
  527. self.assertDictEqual(
  528. item.serialize(), {
  529. 'type': 'string',
  530. 'title': item.title,
  531. 'description': item.description,
  532. 'minLength': item.min_length,
  533. 'maxLength': item.max_length,
  534. 'default': 'foo'
  535. }
  536. )
  537. item = schema.StringItem(title='Foo',
  538. description='Foo Item',
  539. min_length=1,
  540. max_length=3,
  541. enum=('foo', 'bar'))
  542. self.assertDictEqual(
  543. item.serialize(), {
  544. 'type': 'string',
  545. 'title': item.title,
  546. 'description': item.description,
  547. 'minLength': item.min_length,
  548. 'maxLength': item.max_length,
  549. 'enum': ['foo', 'bar']
  550. }
  551. )
  552. item = schema.StringItem(title='Foo',
  553. description='Foo Item',
  554. min_length=1,
  555. max_length=3,
  556. enum=('foo', 'bar'),
  557. enumNames=('Foo', 'Bar'))
  558. self.assertDictEqual(
  559. item.serialize(), {
  560. 'type': 'string',
  561. 'title': item.title,
  562. 'description': item.description,
  563. 'minLength': item.min_length,
  564. 'maxLength': item.max_length,
  565. 'enum': ['foo', 'bar'],
  566. 'enumNames': ['Foo', 'Bar']
  567. }
  568. )
  569. item = schema.StringItem(title='Foo',
  570. description='Foo Item',
  571. pattern=r'^([\w_-]+)$')
  572. self.assertDictEqual(
  573. item.serialize(), {
  574. 'type': 'string',
  575. 'title': item.title,
  576. 'description': item.description,
  577. 'pattern': item.pattern
  578. }
  579. )
  580. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  581. def test_string_config_validation(self):
  582. class TestConf(schema.Schema):
  583. item = schema.StringItem(title='Foo', description='Foo Item')
  584. try:
  585. jsonschema.validate({'item': 'the item'}, TestConf.serialize())
  586. except jsonschema.exceptions.ValidationError as exc:
  587. self.fail('ValidationError raised: {0}'.format(exc))
  588. class TestConf(schema.Schema):
  589. item = schema.StringItem(title='Foo', description='Foo Item',
  590. min_length=1, max_length=10)
  591. try:
  592. jsonschema.validate({'item': 'the item'}, TestConf.serialize())
  593. except jsonschema.exceptions.ValidationError as exc:
  594. self.fail('ValidationError raised: {0}'.format(exc))
  595. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  596. jsonschema.validate({'item': 3}, TestConf.serialize())
  597. self.assertIn('is not of type', excinfo.exception.message)
  598. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  599. jsonschema.validate({'item': 'the item the item'}, TestConf.serialize())
  600. self.assertIn('is too long', excinfo.exception.message)
  601. class TestConf(schema.Schema):
  602. item = schema.StringItem(title='Foo', description='Foo Item',
  603. min_length=10, max_length=100)
  604. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  605. jsonschema.validate({'item': 'the item'}, TestConf.serialize())
  606. self.assertIn('is too short', excinfo.exception.message)
  607. class TestConf(schema.Schema):
  608. item = schema.StringItem(title='Foo',
  609. description='Foo Item',
  610. enum=('foo', 'bar'))
  611. try:
  612. jsonschema.validate({'item': 'foo'}, TestConf.serialize())
  613. except jsonschema.exceptions.ValidationError as exc:
  614. self.fail('ValidationError raised: {0}'.format(exc))
  615. class TestConf(schema.Schema):
  616. item = schema.StringItem(title='Foo',
  617. description='Foo Item',
  618. enum=('foo', 'bar'))
  619. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  620. jsonschema.validate({'item': 'bin'}, TestConf.serialize())
  621. self.assertIn('is not one of', excinfo.exception.message)
  622. class TestConf(schema.Schema):
  623. item = schema.StringItem(title='Foo', description='Foo Item',
  624. pattern=r'^([\w_-]+)$')
  625. try:
  626. jsonschema.validate({'item': 'the-item'}, TestConf.serialize(),
  627. format_checker=jsonschema.FormatChecker())
  628. except jsonschema.exceptions.ValidationError as exc:
  629. self.fail('ValidationError raised: {0}'.format(exc))
  630. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  631. jsonschema.validate({'item': 'the item'}, TestConf.serialize(),
  632. format_checker=jsonschema.FormatChecker())
  633. self.assertIn('does not match', excinfo.exception.message)
  634. def test_email_config(self):
  635. item = schema.EMailItem(title='Foo', description='Foo Item')
  636. self.assertDictEqual(
  637. item.serialize(), {
  638. 'type': 'string',
  639. 'title': item.title,
  640. 'description': item.description,
  641. 'format': item.format
  642. }
  643. )
  644. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  645. def test_email_config_validation(self):
  646. class TestConf(schema.Schema):
  647. item = schema.EMailItem(title='Item', description='Item description')
  648. try:
  649. jsonschema.validate({'item': 'nobody@nowhere.com'}, TestConf.serialize(),
  650. format_checker=jsonschema.FormatChecker())
  651. except jsonschema.exceptions.ValidationError as exc:
  652. self.fail('ValidationError raised: {0}'.format(exc))
  653. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  654. jsonschema.validate({'item': '3'}, TestConf.serialize(),
  655. format_checker=jsonschema.FormatChecker())
  656. self.assertIn('is not a', excinfo.exception.message)
  657. def test_ipv4_config(self):
  658. item = schema.IPv4Item(title='Foo', description='Foo Item')
  659. self.assertDictEqual(
  660. item.serialize(), {
  661. 'type': 'string',
  662. 'title': item.title,
  663. 'description': item.description,
  664. 'format': item.format
  665. }
  666. )
  667. @skipIf(JSONSCHEMA_VERSION <= _LooseVersion('2.5.0'), 'Requires jsonschema 2.5.0 or greater')
  668. def test_ipv4_config_validation(self):
  669. class TestConf(schema.Schema):
  670. item = schema.IPv4Item(title='Item', description='Item description')
  671. try:
  672. jsonschema.validate({'item': '127.0.0.1'}, TestConf.serialize(),
  673. format_checker=jsonschema.FormatChecker())
  674. except jsonschema.exceptions.ValidationError as exc:
  675. self.fail('ValidationError raised: {0}'.format(exc))
  676. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  677. jsonschema.validate({'item': '3'}, TestConf.serialize(),
  678. format_checker=jsonschema.FormatChecker())
  679. self.assertIn('is not a', excinfo.exception.message)
  680. def test_ipv6_config(self):
  681. item = schema.IPv6Item(title='Foo', description='Foo Item')
  682. self.assertDictEqual(
  683. item.serialize(), {
  684. 'type': 'string',
  685. 'title': item.title,
  686. 'description': item.description,
  687. 'format': item.format
  688. }
  689. )
  690. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  691. def test_ipv6_config_validation(self):
  692. class TestConf(schema.Schema):
  693. item = schema.IPv6Item(title='Item', description='Item description')
  694. try:
  695. jsonschema.validate(
  696. {'item': salt.utils.stringutils.to_str('::1')},
  697. TestConf.serialize(),
  698. format_checker=jsonschema.FormatChecker())
  699. except jsonschema.exceptions.ValidationError as exc:
  700. self.fail('ValidationError raised: {0}'.format(exc))
  701. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  702. jsonschema.validate({'item': '3'}, TestConf.serialize(),
  703. format_checker=jsonschema.FormatChecker())
  704. self.assertIn('is not a', excinfo.exception.message)
  705. def test_hostname_config(self):
  706. item = schema.HostnameItem(title='Foo', description='Foo Item')
  707. self.assertDictEqual(
  708. item.serialize(), {
  709. 'type': 'string',
  710. 'title': item.title,
  711. 'description': item.description,
  712. 'format': item.format
  713. }
  714. )
  715. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  716. def test_hostname_config_validation(self):
  717. class TestConf(schema.Schema):
  718. item = schema.HostnameItem(title='Item', description='Item description')
  719. try:
  720. jsonschema.validate({'item': 'localhost'}, TestConf.serialize(),
  721. format_checker=jsonschema.FormatChecker())
  722. except jsonschema.exceptions.ValidationError as exc:
  723. self.fail('ValidationError raised: {0}'.format(exc))
  724. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  725. jsonschema.validate({'item': '3'}, TestConf.serialize(),
  726. format_checker=jsonschema.FormatChecker())
  727. self.assertIn('is not a', excinfo.exception.message)
  728. def test_datetime_config(self):
  729. item = schema.DateTimeItem(title='Foo', description='Foo Item')
  730. self.assertDictEqual(
  731. item.serialize(), {
  732. 'type': 'string',
  733. 'title': item.title,
  734. 'description': item.description,
  735. 'format': item.format
  736. }
  737. )
  738. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  739. @skipIf(any([HAS_ISODATE, HAS_STRICT_RFC3339]) is False, 'The \'strict_rfc3339\' or \'isodate\' library is missing')
  740. def test_datetime_config_validation(self):
  741. class TestConf(schema.Schema):
  742. item = schema.DateTimeItem(title='Item', description='Item description')
  743. try:
  744. jsonschema.validate({'item': '2015-07-01T18:05:27+01:00'}, TestConf.serialize(),
  745. format_checker=jsonschema.FormatChecker())
  746. except jsonschema.exceptions.ValidationError as exc:
  747. self.fail('ValidationError raised: {0}'.format(exc))
  748. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  749. jsonschema.validate({'item': '3'}, TestConf.serialize(),
  750. format_checker=jsonschema.FormatChecker())
  751. self.assertIn('is not a', excinfo.exception.message)
  752. def test_secret_config(self):
  753. item = schema.SecretItem(title='Foo', description='Foo Item')
  754. self.assertDictEqual(
  755. item.serialize(), {
  756. 'type': 'string',
  757. 'title': item.title,
  758. 'description': item.description,
  759. 'format': item.format
  760. }
  761. )
  762. def test_uri_config(self):
  763. item = schema.UriItem(title='Foo', description='Foo Item')
  764. self.assertDictEqual(
  765. item.serialize(), {
  766. 'type': 'string',
  767. 'title': item.title,
  768. 'description': item.description,
  769. 'format': item.format
  770. }
  771. )
  772. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  773. @skipIf(HAS_RFC3987 is False, 'The \'rfc3987\' library is missing')
  774. def test_uri_config_validation(self):
  775. class TestConf(schema.Schema):
  776. item = schema.UriItem(title='Item', description='Item description')
  777. try:
  778. jsonschema.validate({'item': 'ssh://localhost'}, TestConf.serialize(),
  779. format_checker=jsonschema.FormatChecker())
  780. except jsonschema.exceptions.ValidationError as exc:
  781. self.fail('ValidationError raised: {0}'.format(exc))
  782. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  783. jsonschema.validate({'item': '3'}, TestConf.serialize(),
  784. format_checker=jsonschema.FormatChecker())
  785. self.assertIn('is not a', excinfo.exception.message)
  786. def test_number_config(self):
  787. item = schema.NumberItem(title='How many dogs', description='Question')
  788. self.assertDictEqual(
  789. item.serialize(), {
  790. 'type': 'number',
  791. 'title': item.title,
  792. 'description': item.description
  793. }
  794. )
  795. item = schema.NumberItem(title='How many dogs',
  796. description='Question',
  797. minimum=0,
  798. maximum=10)
  799. self.assertDictEqual(
  800. item.serialize(), {
  801. 'type': 'number',
  802. 'title': item.title,
  803. 'description': item.description,
  804. 'minimum': item.minimum,
  805. 'maximum': item.maximum
  806. }
  807. )
  808. item = schema.NumberItem(title='How many dogs',
  809. description='Question',
  810. multiple_of=2)
  811. self.assertDictEqual(
  812. item.serialize(), {
  813. 'type': 'number',
  814. 'title': item.title,
  815. 'description': item.description,
  816. 'multipleOf': item.multiple_of
  817. }
  818. )
  819. item = schema.NumberItem(title='How many dogs',
  820. description='Question',
  821. minimum=0,
  822. exclusive_minimum=True,
  823. maximum=10,
  824. exclusive_maximum=True)
  825. self.assertDictEqual(
  826. item.serialize(), {
  827. 'type': 'number',
  828. 'title': item.title,
  829. 'description': item.description,
  830. 'minimum': item.minimum,
  831. 'maximum': item.maximum,
  832. 'exclusiveMinimum': True,
  833. 'exclusiveMaximum': True
  834. }
  835. )
  836. item = schema.NumberItem(title='How many dogs',
  837. description='Question',
  838. minimum=0,
  839. maximum=10,
  840. default=0)
  841. self.assertDictEqual(
  842. item.serialize(), {
  843. 'type': 'number',
  844. 'title': item.title,
  845. 'description': item.description,
  846. 'minimum': item.minimum,
  847. 'maximum': item.maximum,
  848. 'default': 0
  849. }
  850. )
  851. item = schema.NumberItem(title='How many dogs',
  852. description='Question',
  853. minimum=0,
  854. maximum=10,
  855. default=0,
  856. enum=(0, 2, 4, 6))
  857. self.assertDictEqual(
  858. item.serialize(), {
  859. 'type': 'number',
  860. 'title': item.title,
  861. 'description': item.description,
  862. 'minimum': item.minimum,
  863. 'maximum': item.maximum,
  864. 'default': 0,
  865. 'enum': [0, 2, 4, 6]
  866. }
  867. )
  868. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  869. def test_number_config_validation(self):
  870. class TestConf(schema.Schema):
  871. item = schema.NumberItem(title='How many dogs', description='Question')
  872. try:
  873. jsonschema.validate({'item': 2}, TestConf.serialize())
  874. except jsonschema.exceptions.ValidationError as exc:
  875. self.fail('ValidationError raised: {0}'.format(exc))
  876. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  877. jsonschema.validate({'item': '3'}, TestConf.serialize())
  878. self.assertIn('is not of type', excinfo.exception.message)
  879. class TestConf(schema.Schema):
  880. item = schema.NumberItem(title='How many dogs',
  881. description='Question',
  882. multiple_of=2.2)
  883. try:
  884. jsonschema.validate({'item': 4.4}, TestConf.serialize())
  885. except jsonschema.exceptions.ValidationError as exc:
  886. self.fail('ValidationError raised: {0}'.format(exc))
  887. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  888. jsonschema.validate({'item': 4}, TestConf.serialize())
  889. self.assertIn('is not a multiple of', excinfo.exception.message)
  890. class TestConf(schema.Schema):
  891. item = schema.NumberItem(title='Foo', description='Foo Item',
  892. minimum=1, maximum=10)
  893. try:
  894. jsonschema.validate({'item': 3}, TestConf.serialize())
  895. except jsonschema.exceptions.ValidationError as exc:
  896. self.fail('ValidationError raised: {0}'.format(exc))
  897. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  898. jsonschema.validate({'item': 11}, TestConf.serialize())
  899. self.assertIn('is greater than the maximum of', excinfo.exception.message)
  900. class TestConf(schema.Schema):
  901. item = schema.NumberItem(title='Foo', description='Foo Item',
  902. minimum=10, maximum=100)
  903. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  904. jsonschema.validate({'item': 3}, TestConf.serialize())
  905. self.assertIn('is less than the minimum of', excinfo.exception.message)
  906. class TestConf(schema.Schema):
  907. item = schema.NumberItem(title='How many dogs',
  908. description='Question',
  909. minimum=0,
  910. exclusive_minimum=True,
  911. maximum=10,
  912. exclusive_maximum=True)
  913. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  914. jsonschema.validate({'item': 0}, TestConf.serialize())
  915. self.assertIn('is less than or equal to the minimum of', excinfo.exception.message)
  916. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  917. jsonschema.validate({'item': 10}, TestConf.serialize())
  918. self.assertIn('is greater than or equal to the maximum of', excinfo.exception.message)
  919. class TestConf(schema.Schema):
  920. item = schema.NumberItem(title='Foo',
  921. description='Foo Item',
  922. enum=(0, 2, 4, 6))
  923. try:
  924. jsonschema.validate({'item': 4}, TestConf.serialize())
  925. except jsonschema.exceptions.ValidationError as exc:
  926. self.fail('ValidationError raised: {0}'.format(exc))
  927. class TestConf(schema.Schema):
  928. item = schema.NumberItem(title='Foo',
  929. description='Foo Item',
  930. enum=(0, 2, 4, 6))
  931. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  932. jsonschema.validate({'item': 3}, TestConf.serialize())
  933. self.assertIn('is not one of', excinfo.exception.message)
  934. def test_integer_config(self):
  935. item = schema.IntegerItem(title='How many dogs', description='Question')
  936. self.assertDictEqual(
  937. item.serialize(), {
  938. 'type': 'integer',
  939. 'title': item.title,
  940. 'description': item.description
  941. }
  942. )
  943. item = schema.IntegerItem(title='How many dogs',
  944. description='Question',
  945. minimum=0,
  946. maximum=10)
  947. self.assertDictEqual(
  948. item.serialize(), {
  949. 'type': 'integer',
  950. 'title': item.title,
  951. 'description': item.description,
  952. 'minimum': item.minimum,
  953. 'maximum': item.maximum
  954. }
  955. )
  956. item = schema.IntegerItem(title='How many dogs',
  957. description='Question',
  958. multiple_of=2)
  959. self.assertDictEqual(
  960. item.serialize(), {
  961. 'type': 'integer',
  962. 'title': item.title,
  963. 'description': item.description,
  964. 'multipleOf': item.multiple_of
  965. }
  966. )
  967. item = schema.IntegerItem(title='How many dogs',
  968. description='Question',
  969. minimum=0,
  970. exclusive_minimum=True,
  971. maximum=10,
  972. exclusive_maximum=True)
  973. self.assertDictEqual(
  974. item.serialize(), {
  975. 'type': 'integer',
  976. 'title': item.title,
  977. 'description': item.description,
  978. 'minimum': item.minimum,
  979. 'maximum': item.maximum,
  980. 'exclusiveMinimum': True,
  981. 'exclusiveMaximum': True
  982. }
  983. )
  984. item = schema.IntegerItem(title='How many dogs',
  985. description='Question',
  986. minimum=0,
  987. maximum=10,
  988. default=0)
  989. self.assertDictEqual(
  990. item.serialize(), {
  991. 'type': 'integer',
  992. 'title': item.title,
  993. 'description': item.description,
  994. 'minimum': item.minimum,
  995. 'maximum': item.maximum,
  996. 'default': 0
  997. }
  998. )
  999. item = schema.IntegerItem(title='How many dogs',
  1000. description='Question',
  1001. minimum=0,
  1002. maximum=10,
  1003. default=0,
  1004. enum=(0, 2, 4, 6))
  1005. self.assertDictEqual(
  1006. item.serialize(), {
  1007. 'type': 'integer',
  1008. 'title': item.title,
  1009. 'description': item.description,
  1010. 'minimum': item.minimum,
  1011. 'maximum': item.maximum,
  1012. 'default': 0,
  1013. 'enum': [0, 2, 4, 6]
  1014. }
  1015. )
  1016. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1017. def test_integer_config_validation(self):
  1018. class TestConf(schema.Schema):
  1019. item = schema.IntegerItem(title='How many dogs', description='Question')
  1020. try:
  1021. jsonschema.validate({'item': 2}, TestConf.serialize())
  1022. except jsonschema.exceptions.ValidationError as exc:
  1023. self.fail('ValidationError raised: {0}'.format(exc))
  1024. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1025. jsonschema.validate({'item': 3.1}, TestConf.serialize())
  1026. self.assertIn('is not of type', excinfo.exception.message)
  1027. class TestConf(schema.Schema):
  1028. item = schema.IntegerItem(title='How many dogs',
  1029. description='Question',
  1030. multiple_of=2)
  1031. try:
  1032. jsonschema.validate({'item': 4}, TestConf.serialize())
  1033. except jsonschema.exceptions.ValidationError as exc:
  1034. self.fail('ValidationError raised: {0}'.format(exc))
  1035. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1036. jsonschema.validate({'item': 3}, TestConf.serialize())
  1037. self.assertIn('is not a multiple of', excinfo.exception.message)
  1038. class TestConf(schema.Schema):
  1039. item = schema.IntegerItem(title='Foo', description='Foo Item',
  1040. minimum=1, maximum=10)
  1041. try:
  1042. jsonschema.validate({'item': 3}, TestConf.serialize())
  1043. except jsonschema.exceptions.ValidationError as exc:
  1044. self.fail('ValidationError raised: {0}'.format(exc))
  1045. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1046. jsonschema.validate({'item': 11}, TestConf.serialize())
  1047. self.assertIn('is greater than the maximum of', excinfo.exception.message)
  1048. class TestConf(schema.Schema):
  1049. item = schema.IntegerItem(title='Foo', description='Foo Item',
  1050. minimum=10, maximum=100)
  1051. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1052. jsonschema.validate({'item': 3}, TestConf.serialize())
  1053. self.assertIn('is less than the minimum of', excinfo.exception.message)
  1054. class TestConf(schema.Schema):
  1055. item = schema.IntegerItem(title='How many dogs',
  1056. description='Question',
  1057. minimum=0,
  1058. exclusive_minimum=True,
  1059. maximum=10,
  1060. exclusive_maximum=True)
  1061. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1062. jsonschema.validate({'item': 0}, TestConf.serialize())
  1063. self.assertIn('is less than or equal to the minimum of', excinfo.exception.message)
  1064. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1065. jsonschema.validate({'item': 10}, TestConf.serialize())
  1066. self.assertIn('is greater than or equal to the maximum of', excinfo.exception.message)
  1067. class TestConf(schema.Schema):
  1068. item = schema.IntegerItem(title='Foo',
  1069. description='Foo Item',
  1070. enum=(0, 2, 4, 6))
  1071. try:
  1072. jsonschema.validate({'item': 4}, TestConf.serialize())
  1073. except jsonschema.exceptions.ValidationError as exc:
  1074. self.fail('ValidationError raised: {0}'.format(exc))
  1075. class TestConf(schema.Schema):
  1076. item = schema.IntegerItem(title='Foo',
  1077. description='Foo Item',
  1078. enum=(0, 2, 4, 6))
  1079. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1080. jsonschema.validate({'item': 3}, TestConf.serialize())
  1081. self.assertIn('is not one of', excinfo.exception.message)
  1082. def test_array_config(self):
  1083. string_item = schema.StringItem(title='Dog Name',
  1084. description='The dog name')
  1085. item = schema.ArrayItem(title='Dog Names',
  1086. description='Name your dogs',
  1087. items=string_item)
  1088. self.assertDictEqual(
  1089. item.serialize(), {
  1090. 'type': 'array',
  1091. 'title': item.title,
  1092. 'description': item.description,
  1093. 'items': {
  1094. 'type': 'string',
  1095. 'title': string_item.title,
  1096. 'description': string_item.description
  1097. }
  1098. }
  1099. )
  1100. integer_item = schema.IntegerItem(title='Dog Age',
  1101. description='The dog age')
  1102. item = schema.ArrayItem(title='Dog Names',
  1103. description='Name your dogs',
  1104. items=(string_item, integer_item))
  1105. self.assertDictEqual(
  1106. item.serialize(), {
  1107. 'type': 'array',
  1108. 'title': item.title,
  1109. 'description': item.description,
  1110. 'items': [
  1111. {
  1112. 'type': 'string',
  1113. 'title': string_item.title,
  1114. 'description': string_item.description
  1115. },
  1116. {
  1117. 'type': 'integer',
  1118. 'title': integer_item.title,
  1119. 'description': integer_item.description
  1120. }
  1121. ]
  1122. }
  1123. )
  1124. item = schema.ArrayItem(title='Dog Names',
  1125. description='Name your dogs',
  1126. items=(schema.StringItem(),
  1127. schema.IntegerItem()),
  1128. min_items=1,
  1129. max_items=3,
  1130. additional_items=False,
  1131. unique_items=True)
  1132. self.assertDictEqual(
  1133. item.serialize(), {
  1134. 'type': 'array',
  1135. 'title': item.title,
  1136. 'description': item.description,
  1137. 'minItems': item.min_items,
  1138. 'maxItems': item.max_items,
  1139. 'uniqueItems': item.unique_items,
  1140. 'additionalItems': item.additional_items,
  1141. 'items': [
  1142. {
  1143. 'type': 'string',
  1144. },
  1145. {
  1146. 'type': 'integer',
  1147. }
  1148. ]
  1149. }
  1150. )
  1151. class HowManyConfig(schema.Schema):
  1152. item = schema.IntegerItem(title='How many dogs', description='Question')
  1153. item = schema.ArrayItem(title='Dog Names',
  1154. description='Name your dogs',
  1155. items=HowManyConfig())
  1156. self.assertDictEqual(
  1157. item.serialize(), {
  1158. 'type': 'array',
  1159. 'title': item.title,
  1160. 'description': item.description,
  1161. 'items': HowManyConfig.serialize()
  1162. }
  1163. )
  1164. class AgesConfig(schema.Schema):
  1165. item = schema.IntegerItem()
  1166. item = schema.ArrayItem(title='Dog Names',
  1167. description='Name your dogs',
  1168. items=(HowManyConfig(), AgesConfig()))
  1169. self.assertDictEqual(
  1170. item.serialize(), {
  1171. 'type': 'array',
  1172. 'title': item.title,
  1173. 'description': item.description,
  1174. 'items': [
  1175. HowManyConfig.serialize(),
  1176. AgesConfig.serialize()
  1177. ]
  1178. }
  1179. )
  1180. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1181. def test_array_config_validation(self):
  1182. class TestConf(schema.Schema):
  1183. item = schema.ArrayItem(title='Dog Names',
  1184. description='Name your dogs',
  1185. items=schema.StringItem())
  1186. try:
  1187. jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(),
  1188. format_checker=jsonschema.FormatChecker())
  1189. except jsonschema.exceptions.ValidationError as exc:
  1190. self.fail('ValidationError raised: {0}'.format(exc))
  1191. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1192. jsonschema.validate({'item': ['Tobias', 'Óscar', 3]}, TestConf.serialize(),
  1193. format_checker=jsonschema.FormatChecker())
  1194. self.assertIn('is not of type', excinfo.exception.message)
  1195. class TestConf(schema.Schema):
  1196. item = schema.ArrayItem(title='Dog Names',
  1197. description='Name your dogs',
  1198. items=schema.StringItem(),
  1199. min_items=1,
  1200. max_items=2)
  1201. try:
  1202. jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(),
  1203. format_checker=jsonschema.FormatChecker())
  1204. except jsonschema.exceptions.ValidationError as exc:
  1205. self.fail('ValidationError raised: {0}'.format(exc))
  1206. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1207. jsonschema.validate({'item': ['Tobias', 'Óscar', 'Pepe']}, TestConf.serialize(),
  1208. format_checker=jsonschema.FormatChecker())
  1209. self.assertIn('is too long', excinfo.exception.message)
  1210. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1211. jsonschema.validate({'item': []}, TestConf.serialize(),
  1212. format_checker=jsonschema.FormatChecker())
  1213. self.assertIn('is too short', excinfo.exception.message)
  1214. class TestConf(schema.Schema):
  1215. item = schema.ArrayItem(title='Dog Names',
  1216. description='Name your dogs',
  1217. items=schema.StringItem(),
  1218. uniqueItems=True)
  1219. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1220. jsonschema.validate({'item': ['Tobias', 'Tobias']}, TestConf.serialize(),
  1221. format_checker=jsonschema.FormatChecker())
  1222. self.assertIn('has non-unique elements', excinfo.exception.message)
  1223. class TestConf(schema.Schema):
  1224. item = schema.ArrayItem(items=(schema.StringItem(),
  1225. schema.IntegerItem()))
  1226. try:
  1227. jsonschema.validate({'item': ['Óscar', 4]}, TestConf.serialize(),
  1228. format_checker=jsonschema.FormatChecker())
  1229. except jsonschema.exceptions.ValidationError as exc:
  1230. self.fail('ValidationError raised: {0}'.format(exc))
  1231. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1232. jsonschema.validate({'item': ['Tobias', 'Óscar']}, TestConf.serialize(),
  1233. format_checker=jsonschema.FormatChecker())
  1234. self.assertIn('is not of type', excinfo.exception.message)
  1235. class TestConf(schema.Schema):
  1236. item = schema.ArrayItem(
  1237. items=schema.ArrayItem(
  1238. items=(schema.StringItem(),
  1239. schema.IntegerItem())
  1240. )
  1241. )
  1242. try:
  1243. jsonschema.validate({'item': [['Tobias', 8], ['Óscar', 4]]}, TestConf.serialize(),
  1244. format_checker=jsonschema.FormatChecker())
  1245. except jsonschema.exceptions.ValidationError as exc:
  1246. self.fail('ValidationError raised: {0}'.format(exc))
  1247. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1248. jsonschema.validate({'item': [['Tobias', 8], ['Óscar', '4']]}, TestConf.serialize(),
  1249. format_checker=jsonschema.FormatChecker())
  1250. self.assertIn('is not of type', excinfo.exception.message)
  1251. class TestConf(schema.Schema):
  1252. item = schema.ArrayItem(items=schema.StringItem(enum=['Tobias', 'Óscar']))
  1253. try:
  1254. jsonschema.validate({'item': ['Óscar']}, TestConf.serialize(),
  1255. format_checker=jsonschema.FormatChecker())
  1256. except jsonschema.exceptions.ValidationError as exc:
  1257. self.fail('ValidationError raised: {0}'.format(exc))
  1258. try:
  1259. jsonschema.validate({'item': ['Tobias']}, TestConf.serialize(),
  1260. format_checker=jsonschema.FormatChecker())
  1261. except jsonschema.exceptions.ValidationError as exc:
  1262. self.fail('ValidationError raised: {0}'.format(exc))
  1263. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1264. jsonschema.validate({'item': ['Pepe']}, TestConf.serialize(),
  1265. format_checker=jsonschema.FormatChecker())
  1266. self.assertIn('is not one of', excinfo.exception.message)
  1267. def test_dict_config(self):
  1268. item = schema.DictItem(
  1269. title='Poligon',
  1270. description='Describe the Poligon',
  1271. properties={
  1272. 'sides': schema.IntegerItem()
  1273. }
  1274. )
  1275. self.assertDictEqual(
  1276. item.serialize(), {
  1277. 'type': 'object',
  1278. 'title': item.title,
  1279. 'description': item.description,
  1280. 'properties': {
  1281. 'sides': {'type': 'integer'}
  1282. }
  1283. }
  1284. )
  1285. item = schema.DictItem(
  1286. title='Poligon',
  1287. description='Describe the Poligon',
  1288. properties={
  1289. 'sides': schema.IntegerItem()
  1290. },
  1291. min_properties=1,
  1292. max_properties=2
  1293. )
  1294. self.assertDictEqual(
  1295. item.serialize(), {
  1296. 'type': 'object',
  1297. 'title': item.title,
  1298. 'description': item.description,
  1299. 'properties': {
  1300. 'sides': {'type': 'integer'}
  1301. },
  1302. 'minProperties': 1,
  1303. 'maxProperties': 2
  1304. }
  1305. )
  1306. item = schema.DictItem(
  1307. title='Poligon',
  1308. description='Describe the Poligon',
  1309. pattern_properties={
  1310. 's.*': schema.IntegerItem()
  1311. },
  1312. min_properties=1,
  1313. max_properties=2
  1314. )
  1315. self.assertDictEqual(
  1316. item.serialize(), {
  1317. 'type': 'object',
  1318. 'title': item.title,
  1319. 'description': item.description,
  1320. 'patternProperties': {
  1321. 's.*': {'type': 'integer'}
  1322. },
  1323. 'minProperties': 1,
  1324. 'maxProperties': 2
  1325. }
  1326. )
  1327. item = schema.DictItem(
  1328. title='Poligon',
  1329. description='Describe the Poligon',
  1330. properties={
  1331. 'color': schema.StringItem(enum=['red', 'green', 'blue'])
  1332. },
  1333. pattern_properties={
  1334. 's*': schema.IntegerItem()
  1335. },
  1336. min_properties=1,
  1337. max_properties=2
  1338. )
  1339. self.assertDictEqual(
  1340. item.serialize(), {
  1341. 'type': 'object',
  1342. 'title': item.title,
  1343. 'description': item.description,
  1344. 'properties': {
  1345. 'color': {
  1346. 'type': 'string',
  1347. 'enum': ['red', 'green', 'blue']
  1348. }
  1349. },
  1350. 'patternProperties': {
  1351. 's*': {'type': 'integer'}
  1352. },
  1353. 'minProperties': 1,
  1354. 'maxProperties': 2
  1355. }
  1356. )
  1357. item = schema.DictItem(
  1358. title='Poligon',
  1359. description='Describe the Poligon',
  1360. properties={
  1361. 'color': schema.StringItem(enum=['red', 'green', 'blue'])
  1362. },
  1363. pattern_properties={
  1364. 's*': schema.IntegerItem()
  1365. },
  1366. additional_properties=True,
  1367. min_properties=1,
  1368. max_properties=2
  1369. )
  1370. self.assertDictEqual(
  1371. item.serialize(), {
  1372. 'type': 'object',
  1373. 'title': item.title,
  1374. 'description': item.description,
  1375. 'properties': {
  1376. 'color': {
  1377. 'type': 'string',
  1378. 'enum': ['red', 'green', 'blue']
  1379. }
  1380. },
  1381. 'patternProperties': {
  1382. 's*': {'type': 'integer'}
  1383. },
  1384. 'minProperties': 1,
  1385. 'maxProperties': 2,
  1386. 'additionalProperties': True
  1387. }
  1388. )
  1389. item = schema.DictItem(
  1390. title='Poligon',
  1391. description='Describe the Poligon',
  1392. properties={
  1393. 'sides': schema.IntegerItem()
  1394. },
  1395. additional_properties=schema.OneOfItem(items=[schema.BooleanItem(),
  1396. schema.StringItem()])
  1397. )
  1398. self.assertDictEqual(
  1399. item.serialize(), {
  1400. 'type': 'object',
  1401. 'title': item.title,
  1402. 'description': item.description,
  1403. 'properties': {
  1404. 'sides': {'type': 'integer'}
  1405. },
  1406. 'additionalProperties': {
  1407. 'oneOf': [
  1408. {'type': 'boolean'},
  1409. {'type': 'string'}
  1410. ]
  1411. }
  1412. }
  1413. )
  1414. class TestConf(schema.Schema):
  1415. item = schema.DictItem(
  1416. title='Poligon',
  1417. description='Describe the Poligon',
  1418. properties={
  1419. 'sides': schema.IntegerItem(required=True)
  1420. },
  1421. additional_properties=schema.OneOfItem(items=[schema.BooleanItem(),
  1422. schema.StringItem()])
  1423. )
  1424. self.assertDictContainsSubset(
  1425. TestConf.serialize(), {
  1426. '$schema': 'http://json-schema.org/draft-04/schema#',
  1427. 'type': 'object',
  1428. 'properties': {
  1429. 'item': {
  1430. 'title': 'Poligon',
  1431. 'description': 'Describe the Poligon',
  1432. 'type': 'object',
  1433. 'properties': {
  1434. 'sides': {
  1435. 'type': 'integer'
  1436. }
  1437. },
  1438. 'additionalProperties': {
  1439. 'oneOf': [
  1440. {
  1441. 'type': 'boolean'
  1442. },
  1443. {
  1444. 'type': 'string'
  1445. }
  1446. ]
  1447. },
  1448. 'required': [
  1449. 'sides'
  1450. ],
  1451. }
  1452. },
  1453. 'x-ordering': [
  1454. 'item'
  1455. ],
  1456. 'additionalProperties': False
  1457. }
  1458. )
  1459. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1460. def test_dict_config_validation(self):
  1461. class TestConf(schema.Schema):
  1462. item = schema.DictItem(
  1463. title='Poligon',
  1464. description='Describe the Poligon',
  1465. properties={
  1466. 'sides': schema.IntegerItem()
  1467. }
  1468. )
  1469. try:
  1470. jsonschema.validate({'item': {'sides': 1}}, TestConf.serialize())
  1471. except jsonschema.exceptions.ValidationError as exc:
  1472. self.fail('ValidationError raised: {0}'.format(exc))
  1473. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1474. jsonschema.validate({'item': {'sides': '1'}}, TestConf.serialize())
  1475. self.assertIn('is not of type', excinfo.exception.message)
  1476. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1477. jsonschema.validate({'item': 2}, TestConf.serialize())
  1478. self.assertIn('is not of type', excinfo.exception.message)
  1479. class TestConf(schema.Schema):
  1480. item = schema.DictItem(
  1481. title='Poligon',
  1482. description='Describe the Poligon',
  1483. properties={
  1484. 'color': schema.StringItem(enum=['red', 'green', 'blue'])
  1485. },
  1486. pattern_properties={
  1487. 'si.*': schema.IntegerItem()
  1488. },
  1489. )
  1490. try:
  1491. jsonschema.validate({'item': {'sides': 1, 'color': 'red'}}, TestConf.serialize())
  1492. except jsonschema.exceptions.ValidationError as exc:
  1493. self.fail('ValidationError raised: {0}'.format(exc))
  1494. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1495. jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize())
  1496. self.assertIn('is not of type', excinfo.exception.message)
  1497. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1498. jsonschema.validate({'item': 2}, TestConf.serialize())
  1499. self.assertIn('is not of type', excinfo.exception.message)
  1500. class TestConf(schema.Schema):
  1501. item = schema.DictItem(
  1502. title='Poligon',
  1503. description='Describe the Poligon',
  1504. properties={
  1505. 'color': schema.StringItem(enum=['red', 'green', 'blue'])
  1506. },
  1507. pattern_properties={
  1508. 'si.*': schema.IntegerItem()
  1509. },
  1510. additional_properties=False
  1511. )
  1512. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1513. jsonschema.validate({'item': {'color': 'green', 'sides': 4, 'surfaces': 4}}, TestConf.serialize())
  1514. if JSONSCHEMA_VERSION < _LooseVersion('2.6.0'):
  1515. self.assertIn(
  1516. 'Additional properties are not allowed',
  1517. excinfo.exception.message)
  1518. else:
  1519. self.assertIn(
  1520. '\'surfaces\' does not match any of the regexes',
  1521. excinfo.exception.message)
  1522. class TestConf(schema.Schema):
  1523. item = schema.DictItem(
  1524. title='Poligon',
  1525. description='Describe the Poligon',
  1526. properties={
  1527. 'color': schema.StringItem(enum=['red', 'green', 'blue'])
  1528. },
  1529. additional_properties=schema.OneOfItem(items=[
  1530. schema.BooleanItem(),
  1531. schema.IntegerItem()
  1532. ])
  1533. )
  1534. try:
  1535. jsonschema.validate({'item': {'sides': 1,
  1536. 'color': 'red',
  1537. 'rugged_surface': False}}, TestConf.serialize())
  1538. except jsonschema.exceptions.ValidationError as exc:
  1539. self.fail('ValidationError raised: {0}'.format(exc))
  1540. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1541. jsonschema.validate({'item': {'sides': '4', 'color': 'blue'}}, TestConf.serialize())
  1542. if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'):
  1543. self.assertIn('\'4\' is not of type \'boolean\'', excinfo.exception.message)
  1544. else:
  1545. self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
  1546. class TestConf(schema.Schema):
  1547. item = schema.DictItem(
  1548. title='Poligon',
  1549. description='Describe the Poligon',
  1550. properties={
  1551. 'color': schema.StringItem(enum=['red', 'green', 'blue'])
  1552. },
  1553. additional_properties=schema.OneOfItem(items=[
  1554. schema.BooleanItem(),
  1555. schema.IntegerItem()
  1556. ]),
  1557. min_properties=2,
  1558. max_properties=3
  1559. )
  1560. try:
  1561. jsonschema.validate({'item': {'color': 'red', 'sides': 1}}, TestConf.serialize())
  1562. except jsonschema.exceptions.ValidationError as exc:
  1563. self.fail('ValidationError raised: {0}'.format(exc))
  1564. try:
  1565. jsonschema.validate({'item': {'sides': 1, 'color': 'red', 'rugged_surface': False}}, TestConf.serialize())
  1566. except jsonschema.exceptions.ValidationError as exc:
  1567. self.fail('ValidationError raised: {0}'.format(exc))
  1568. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1569. jsonschema.validate({'item': {'color': 'blue'}}, TestConf.serialize())
  1570. self.assertIn('does not have enough properties', excinfo.exception.message)
  1571. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1572. jsonschema.validate({'item': {'sides': 4,
  1573. 'color': 'blue',
  1574. 'rugged_surface': False,
  1575. 'opaque': True}}, TestConf.serialize())
  1576. self.assertIn('has too many properties', excinfo.exception.message)
  1577. class TestConf(schema.Schema):
  1578. item = schema.DictItem(
  1579. title='Poligon',
  1580. description='Describe the Poligon',
  1581. properties={
  1582. 'sides': schema.IntegerItem(required=True)
  1583. },
  1584. additional_properties=schema.OneOfItem(items=[schema.BooleanItem(),
  1585. schema.StringItem()])
  1586. )
  1587. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1588. jsonschema.validate({'item': {'color': 'blue',
  1589. 'rugged_surface': False,
  1590. 'opaque': True}}, TestConf.serialize())
  1591. self.assertIn('\'sides\' is a required property', excinfo.exception.message)
  1592. class Props(schema.Schema):
  1593. sides = schema.IntegerItem(required=True)
  1594. class TestConf(schema.Schema):
  1595. item = schema.DictItem(
  1596. title='Poligon',
  1597. description='Describe the Poligon',
  1598. properties=Props(),
  1599. additional_properties=schema.OneOfItem(items=[schema.BooleanItem(),
  1600. schema.StringItem()])
  1601. )
  1602. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1603. jsonschema.validate({'item': {'color': 'blue',
  1604. 'rugged_surface': False,
  1605. 'opaque': True}}, TestConf.serialize())
  1606. self.assertIn('\'sides\' is a required property', excinfo.exception.message)
  1607. def test_oneof_config(self):
  1608. item = schema.OneOfItem(
  1609. items=(schema.StringItem(title='Yes', enum=['yes']),
  1610. schema.StringItem(title='No', enum=['no']))
  1611. )
  1612. self.assertEqual(
  1613. item.serialize(), {
  1614. 'oneOf': [i.serialize() for i in item.items]
  1615. }
  1616. )
  1617. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1618. def test_oneof_config_validation(self):
  1619. class TestConf(schema.Schema):
  1620. item = schema.ArrayItem(
  1621. title='Hungry',
  1622. description='Are you hungry?',
  1623. items=schema.OneOfItem(
  1624. items=(schema.StringItem(title='Yes', enum=['yes']),
  1625. schema.StringItem(title='No', enum=['no']))
  1626. )
  1627. )
  1628. try:
  1629. jsonschema.validate({'item': ['no']}, TestConf.serialize())
  1630. except jsonschema.exceptions.ValidationError as exc:
  1631. self.fail('ValidationError raised: {0}'.format(exc))
  1632. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1633. jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
  1634. if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'):
  1635. self.assertIn('\'maybe\' is not one of [\'yes\']', excinfo.exception.message)
  1636. else:
  1637. self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
  1638. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1639. jsonschema.validate({'item': 2}, TestConf.serialize())
  1640. self.assertIn('is not of type', excinfo.exception.message)
  1641. def test_anyof_config(self):
  1642. item = schema.AnyOfItem(
  1643. items=(schema.StringItem(title='Yes', enum=['yes']),
  1644. schema.StringItem(title='No', enum=['no']))
  1645. )
  1646. self.assertEqual(
  1647. item.serialize(), {
  1648. 'anyOf': [i.serialize() for i in item.items] # pylint: disable=E1133
  1649. }
  1650. )
  1651. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1652. def test_anyof_config_validation(self):
  1653. class TestConf(schema.Schema):
  1654. item = schema.ArrayItem(
  1655. title='Hungry',
  1656. description='Are you hungry?',
  1657. items=schema.AnyOfItem(
  1658. items=(schema.StringItem(title='Yes', enum=['yes']),
  1659. schema.StringItem(title='No', enum=['no']),
  1660. schema.BooleanItem())
  1661. )
  1662. )
  1663. try:
  1664. jsonschema.validate({'item': ['no']}, TestConf.serialize())
  1665. except jsonschema.exceptions.ValidationError as exc:
  1666. self.fail('ValidationError raised: {0}'.format(exc))
  1667. try:
  1668. jsonschema.validate({'item': ['yes']}, TestConf.serialize())
  1669. except jsonschema.exceptions.ValidationError as exc:
  1670. self.fail('ValidationError raised: {0}'.format(exc))
  1671. try:
  1672. jsonschema.validate({'item': [True]}, TestConf.serialize())
  1673. except jsonschema.exceptions.ValidationError as exc:
  1674. self.fail('ValidationError raised: {0}'.format(exc))
  1675. try:
  1676. jsonschema.validate({'item': [False]}, TestConf.serialize())
  1677. except jsonschema.exceptions.ValidationError as exc:
  1678. self.fail('ValidationError raised: {0}'.format(exc))
  1679. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1680. jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
  1681. if JSONSCHEMA_VERSION >= _LooseVersion('3.0.0'):
  1682. self.assertIn('\'maybe\' is not one of [\'yes\']', excinfo.exception.message)
  1683. else:
  1684. self.assertIn('is not valid under any of the given schemas', excinfo.exception.message)
  1685. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1686. jsonschema.validate({'item': 2}, TestConf.serialize())
  1687. self.assertIn('is not of type', excinfo.exception.message)
  1688. def test_allof_config(self):
  1689. item = schema.AllOfItem(
  1690. items=(schema.StringItem(min_length=2),
  1691. schema.StringItem(max_length=3))
  1692. )
  1693. self.assertEqual(
  1694. item.serialize(), {
  1695. 'allOf': [i.serialize() for i in item.items] # pylint: disable=E1133
  1696. }
  1697. )
  1698. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1699. def test_allof_config_validation(self):
  1700. class TestConf(schema.Schema):
  1701. item = schema.ArrayItem(
  1702. title='Hungry',
  1703. description='Are you hungry?',
  1704. items=schema.AllOfItem(
  1705. items=(schema.StringItem(min_length=2),
  1706. schema.StringItem(max_length=3))
  1707. )
  1708. )
  1709. try:
  1710. jsonschema.validate({'item': ['no']}, TestConf.serialize())
  1711. except jsonschema.exceptions.ValidationError as exc:
  1712. self.fail('ValidationError raised: {0}'.format(exc))
  1713. try:
  1714. jsonschema.validate({'item': ['yes']}, TestConf.serialize())
  1715. except jsonschema.exceptions.ValidationError as exc:
  1716. self.fail('ValidationError raised: {0}'.format(exc))
  1717. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1718. jsonschema.validate({'item': ['maybe']}, TestConf.serialize())
  1719. self.assertIn('is too long', excinfo.exception.message)
  1720. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1721. jsonschema.validate({'item': ['hmmmm']}, TestConf.serialize())
  1722. self.assertIn('is too long', excinfo.exception.message)
  1723. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1724. jsonschema.validate({'item': 2}, TestConf.serialize())
  1725. self.assertIn('is not of type', excinfo.exception.message)
  1726. def test_not_config(self):
  1727. item = schema.NotItem(item=schema.BooleanItem())
  1728. self.assertEqual(
  1729. item.serialize(), {
  1730. 'not': item.item.serialize()
  1731. }
  1732. )
  1733. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  1734. def test_not_config_validation(self):
  1735. class TestConf(schema.Schema):
  1736. item = schema.ArrayItem(
  1737. title='Hungry',
  1738. description='Are you hungry?',
  1739. items=schema.NotItem(item=schema.BooleanItem())
  1740. )
  1741. try:
  1742. jsonschema.validate({'item': ['no']}, TestConf.serialize())
  1743. except jsonschema.exceptions.ValidationError as exc:
  1744. self.fail('ValidationError raised: {0}'.format(exc))
  1745. try:
  1746. jsonschema.validate({'item': ['yes']}, TestConf.serialize())
  1747. except jsonschema.exceptions.ValidationError as exc:
  1748. self.fail('ValidationError raised: {0}'.format(exc))
  1749. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1750. jsonschema.validate({'item': [True]}, TestConf.serialize())
  1751. self.assertIn('is not allowed for', excinfo.exception.message)
  1752. with self.assertRaises(jsonschema.exceptions.ValidationError) as excinfo:
  1753. jsonschema.validate({'item': [False]}, TestConf.serialize())
  1754. self.assertIn('is not allowed for', excinfo.exception.message)
  1755. def test_item_name_override_class_attrname(self):
  1756. class TestConf(schema.Schema):
  1757. item = schema.BooleanItem(name='hungry', title='Hungry', description='Are you hungry?')
  1758. expected = {
  1759. "$schema": "http://json-schema.org/draft-04/schema#",
  1760. "type": "object",
  1761. "properties": {
  1762. "hungry": {
  1763. "type": "boolean",
  1764. "description": "Are you hungry?",
  1765. "title": "Hungry"
  1766. }
  1767. },
  1768. "x-ordering": [
  1769. "hungry"
  1770. ],
  1771. "additionalProperties": False
  1772. }
  1773. self.assertDictEqual(TestConf.serialize(), expected)
  1774. def test_config_name_override_class_attrname(self):
  1775. class TestConf(schema.Schema):
  1776. item = schema.BooleanItem(title='Hungry', description='Are you hungry?')
  1777. class TestConf2(schema.Schema):
  1778. a_name = TestConf(name='another_name')
  1779. expected = {
  1780. "$schema": "http://json-schema.org/draft-04/schema#",
  1781. "type": "object",
  1782. "properties": {
  1783. "another_name": {
  1784. "id": "https://non-existing.saltstack.com/schemas/another_name.json#",
  1785. "type": "object",
  1786. "properties": {
  1787. "item": {
  1788. "type": "boolean",
  1789. "description": "Are you hungry?",
  1790. "title": "Hungry"
  1791. }
  1792. },
  1793. "x-ordering": [
  1794. "item"
  1795. ],
  1796. "additionalProperties": False
  1797. }
  1798. },
  1799. "x-ordering": [
  1800. "another_name"
  1801. ],
  1802. "additionalProperties": False
  1803. }
  1804. self.assertDictEqual(TestConf2.serialize(), expected)
  1805. class ComplexSchemaItem(schema.ComplexSchemaItem):
  1806. _complex_attributes = ['thirsty']
  1807. thirsty = schema.BooleanItem(title='Thirsty',
  1808. description='Are you thirsty?')
  1809. class ComplexComplexSchemaItem(schema.ComplexSchemaItem):
  1810. _complex_attributes = ['hungry', 'complex_item']
  1811. hungry = schema.BooleanItem(title='Hungry',
  1812. description='Are you hungry?',
  1813. required=True)
  1814. complex_item = ComplexSchemaItem(definition_name='test_definition')
  1815. class TestComplexDefinitionsSchema(schema.DefinitionsSchema):
  1816. title = 'Test Complex Definition Schema'
  1817. complex_item = ComplexSchemaItem()
  1818. class TestOneOfComplexDefinitionsSchema(schema.DefinitionsSchema):
  1819. title = 'Test OneOf Complex Definitions Schema'
  1820. one_of_item = schema.OneOfItem(
  1821. items=[ComplexSchemaItem(), schema.StringItem()])
  1822. class TestArrayComplexDefinitionsSchema(schema.DefinitionsSchema):
  1823. title = 'Test Array Complex Definitions Schema'
  1824. array_item = schema.ArrayItem(items=ComplexSchemaItem())
  1825. class TestDictComplexDefinitionsSchema(schema.DefinitionsSchema):
  1826. title = 'Test Dict Complex Definitions Schema'
  1827. dict_item = schema.DictItem(
  1828. properties={'complex_obj': ComplexSchemaItem(required=True)},
  1829. additional_properties=ComplexSchemaItem())
  1830. class TestComplexComplexDefinitionsSchema(schema.DefinitionsSchema):
  1831. title = 'Test Complex Complex Definition Schema'
  1832. complex_complex_item = ComplexComplexSchemaItem()
  1833. class ComplexSchemaTestCase(TestCase):
  1834. ''' Test cases with definition schemas containing complex items'''
  1835. obj = ComplexSchemaItem()
  1836. complex_obj = ComplexComplexSchemaItem()
  1837. schema = TestComplexDefinitionsSchema()
  1838. one_of_schema = TestOneOfComplexDefinitionsSchema()
  1839. array_schema = TestArrayComplexDefinitionsSchema()
  1840. dict_schema = TestDictComplexDefinitionsSchema()
  1841. complex_schema = TestComplexComplexDefinitionsSchema()
  1842. def test_complex_schema_item_serialize(self):
  1843. obj = copy.deepcopy(self.obj)
  1844. expected_serialized = {'$ref':
  1845. '#/definitions/ComplexSchemaItem'}
  1846. self.assertDictEqual(obj.serialize(), expected_serialized)
  1847. def test_complex_schema_item_definition(self):
  1848. obj = copy.deepcopy(self.obj)
  1849. expected_def = {
  1850. 'type': 'object',
  1851. 'title': 'ComplexSchemaItem',
  1852. 'properties': {
  1853. 'thirsty': {
  1854. 'type': 'boolean',
  1855. 'title': 'Thirsty',
  1856. 'description': 'Are you thirsty?'}}}
  1857. self.assertDictEqual(obj.get_definition(), expected_def)
  1858. def test_complex_complex_schema_item_definition(self):
  1859. complex_obj = copy.deepcopy(self.complex_obj)
  1860. expected_def = {
  1861. 'type': 'object',
  1862. 'title': 'ComplexComplexSchemaItem',
  1863. 'properties': {
  1864. 'hungry': {
  1865. 'type': 'boolean',
  1866. 'title': 'Hungry',
  1867. 'description': 'Are you hungry?'},
  1868. 'complex_item': {
  1869. 'type': 'object',
  1870. '$ref': '#/definitions/test_definition'}},
  1871. 'required': ['hungry']}
  1872. self.assertDictEqual(complex_obj.get_definition(), expected_def)
  1873. def test_complex_definition_schema(self):
  1874. serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.schema.serialize()))
  1875. expected = {
  1876. '$schema': 'http://json-schema.org/draft-04/schema#',
  1877. 'title': 'Test Complex Definition Schema',
  1878. 'type': 'object',
  1879. 'properties': {
  1880. 'complex_item': {
  1881. '$ref': '#/definitions/ComplexSchemaItem'}},
  1882. 'x-ordering': ['complex_item'],
  1883. 'additionalProperties': False,
  1884. 'definitions': {
  1885. 'ComplexSchemaItem': {
  1886. 'type': 'object',
  1887. 'title': 'ComplexSchemaItem',
  1888. 'properties': {
  1889. 'thirsty': {
  1890. 'type': 'boolean',
  1891. 'title': 'Thirsty',
  1892. 'description': 'Are you thirsty?'}}}}}
  1893. self.assertDictEqual(serialized, expected)
  1894. def test_one_of_complex_definition_schema(self):
  1895. serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.one_of_schema.serialize()))
  1896. expected = {
  1897. '$schema': 'http://json-schema.org/draft-04/schema#',
  1898. 'title': 'Test OneOf Complex Definitions Schema',
  1899. 'type': 'object',
  1900. 'properties': {
  1901. 'one_of_item': {
  1902. 'oneOf': [{'$ref': '#/definitions/ComplexSchemaItem'},
  1903. {'type': 'string'}]}},
  1904. 'x-ordering': ['one_of_item'],
  1905. 'additionalProperties': False,
  1906. 'definitions': {
  1907. 'ComplexSchemaItem': {
  1908. 'type': 'object',
  1909. 'title': 'ComplexSchemaItem',
  1910. 'properties': {
  1911. 'thirsty': {
  1912. 'type': 'boolean',
  1913. 'title': 'Thirsty',
  1914. 'description': 'Are you thirsty?'}}}}}
  1915. self.assertDictEqual(serialized, expected)
  1916. def test_array_complex_definition_schema(self):
  1917. serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.array_schema.serialize()))
  1918. expected = {
  1919. '$schema': 'http://json-schema.org/draft-04/schema#',
  1920. 'title': 'Test Array Complex Definitions Schema',
  1921. 'type': 'object',
  1922. 'properties': {
  1923. 'array_item': {
  1924. 'type': 'array',
  1925. 'title': 'array_item',
  1926. 'items': {'$ref': '#/definitions/ComplexSchemaItem'}}},
  1927. 'x-ordering': ['array_item'],
  1928. 'additionalProperties': False,
  1929. 'definitions': {
  1930. 'ComplexSchemaItem': {
  1931. 'type': 'object',
  1932. 'title': 'ComplexSchemaItem',
  1933. 'properties': {
  1934. 'thirsty': {
  1935. 'type': 'boolean',
  1936. 'title': 'Thirsty',
  1937. 'description': 'Are you thirsty?'}}}}}
  1938. self.assertDictEqual(serialized, expected)
  1939. def test_dict_complex_definition_schema(self):
  1940. serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(self.dict_schema.serialize()))
  1941. expected = {
  1942. '$schema': 'http://json-schema.org/draft-04/schema#',
  1943. 'title': 'Test Dict Complex Definitions Schema',
  1944. 'type': 'object',
  1945. 'properties': {
  1946. 'dict_item': {
  1947. 'type': 'object',
  1948. 'title': 'dict_item',
  1949. 'required': ['complex_obj'],
  1950. 'properties':
  1951. {'complex_obj':
  1952. {'$ref': '#/definitions/ComplexSchemaItem'}},
  1953. 'additionalProperties':
  1954. {'$ref': '#/definitions/ComplexSchemaItem'}}},
  1955. 'x-ordering': ['dict_item'],
  1956. 'additionalProperties': False,
  1957. 'definitions': {
  1958. 'ComplexSchemaItem': {
  1959. 'type': 'object',
  1960. 'title': 'ComplexSchemaItem',
  1961. 'properties': {
  1962. 'thirsty': {
  1963. 'type': 'boolean',
  1964. 'title': 'Thirsty',
  1965. 'description': 'Are you thirsty?'}}}}}
  1966. self.assertDictEqual(serialized, expected)
  1967. def test_complex_complex_definition_schema(self):
  1968. serialized = salt.utils.yaml.safe_load(salt.utils.json.dumps(
  1969. self.complex_schema.serialize()
  1970. ))
  1971. expected = {
  1972. '$schema': 'http://json-schema.org/draft-04/schema#',
  1973. 'title': 'Test Complex Complex Definition Schema',
  1974. 'type': 'object',
  1975. 'properties': {
  1976. 'complex_complex_item': {
  1977. '$ref': '#/definitions/ComplexComplexSchemaItem'}},
  1978. 'x-ordering': ['complex_complex_item'],
  1979. 'additionalProperties': False,
  1980. 'definitions': {
  1981. 'ComplexComplexSchemaItem': {
  1982. 'type': 'object',
  1983. 'title': 'ComplexComplexSchemaItem',
  1984. 'properties': {
  1985. 'hungry': {
  1986. 'type': 'boolean',
  1987. 'title': 'Hungry',
  1988. 'description': 'Are you hungry?'},
  1989. 'complex_item': {
  1990. 'type': 'object',
  1991. '$ref': '#/definitions/test_definition'}},
  1992. 'required': ['hungry']},
  1993. 'test_definition': {
  1994. 'type': 'object',
  1995. 'title': 'test_definition',
  1996. 'properties': {
  1997. 'thirsty': {
  1998. 'type': 'boolean',
  1999. 'title': 'Thirsty',
  2000. 'description': 'Are you thirsty?'}}}}}
  2001. self.assertDictEqual(serialized, expected)
  2002. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2003. def test_complex_schema_item_thirsty_valid(self):
  2004. serialized = self.schema.serialize()
  2005. try:
  2006. jsonschema.validate({'complex_item': {'thirsty': True}},
  2007. serialized)
  2008. except jsonschema.exceptions.ValidationError as exc:
  2009. self.fail('ValidationError raised: {0}'.format(exc))
  2010. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2011. def test_complex_schema_item_thirsty_invalid(self):
  2012. serialized = self.schema.serialize()
  2013. with self.assertRaises(jsonschema.exceptions.ValidationError) \
  2014. as excinfo:
  2015. jsonschema.validate({'complex_item': {'thirsty': 'Foo'}},
  2016. serialized)
  2017. expected = "u'Foo' is not of type u'boolean'" if six.PY2 \
  2018. else "'Foo' is not of type 'boolean'"
  2019. self.assertIn(expected, excinfo.exception.message)
  2020. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2021. def test_complex_complex_schema_item_hungry_valid(self):
  2022. serialized = self.complex_schema.serialize()
  2023. try:
  2024. jsonschema.validate({'complex_complex_item': {'hungry': True}},
  2025. serialized)
  2026. except jsonschema.exceptions.ValidationError as exc:
  2027. self.fail('ValidationError raised: {0}'.format(exc))
  2028. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2029. def test_both_complex_complex_schema_all_items_valid(self):
  2030. serialized = self.complex_schema.serialize()
  2031. try:
  2032. jsonschema.validate({'complex_complex_item':
  2033. {'hungry': True,
  2034. 'complex_item': {'thirsty': True}}},
  2035. serialized)
  2036. except jsonschema.exceptions.ValidationError as exc:
  2037. self.fail('ValidationError raised: {0}'.format(exc))
  2038. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2039. def test_complex_complex_schema_item_hungry_invalid(self):
  2040. serialized = self.complex_schema.serialize()
  2041. with self.assertRaises(jsonschema.exceptions.ValidationError) \
  2042. as excinfo:
  2043. jsonschema.validate({'complex_complex_item': {'hungry': 'Foo'}},
  2044. serialized)
  2045. expected = "u'Foo' is not of type u'boolean'" if six.PY2 \
  2046. else "'Foo' is not of type 'boolean'"
  2047. self.assertIn(expected, excinfo.exception.message)
  2048. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2049. def test_complex_complex_schema_item_inner_thirsty_invalid(self):
  2050. serialized = self.complex_schema.serialize()
  2051. with self.assertRaises(jsonschema.exceptions.ValidationError) \
  2052. as excinfo:
  2053. jsonschema.validate(
  2054. {'complex_complex_item': {'hungry': True,
  2055. 'complex_item': {'thirsty': 'Bar'}}},
  2056. serialized)
  2057. expected = "u'Bar' is not of type u'boolean'" if six.PY2 \
  2058. else "'Bar' is not of type 'boolean'"
  2059. self.assertIn(expected, excinfo.exception.message)
  2060. @skipIf(HAS_JSONSCHEMA is False, 'The \'jsonschema\' library is missing')
  2061. def test_complex_complex_schema_item_missing_required_hungry(self):
  2062. serialized = self.complex_schema.serialize()
  2063. with self.assertRaises(jsonschema.exceptions.ValidationError) \
  2064. as excinfo:
  2065. jsonschema.validate(
  2066. {'complex_complex_item': {'complex_item': {'thirsty': True}}},
  2067. serialized)
  2068. self.assertIn('\'hungry\' is a required property',
  2069. excinfo.exception.message)