test_boto_apigateway.py 88 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347
  1. # -*- coding: utf-8 -*-
  2. # Import Python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. import datetime
  5. import logging
  6. import os
  7. import random
  8. import string
  9. import pytest
  10. # Import Salt libs
  11. import salt.config
  12. import salt.loader
  13. # Import Salt Libs
  14. import salt.states.boto_apigateway as boto_apigateway
  15. import salt.utils.files
  16. import salt.utils.yaml
  17. # Import 3rd-party libs
  18. from salt.ext.six.moves import range
  19. from salt.utils.versions import LooseVersion
  20. # Import Salt Testing libs
  21. from tests.support.mixins import LoaderModuleMockMixin
  22. from tests.support.mock import MagicMock, patch
  23. from tests.support.unit import TestCase, skipIf
  24. # pylint: disable=import-error,no-name-in-module
  25. from tests.unit.modules.test_boto_apigateway import BotoApiGatewayTestCaseMixin
  26. try:
  27. import boto3
  28. import botocore
  29. from botocore.exceptions import ClientError
  30. HAS_BOTO = True
  31. except ImportError:
  32. HAS_BOTO = False
  33. # pylint: enable=import-error,no-name-in-module
  34. # the boto_apigateway module relies on the connect_to_region() method
  35. # which was added in boto 2.8.0
  36. # https://github.com/boto/boto/commit/33ac26b416fbb48a60602542b4ce15dcc7029f12
  37. required_boto3_version = "1.2.1"
  38. required_botocore_version = "1.4.49"
  39. region = "us-east-1"
  40. access_key = "GKTADJGHEIQSXMKKRBJ08H"
  41. secret_key = "askdjghsdfjkghWupUjasdflkdfklgjsdfjajkghs"
  42. conn_parameters = {
  43. "region": region,
  44. "key": access_key,
  45. "keyid": secret_key,
  46. "profile": {},
  47. }
  48. error_message = (
  49. "An error occurred (101) when calling the {0} operation: Test-defined error"
  50. )
  51. error_content = {"Error": {"Code": 101, "Message": "Test-defined error"}}
  52. api_ret = dict(
  53. description='{\n "context": "See deployment or stage description",\n "provisioned_by": "Salt boto_apigateway.present State"\n}',
  54. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  55. id="vni0vq8wzi",
  56. name="unit test api",
  57. )
  58. no_apis_ret = {"items": []}
  59. apis_ret = {"items": [api_ret]}
  60. mock_model_ret = dict(
  61. contentType="application/json",
  62. description="mock model",
  63. id="123abc",
  64. name="mock model",
  65. schema=(
  66. "{\n"
  67. ' "$schema": "http://json-schema.org/draft-04/schema#",\n'
  68. ' "properties": {\n'
  69. ' "field": {\n'
  70. ' "type": "string"\n'
  71. " }\n"
  72. " }\n"
  73. "}"
  74. ),
  75. )
  76. models_ret = {
  77. "items": [
  78. dict(
  79. contentType="application/json",
  80. description="Error",
  81. id="50nw8r",
  82. name="Error",
  83. schema=(
  84. "{\n"
  85. ' "$schema": "http://json-schema.org/draft-04/schema#",\n'
  86. ' "properties": {\n'
  87. ' "code": {\n'
  88. ' "format": "int32",\n'
  89. ' "type": "integer"\n'
  90. " },\n"
  91. ' "fields": {\n'
  92. ' "type": "string"\n'
  93. " },\n"
  94. ' "message": {\n'
  95. ' "type": "string"\n'
  96. " }\n"
  97. " },\n"
  98. ' "title": "Error Schema",\n'
  99. ' "type": "object"\n'
  100. "}"
  101. ),
  102. ),
  103. dict(
  104. contentType="application/json",
  105. description="User",
  106. id="terlnw",
  107. name="User",
  108. schema=(
  109. "{\n"
  110. ' "$schema": "http://json-schema.org/draft-04/schema#",\n'
  111. ' "properties": {\n'
  112. ' "password": {\n'
  113. ' "description": "A password for the new user",\n'
  114. ' "type": "string"\n'
  115. " },\n"
  116. ' "username": {\n'
  117. ' "description": "A unique username for the user",\n'
  118. ' "type": "string"\n'
  119. " }\n"
  120. " },\n"
  121. ' "title": "User Schema",\n'
  122. ' "type": "object"\n'
  123. "}"
  124. ),
  125. ),
  126. ]
  127. }
  128. root_resources_ret = {"items": [dict(id="bgk0rk8rqb", path="/")]}
  129. resources_ret = {
  130. "items": [
  131. dict(id="bgk0rk8rqb", path="/"),
  132. dict(
  133. id="9waiaz",
  134. parentId="bgk0rk8rqb",
  135. path="/users",
  136. pathPart="users",
  137. resourceMethods={"POST": {}},
  138. ),
  139. ]
  140. }
  141. no_resources_ret = {"items": []}
  142. stage1_deployment1_ret = dict(
  143. cacheClusterEnabled=False,
  144. cacheClusterSize=0.5,
  145. cacheClusterStatus="NOT_AVAILABLE",
  146. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  147. deploymentId="kobnrb",
  148. description=(
  149. "{\n"
  150. ' "current_deployment_label": {\n'
  151. ' "api_name": "unit test api",\n'
  152. ' "swagger_file": "temp-swagger-sample.yaml",\n'
  153. ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n'
  154. ' "swagger_info_object": {\n'
  155. ' "description": "salt boto apigateway unit test service",\n'
  156. ' "title": "salt boto apigateway unit test service",\n'
  157. ' "version": "0.0.0"\n'
  158. " }\n"
  159. " }\n"
  160. "}"
  161. ),
  162. lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  163. methodSettings=dict(),
  164. stageName="test",
  165. variables=dict(),
  166. )
  167. stage1_deployment1_vars_ret = dict(
  168. cacheClusterEnabled=False,
  169. cacheClusterSize=0.5,
  170. cacheClusterStatus="NOT_AVAILABLE",
  171. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  172. deploymentId="kobnrb",
  173. description=(
  174. "{\n"
  175. ' "current_deployment_label": {\n'
  176. ' "api_name": "unit test api",\n'
  177. ' "swagger_file": "temp-swagger-sample.yaml",\n'
  178. ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n'
  179. ' "swagger_info_object": {\n'
  180. ' "description": "salt boto apigateway unit test service",\n'
  181. ' "title": "salt boto apigateway unit test service",\n'
  182. ' "version": "0.0.0"\n'
  183. " }\n"
  184. " }\n"
  185. "}"
  186. ),
  187. lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  188. methodSettings=dict(),
  189. stageName="test",
  190. variables={"var1": "val1"},
  191. )
  192. stage1_deployment2_ret = dict(
  193. cacheClusterEnabled=False,
  194. cacheClusterSize=0.5,
  195. cacheClusterStatus="NOT_AVAILABLE",
  196. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  197. deploymentId="kobnrc",
  198. description=(
  199. "{\n"
  200. ' "current_deployment_label": {\n'
  201. ' "api_name": "unit test api",\n'
  202. ' "swagger_file": "temp-swagger-sample.yaml",\n'
  203. ' "swagger_file_md5sum": "5fd538c4336ed5c54b4bf39ddf97c661",\n'
  204. ' "swagger_info_object": {\n'
  205. ' "description": "salt boto apigateway unit test service",\n'
  206. ' "title": "salt boto apigateway unit test service",\n'
  207. ' "version": "0.0.2"\n'
  208. " }\n"
  209. " }\n"
  210. "}"
  211. ),
  212. lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  213. methodSettings=dict(),
  214. stageName="test",
  215. variables=dict(),
  216. )
  217. stage2_ret = dict(
  218. cacheClusterEnabled=False,
  219. cacheClusterSize=0.5,
  220. cacheClusterStatus="NOT_AVAILABLE",
  221. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  222. deploymentId="kobnrb",
  223. description=(
  224. "{\n"
  225. ' "current_deployment_label": {\n'
  226. ' "api_name": "unit test api",\n'
  227. ' "swagger_file": "temp-swagger-sample.yaml",\n'
  228. ' "swagger_file_md5sum": "4fb17e43bab3a96e7f2410a1597cd0a5",\n'
  229. ' "swagger_info_object": {\n'
  230. ' "description": "salt boto apigateway unit test service",\n'
  231. ' "title": "salt boto apigateway unit test service",\n'
  232. ' "version": "0.0.0"\n'
  233. " }\n"
  234. " }\n"
  235. "}"
  236. ),
  237. lastUpdatedDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  238. methodSettings=dict(),
  239. stageName="dev",
  240. variables=dict(),
  241. )
  242. stages_stage2_ret = {"item": [stage2_ret]}
  243. no_stages_ret = {"item": []}
  244. deployment1_ret = dict(
  245. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  246. description=(
  247. "{\n"
  248. ' "api_name": "unit test api",\n'
  249. ' "swagger_file": "temp-swagger-sample.yaml",\n'
  250. ' "swagger_file_md5sum": "55a948ff90ad80ff747ec91657c7a299",\n'
  251. ' "swagger_info_object": {\n'
  252. ' "description": "salt boto apigateway unit test service",\n'
  253. ' "title": "salt boto apigateway unit test service",\n'
  254. ' "version": "0.0.0"\n'
  255. " }\n"
  256. "}"
  257. ),
  258. id="kobnrb",
  259. )
  260. deployment2_ret = dict(
  261. createdDate=datetime.datetime(2015, 11, 17, 16, 33, 50),
  262. description=(
  263. "{\n"
  264. ' "api_name": "unit test api",\n'
  265. ' "swagger_file": "temp-swagger-sample.yaml",\n'
  266. ' "swagger_file_md5sum": "5fd538c4336ed5c54b4bf39ddf97c661",\n'
  267. ' "swagger_info_object": {\n'
  268. ' "description": "salt boto apigateway unit test service",\n'
  269. ' "title": "salt boto apigateway unit test service",\n'
  270. ' "version": "0.0.2"\n'
  271. " }\n"
  272. "}"
  273. ),
  274. id="kobnrc",
  275. )
  276. deployments_ret = {"items": [deployment1_ret, deployment2_ret]}
  277. function_ret = dict(
  278. FunctionName="unit_test_api_users_post",
  279. Runtime="python2.7",
  280. Role=None,
  281. Handler="handler",
  282. Description="abcdefg",
  283. Timeout=5,
  284. MemorySize=128,
  285. CodeSha256="abcdef",
  286. CodeSize=199,
  287. FunctionArn="arn:lambda:us-east-1:1234:Something",
  288. LastModified="yes",
  289. )
  290. method_integration_response_200_ret = dict(
  291. responseParameters={"method.response.header.Access-Control-Allow-Origin": "*"},
  292. responseTemplates={},
  293. selectionPattern=".*",
  294. statusCode="200",
  295. )
  296. method_integration_ret = dict(
  297. cacheKeyParameters={},
  298. cacheNamespace="9waiaz",
  299. credentials="arn:aws:iam::1234:role/apigatewayrole",
  300. httpMethod="POST",
  301. integrationResponses={"200": method_integration_response_200_ret},
  302. requestParameters={},
  303. requestTemplates={
  304. "application/json": (
  305. "#set($inputRoot = $input.path('$'))"
  306. "{"
  307. '"header-params" : {#set ($map = $input.params().header)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},'
  308. '"query-params" : {#set ($map = $input.params().querystring)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},'
  309. '"path-params" : {#set ($map = $input.params().path)#foreach( $param in $map.entrySet() )"$param.key" : "$param.value" #if( $foreach.hasNext ), #end#end},'
  310. "\"body-params\" : $input.json('$')"
  311. "}"
  312. )
  313. },
  314. type="AWS",
  315. uri=(
  316. "arn:aws:apigateway:us-west-2:"
  317. "lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:1234567:"
  318. "function:unit_test_api_api_users_post/invocations"
  319. ),
  320. )
  321. method_response_200_ret = dict(
  322. responseModels={"application/json": "User"},
  323. responseParameters={"method.response.header.Access-Control-Allow-Origin": False},
  324. statusCode="200",
  325. )
  326. method_ret = dict(
  327. apiKeyRequired=False,
  328. authorizationType="None",
  329. httpMethod="POST",
  330. methodIntegration=method_integration_ret,
  331. methodResponses={"200": method_response_200_ret},
  332. requestModels={"application/json": "User"},
  333. requestParameters={},
  334. )
  335. throttle_rateLimit = 10.0
  336. association_stage_1 = {"apiId": "apiId1", "stage": "stage1"}
  337. association_stage_2 = {"apiId": "apiId1", "stage": "stage2"}
  338. log = logging.getLogger(__name__)
  339. def _has_required_boto():
  340. """
  341. Returns True/False boolean depending on if Boto is installed and correct
  342. version.
  343. """
  344. if not HAS_BOTO:
  345. return False
  346. elif LooseVersion(boto3.__version__) < LooseVersion(required_boto3_version):
  347. return False
  348. else:
  349. return True
  350. def _has_required_botocore():
  351. """
  352. Returns True/False boolean depending on if botocore supports usage plan
  353. """
  354. if not HAS_BOTO:
  355. return False
  356. elif LooseVersion(botocore.__version__) < LooseVersion(required_botocore_version):
  357. return False
  358. else:
  359. return True
  360. class TempSwaggerFile(object):
  361. _tmp_swagger_dict = {
  362. "info": {
  363. "version": "0.0.0",
  364. "description": "salt boto apigateway unit test service",
  365. "title": "salt boto apigateway unit test service",
  366. },
  367. "paths": {
  368. "/users": {
  369. "post": {
  370. "responses": {
  371. "200": {
  372. "headers": {
  373. "Access-Control-Allow-Origin": {"type": "string"}
  374. },
  375. "description": "The username of the new user",
  376. "schema": {"$ref": "#/definitions/User"},
  377. }
  378. },
  379. "parameters": [
  380. {
  381. "in": "body",
  382. "description": "New user details.",
  383. "name": "NewUser",
  384. "schema": {"$ref": "#/definitions/User"},
  385. }
  386. ],
  387. "produces": ["application/json"],
  388. "description": "Creates a new user.",
  389. "tags": ["Auth"],
  390. "consumes": ["application/json"],
  391. "summary": "Registers a new user",
  392. }
  393. }
  394. },
  395. "schemes": ["https"],
  396. "produces": ["application/json"],
  397. "basePath": "/api",
  398. "host": "rm06h9oac4.execute-api.us-west-2.amazonaws.com",
  399. "definitions": {
  400. "User": {
  401. "properties": {
  402. "username": {
  403. "type": "string",
  404. "description": "A unique username for the user",
  405. },
  406. "password": {
  407. "type": "string",
  408. "description": "A password for the new user",
  409. },
  410. }
  411. },
  412. "Error": {
  413. "properties": {
  414. "fields": {"type": "string"},
  415. "message": {"type": "string"},
  416. "code": {"type": "integer", "format": "int32"},
  417. }
  418. },
  419. },
  420. "swagger": "2.0",
  421. }
  422. def __enter__(self):
  423. self.swaggerfile = "temp-swagger-sample.yaml"
  424. with salt.utils.files.fopen(self.swaggerfile, "w") as fp_:
  425. salt.utils.yaml.safe_dump(self.swaggerdict, fp_, default_flow_style=False)
  426. return self.swaggerfile
  427. def __exit__(self, objtype, value, traceback):
  428. os.remove(self.swaggerfile)
  429. def __init__(self, create_invalid_file=False):
  430. if create_invalid_file:
  431. self.swaggerdict = TempSwaggerFile._tmp_swagger_dict.copy()
  432. # add an invalid top level key
  433. self.swaggerdict["invalid_key"] = "invalid"
  434. # remove one of the required keys 'schemes'
  435. self.swaggerdict.pop("schemes", None)
  436. # set swagger version to an unsupported version 3.0
  437. self.swaggerdict["swagger"] = "3.0"
  438. # missing info object
  439. self.swaggerdict.pop("info", None)
  440. else:
  441. self.swaggerdict = TempSwaggerFile._tmp_swagger_dict
  442. class BotoApiGatewayStateTestCaseBase(TestCase, LoaderModuleMockMixin):
  443. conn = None
  444. @classmethod
  445. def setUpClass(cls):
  446. cls.opts = salt.config.DEFAULT_MINION_OPTS.copy()
  447. cls.opts["grains"] = salt.loader.grains(cls.opts)
  448. @classmethod
  449. def tearDownClass(cls):
  450. del cls.opts
  451. def setup_loader_modules(self):
  452. context = {}
  453. utils = salt.loader.utils(
  454. self.opts,
  455. whitelist=["boto", "boto3", "args", "systemd", "path", "platform", "reg"],
  456. context=context,
  457. )
  458. serializers = salt.loader.serializers(self.opts)
  459. self.funcs = salt.loader.minion_mods(
  460. self.opts, context=context, utils=utils, whitelist=["boto_apigateway"]
  461. )
  462. self.salt_states = salt.loader.states(
  463. opts=self.opts,
  464. functions=self.funcs,
  465. utils=utils,
  466. whitelist=["boto_apigateway"],
  467. serializers=serializers,
  468. )
  469. return {
  470. boto_apigateway: {
  471. "__opts__": self.opts,
  472. "__utils__": utils,
  473. "__salt__": self.funcs,
  474. "__states__": self.salt_states,
  475. "__serializers__": serializers,
  476. }
  477. }
  478. # Set up MagicMock to replace the boto3 session
  479. def setUp(self):
  480. self.addCleanup(delattr, self, "funcs")
  481. self.addCleanup(delattr, self, "salt_states")
  482. # connections keep getting cached from prior tests, can't find the
  483. # correct context object to clear it. So randomize the cache key, to prevent any
  484. # cache hits
  485. conn_parameters["key"] = "".join(
  486. random.choice(string.ascii_lowercase + string.digits) for _ in range(50)
  487. )
  488. patcher = patch("boto3.session.Session")
  489. self.addCleanup(patcher.stop)
  490. mock_session = patcher.start()
  491. session_instance = mock_session.return_value
  492. self.conn = MagicMock()
  493. self.addCleanup(delattr, self, "conn")
  494. session_instance.client.return_value = self.conn
  495. @skipIf(HAS_BOTO is False, "The boto module must be installed.")
  496. @skipIf(
  497. _has_required_boto() is False,
  498. "The boto3 module must be greater than"
  499. " or equal to version {0}".format(required_boto3_version),
  500. )
  501. class BotoApiGatewayTestCase(
  502. BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin
  503. ):
  504. """
  505. TestCase for salt.modules.boto_apigateway state.module
  506. """
  507. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  508. def test_present_when_swagger_file_is_invalid(self):
  509. """
  510. Tests present when the swagger file is invalid.
  511. """
  512. result = {}
  513. with TempSwaggerFile(create_invalid_file=True) as swagger_file:
  514. result = self.salt_states["boto_apigateway.present"](
  515. "api present",
  516. "unit test api",
  517. swagger_file,
  518. "test",
  519. False,
  520. "arn:aws:iam::1234:role/apigatewayrole",
  521. **conn_parameters
  522. )
  523. self.assertFalse(result.get("result", True))
  524. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  525. def test_present_when_stage_is_already_at_desired_deployment(self):
  526. """
  527. Tests scenario where no action will be taken since we're already
  528. at desired state
  529. """
  530. self.conn.get_rest_apis.return_value = apis_ret
  531. self.conn.get_deployment.return_value = deployment1_ret
  532. self.conn.get_stage.return_value = stage1_deployment1_ret
  533. self.conn.update_stage.side_effect = ClientError(
  534. error_content, "update_stage should not be called"
  535. )
  536. result = {}
  537. with TempSwaggerFile() as swagger_file:
  538. result = self.salt_states["boto_apigateway.present"](
  539. "api present",
  540. "unit test api",
  541. swagger_file,
  542. "test",
  543. False,
  544. "arn:aws:iam::1234:role/apigatewayrole",
  545. **conn_parameters
  546. )
  547. self.assertFalse(result.get("abort"))
  548. self.assertTrue(result.get("current"))
  549. self.assertIs(result.get("result"), True)
  550. self.assertNotIn("update_stage should not be called", result.get("comment", ""))
  551. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  552. def test_present_when_stage_is_already_at_desired_deployment_and_needs_stage_variables_update(
  553. self,
  554. ):
  555. """
  556. Tests scenario where the deployment is current except for the need to update stage variables
  557. from {} to {'var1':'val1'}
  558. """
  559. self.conn.get_rest_apis.return_value = apis_ret
  560. self.conn.get_deployment.return_value = deployment1_ret
  561. self.conn.get_stage.return_value = stage1_deployment1_ret
  562. self.conn.update_stage.return_value = stage1_deployment1_vars_ret
  563. result = {}
  564. with TempSwaggerFile() as swagger_file:
  565. result = self.salt_states["boto_apigateway.present"](
  566. "api present",
  567. "unit test api",
  568. swagger_file,
  569. "test",
  570. False,
  571. "arn:aws:iam::1234:role/apigatewayrole",
  572. stage_variables={"var1": "val1"},
  573. **conn_parameters
  574. )
  575. self.assertFalse(result.get("abort"))
  576. self.assertTrue(result.get("current"))
  577. self.assertIs(result.get("result"), True)
  578. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  579. def test_present_when_stage_exists_and_is_to_associate_to_existing_deployment(self):
  580. """
  581. Tests scenario where we merely reassociate a stage to a pre-existing
  582. deployments
  583. """
  584. self.conn.get_rest_apis.return_value = apis_ret
  585. self.conn.get_deployment.return_value = deployment2_ret
  586. self.conn.get_deployments.return_value = deployments_ret
  587. self.conn.get_stage.return_value = stage1_deployment2_ret
  588. self.conn.update_stage.return_value = stage1_deployment1_ret
  589. # should never get to the following calls
  590. self.conn.create_stage.side_effect = ClientError(error_content, "create_stage")
  591. self.conn.create_deployment.side_effect = ClientError(
  592. error_content, "create_deployment"
  593. )
  594. result = {}
  595. with TempSwaggerFile() as swagger_file:
  596. result = self.salt_states["boto_apigateway.present"](
  597. "api present",
  598. "unit test api",
  599. swagger_file,
  600. "test",
  601. False,
  602. "arn:aws:iam::1234:role/apigatewayrole",
  603. **conn_parameters
  604. )
  605. self.assertTrue(result.get("publish"))
  606. self.assertIs(result.get("result"), True)
  607. self.assertFalse(result.get("abort"))
  608. self.assertTrue(result.get("changes", {}).get("new", [{}])[0])
  609. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  610. def test_present_when_stage_is_to_associate_to_new_deployment(self):
  611. """
  612. Tests creation of a new api/model/resource given nothing has been created previously
  613. """
  614. # no api existed
  615. self.conn.get_rest_apis.return_value = no_apis_ret
  616. # create top level api
  617. self.conn.create_rest_api.return_value = api_ret
  618. # no models existed in the newly created api
  619. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  620. # create model return values
  621. self.conn.create_model.return_value = mock_model_ret
  622. # various paths/resources already created
  623. self.conn.get_resources.return_value = resources_ret
  624. # the following should never be called
  625. self.conn.create_resource.side_effect = ClientError(
  626. error_content, "create_resource"
  627. )
  628. # create api method for POST
  629. self.conn.put_method.return_value = method_ret
  630. # create api method integration for POST
  631. self.conn.put_integration.return_value = method_integration_ret
  632. # create api method response for POST/200
  633. self.conn.put_method_response.return_value = method_response_200_ret
  634. # create api method integration response for POST
  635. self.conn.put_intgration_response.return_value = (
  636. method_integration_response_200_ret
  637. )
  638. result = {}
  639. with patch.dict(
  640. self.funcs,
  641. {
  642. "boto_lambda.describe_function": MagicMock(
  643. return_value={"function": function_ret}
  644. )
  645. },
  646. ):
  647. with TempSwaggerFile() as swagger_file:
  648. result = self.salt_states["boto_apigateway.present"](
  649. "api present",
  650. "unit test api",
  651. swagger_file,
  652. "test",
  653. False,
  654. "arn:aws:iam::1234:role/apigatewayrole",
  655. **conn_parameters
  656. )
  657. self.assertIs(result.get("result"), True)
  658. self.assertIs(result.get("abort"), None)
  659. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  660. def test_present_when_stage_associating_to_new_deployment_errored_on_api_creation(
  661. self,
  662. ):
  663. """
  664. Tests creation of a new api/model/resource given nothing has been created previously,
  665. and we failed on creating the top level api object.
  666. """
  667. # no api existed
  668. self.conn.get_rest_apis.return_value = no_apis_ret
  669. # create top level api
  670. self.conn.create_rest_api.side_effect = ClientError(
  671. error_content, "create_rest_api"
  672. )
  673. result = {}
  674. with TempSwaggerFile() as swagger_file:
  675. result = self.salt_states["boto_apigateway.present"](
  676. "api present",
  677. "unit test api",
  678. swagger_file,
  679. "test",
  680. False,
  681. "arn:aws:iam::1234:role/apigatewayrole",
  682. **conn_parameters
  683. )
  684. self.assertIs(result.get("abort"), True)
  685. self.assertIs(result.get("result"), False)
  686. self.assertIn("create_rest_api", result.get("comment", ""))
  687. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  688. def test_present_when_stage_associating_to_new_deployment_errored_on_model_creation(
  689. self,
  690. ):
  691. """
  692. Tests creation of a new api/model/resource given nothing has been created previously,
  693. and we failed on creating the models after successful creation of top level api object.
  694. """
  695. # no api existed
  696. self.conn.get_rest_apis.return_value = no_apis_ret
  697. # create top level api
  698. self.conn.create_rest_api.return_value = api_ret
  699. # no models existed in the newly created api
  700. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  701. # create model return error
  702. self.conn.create_model.side_effect = ClientError(error_content, "create_model")
  703. result = {}
  704. with TempSwaggerFile() as swagger_file:
  705. result = self.salt_states["boto_apigateway.present"](
  706. "api present",
  707. "unit test api",
  708. swagger_file,
  709. "test",
  710. False,
  711. "arn:aws:iam::1234:role/apigatewayrole",
  712. **conn_parameters
  713. )
  714. self.assertIs(result.get("abort"), True)
  715. self.assertIs(result.get("result"), False)
  716. self.assertIn("create_model", result.get("comment", ""))
  717. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  718. def test_present_when_stage_associating_to_new_deployment_errored_on_resource_creation(
  719. self,
  720. ):
  721. """
  722. Tests creation of a new api/model/resource given nothing has been created previously,
  723. and we failed on creating the resource (paths) after successful creation of top level api/model
  724. objects.
  725. """
  726. # no api existed
  727. self.conn.get_rest_apis.return_value = no_apis_ret
  728. # create top level api
  729. self.conn.create_rest_api.return_value = api_ret
  730. # no models existed in the newly created api
  731. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  732. # create model return values
  733. self.conn.create_model.return_value = mock_model_ret
  734. # get resources has nothing intiially except to the root path '/'
  735. self.conn.get_resources.return_value = root_resources_ret
  736. # create resources return error
  737. self.conn.create_resource.side_effect = ClientError(
  738. error_content, "create_resource"
  739. )
  740. result = {}
  741. with TempSwaggerFile() as swagger_file:
  742. result = self.salt_states["boto_apigateway.present"](
  743. "api present",
  744. "unit test api",
  745. swagger_file,
  746. "test",
  747. False,
  748. "arn:aws:iam::1234:role/apigatewayrole",
  749. **conn_parameters
  750. )
  751. self.assertIs(result.get("abort"), True)
  752. self.assertIs(result.get("result"), False)
  753. self.assertIn("create_resource", result.get("comment", ""))
  754. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  755. def test_present_when_stage_associating_to_new_deployment_errored_on_put_method(
  756. self,
  757. ):
  758. """
  759. Tests creation of a new api/model/resource given nothing has been created previously,
  760. and we failed on adding a post method to the resource after successful creation of top level
  761. api, model, resource objects.
  762. """
  763. # no api existed
  764. self.conn.get_rest_apis.return_value = no_apis_ret
  765. # create top level api
  766. self.conn.create_rest_api.return_value = api_ret
  767. # no models existed in the newly created api
  768. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  769. # create model return values
  770. self.conn.create_model.return_value = mock_model_ret
  771. # various paths/resources already created
  772. self.conn.get_resources.return_value = resources_ret
  773. # the following should never be called
  774. self.conn.create_resource.side_effect = ClientError(
  775. error_content, "create_resource"
  776. )
  777. # create api method for POST
  778. self.conn.put_method.side_effect = ClientError(error_content, "put_method")
  779. result = {}
  780. with patch.dict(
  781. self.funcs,
  782. {
  783. "boto_lambda.describe_function": MagicMock(
  784. return_value={"function": function_ret}
  785. )
  786. },
  787. ):
  788. with TempSwaggerFile() as swagger_file:
  789. result = self.salt_states["boto_apigateway.present"](
  790. "api present",
  791. "unit test api",
  792. swagger_file,
  793. "test",
  794. False,
  795. "arn:aws:iam::1234:role/apigatewayrole",
  796. **conn_parameters
  797. )
  798. self.assertIs(result.get("abort"), True)
  799. self.assertIs(result.get("result"), False)
  800. self.assertIn("put_method", result.get("comment", ""))
  801. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  802. def test_present_when_stage_associating_to_new_deployment_errored_on_lambda_function_lookup(
  803. self,
  804. ):
  805. """
  806. Tests creation of a new api/model/resource given nothing has been created previously,
  807. and we failed on adding a post method due to a lamda look up failure after successful
  808. creation of top level api, model, resource objects.
  809. """
  810. # no api existed
  811. self.conn.get_rest_apis.return_value = no_apis_ret
  812. # create top level api
  813. self.conn.create_rest_api.return_value = api_ret
  814. # no models existed in the newly created api
  815. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  816. # create model return values
  817. self.conn.create_model.return_value = mock_model_ret
  818. # various paths/resources already created
  819. self.conn.get_resources.return_value = resources_ret
  820. # the following should never be called
  821. self.conn.create_resource.side_effect = ClientError(
  822. error_content, "create_resource"
  823. )
  824. # create api method for POST
  825. self.conn.put_method.return_value = method_ret
  826. # create api method integration for POST
  827. self.conn.put_integration.side_effect = ClientError(
  828. error_content, "put_integration should not be invoked"
  829. )
  830. result = {}
  831. with patch.dict(
  832. self.funcs,
  833. {
  834. "boto_lambda.describe_function": MagicMock(
  835. return_value={"error": "no such lambda"}
  836. )
  837. },
  838. ):
  839. with TempSwaggerFile() as swagger_file:
  840. result = self.salt_states["boto_apigateway.present"](
  841. "api present",
  842. "unit test api",
  843. swagger_file,
  844. "test",
  845. False,
  846. "arn:aws:iam::1234:role/apigatewayrole",
  847. **conn_parameters
  848. )
  849. self.assertIs(result.get("result"), False)
  850. self.assertNotIn(
  851. "put_integration should not be invoked", result.get("comment", "")
  852. )
  853. self.assertIn("not find lambda function", result.get("comment", ""))
  854. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  855. def test_present_when_stage_associating_to_new_deployment_errored_on_put_integration(
  856. self,
  857. ):
  858. """
  859. Tests creation of a new api/model/resource given nothing has been created previously,
  860. and we failed on adding an integration for the post method to the resource after
  861. successful creation of top level api, model, resource objects.
  862. """
  863. # no api existed
  864. self.conn.get_rest_apis.return_value = no_apis_ret
  865. # create top level api
  866. self.conn.create_rest_api.return_value = api_ret
  867. # no models existed in the newly created api
  868. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  869. # create model return values
  870. self.conn.create_model.return_value = mock_model_ret
  871. # various paths/resources already created
  872. self.conn.get_resources.return_value = resources_ret
  873. # the following should never be called
  874. self.conn.create_resource.side_effect = ClientError(
  875. error_content, "create_resource"
  876. )
  877. # create api method for POST
  878. self.conn.put_method.return_value = method_ret
  879. # create api method integration for POST
  880. self.conn.put_integration.side_effect = ClientError(
  881. error_content, "put_integration"
  882. )
  883. result = {}
  884. with patch.dict(
  885. self.funcs,
  886. {
  887. "boto_lambda.describe_function": MagicMock(
  888. return_value={"function": function_ret}
  889. )
  890. },
  891. ):
  892. with TempSwaggerFile() as swagger_file:
  893. result = self.salt_states["boto_apigateway.present"](
  894. "api present",
  895. "unit test api",
  896. swagger_file,
  897. "test",
  898. False,
  899. "arn:aws:iam::1234:role/apigatewayrole",
  900. **conn_parameters
  901. )
  902. self.assertIs(result.get("abort"), True)
  903. self.assertIs(result.get("result"), False)
  904. self.assertIn("put_integration", result.get("comment", ""))
  905. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  906. def test_present_when_stage_associating_to_new_deployment_errored_on_put_method_response(
  907. self,
  908. ):
  909. """
  910. Tests creation of a new api/model/resource given nothing has been created previously,
  911. and we failed on adding a method response for the post method to the resource after
  912. successful creation of top level api, model, resource objects.
  913. """
  914. # no api existed
  915. self.conn.get_rest_apis.return_value = no_apis_ret
  916. # create top level api
  917. self.conn.create_rest_api.return_value = api_ret
  918. # no models existed in the newly created api
  919. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  920. # create model return values
  921. self.conn.create_model.return_value = mock_model_ret
  922. # various paths/resources already created
  923. self.conn.get_resources.return_value = resources_ret
  924. # the following should never be called
  925. self.conn.create_resource.side_effect = ClientError(
  926. error_content, "create_resource"
  927. )
  928. # create api method for POST
  929. self.conn.put_method.return_value = method_ret
  930. # create api method integration for POST
  931. self.conn.put_integration.return_value = method_integration_ret
  932. # create api method response for POST/200
  933. self.conn.put_method_response.side_effect = ClientError(
  934. error_content, "put_method_response"
  935. )
  936. result = {}
  937. with patch.dict(
  938. self.funcs,
  939. {
  940. "boto_lambda.describe_function": MagicMock(
  941. return_value={"function": function_ret}
  942. )
  943. },
  944. ):
  945. with TempSwaggerFile() as swagger_file:
  946. result = self.salt_states["boto_apigateway.present"](
  947. "api present",
  948. "unit test api",
  949. swagger_file,
  950. "test",
  951. False,
  952. "arn:aws:iam::1234:role/apigatewayrole",
  953. **conn_parameters
  954. )
  955. self.assertIs(result.get("abort"), True)
  956. self.assertIs(result.get("result"), False)
  957. self.assertIn("put_method_response", result.get("comment", ""))
  958. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  959. def test_present_when_stage_associating_to_new_deployment_errored_on_put_integration_response(
  960. self,
  961. ):
  962. """
  963. Tests creation of a new api/model/resource given nothing has been created previously,
  964. and we failed on adding an integration response for the post method to the resource after
  965. successful creation of top level api, model, resource objects.
  966. """
  967. # no api existed
  968. self.conn.get_rest_apis.return_value = no_apis_ret
  969. # create top level api
  970. self.conn.create_rest_api.return_value = api_ret
  971. # no models existed in the newly created api
  972. self.conn.get_model.side_effect = ClientError(error_content, "get_model")
  973. # create model return values
  974. self.conn.create_model.return_value = mock_model_ret
  975. # various paths/resources already created
  976. self.conn.get_resources.return_value = resources_ret
  977. # the following should never be called
  978. self.conn.create_resource.side_effect = ClientError(
  979. error_content, "create_resource"
  980. )
  981. # create api method for POST
  982. self.conn.put_method.return_value = method_ret
  983. # create api method integration for POST
  984. self.conn.put_integration.return_value = method_integration_ret
  985. # create api method response for POST/200
  986. self.conn.put_method_response.return_value = method_response_200_ret
  987. # create api method integration response for POST
  988. self.conn.put_integration_response.side_effect = ClientError(
  989. error_content, "put_integration_response"
  990. )
  991. result = {}
  992. with patch.dict(
  993. self.funcs,
  994. {
  995. "boto_lambda.describe_function": MagicMock(
  996. return_value={"function": function_ret}
  997. )
  998. },
  999. ):
  1000. with TempSwaggerFile() as swagger_file:
  1001. result = self.salt_states["boto_apigateway.present"](
  1002. "api present",
  1003. "unit test api",
  1004. swagger_file,
  1005. "test",
  1006. False,
  1007. "arn:aws:iam::1234:role/apigatewayrole",
  1008. **conn_parameters
  1009. )
  1010. self.assertIs(result.get("abort"), True)
  1011. self.assertIs(result.get("result"), False)
  1012. self.assertIn("put_integration_response", result.get("comment", ""))
  1013. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1014. def test_absent_when_rest_api_does_not_exist(self):
  1015. """
  1016. Tests scenario where the given api_name does not exist, absent state should return True
  1017. with no changes.
  1018. """
  1019. self.conn.get_rest_apis.return_value = apis_ret
  1020. self.conn.get_stage.side_effect = ClientError(
  1021. error_content, "get_stage should not be called"
  1022. )
  1023. result = self.salt_states["boto_apigateway.absent"](
  1024. "api present",
  1025. "no_such_rest_api",
  1026. "no_such_stage",
  1027. nuke_api=False,
  1028. **conn_parameters
  1029. )
  1030. self.assertIs(result.get("result"), True)
  1031. self.assertNotIn("get_stage should not be called", result.get("comment", ""))
  1032. self.assertEqual(result.get("changes"), {})
  1033. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1034. def test_absent_when_stage_is_invalid(self):
  1035. """
  1036. Tests scenario where the stagename doesn't exist
  1037. """
  1038. self.conn.get_rest_apis.return_value = apis_ret
  1039. self.conn.get_stage.return_value = stage1_deployment1_ret
  1040. self.conn.delete_stage.side_effect = ClientError(error_content, "delete_stage")
  1041. result = self.salt_states["boto_apigateway.absent"](
  1042. "api present",
  1043. "unit test api",
  1044. "no_such_stage",
  1045. nuke_api=False,
  1046. **conn_parameters
  1047. )
  1048. self.assertTrue(result.get("abort", False))
  1049. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1050. def test_absent_when_stage_is_valid_and_only_one_stage_is_associated_to_deployment(
  1051. self,
  1052. ):
  1053. """
  1054. Tests scenario where the stagename exists
  1055. """
  1056. self.conn.get_rest_apis.return_value = apis_ret
  1057. self.conn.get_stage.return_value = stage1_deployment1_ret
  1058. self.conn.delete_stage.return_value = {
  1059. "ResponseMetadata": {
  1060. "HTTPStatusCode": 200,
  1061. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1062. }
  1063. }
  1064. self.conn.get_stages.return_value = no_stages_ret
  1065. self.conn.delete_deployment.return_value = {
  1066. "ResponseMetadata": {
  1067. "HTTPStatusCode": 200,
  1068. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1069. }
  1070. }
  1071. result = self.salt_states["boto_apigateway.absent"](
  1072. "api present", "unit test api", "test", nuke_api=False, **conn_parameters
  1073. )
  1074. self.assertTrue(result.get("result", False))
  1075. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1076. def test_absent_when_stage_is_valid_and_two_stages_are_associated_to_deployment(
  1077. self,
  1078. ):
  1079. """
  1080. Tests scenario where the stagename exists and there are two stages associated with same deployment
  1081. """
  1082. self.conn.get_rest_apis.return_value = apis_ret
  1083. self.conn.get_stage.return_value = stage1_deployment1_ret
  1084. self.conn.delete_stage.return_value = {
  1085. "ResponseMetadata": {
  1086. "HTTPStatusCode": 200,
  1087. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1088. }
  1089. }
  1090. self.conn.get_stages.return_value = stages_stage2_ret
  1091. result = self.salt_states["boto_apigateway.absent"](
  1092. "api present", "unit test api", "test", nuke_api=False, **conn_parameters
  1093. )
  1094. self.assertTrue(result.get("result", False))
  1095. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1096. def test_absent_when_failing_to_delete_a_deployment_no_longer_associated_with_any_stages(
  1097. self,
  1098. ):
  1099. """
  1100. Tests scenario where stagename exists and is deleted, but a failure occurs when trying to delete
  1101. the deployment which is no longer associated to any other stages
  1102. """
  1103. self.conn.get_rest_apis.return_value = apis_ret
  1104. self.conn.get_stage.return_value = stage1_deployment1_ret
  1105. self.conn.delete_stage.return_value = {
  1106. "ResponseMetadata": {
  1107. "HTTPStatusCode": 200,
  1108. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1109. }
  1110. }
  1111. self.conn.get_stages.return_value = no_stages_ret
  1112. self.conn.delete_deployment.side_effect = ClientError(
  1113. error_content, "delete_deployment"
  1114. )
  1115. result = self.salt_states["boto_apigateway.absent"](
  1116. "api present", "unit test api", "test", nuke_api=False, **conn_parameters
  1117. )
  1118. self.assertTrue(result.get("abort", False))
  1119. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1120. def test_absent_when_nuke_api_and_no_more_stages_deployments_remain(self):
  1121. """
  1122. Tests scenario where the stagename exists and there are no stages associated with same deployment,
  1123. the api would be deleted.
  1124. """
  1125. self.conn.get_rest_apis.return_value = apis_ret
  1126. self.conn.get_stage.return_value = stage1_deployment1_ret
  1127. self.conn.delete_stage.return_value = {
  1128. "ResponseMetadata": {
  1129. "HTTPStatusCode": 200,
  1130. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1131. }
  1132. }
  1133. self.conn.get_stages.return_value = no_stages_ret
  1134. self.conn.get_deployments.return_value = deployments_ret
  1135. self.conn.delete_rest_api.return_value = {
  1136. "ResponseMetadata": {
  1137. "HTTPStatusCode": 200,
  1138. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1139. }
  1140. }
  1141. result = self.salt_states["boto_apigateway.absent"](
  1142. "api present", "unit test api", "test", nuke_api=True, **conn_parameters
  1143. )
  1144. self.assertIs(result.get("result"), True)
  1145. self.assertIsNot(result.get("abort"), True)
  1146. self.assertIs(
  1147. result.get("changes", {})
  1148. .get("new", [{}])[0]
  1149. .get("delete_api", {})
  1150. .get("deleted"),
  1151. True,
  1152. )
  1153. @pytest.mark.slow_test(seconds=1) # Test takes >0.1 and <=1 seconds
  1154. def test_absent_when_nuke_api_and_other_stages_deployments_exist(self):
  1155. """
  1156. Tests scenario where the stagename exists and there are two stages associated with same deployment,
  1157. though nuke_api is requested, due to remaining deployments, we will not call the delete_rest_api call.
  1158. """
  1159. self.conn.get_rest_apis.return_value = apis_ret
  1160. self.conn.get_stage.return_value = stage1_deployment1_ret
  1161. self.conn.delete_stage.return_value = {
  1162. "ResponseMetadata": {
  1163. "HTTPStatusCode": 200,
  1164. "RequestId": "2d31072c-9d15-11e5-9977-6d9fcfda9c0a",
  1165. }
  1166. }
  1167. self.conn.get_stages.return_value = stages_stage2_ret
  1168. self.conn.get_deployments.return_value = deployments_ret
  1169. self.conn.delete_rest_api.side_effect = ClientError(
  1170. error_content, "unexpected_api_delete"
  1171. )
  1172. result = self.salt_states["boto_apigateway.absent"](
  1173. "api present", "unit test api", "test", nuke_api=True, **conn_parameters
  1174. )
  1175. self.assertIs(result.get("result"), True)
  1176. self.assertIsNot(result.get("abort"), True)
  1177. @skipIf(HAS_BOTO is False, "The boto module must be installed.")
  1178. @skipIf(
  1179. _has_required_boto() is False,
  1180. "The boto3 module must be greater than"
  1181. " or equal to version {0}".format(required_boto3_version),
  1182. )
  1183. @skipIf(
  1184. _has_required_botocore() is False,
  1185. "The botocore module must be greater than"
  1186. " or equal to version {0}".format(required_botocore_version),
  1187. )
  1188. class BotoApiGatewayUsagePlanTestCase(
  1189. BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin
  1190. ):
  1191. """
  1192. TestCase for salt.modules.boto_apigateway state.module, usage_plans portion
  1193. """
  1194. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1195. def test_usage_plan_present_if_describe_fails(self, *args):
  1196. """
  1197. Tests correct error processing for describe_usage_plan failure
  1198. """
  1199. with patch.dict(
  1200. boto_apigateway.__salt__,
  1201. {
  1202. "boto_apigateway.describe_usage_plans": MagicMock(
  1203. return_value={"error": "error"}
  1204. )
  1205. },
  1206. ):
  1207. result = boto_apigateway.usage_plan_present(
  1208. "name", "plan_name", **conn_parameters
  1209. )
  1210. self.assertIn("result", result)
  1211. self.assertEqual(result["result"], False)
  1212. self.assertIn("comment", result)
  1213. self.assertEqual(
  1214. result["comment"], "Failed to describe existing usage plans"
  1215. )
  1216. self.assertIn("changes", result)
  1217. self.assertEqual(result["changes"], {})
  1218. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1219. def test_usage_plan_present_if_there_is_no_such_plan_and_test_option_is_set(
  1220. self, *args
  1221. ):
  1222. """
  1223. TestCse for salt.modules.boto_apigateway state.module, checking that if __opts__['test'] is set
  1224. and usage plan does not exist, correct diagnostic will be returned
  1225. """
  1226. with patch.dict(boto_apigateway.__opts__, {"test": True}):
  1227. with patch.dict(
  1228. boto_apigateway.__salt__,
  1229. {
  1230. "boto_apigateway.describe_usage_plans": MagicMock(
  1231. return_value={"plans": []}
  1232. )
  1233. },
  1234. ):
  1235. result = boto_apigateway.usage_plan_present(
  1236. "name", "plan_name", **conn_parameters
  1237. )
  1238. self.assertIn("comment", result)
  1239. self.assertEqual(
  1240. result["comment"], "a new usage plan plan_name would be created"
  1241. )
  1242. self.assertIn("result", result)
  1243. self.assertEqual(result["result"], None)
  1244. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1245. def test_usage_plan_present_if_create_usage_plan_fails(self, *args):
  1246. """
  1247. Tests behavior for the case when creating a new usage plan fails
  1248. """
  1249. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1250. with patch.dict(
  1251. boto_apigateway.__salt__,
  1252. {
  1253. "boto_apigateway.describe_usage_plans": MagicMock(
  1254. return_value={"plans": []}
  1255. ),
  1256. "boto_apigateway.create_usage_plan": MagicMock(
  1257. return_value={"error": "error"}
  1258. ),
  1259. },
  1260. ):
  1261. result = boto_apigateway.usage_plan_present(
  1262. "name", "plan_name", **conn_parameters
  1263. )
  1264. self.assertIn("result", result)
  1265. self.assertEqual(result["result"], False)
  1266. self.assertIn("comment", result)
  1267. self.assertEqual(
  1268. result["comment"], "Failed to create a usage plan plan_name, error"
  1269. )
  1270. self.assertIn("changes", result)
  1271. self.assertEqual(result["changes"], {})
  1272. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1273. def test_usage_plan_present_if_plan_is_there_and_needs_no_updates(self, *args):
  1274. """
  1275. Tests behavior for the case when plan is present and needs no updates
  1276. """
  1277. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1278. with patch.dict(
  1279. boto_apigateway.__salt__,
  1280. {
  1281. "boto_apigateway.describe_usage_plans": MagicMock(
  1282. return_value={"plans": [{"id": "planid", "name": "planname"}]}
  1283. ),
  1284. "boto_apigateway.update_usage_plan": MagicMock(),
  1285. },
  1286. ):
  1287. result = boto_apigateway.usage_plan_present(
  1288. "name", "plan_name", **conn_parameters
  1289. )
  1290. self.assertIn("result", result)
  1291. self.assertEqual(result["result"], True)
  1292. self.assertIn("comment", result)
  1293. self.assertEqual(
  1294. result["comment"],
  1295. "usage plan plan_name is already in a correct state",
  1296. )
  1297. self.assertIn("changes", result)
  1298. self.assertEqual(result["changes"], {})
  1299. self.assertTrue(
  1300. boto_apigateway.__salt__[
  1301. "boto_apigateway.update_usage_plan"
  1302. ].call_count
  1303. == 0
  1304. )
  1305. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1306. def test_usage_plan_present_if_plan_is_there_and_needs_updates_but_test_is_set(
  1307. self, *args
  1308. ):
  1309. """
  1310. Tests behavior when usage plan needs to be updated by tests option is set
  1311. """
  1312. with patch.dict(boto_apigateway.__opts__, {"test": True}):
  1313. with patch.dict(
  1314. boto_apigateway.__salt__,
  1315. {
  1316. "boto_apigateway.describe_usage_plans": MagicMock(
  1317. return_value={
  1318. "plans": [
  1319. {
  1320. "id": "planid",
  1321. "name": "planname",
  1322. "throttle": {"rateLimit": 10.0},
  1323. }
  1324. ]
  1325. }
  1326. ),
  1327. "boto_apigateway.update_usage_plan": MagicMock(),
  1328. },
  1329. ):
  1330. result = boto_apigateway.usage_plan_present(
  1331. "name", "plan_name", **conn_parameters
  1332. )
  1333. self.assertIn("comment", result)
  1334. self.assertEqual(
  1335. result["comment"], "a new usage plan plan_name would be updated"
  1336. )
  1337. self.assertIn("result", result)
  1338. self.assertEqual(result["result"], None)
  1339. self.assertTrue(
  1340. boto_apigateway.__salt__[
  1341. "boto_apigateway.update_usage_plan"
  1342. ].call_count
  1343. == 0
  1344. )
  1345. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1346. def test_usage_plan_present_if_plan_is_there_and_needs_updates_but_update_fails(
  1347. self, *args
  1348. ):
  1349. """
  1350. Tests error processing for the case when updating an existing usage plan fails
  1351. """
  1352. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1353. with patch.dict(
  1354. boto_apigateway.__salt__,
  1355. {
  1356. "boto_apigateway.describe_usage_plans": MagicMock(
  1357. return_value={
  1358. "plans": [
  1359. {
  1360. "id": "planid",
  1361. "name": "planname",
  1362. "throttle": {"rateLimit": 10.0},
  1363. }
  1364. ]
  1365. }
  1366. ),
  1367. "boto_apigateway.update_usage_plan": MagicMock(
  1368. return_value={"error": "error"}
  1369. ),
  1370. },
  1371. ):
  1372. result = boto_apigateway.usage_plan_present(
  1373. "name", "plan_name", **conn_parameters
  1374. )
  1375. self.assertIn("result", result)
  1376. self.assertEqual(result["result"], False)
  1377. self.assertIn("comment", result)
  1378. self.assertEqual(
  1379. result["comment"], "Failed to update a usage plan plan_name, error"
  1380. )
  1381. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1382. def test_usage_plan_present_if_plan_has_been_created(self, *args):
  1383. """
  1384. Tests successful case for creating a new usage plan
  1385. """
  1386. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1387. with patch.dict(
  1388. boto_apigateway.__salt__,
  1389. {
  1390. "boto_apigateway.describe_usage_plans": MagicMock(
  1391. side_effect=[{"plans": []}, {"plans": [{"id": "id"}]}]
  1392. ),
  1393. "boto_apigateway.create_usage_plan": MagicMock(
  1394. return_value={"created": True}
  1395. ),
  1396. },
  1397. ):
  1398. result = boto_apigateway.usage_plan_present(
  1399. "name", "plan_name", **conn_parameters
  1400. )
  1401. self.assertIn("result", result)
  1402. self.assertEqual(result["result"], True)
  1403. self.assertIn("comment", result)
  1404. self.assertEqual(
  1405. result["comment"], "A new usage plan plan_name has been created"
  1406. )
  1407. self.assertEqual(result["changes"]["old"], {"plan": None})
  1408. self.assertEqual(result["changes"]["new"], {"plan": {"id": "id"}})
  1409. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1410. def test_usage_plan_present_if_plan_has_been_updated(self, *args):
  1411. """
  1412. Tests successful case for updating a usage plan
  1413. """
  1414. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1415. with patch.dict(
  1416. boto_apigateway.__salt__,
  1417. {
  1418. "boto_apigateway.describe_usage_plans": MagicMock(
  1419. side_effect=[
  1420. {"plans": [{"id": "id"}]},
  1421. {
  1422. "plans": [
  1423. {
  1424. "id": "id",
  1425. "throttle": {"rateLimit": throttle_rateLimit},
  1426. }
  1427. ]
  1428. },
  1429. ]
  1430. ),
  1431. "boto_apigateway.update_usage_plan": MagicMock(
  1432. return_value={"updated": True}
  1433. ),
  1434. },
  1435. ):
  1436. result = boto_apigateway.usage_plan_present(
  1437. "name",
  1438. "plan_name",
  1439. throttle={"rateLimit": throttle_rateLimit},
  1440. **conn_parameters
  1441. )
  1442. self.assertIn("result", result)
  1443. self.assertEqual(result["result"], True)
  1444. self.assertIn("comment", result)
  1445. self.assertEqual(
  1446. result["comment"], "usage plan plan_name has been updated"
  1447. )
  1448. self.assertEqual(result["changes"]["old"], {"plan": {"id": "id"}})
  1449. self.assertEqual(
  1450. result["changes"]["new"],
  1451. {
  1452. "plan": {
  1453. "id": "id",
  1454. "throttle": {"rateLimit": throttle_rateLimit},
  1455. }
  1456. },
  1457. )
  1458. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1459. def test_usage_plan_present_if_ValueError_is_raised(self, *args):
  1460. """
  1461. Tests error processing for the case when ValueError is raised when creating a usage plan
  1462. """
  1463. with patch.dict(
  1464. boto_apigateway.__salt__,
  1465. {
  1466. "boto_apigateway.describe_usage_plans": MagicMock(
  1467. side_effect=ValueError("error")
  1468. )
  1469. },
  1470. ):
  1471. result = boto_apigateway.usage_plan_present(
  1472. "name",
  1473. "plan_name",
  1474. throttle={"rateLimit": throttle_rateLimit},
  1475. **conn_parameters
  1476. )
  1477. self.assertIn("result", result)
  1478. self.assertEqual(result["result"], False)
  1479. self.assertIn("comment", result)
  1480. self.assertEqual(result["comment"], repr(("error",)))
  1481. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1482. def test_usage_plan_present_if_IOError_is_raised(self, *args):
  1483. """
  1484. Tests error processing for the case when IOError is raised when creating a usage plan
  1485. """
  1486. with patch.dict(
  1487. boto_apigateway.__salt__,
  1488. {
  1489. "boto_apigateway.describe_usage_plans": MagicMock(
  1490. side_effect=IOError("error")
  1491. )
  1492. },
  1493. ):
  1494. result = boto_apigateway.usage_plan_present(
  1495. "name",
  1496. "plan_name",
  1497. throttle={"rateLimit": throttle_rateLimit},
  1498. **conn_parameters
  1499. )
  1500. self.assertIn("result", result)
  1501. self.assertEqual(result["result"], False)
  1502. self.assertIn("comment", result)
  1503. self.assertEqual(result["comment"], repr(("error",)))
  1504. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1505. def test_usage_plan_absent_if_describe_fails(self, *args):
  1506. """
  1507. Tests correct error processing for describe_usage_plan failure
  1508. """
  1509. with patch.dict(
  1510. boto_apigateway.__salt__,
  1511. {
  1512. "boto_apigateway.describe_usage_plans": MagicMock(
  1513. return_value={"error": "error"}
  1514. )
  1515. },
  1516. ):
  1517. result = {}
  1518. result = boto_apigateway.usage_plan_absent(
  1519. "name", "plan_name", **conn_parameters
  1520. )
  1521. self.assertIn("result", result)
  1522. self.assertEqual(result["result"], False)
  1523. self.assertIn("comment", result)
  1524. self.assertEqual(
  1525. result["comment"], "Failed to describe existing usage plans"
  1526. )
  1527. self.assertIn("changes", result)
  1528. self.assertEqual(result["changes"], {})
  1529. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1530. def test_usage_plan_absent_if_plan_is_not_present(self, *args):
  1531. """
  1532. Tests behavior for the case when the plan that needs to be absent does not exist
  1533. """
  1534. with patch.dict(
  1535. boto_apigateway.__salt__,
  1536. {
  1537. "boto_apigateway.describe_usage_plans": MagicMock(
  1538. return_value={"plans": []}
  1539. )
  1540. },
  1541. ):
  1542. result = {}
  1543. result = boto_apigateway.usage_plan_absent(
  1544. "name", "plan_name", **conn_parameters
  1545. )
  1546. self.assertIn("result", result)
  1547. self.assertEqual(result["result"], True)
  1548. self.assertIn("comment", result)
  1549. self.assertEqual(
  1550. result["comment"], "Usage plan plan_name does not exist already"
  1551. )
  1552. self.assertIn("changes", result)
  1553. self.assertEqual(result["changes"], {})
  1554. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1555. def test_usage_plan_absent_if_plan_is_present_but_test_option_is_set(self, *args):
  1556. """
  1557. Tests behavior for the case when usage plan needs to be deleted by tests option is set
  1558. """
  1559. with patch.dict(boto_apigateway.__opts__, {"test": True}):
  1560. with patch.dict(
  1561. boto_apigateway.__salt__,
  1562. {
  1563. "boto_apigateway.describe_usage_plans": MagicMock(
  1564. return_value={"plans": [{"id": "id"}]}
  1565. )
  1566. },
  1567. ):
  1568. result = {}
  1569. result = boto_apigateway.usage_plan_absent(
  1570. "name", "plan_name", **conn_parameters
  1571. )
  1572. self.assertIn("result", result)
  1573. self.assertEqual(result["result"], None)
  1574. self.assertIn("comment", result)
  1575. self.assertEqual(
  1576. result["comment"],
  1577. "Usage plan plan_name exists and would be deleted",
  1578. )
  1579. self.assertIn("changes", result)
  1580. self.assertEqual(result["changes"], {})
  1581. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1582. def test_usage_plan_absent_if_plan_is_present_but_delete_fails(self, *args):
  1583. """
  1584. Tests correct error processing when deleting a usage plan fails
  1585. """
  1586. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1587. with patch.dict(
  1588. boto_apigateway.__salt__,
  1589. {
  1590. "boto_apigateway.describe_usage_plans": MagicMock(
  1591. return_value={"plans": [{"id": "id"}]}
  1592. ),
  1593. "boto_apigateway.delete_usage_plan": MagicMock(
  1594. return_value={"error": "error"}
  1595. ),
  1596. },
  1597. ):
  1598. result = boto_apigateway.usage_plan_absent(
  1599. "name", "plan_name", **conn_parameters
  1600. )
  1601. self.assertIn("result", result)
  1602. self.assertEqual(result["result"], False)
  1603. self.assertIn("comment", result)
  1604. self.assertEqual(
  1605. result["comment"],
  1606. "Failed to delete usage plan plan_name, "
  1607. + repr({"error": "error"}),
  1608. )
  1609. self.assertIn("changes", result)
  1610. self.assertEqual(result["changes"], {})
  1611. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1612. def test_usage_plan_absent_if_plan_has_been_deleted(self, *args):
  1613. """
  1614. Tests successful case for deleting a usage plan
  1615. """
  1616. with patch.dict(boto_apigateway.__opts__, {"test": False}):
  1617. with patch.dict(
  1618. boto_apigateway.__salt__,
  1619. {
  1620. "boto_apigateway.describe_usage_plans": MagicMock(
  1621. return_value={"plans": [{"id": "id"}]}
  1622. ),
  1623. "boto_apigateway.delete_usage_plan": MagicMock(
  1624. return_value={"deleted": True}
  1625. ),
  1626. },
  1627. ):
  1628. result = boto_apigateway.usage_plan_absent(
  1629. "name", "plan_name", **conn_parameters
  1630. )
  1631. self.assertIn("result", result)
  1632. self.assertEqual(result["result"], True)
  1633. self.assertIn("comment", result)
  1634. self.assertEqual(
  1635. result["comment"], "Usage plan plan_name has been deleted"
  1636. )
  1637. self.assertIn("changes", result)
  1638. self.assertEqual(
  1639. result["changes"],
  1640. {"new": {"plan": None}, "old": {"plan": {"id": "id"}}},
  1641. )
  1642. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1643. def test_usage_plan_absent_if_ValueError_is_raised(self, *args):
  1644. """
  1645. Tests correct error processing for the case when ValueError is raised when deleting a usage plan
  1646. """
  1647. with patch.dict(
  1648. boto_apigateway.__salt__,
  1649. {
  1650. "boto_apigateway.describe_usage_plans": MagicMock(
  1651. side_effect=ValueError("error")
  1652. )
  1653. },
  1654. ):
  1655. result = boto_apigateway.usage_plan_absent(
  1656. "name", "plan_name", **conn_parameters
  1657. )
  1658. self.assertIn("result", result)
  1659. self.assertEqual(result["result"], False)
  1660. self.assertIn("comment", result)
  1661. self.assertEqual(result["comment"], repr(("error",)))
  1662. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1663. def test_usage_plan_absent_if_IOError_is_raised(self, *args):
  1664. """
  1665. Tests correct error processing for the case when IOError is raised when deleting a usage plan
  1666. """
  1667. with patch.dict(
  1668. boto_apigateway.__salt__,
  1669. {
  1670. "boto_apigateway.describe_usage_plans": MagicMock(
  1671. side_effect=IOError("error")
  1672. )
  1673. },
  1674. ):
  1675. result = boto_apigateway.usage_plan_absent(
  1676. "name", "plan_name", **conn_parameters
  1677. )
  1678. self.assertIn("result", result)
  1679. self.assertEqual(result["result"], False)
  1680. self.assertIn("comment", result)
  1681. self.assertEqual(result["comment"], repr(("error",)))
  1682. @skipIf(HAS_BOTO is False, "The boto module must be installed.")
  1683. @skipIf(
  1684. _has_required_boto() is False,
  1685. "The boto3 module must be greater than"
  1686. " or equal to version {0}".format(required_boto3_version),
  1687. )
  1688. @skipIf(
  1689. _has_required_botocore() is False,
  1690. "The botocore module must be greater than"
  1691. " or equal to version {0}".format(required_botocore_version),
  1692. )
  1693. class BotoApiGatewayUsagePlanAssociationTestCase(
  1694. BotoApiGatewayStateTestCaseBase, BotoApiGatewayTestCaseMixin
  1695. ):
  1696. """
  1697. TestCase for salt.modules.boto_apigateway state.module, usage_plans_association portion
  1698. """
  1699. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1700. def test_usage_plan_association_present_if_describe_fails(self, *args):
  1701. """
  1702. Tests correct error processing for describe_usage_plan failure
  1703. """
  1704. with patch.dict(
  1705. boto_apigateway.__salt__,
  1706. {
  1707. "boto_apigateway.describe_usage_plans": MagicMock(
  1708. return_value={"error": "error"}
  1709. )
  1710. },
  1711. ):
  1712. result = boto_apigateway.usage_plan_association_present(
  1713. "name", "plan_name", [association_stage_1], **conn_parameters
  1714. )
  1715. self.assertIn("result", result)
  1716. self.assertEqual(result["result"], False)
  1717. self.assertIn("comment", result)
  1718. self.assertEqual(
  1719. result["comment"], "Failed to describe existing usage plans"
  1720. )
  1721. self.assertIn("changes", result)
  1722. self.assertEqual(result["changes"], {})
  1723. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1724. def test_usage_plan_association_present_if_plan_is_not_present(self, *args):
  1725. """
  1726. Tests correct error processing if a plan for which association has been requested is not present
  1727. """
  1728. with patch.dict(
  1729. boto_apigateway.__salt__,
  1730. {
  1731. "boto_apigateway.describe_usage_plans": MagicMock(
  1732. return_value={"plans": []}
  1733. )
  1734. },
  1735. ):
  1736. result = boto_apigateway.usage_plan_association_present(
  1737. "name", "plan_name", [association_stage_1], **conn_parameters
  1738. )
  1739. self.assertIn("result", result)
  1740. self.assertEqual(result["result"], False)
  1741. self.assertIn("comment", result)
  1742. self.assertEqual(result["comment"], "Usage plan plan_name does not exist")
  1743. self.assertIn("changes", result)
  1744. self.assertEqual(result["changes"], {})
  1745. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1746. def test_usage_plan_association_present_if_multiple_plans_with_the_same_name_exist(
  1747. self, *args
  1748. ):
  1749. """
  1750. Tests correct error processing for the case when multiple plans with the same name exist
  1751. """
  1752. with patch.dict(
  1753. boto_apigateway.__salt__,
  1754. {
  1755. "boto_apigateway.describe_usage_plans": MagicMock(
  1756. return_value={"plans": [{"id": "id1"}, {"id": "id2"}]}
  1757. )
  1758. },
  1759. ):
  1760. result = boto_apigateway.usage_plan_association_present(
  1761. "name", "plan_name", [association_stage_1], **conn_parameters
  1762. )
  1763. self.assertIn("result", result)
  1764. self.assertEqual(result["result"], False)
  1765. self.assertIn("comment", result)
  1766. self.assertEqual(
  1767. result["comment"],
  1768. "There are multiple usage plans with the same name - it is not supported",
  1769. )
  1770. self.assertIn("changes", result)
  1771. self.assertEqual(result["changes"], {})
  1772. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1773. def test_usage_plan_association_present_if_association_already_exists(self, *args):
  1774. """
  1775. Tests the behavior for the case when requested association is already present
  1776. """
  1777. with patch.dict(
  1778. boto_apigateway.__salt__,
  1779. {
  1780. "boto_apigateway.describe_usage_plans": MagicMock(
  1781. return_value={
  1782. "plans": [{"id": "id1", "apiStages": [association_stage_1]}]
  1783. }
  1784. )
  1785. },
  1786. ):
  1787. result = boto_apigateway.usage_plan_association_present(
  1788. "name", "plan_name", [association_stage_1], **conn_parameters
  1789. )
  1790. self.assertIn("result", result)
  1791. self.assertEqual(result["result"], True)
  1792. self.assertIn("comment", result)
  1793. self.assertEqual(
  1794. result["comment"], "Usage plan is already asssociated to all api stages"
  1795. )
  1796. self.assertIn("changes", result)
  1797. self.assertEqual(result["changes"], {})
  1798. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1799. def test_usage_plan_association_present_if_update_fails(self, *args):
  1800. """
  1801. Tests correct error processing for the case when adding associations fails
  1802. """
  1803. with patch.dict(
  1804. boto_apigateway.__salt__,
  1805. {
  1806. "boto_apigateway.describe_usage_plans": MagicMock(
  1807. return_value={
  1808. "plans": [{"id": "id1", "apiStages": [association_stage_1]}]
  1809. }
  1810. ),
  1811. "boto_apigateway.attach_usage_plan_to_apis": MagicMock(
  1812. return_value={"error": "error"}
  1813. ),
  1814. },
  1815. ):
  1816. result = boto_apigateway.usage_plan_association_present(
  1817. "name", "plan_name", [association_stage_2], **conn_parameters
  1818. )
  1819. self.assertIn("result", result)
  1820. self.assertEqual(result["result"], False)
  1821. self.assertIn("comment", result)
  1822. self.assertTrue(
  1823. result["comment"].startswith("Failed to associate a usage plan")
  1824. )
  1825. self.assertIn("changes", result)
  1826. self.assertEqual(result["changes"], {})
  1827. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1828. def test_usage_plan_association_present_success(self, *args):
  1829. """
  1830. Tests successful case for adding usage plan associations to a given api stage
  1831. """
  1832. with patch.dict(
  1833. boto_apigateway.__salt__,
  1834. {
  1835. "boto_apigateway.describe_usage_plans": MagicMock(
  1836. return_value={
  1837. "plans": [{"id": "id1", "apiStages": [association_stage_1]}]
  1838. }
  1839. ),
  1840. "boto_apigateway.attach_usage_plan_to_apis": MagicMock(
  1841. return_value={
  1842. "result": {
  1843. "apiStages": [association_stage_1, association_stage_2]
  1844. }
  1845. }
  1846. ),
  1847. },
  1848. ):
  1849. result = boto_apigateway.usage_plan_association_present(
  1850. "name", "plan_name", [association_stage_2], **conn_parameters
  1851. )
  1852. self.assertIn("result", result)
  1853. self.assertEqual(result["result"], True)
  1854. self.assertIn("comment", result)
  1855. self.assertEqual(
  1856. result["comment"], "successfully associated usage plan to apis"
  1857. )
  1858. self.assertIn("changes", result)
  1859. self.assertEqual(
  1860. result["changes"],
  1861. {
  1862. "new": [association_stage_1, association_stage_2],
  1863. "old": [association_stage_1],
  1864. },
  1865. )
  1866. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1867. def test_usage_plan_association_present_if_value_error_is_thrown(self, *args):
  1868. """
  1869. Tests correct error processing for the case when IOError is raised while trying to set usage plan associations
  1870. """
  1871. with patch.dict(
  1872. boto_apigateway.__salt__,
  1873. {
  1874. "boto_apigateway.describe_usage_plans": MagicMock(
  1875. side_effect=ValueError("error")
  1876. )
  1877. },
  1878. ):
  1879. result = boto_apigateway.usage_plan_association_present(
  1880. "name", "plan_name", [], **conn_parameters
  1881. )
  1882. self.assertIn("result", result)
  1883. self.assertEqual(result["result"], False)
  1884. self.assertIn("comment", result)
  1885. self.assertEqual(result["comment"], repr(("error",)))
  1886. self.assertIn("changes", result)
  1887. self.assertEqual(result["changes"], {})
  1888. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1889. def test_usage_plan_association_present_if_io_error_is_thrown(self, *args):
  1890. """
  1891. Tests correct error processing for the case when IOError is raised while trying to set usage plan associations
  1892. """
  1893. with patch.dict(
  1894. boto_apigateway.__salt__,
  1895. {
  1896. "boto_apigateway.describe_usage_plans": MagicMock(
  1897. side_effect=IOError("error")
  1898. )
  1899. },
  1900. ):
  1901. result = boto_apigateway.usage_plan_association_present(
  1902. "name", "plan_name", [], **conn_parameters
  1903. )
  1904. self.assertIn("result", result)
  1905. self.assertEqual(result["result"], False)
  1906. self.assertIn("comment", result)
  1907. self.assertEqual(result["comment"], repr(("error",)))
  1908. self.assertIn("changes", result)
  1909. self.assertEqual(result["changes"], {})
  1910. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1911. def test_usage_plan_association_absent_if_describe_fails(self, *args):
  1912. """
  1913. Tests correct error processing for describe_usage_plan failure
  1914. """
  1915. with patch.dict(
  1916. boto_apigateway.__salt__,
  1917. {
  1918. "boto_apigateway.describe_usage_plans": MagicMock(
  1919. return_value={"error": "error"}
  1920. )
  1921. },
  1922. ):
  1923. result = boto_apigateway.usage_plan_association_absent(
  1924. "name", "plan_name", [association_stage_1], **conn_parameters
  1925. )
  1926. self.assertIn("result", result)
  1927. self.assertEqual(result["result"], False)
  1928. self.assertIn("comment", result)
  1929. self.assertEqual(
  1930. result["comment"], "Failed to describe existing usage plans"
  1931. )
  1932. self.assertIn("changes", result)
  1933. self.assertEqual(result["changes"], {})
  1934. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1935. def test_usage_plan_association_absent_if_plan_is_not_present(self, *args):
  1936. """
  1937. Tests error processing for the case when plan for which associations need to be modified is not present
  1938. """
  1939. with patch.dict(
  1940. boto_apigateway.__salt__,
  1941. {
  1942. "boto_apigateway.describe_usage_plans": MagicMock(
  1943. return_value={"plans": []}
  1944. )
  1945. },
  1946. ):
  1947. result = boto_apigateway.usage_plan_association_absent(
  1948. "name", "plan_name", [association_stage_1], **conn_parameters
  1949. )
  1950. self.assertIn("result", result)
  1951. self.assertEqual(result["result"], False)
  1952. self.assertIn("comment", result)
  1953. self.assertEqual(result["comment"], "Usage plan plan_name does not exist")
  1954. self.assertIn("changes", result)
  1955. self.assertEqual(result["changes"], {})
  1956. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1957. def test_usage_plan_association_absent_if_multiple_plans_with_the_same_name_exist(
  1958. self, *args
  1959. ):
  1960. """
  1961. Tests the case when there are multiple plans with the same name but different Ids
  1962. """
  1963. with patch.dict(
  1964. boto_apigateway.__salt__,
  1965. {
  1966. "boto_apigateway.describe_usage_plans": MagicMock(
  1967. return_value={"plans": [{"id": "id1"}, {"id": "id2"}]}
  1968. )
  1969. },
  1970. ):
  1971. result = boto_apigateway.usage_plan_association_absent(
  1972. "name", "plan_name", [association_stage_1], **conn_parameters
  1973. )
  1974. self.assertIn("result", result)
  1975. self.assertEqual(result["result"], False)
  1976. self.assertIn("comment", result)
  1977. self.assertEqual(
  1978. result["comment"],
  1979. "There are multiple usage plans with the same name - it is not supported",
  1980. )
  1981. self.assertIn("changes", result)
  1982. self.assertEqual(result["changes"], {})
  1983. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  1984. def test_usage_plan_association_absent_if_plan_has_no_associations(self, *args):
  1985. """
  1986. Tests the case when the plan has no associations at all
  1987. """
  1988. with patch.dict(
  1989. boto_apigateway.__salt__,
  1990. {
  1991. "boto_apigateway.describe_usage_plans": MagicMock(
  1992. return_value={"plans": [{"id": "id1", "apiStages": []}]}
  1993. )
  1994. },
  1995. ):
  1996. result = boto_apigateway.usage_plan_association_absent(
  1997. "name", "plan_name", [association_stage_1], **conn_parameters
  1998. )
  1999. self.assertIn("result", result)
  2000. self.assertEqual(result["result"], True)
  2001. self.assertIn("comment", result)
  2002. self.assertEqual(
  2003. result["comment"],
  2004. "Usage plan plan_name has no associated stages already",
  2005. )
  2006. self.assertIn("changes", result)
  2007. self.assertEqual(result["changes"], {})
  2008. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  2009. def test_usage_plan_association_absent_if_plan_has_no_specific_association(
  2010. self, *args
  2011. ):
  2012. """
  2013. Tests the case when requested association is not present already
  2014. """
  2015. with patch.dict(
  2016. boto_apigateway.__salt__,
  2017. {
  2018. "boto_apigateway.describe_usage_plans": MagicMock(
  2019. return_value={
  2020. "plans": [{"id": "id1", "apiStages": [association_stage_1]}]
  2021. }
  2022. )
  2023. },
  2024. ):
  2025. result = boto_apigateway.usage_plan_association_absent(
  2026. "name", "plan_name", [association_stage_2], **conn_parameters
  2027. )
  2028. self.assertIn("result", result)
  2029. self.assertEqual(result["result"], True)
  2030. self.assertIn("comment", result)
  2031. self.assertEqual(
  2032. result["comment"],
  2033. "Usage plan is already not asssociated to any api stages",
  2034. )
  2035. self.assertIn("changes", result)
  2036. self.assertEqual(result["changes"], {})
  2037. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  2038. def test_usage_plan_association_absent_if_detaching_association_fails(self, *args):
  2039. """
  2040. Tests correct error processing when detaching the usage plan from the api function is called
  2041. """
  2042. with patch.dict(
  2043. boto_apigateway.__salt__,
  2044. {
  2045. "boto_apigateway.describe_usage_plans": MagicMock(
  2046. return_value={
  2047. "plans": [
  2048. {
  2049. "id": "id1",
  2050. "apiStages": [association_stage_1, association_stage_2],
  2051. }
  2052. ]
  2053. }
  2054. ),
  2055. "boto_apigateway.detach_usage_plan_from_apis": MagicMock(
  2056. return_value={"error": "error"}
  2057. ),
  2058. },
  2059. ):
  2060. result = boto_apigateway.usage_plan_association_absent(
  2061. "name", "plan_name", [association_stage_2], **conn_parameters
  2062. )
  2063. self.assertIn("result", result)
  2064. self.assertEqual(result["result"], False)
  2065. self.assertIn("comment", result)
  2066. self.assertTrue(
  2067. result["comment"].startswith(
  2068. "Failed to disassociate a usage plan plan_name from the apis"
  2069. )
  2070. )
  2071. self.assertIn("changes", result)
  2072. self.assertEqual(result["changes"], {})
  2073. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  2074. def test_usage_plan_association_absent_success(self, *args):
  2075. """
  2076. Tests successful case of disaccosiation the usage plan from api stages
  2077. """
  2078. with patch.dict(
  2079. boto_apigateway.__salt__,
  2080. {
  2081. "boto_apigateway.describe_usage_plans": MagicMock(
  2082. return_value={
  2083. "plans": [
  2084. {
  2085. "id": "id1",
  2086. "apiStages": [association_stage_1, association_stage_2],
  2087. }
  2088. ]
  2089. }
  2090. ),
  2091. "boto_apigateway.detach_usage_plan_from_apis": MagicMock(
  2092. return_value={"result": {"apiStages": [association_stage_1]}}
  2093. ),
  2094. },
  2095. ):
  2096. result = boto_apigateway.usage_plan_association_absent(
  2097. "name", "plan_name", [association_stage_2], **conn_parameters
  2098. )
  2099. self.assertIn("result", result)
  2100. self.assertEqual(result["result"], True)
  2101. self.assertIn("comment", result)
  2102. self.assertEqual(
  2103. result["comment"], "successfully disassociated usage plan from apis"
  2104. )
  2105. self.assertIn("changes", result)
  2106. self.assertEqual(
  2107. result["changes"],
  2108. {
  2109. "new": [association_stage_1],
  2110. "old": [association_stage_1, association_stage_2],
  2111. },
  2112. )
  2113. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  2114. def test_usage_plan_association_absent_if_ValueError_is_raised(self, *args):
  2115. """
  2116. Tests correct error processing for the case where ValueError is raised while trying to remove plan associations
  2117. """
  2118. with patch.dict(
  2119. boto_apigateway.__salt__,
  2120. {
  2121. "boto_apigateway.describe_usage_plans": MagicMock(
  2122. side_effect=ValueError("error")
  2123. )
  2124. },
  2125. ):
  2126. result = boto_apigateway.usage_plan_association_absent(
  2127. "name", "plan_name", [association_stage_1], **conn_parameters
  2128. )
  2129. self.assertIn("result", result)
  2130. self.assertEqual(result["result"], False)
  2131. self.assertIn("comment", result)
  2132. self.assertEqual(result["comment"], repr(("error",)))
  2133. self.assertIn("changes", result)
  2134. self.assertEqual(result["changes"], {})
  2135. @pytest.mark.slow_test(seconds=60) # Test takes >30 and <=60 seconds
  2136. def test_usage_plan_association_absent_if_IOError_is_raised(self, *args):
  2137. """
  2138. Tests correct error processing for the case where IOError exception is raised while trying to remove plan associations
  2139. """
  2140. with patch.dict(
  2141. boto_apigateway.__salt__,
  2142. {
  2143. "boto_apigateway.describe_usage_plans": MagicMock(
  2144. side_effect=IOError("error")
  2145. )
  2146. },
  2147. ):
  2148. result = boto_apigateway.usage_plan_association_absent(
  2149. "name", "plan_name", [association_stage_1], **conn_parameters
  2150. )
  2151. self.assertIn("result", result)
  2152. self.assertEqual(result["result"], False)
  2153. self.assertIn("comment", result)
  2154. self.assertEqual(result["comment"], repr(("error",)))
  2155. self.assertIn("changes", result)
  2156. self.assertEqual(result["changes"], {})