diff --git a/.gitignore b/.gitignore index 0e397fc3..16a48619 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,7 @@ lambda/mbj-newdwh2021-staging-PublishFromLog/node_modules/* __pycache__/ .env **/.vscode/settings.json + +# python test +.coverage +.report/ \ No newline at end of file diff --git a/ecs/crm-datafetch/.env.example b/ecs/crm-datafetch/.env.example new file mode 100644 index 00000000..4f105876 --- /dev/null +++ b/ecs/crm-datafetch/.env.example @@ -0,0 +1,17 @@ +CRM_AUTH_DOMAIN=test +CRM_USER_NAME=test +CRM_USER_PASSWORD=test +CRM_USER_SECURITY_TOKEN=test +CRM_CONFIG_BUCKET=test +CRM_BACKUP_BUCKET=test +IMPORT_DATA_BUCKET=test +OBJECT_INFO_FOLDER=test +OBJECT_INFO_FILENAME=test +PROCESS_RESULT_FOLDER=test +PROCESS_RESULT_FILENAME=test +LAST_FETCH_DATE_FOLDER=test +CRM_IMPORT_DATA_FOLDER=test +LAST_FETCH_DATE_BACKUP_FOLDER=test +RESPONSE_JSON_BACKUP_FOLDER=test +CRM_IMPORT_DATA_BACKUP_FOLDER=test +LOG_LEVEL=INFO \ No newline at end of file diff --git a/ecs/crm-datafetch/.vscode/python.code-snippets b/ecs/crm-datafetch/.vscode/python.code-snippets new file mode 100644 index 00000000..3b658c8d --- /dev/null +++ b/ecs/crm-datafetch/.vscode/python.code-snippets @@ -0,0 +1,17 @@ +{ + "Generate Test docstring": { + "scope": "python", + "prefix": "\"\"\"\"\"\"", + "body": [ + "\"\"\"", + "Cases:", + " $1", + "Arranges:", + " $2", + "Expects:", + " $3", + "\"\"\"" + ], + "description": "Test docstring (User Snippets)" + } +} diff --git a/ecs/crm-datafetch/Pipfile b/ecs/crm-datafetch/Pipfile index 33501d98..82796598 100644 --- a/ecs/crm-datafetch/Pipfile +++ b/ecs/crm-datafetch/Pipfile @@ -3,6 +3,11 @@ url = "https://pypi.org/simple" verify_ssl = true name = "pypi" +[scripts] +test = "pytest tests/" +"test:cov" = "pytest --cov=src --cov-branch --cov-report=term-missing tests/" +"test:report" = "pytest --cov=src --cov-branch --cov-report=term-missing --html=.report/test_result.html tests/" + [packages] boto3 = "*" simple-salesforce = "*" @@ -11,6 +16,10 @@ tenacity = "*" [dev-packages] autopep8 = "*" flake8 = "*" +pytest = "*" +pytest-cov = "*" +pytest-html = "*" +moto = "*" [requires] python_version = "3.8" diff --git a/ecs/crm-datafetch/Pipfile.lock b/ecs/crm-datafetch/Pipfile.lock index c8c60540..cfd16ed2 100644 --- a/ecs/crm-datafetch/Pipfile.lock +++ b/ecs/crm-datafetch/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "ec1d83143aff859500979be73f67196dcfe2298ad3553a7d81ad0605a277d672" + "sha256": "7006de596d6123ecd56760b584ab75430fa6bcfc0ecd3fdf49f08368ff53477d" }, "pipfile-spec": 6, "requires": { @@ -18,11 +18,11 @@ "default": { "attrs": { "hashes": [ - "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", - "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" + "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", + "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==21.4.0" + "markers": "python_version >= '3.5'", + "version": "==22.1.0" }, "authlib": { "hashes": [ @@ -33,19 +33,19 @@ }, "boto3": { "hashes": [ - "sha256:5c775dcb12ca5d6be3f5aa3c49d77783faa64eb30fd3f4af93ff116bb42f9ffb", - "sha256:5d9bcc355cf6edd7f3849fedac4252e12a0aa2b436cdbc0d4371b16a0f852a30" + "sha256:aec404d06690a0ca806592efc6bbe30a9ac88fa2ad73b6d907f7794a8b3b1459", + "sha256:df3d6ef02304bd7c3711090936c092d5db735dda109decac1e236c3ef7fdb7af" ], "index": "pypi", - "version": "==1.24.34" + "version": "==1.24.42" }, "botocore": { "hashes": [ - "sha256:0d824a5315f5f5c3bea53c14107a69695ef43190edf647f1281bac8f172ca77c", - "sha256:9c695d47f1f1212f3e306e51f7bacdf67e58055194ddcf7d8296660b124cf135" + "sha256:38a180a6666c5a9b069a75ec3cf374ff2a64c3e90c9f24a916858bcdeb04456d", + "sha256:f8e6c2f69a9d577fb9c69e4e74f49f4315a48decee0e7dc88b6e470772110884" ], "markers": "python_version >= '3.7'", - "version": "==1.27.34" + "version": "==1.27.42" }, "cached-property": { "hashes": [ @@ -352,11 +352,11 @@ }, "urllib3": { "hashes": [ - "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec", - "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6" + "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc", + "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.10" + "version": "==1.26.11" }, "zeep": { "hashes": [ @@ -368,6 +368,14 @@ } }, "develop": { + "attrs": { + "hashes": [ + "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", + "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" + ], + "markers": "python_version >= '3.5'", + "version": "==22.1.0" + }, "autopep8": { "hashes": [ "sha256:44f0932855039d2c15c4510d6df665e4730f2b8582704fa48f9c55bd3e17d979", @@ -376,36 +384,419 @@ "index": "pypi", "version": "==1.6.0" }, - "flake8": { + "boto3": { "hashes": [ - "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d", - "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d" + "sha256:aec404d06690a0ca806592efc6bbe30a9ac88fa2ad73b6d907f7794a8b3b1459", + "sha256:df3d6ef02304bd7c3711090936c092d5db735dda109decac1e236c3ef7fdb7af" ], "index": "pypi", - "version": "==4.0.1" + "version": "==1.24.42" + }, + "botocore": { + "hashes": [ + "sha256:38a180a6666c5a9b069a75ec3cf374ff2a64c3e90c9f24a916858bcdeb04456d", + "sha256:f8e6c2f69a9d577fb9c69e4e74f49f4315a48decee0e7dc88b6e470772110884" + ], + "markers": "python_version >= '3.7'", + "version": "==1.27.42" + }, + "certifi": { + "hashes": [ + "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", + "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" + ], + "markers": "python_version >= '3.6'", + "version": "==2022.6.15" + }, + "cffi": { + "hashes": [ + "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5", + "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef", + "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104", + "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426", + "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405", + "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375", + "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a", + "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e", + "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc", + "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf", + "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185", + "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497", + "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3", + "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35", + "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c", + "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83", + "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21", + "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca", + "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984", + "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac", + "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd", + "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee", + "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a", + "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2", + "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192", + "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7", + "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585", + "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f", + "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e", + "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27", + "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b", + "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e", + "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e", + "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d", + "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c", + "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415", + "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82", + "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02", + "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314", + "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325", + "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c", + "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3", + "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914", + "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045", + "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d", + "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9", + "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5", + "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2", + "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c", + "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3", + "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2", + "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8", + "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d", + "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d", + "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9", + "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162", + "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76", + "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4", + "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e", + "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9", + "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6", + "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b", + "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01", + "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0" + ], + "version": "==1.15.1" + }, + "charset-normalizer": { + "hashes": [ + "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5", + "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413" + ], + "markers": "python_version >= '3.6'", + "version": "==2.1.0" + }, + "coverage": { + "extras": [ + "toml" + ], + "hashes": [ + "sha256:0895ea6e6f7f9939166cc835df8fa4599e2d9b759b02d1521b574e13b859ac32", + "sha256:0f211df2cba951ffcae210ee00e54921ab42e2b64e0bf2c0befc977377fb09b7", + "sha256:147605e1702d996279bb3cc3b164f408698850011210d133a2cb96a73a2f7996", + "sha256:24b04d305ea172ccb21bee5bacd559383cba2c6fcdef85b7701cf2de4188aa55", + "sha256:25b7ec944f114f70803d6529394b64f8749e93cbfac0fe6c5ea1b7e6c14e8a46", + "sha256:2b20286c2b726f94e766e86a3fddb7b7e37af5d0c635bdfa7e4399bc523563de", + "sha256:2dff52b3e7f76ada36f82124703f4953186d9029d00d6287f17c68a75e2e6039", + "sha256:2f8553878a24b00d5ab04b7a92a2af50409247ca5c4b7a2bf4eabe94ed20d3ee", + "sha256:3def6791adf580d66f025223078dc84c64696a26f174131059ce8e91452584e1", + "sha256:422fa44070b42fef9fb8dabd5af03861708cdd6deb69463adc2130b7bf81332f", + "sha256:4f89d8e03c8a3757aae65570d14033e8edf192ee9298303db15955cadcff0c63", + "sha256:5336e0352c0b12c7e72727d50ff02557005f79a0b8dcad9219c7c4940a930083", + "sha256:54d8d0e073a7f238f0666d3c7c0d37469b2aa43311e4024c925ee14f5d5a1cbe", + "sha256:5ef42e1db047ca42827a85e34abe973971c635f83aed49611b7f3ab49d0130f0", + "sha256:5f65e5d3ff2d895dab76b1faca4586b970a99b5d4b24e9aafffc0ce94a6022d6", + "sha256:6c3ccfe89c36f3e5b9837b9ee507472310164f352c9fe332120b764c9d60adbe", + "sha256:6d0b48aff8e9720bdec315d67723f0babd936a7211dc5df453ddf76f89c59933", + "sha256:6fe75dcfcb889b6800f072f2af5a331342d63d0c1b3d2bf0f7b4f6c353e8c9c0", + "sha256:79419370d6a637cb18553ecb25228893966bd7935a9120fa454e7076f13b627c", + "sha256:7bb00521ab4f99fdce2d5c05a91bddc0280f0afaee0e0a00425e28e209d4af07", + "sha256:80db4a47a199c4563d4a25919ff29c97c87569130375beca3483b41ad5f698e8", + "sha256:866ebf42b4c5dbafd64455b0a1cd5aa7b4837a894809413b930026c91e18090b", + "sha256:8af6c26ba8df6338e57bedbf916d76bdae6308e57fc8f14397f03b5da8622b4e", + "sha256:a13772c19619118903d65a91f1d5fea84be494d12fd406d06c849b00d31bf120", + "sha256:a697977157adc052284a7160569b36a8bbec09db3c3220642e6323b47cec090f", + "sha256:a9032f9b7d38bdf882ac9f66ebde3afb8145f0d4c24b2e600bc4c6304aafb87e", + "sha256:b5e28db9199dd3833cc8a07fa6cf429a01227b5d429facb56eccd765050c26cd", + "sha256:c77943ef768276b61c96a3eb854eba55633c7a3fddf0a79f82805f232326d33f", + "sha256:d230d333b0be8042ac34808ad722eabba30036232e7a6fb3e317c49f61c93386", + "sha256:d4548be38a1c810d79e097a38107b6bf2ff42151900e47d49635be69943763d8", + "sha256:d4e7ced84a11c10160c0697a6cc0b214a5d7ab21dfec1cd46e89fbf77cc66fae", + "sha256:d56f105592188ce7a797b2bd94b4a8cb2e36d5d9b0d8a1d2060ff2a71e6b9bbc", + "sha256:d714af0bdba67739598849c9f18efdcc5a0412f4993914a0ec5ce0f1e864d783", + "sha256:d774d9e97007b018a651eadc1b3970ed20237395527e22cbeb743d8e73e0563d", + "sha256:e0524adb49c716ca763dbc1d27bedce36b14f33e6b8af6dba56886476b42957c", + "sha256:e2618cb2cf5a7cc8d698306e42ebcacd02fb7ef8cfc18485c59394152c70be97", + "sha256:e36750fbbc422c1c46c9d13b937ab437138b998fe74a635ec88989afb57a3978", + "sha256:edfdabe7aa4f97ed2b9dd5dde52d2bb29cb466993bb9d612ddd10d0085a683cf", + "sha256:f22325010d8824594820d6ce84fa830838f581a7fd86a9235f0d2ed6deb61e29", + "sha256:f23876b018dfa5d3e98e96f5644b109090f16a4acb22064e0f06933663005d39", + "sha256:f7bd0ffbcd03dc39490a1f40b2669cc414fae0c4e16b77bb26806a4d0b7d1452" + ], + "markers": "python_version >= '3.7'", + "version": "==6.4.2" + }, + "cryptography": { + "hashes": [ + "sha256:190f82f3e87033821828f60787cfa42bff98404483577b591429ed99bed39d59", + "sha256:2be53f9f5505673eeda5f2736bea736c40f051a739bfae2f92d18aed1eb54596", + "sha256:30788e070800fec9bbcf9faa71ea6d8068f5136f60029759fd8c3efec3c9dcb3", + "sha256:3d41b965b3380f10e4611dbae366f6dc3cefc7c9ac4e8842a806b9672ae9add5", + "sha256:4c590ec31550a724ef893c50f9a97a0c14e9c851c85621c5650d699a7b88f7ab", + "sha256:549153378611c0cca1042f20fd9c5030d37a72f634c9326e225c9f666d472884", + "sha256:63f9c17c0e2474ccbebc9302ce2f07b55b3b3fcb211ded18a42d5764f5c10a82", + "sha256:6bc95ed67b6741b2607298f9ea4932ff157e570ef456ef7ff0ef4884a134cc4b", + "sha256:7099a8d55cd49b737ffc99c17de504f2257e3787e02abe6d1a6d136574873441", + "sha256:75976c217f10d48a8b5a8de3d70c454c249e4b91851f6838a4e48b8f41eb71aa", + "sha256:7bc997818309f56c0038a33b8da5c0bfbb3f1f067f315f9abd6fc07ad359398d", + "sha256:80f49023dd13ba35f7c34072fa17f604d2f19bf0989f292cedf7ab5770b87a0b", + "sha256:91ce48d35f4e3d3f1d83e29ef4a9267246e6a3be51864a5b7d2247d5086fa99a", + "sha256:a958c52505c8adf0d3822703078580d2c0456dd1d27fabfb6f76fe63d2971cd6", + "sha256:b62439d7cd1222f3da897e9a9fe53bbf5c104fff4d60893ad1355d4c14a24157", + "sha256:b7f8dd0d4c1f21759695c05a5ec8536c12f31611541f8904083f3dc582604280", + "sha256:d204833f3c8a33bbe11eda63a54b1aad7aa7456ed769a982f21ec599ba5fa282", + "sha256:e007f052ed10cc316df59bc90fbb7ff7950d7e2919c9757fd42a2b8ecf8a5f67", + "sha256:f2dcb0b3b63afb6df7fd94ec6fbddac81b5492513f7b0436210d390c14d46ee8", + "sha256:f721d1885ecae9078c3f6bbe8a88bc0786b6e749bf32ccec1ef2b18929a05046", + "sha256:f7a6de3e98771e183645181b3627e2563dcde3ce94a9e42a3f427d2255190327", + "sha256:f8c0a6e9e1dd3eb0414ba320f85da6b0dcbd543126e30fcc546e7372a7fbf3b9" + ], + "markers": "python_version >= '3.6'", + "version": "==37.0.4" + }, + "flake8": { + "hashes": [ + "sha256:44e3ecd719bba1cb2ae65d1b54212cc9df4f5db15ac271f8856e5e6c2eebefed", + "sha256:9c51d3d1426379fd444d3b79eabbeb887849368bd053039066439523d8486961" + ], + "index": "pypi", + "version": "==5.0.1" + }, + "idna": { + "hashes": [ + "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff", + "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d" + ], + "markers": "python_version >= '3.5'", + "version": "==3.3" + }, + "iniconfig": { + "hashes": [ + "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", + "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" + ], + "version": "==1.1.1" + }, + "jinja2": { + "hashes": [ + "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", + "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" + ], + "markers": "python_version >= '3.7'", + "version": "==3.1.2" + }, + "jmespath": { + "hashes": [ + "sha256:02e2e4cc71b5bcab88332eebf907519190dd9e6e82107fa7f83b1003a6252980", + "sha256:90261b206d6defd58fdd5e85f478bf633a2901798906be2ad389150c5c60edbe" + ], + "markers": "python_version >= '3.7'", + "version": "==1.0.1" + }, + "markupsafe": { + "hashes": [ + "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", + "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", + "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", + "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", + "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", + "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", + "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", + "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", + "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", + "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", + "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", + "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", + "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", + "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", + "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", + "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", + "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", + "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", + "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", + "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", + "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", + "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", + "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", + "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", + "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", + "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", + "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", + "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", + "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", + "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", + "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", + "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", + "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", + "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", + "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", + "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", + "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", + "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", + "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", + "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" + ], + "markers": "python_version >= '3.7'", + "version": "==2.1.1" }, "mccabe": { "hashes": [ - "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", - "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f" + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" ], - "version": "==0.6.1" + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "moto": { + "hashes": [ + "sha256:8bb8e267d9b948509d4739d81d995615a193d2c459f5c0a979aaeb0d3bd4b381", + "sha256:cbe8ad8a949f519771e5d25b670738604757fb67cd474d75d14c20677582e81f" + ], + "index": "pypi", + "version": "==3.1.16" + }, + "packaging": { + "hashes": [ + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3" + }, + "pluggy": { + "hashes": [ + "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", + "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" + ], + "markers": "python_version >= '3.6'", + "version": "==1.0.0" + }, + "py": { + "hashes": [ + "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", + "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==1.11.0" }, "pycodestyle": { "hashes": [ - "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20", - "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f" + "sha256:289cdc0969d589d90752582bef6dff57c5fbc6949ee8b013ad6d6449a8ae9437", + "sha256:beaba44501f89d785be791c9462553f06958a221d166c64e1f107320f839acc2" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==2.8.0" + "markers": "python_version >= '3.6'", + "version": "==2.9.0" + }, + "pycparser": { + "hashes": [ + "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9", + "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206" + ], + "version": "==2.21" }, "pyflakes": { "hashes": [ - "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c", - "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e" + "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2", + "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3" + ], + "markers": "python_version >= '3.6'", + "version": "==2.5.0" + }, + "pyparsing": { + "hashes": [ + "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", + "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" + ], + "markers": "python_full_version >= '3.6.8'", + "version": "==3.0.9" + }, + "pytest": { + "hashes": [ + "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c", + "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45" + ], + "index": "pypi", + "version": "==7.1.2" + }, + "pytest-cov": { + "hashes": [ + "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6", + "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470" + ], + "index": "pypi", + "version": "==3.0.0" + }, + "pytest-html": { + "hashes": [ + "sha256:3ee1cf319c913d19fe53aeb0bc400e7b0bc2dbeb477553733db1dad12eb75ee3", + "sha256:b7f82f123936a3f4d2950bc993c2c1ca09ce262c9ae12f9ac763a2401380b455" + ], + "index": "pypi", + "version": "==3.1.1" + }, + "pytest-metadata": { + "hashes": [ + "sha256:39261ee0086f17649b180baf2a8633e1922a4c4b6fcc28a2de7d8127a82541bf", + "sha256:fcd2f416f15be295943527b3c8ba16a44ae5a7141939c90c3dc5ce9d167cf2a5" + ], + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.0.2" + }, + "python-dateutil": { + "hashes": [ + "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", + "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.4.0" + "version": "==2.8.2" + }, + "pytz": { + "hashes": [ + "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7", + "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c" + ], + "version": "==2022.1" + }, + "requests": { + "hashes": [ + "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", + "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" + ], + "markers": "python_version >= '3.7' and python_version < '4'", + "version": "==2.28.1" + }, + "responses": { + "hashes": [ + "sha256:2dcc863ba63963c0c3d9ee3fa9507cbe36b7d7b0fccb4f0bdfd9e96c539b1487", + "sha256:b82502eb5f09a0289d8e209e7bad71ef3978334f56d09b444253d5ad67bf5253" + ], + "markers": "python_version >= '3.7'", + "version": "==0.21.0" + }, + "s3transfer": { + "hashes": [ + "sha256:06176b74f3a15f61f1b4f25a1fc29a4429040b7647133a463da8fa5bd28d5ecd", + "sha256:2ed07d3866f523cc561bf4a00fc5535827981b117dd7876f036b0c1aca42c947" + ], + "markers": "python_version >= '3.7'", + "version": "==0.6.0" + }, + "six": { + "hashes": [ + "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", + "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.16.0" }, "toml": { "hashes": [ @@ -414,6 +805,38 @@ ], "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==0.10.2" + }, + "tomli": { + "hashes": [ + "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", + "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.1" + }, + "urllib3": { + "hashes": [ + "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc", + "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", + "version": "==1.26.11" + }, + "werkzeug": { + "hashes": [ + "sha256:4d7013ef96fd197d1cdeb03e066c6c5a491ccb44758a5b2b91137319383e5a5a", + "sha256:7e1db6a5ba6b9a8be061e47e900456355b8714c0f238b0313f53afce1a55a79a" + ], + "markers": "python_version >= '3.7'", + "version": "==2.2.1" + }, + "xmltodict": { + "hashes": [ + "sha256:341595a488e3e01a85a9d8911d8912fd922ede5fecc4dce437eb4b6c8d037e56", + "sha256:aa89e8fd76320154a40d19a0df04a4695fb9dc5ba977cbb68ab3e4eb225e7852" + ], + "markers": "python_version >= '3.4'", + "version": "==0.13.0" } } } diff --git a/ecs/crm-datafetch/README.md b/ecs/crm-datafetch/README.md index 5a23e966..243e33e5 100644 --- a/ecs/crm-datafetch/README.md +++ b/ecs/crm-datafetch/README.md @@ -67,3 +67,124 @@ - 環境変数が必要な場合、直接設定するか、上記JSONの`"envFile"`に設定されたパスに`.env`ファイルを作成し、環境変数を入力する - キーボードの「F5」キーを押して起動する - デバッグモードで実行されるため、適当なところにブレークポイントを置いてデバッグすることができる + +## ファイル/フォルダ構成 + +`[〇〇処理]モジュール`と記載されているファイルは、設計書に記載のシートと一致したPythonファイルです + +```text +. +├── Dockerfile -- Dokcerイメージを作成するためのファイル +├── Pipfile -- Pipenv(Pythonの仮想環境管理モジュール)で、依存関係を管理するためのファイル +├── Pipfile.lock -- Pipenvでインストールされた依存関係のバージョン固定ファイル +├── README.md -- README +├── main.py -- CRMデータ取得処理のエントリーポイント +├── src/ -- プロダクトコード置き場 +│ ├── aws/ -- AWSのリソース操作関連のモジュール置き場 +│ ├── backup_crm_csv_data_process.py -- [CSVバックアップ処理]モジュール +│ ├── backup_crm_data_process.py -- [CRM電文データバックアップ処理]モジュール +│ ├── check_object_info_process.py -- [オブジェクト情報形式チェック処理]モジュール +│ ├── config/ -- 設定ファイル関連のモジュール置き場 +│ ├── controller.py -- [コントロール処理]モジュール +│ ├── convert_crm_csv_data_process.py -- [CSV変換処理]モジュール +│ ├── converter/ -- CSV変換処理で実際に変換を行うモジュール置き場 +│ ├── copy_crm_csv_data_process.py -- [CSVアップロード処理]モジュール +│ ├── error/ -- 処理エラー発生時カスタム例外モジュール置き場 +│ ├── fetch_crm_data_process.py -- [CRMデータ取得処理]モジュール +│ ├── parser/ -- JSON設定ファイル読み込み処理モジュール置き場 +│ ├── prepare_data_fetch_process.py -- [データ取得準備処理]モジュール +│ ├── salesforce/ -- SalesforceのAPIリクエストモジュール置き場 +│ ├── set_datetime_period_process.py -- [データ取得期間設定処理]モジュール +│ ├── system_var/ -- 環境変数と定数ファイル置き場 +│ ├── upload_last_fetch_datetime_process.py -- [前回取得日時ファイル更新処理]モジュール +│ ├── upload_result_data_process.py -- [取得処理実施結果アップロード処理]モジュール +│ └── util/ -- ユーティリティモジュール置き場 +│ ├── counter_object.py -- リトライ判定のためのカウントアップクラス +│ ├── dict_checker.py -- 辞書型値オブジェクトの設定値チェック用クラス +│ ├── execute_datetime.py -- 取得処理開始年月日時分秒の管理クラス +│ └── logger.py -- ログ管理クラス +│ +└── tests/ -- テストコード置き場 + ├── aws -- AWS操作モジュールのテスト + ├── ... -- src配下のモジュール構成と同じ階層にテストコードを追加していく + ├── conftest.py -- pytestのフィクスチャやフックを管理するファイル + └── docstring_parser.py -- pytest-htmlのレポート出力用のヘルパー +``` + +## 単体テストについて + +### 前提 + +- Pytestを使用する + - +- カバレッジも取得したいため、pytest-covも使う + - +- レポートを出力するため、pytest-htmlを使う + - +- S3をモック化したいため、motoをつかう + - +- CRMはテスト用の環境を使いたいため、newdwh_opeのアドレスでDeveloper組織を登録する + +### テスト環境構築 + +- Pipenvの仮想環境下で、以下のコマンドを実行する + +```sh +pipenv install --dev +``` + +- `.env.example`をコピーし、同じ階層に`.env`を作成する +- `.env`の以下に示す環境変数の値をDeveloper組織のものに書き換える + - CRM_AUTH_DOMAIN + - CRM_USER_NAME + - CRM_USER_PASSWORD + - CRM_USER_SECURITY_TOKEN +- 以下のコマンドを実行して単体テストを起動する + +```sh +pipenv run test:cov +``` + +#### 拡張機能:Python Test Explorer UIを導入する場合 + +- VSCodeの拡張機能メニューから、「Python Test Explorer for Visual Studio Code」をインストール +- コマンドパレットから「Python Configure Tests」を選択、「Pytest」を選択 +- テストメニュー(フラスコのマーク)から、テストを実行することができるようになる + +#### 各コマンドの説明 + +- `pipenv run test` + - pytestを使用してテストを実行する + - `tests`フォルダに配置されているテストモジュールを対象に、単体テストを実行する +- `pipenv run test:cov` + - pytestのテスト終了時にカバレッジを収集する + - 標準出力とカバレッジファイル(`.coverage`)に出力される +- `pipenv run test:report` + - pytestのテスト終了時にテスト結果をHTMLで出力する + - `.report/test_result.html`が出力される + +## 単体テストの追加方法 + +- `tests`フォルダ内に、`src`フォルダの構成と同じようにフォルダを作り、`test_<テスト対象のモジュール名.py>`というファイルを作成する + - 例:`src/aws/s3.py`をテストする場合は`tests/aws/test_s3.py`というファイル名にする +- テスト関数はクラスにまとめて、テストスイートとする +- テスト関数にはドキュメントコメントを付け、テスト観点・準備作業・期待値を記載すること + - 上記が出力されるスニペットを用意してある。 + - `""""""`と入力し、「Test docstring (User Snippets)」を選択し、ドキュメントコメントを挿入できる + +```python + +from src.aws.s3 import S3Resource +class TestS3Resource: + def test_get_object(self, s3_test, s3_): + """ + Cases: + S3からオブジェクトが取得できるか + Arranges: + - S3をモック化する + - 期待値となるファイルを配置する + Expects: + オブジェクトが取得でき、期待値と正しいこと + """ + # more code... +``` diff --git a/ecs/crm-datafetch/tests/__init__.py b/ecs/crm-datafetch/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/crm-datafetch/tests/aws/__init__.py b/ecs/crm-datafetch/tests/aws/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ecs/crm-datafetch/tests/aws/test_s3.py b/ecs/crm-datafetch/tests/aws/test_s3.py new file mode 100644 index 00000000..1daab1e7 --- /dev/null +++ b/ecs/crm-datafetch/tests/aws/test_s3.py @@ -0,0 +1,300 @@ +import pytest +from src.aws.s3 import BackupBucket, ConfigBucket, DataBucket, S3Resource + + +@pytest.fixture +def bucket_name(): + return 'test-bucket' + + +@pytest.fixture +def s3_test(s3_client, bucket_name): + s3_client.create_bucket(Bucket=bucket_name) + yield + + +class TestS3Resource: + + def test_get_object(self, s3_test, s3_client, bucket_name): + """ + Cases: + - S3からオブジェクトが取得できるか + Arranges: + - S3をモック化する + - 期待値となるファイルを配置する + Expects: + - オブジェクトが取得でき、期待値と正しいこと + """ + # Arrange + s3_client.put_object(Bucket=bucket_name, Key='hogehoge/test.txt', Body=b'aaaaaaaaaaaaaaa') + + # Act + sut = S3Resource(bucket_name) + actual = sut.get_object('hogehoge/test.txt') + + # Assert + assert actual == 'aaaaaaaaaaaaaaa' + + def test_put_object(self, s3_test, s3_client, bucket_name): + """ + Cases: + - S3にオブジェクトをPUTできるか + Arranges: + - S3をモック化する + Expects: + - PUTされたファイルが存在する + - PUTされたファイルの内容が期待値と一致する + """ + sut = S3Resource(bucket_name) + + sut.put_object('hogehoge/test.txt', 'aaaaaaaaaaaaaaa') + actual = s3_client.get_object(Bucket=bucket_name, Key='hogehoge/test.txt') + + assert actual['Body'].read().decode('utf-8') == 'aaaaaaaaaaaaaaa' + + def test_copy(self, s3_test, s3_client, bucket_name): + """ + Cases: + - S3内のオブジェクトを別バケットにコピーできるか + Arranges: + - S3をモック化する + - コピー先バケットを作成する + - 期待値となるファイルをコピー元バケットに配置する + Expects: + - コピーされたファイルが存在する + - コピーされたファイルの内容が期待値と一致する + """ + for_copy_bucket = 'for-copy-bucket' + s3_client.create_bucket(Bucket=for_copy_bucket) + s3_client.put_object(Bucket=bucket_name, Key='hogehoge/test.txt', Body=b'aaaaaaaaaaaaaaa') + + sut = S3Resource(bucket_name) + sut.copy(bucket_name, 'hogehoge/test.txt', for_copy_bucket, 'test.txt') + + actual = s3_client.get_object(Bucket=for_copy_bucket, Key='test.txt') + assert actual['Body'].read() == b'aaaaaaaaaaaaaaa' + + def test_init_raise_no_provide_bucket_name(self): + """ + Cases: + - バケット名を指定しない場合、例外となること + Arranges: + + Expects: + - インスタンス生成時に例外が発生すること + """ + with pytest.raises(Exception) as e: + S3Resource() + assert e.value.args[0] == "__init__() missing 1 required positional argument: 'bucket_name'" + + +class TestConfigBucket: + + def test_get_object_info_file(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - オブジェクト情報ファイルが取得できること + Arranges: + - オブジェクト情報ファイルを配置する + Expects: + - オブジェクト情報ファイルが文字列として取得でき、期待値と一致する + """ + monkeypatch.setattr('src.aws.s3.CRM_CONFIG_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.OBJECT_INFO_FOLDER', 'crm') + monkeypatch.setattr('src.aws.s3.OBJECT_INFO_FILENAME', 'objects.json') + s3_client.put_object(Bucket=bucket_name, Key=f'crm/objects.json', Body=b'aaaaaaaaaaaaaaa') + + sut = ConfigBucket() + actual = sut.get_object_info_file() + + assert actual == 'aaaaaaaaaaaaaaa' + + def test_get_last_fetch_datetime_file(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - オブジェクト最終更新日時ファイルが取得できること + Arranges: + - S3をモック化する + - オブジェクト最終更新日時ファイルを配置する + Expects: + - オブジェクト最終更新日時ファイルが文字列として取得でき、期待値と一致する + """ + monkeypatch.setattr('src.aws.s3.CRM_CONFIG_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.LAST_FETCH_DATE_FOLDER', 'crm') + s3_client.put_object(Bucket=bucket_name, Key=f'crm/Object.json', Body=b'aaaaaaaaaaaaaaa') + + sut = ConfigBucket() + actual = sut.get_last_fetch_datetime_file('Object.json') + + assert actual == 'aaaaaaaaaaaaaaa' + + def test_put_last_fetch_datetime_file(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - オブジェクト最終更新日時ファイルをPUTできること + Arranges: + - S3をモック化する + - 環境変数をテスト用の値に置き換える + Expects: + - オブジェクト最終更新日時ファイルが存在する + """ + monkeypatch.setattr('src.aws.s3.CRM_CONFIG_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.LAST_FETCH_DATE_FOLDER', 'crm') + + sut = ConfigBucket() + sut.put_last_fetch_datetime_file('Object.json', 'aaaaaaaaaaaaaaa') + + actual = s3_client.get_object(Bucket=bucket_name, Key=f'crm/Object.json') + assert actual['Body'].read().decode('utf-8') == 'aaaaaaaaaaaaaaa' + + def test_config_bucket_str(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - 設定ファイル配置バケットを文字列化したとき、バケット名が取得できること + Arranges: + - 環境変数をテスト用の値に置き換える + Expects: + - 環境変数の設定ファイル配置バケット名と一致する + """ + monkeypatch.setattr('src.aws.s3.CRM_CONFIG_BUCKET', bucket_name) + + sut = ConfigBucket() + + assert str(sut) == bucket_name + + +class TestDataBucket: + + def test_put_csv(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - CSVファイルをPUTできること + Arranges: + - S3をモック化する + - 環境変数をテスト用の値に置き換える + Expects: + - PUTしたファイルが存在する + """ + monkeypatch.setattr('src.aws.s3.IMPORT_DATA_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.CRM_IMPORT_DATA_FOLDER', 'crm/target') + + sut = DataBucket() + sut.put_csv('test.csv', 'test,test,test') + + actual = s3_client.get_object(Bucket=bucket_name, Key=f'crm/target/test.csv') + assert actual['Body'].read().decode('utf-8') == 'test,test,test' + + def test_put_csv_from(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - 他のバケットからCSVファイルをコピーできること + Arranges: + - S3をモック化する + - コピー先バケットを作成する + - 期待値となるファイルコピー元バケットに配置する + - 環境変数をテスト用の値に置き換える + Expects: + - コピーしたファイルが存在する + """ + for_copy_bucket = 'for-copy-bucket' + s3_client.create_bucket(Bucket=for_copy_bucket) + s3_client.put_object(Bucket=bucket_name, Key='hogehoge/test.csv', Body=b'test,test,test') + + monkeypatch.setattr('src.aws.s3.IMPORT_DATA_BUCKET', for_copy_bucket) + monkeypatch.setattr('src.aws.s3.CRM_IMPORT_DATA_FOLDER', 'crm/target') + + sut = DataBucket() + sut.put_csv_from(bucket_name, 'hogehoge/test.csv') + actual = s3_client.get_object(Bucket=for_copy_bucket, Key=f'crm/target/test.csv') + + assert actual['Body'].read().decode('utf-8') == 'test,test,test' + + def test_data_bucket_str(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - データ登録バケットを文字列化したとき、バケット名が取得できること + Arranges: + - 環境変数をテスト用の値に置き換える + Expects: + - 環境変数のデータ登録バケット名と一致する + """ + monkeypatch.setattr('src.aws.s3.IMPORT_DATA_BUCKET', bucket_name) + + sut = DataBucket() + + assert str(sut) == bucket_name + + +class TestBackupBucket: + + def test_put_csv(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - CSVファイルをPUTできること + Arranges: + - S3をモック化する + - 環境変数をテスト用の値に置き換える + Expects: + - PUTしたファイルが存在する + """ + monkeypatch.setattr('src.aws.s3.CRM_BACKUP_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.CRM_IMPORT_DATA_BACKUP_FOLDER', 'data_import') + + sut = BackupBucket() + sut.put_csv('test.csv', 'test,test,test') + + actual = s3_client.get_object(Bucket=bucket_name, Key=f'data_import/test.csv') + assert actual['Body'].read().decode('utf-8') == 'test,test,test' + + def test_put_response_json(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - JSONファイルをPUTできること + Arranges: + - S3をモック化する + - 環境変数をテスト用の値に置き換える + Expects: + - PUTしたファイルが存在する + """ + monkeypatch.setattr('src.aws.s3.CRM_BACKUP_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.RESPONSE_JSON_BACKUP_FOLDER', 'response_json') + + sut = BackupBucket() + sut.put_response_json('test.json', {"test": "test"}) + + actual = s3_client.get_object(Bucket=bucket_name, Key=f'response_json/test.json') + assert actual['Body'].read().decode('utf-8') == '{"test": "test"}' + + def test_put_result_json(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - JSONファイルをPUTできること + Arranges: + - S3をモック化する + - 環境変数をテスト用の値に置き換える + Expects: + - PUTしたファイルが存在する + """ + monkeypatch.setattr('src.aws.s3.CRM_BACKUP_BUCKET', bucket_name) + monkeypatch.setattr('src.aws.s3.PROCESS_RESULT_FOLDER', 'data_import') + + sut = BackupBucket() + sut.put_result_json('result.json', {"test": "test"}) + + actual = s3_client.get_object(Bucket=bucket_name, Key=f'data_import/result.json') + assert actual['Body'].read().decode('utf-8') == '{"test": "test"}' + + def test_backup_bucket_str(self, s3_test, s3_client, bucket_name, monkeypatch): + """ + Cases: + - CRMデータバックアップバケットを文字列化したとき、バケット名が取得できること + Arranges: + - 環境変数をテスト用の値に置き換える + Expects: + - 環境変数のCRMデータバックアップバケット名と一致する + """ + monkeypatch.setattr('src.aws.s3.CRM_BACKUP_BUCKET', bucket_name) + + sut = BackupBucket() + + assert str(sut) == bucket_name diff --git a/ecs/crm-datafetch/tests/conftest.py b/ecs/crm-datafetch/tests/conftest.py new file mode 100644 index 00000000..55ade88c --- /dev/null +++ b/ecs/crm-datafetch/tests/conftest.py @@ -0,0 +1,59 @@ +"""pytestでフィクスチャやフック(テスト実行前後に差し込む処理)を管理するモジュール""" +import os +from datetime import datetime + +import boto3 +import pytest +from moto import mock_s3 +from py.xml import html # type: ignore + +from . import docstring_parser + + +@pytest.fixture +def aws_credentials(): + """Mocked AWS Credentials for moto.""" + os.environ["AWS_ACCESS_KEY_ID"] = "testing" + os.environ["AWS_SECRET_ACCESS_KEY"] = "testing" + os.environ["AWS_SECURITY_TOKEN"] = "testing" + os.environ["AWS_SESSION_TOKEN"] = "testing" + + +@pytest.fixture +def s3_client(aws_credentials): + with mock_s3(): + conn = boto3.client("s3", region_name="us-east-1") + yield conn + + +# 以下、レポート出力用の設定 + +def pytest_html_report_title(report): + # レポートタイトル + report.title = "CRMデータ連携 CRMデータ取得機能 単体機能テスト結果報告書" + + +def pytest_html_results_table_header(cells): + del cells[2:] + cells.insert(3, html.th("Cases")) + cells.insert(4, html.th("Arranges")) + cells.insert(5, html.th("Expects")) + cells.append(html.th("Time", class_="sortable time", col="time")) + + +def pytest_html_results_table_row(report, cells): + del cells[2:] + cells.insert(3, html.td(html.pre(report.cases))) # 「テスト内容」をレポートに出力 + cells.insert(4, html.td(html.pre(report.arranges))) # 「期待結果」をレポートに出力 + cells.insert(5, html.td(html.pre(report.expects))) # 「期待結果」をレポートに出力 + cells.append(html.td(datetime.now(), class_="col-time")) # ついでに「時間」もレポートに出力 + + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item, call): + outcome = yield + report = outcome.get_result() + docstring = docstring_parser.parse(str(item.function.__doc__)) + report.cases = docstring.get("Cases", '') # 「テスト内容」を`report`に追加 + report.arranges = docstring.get("Arranges", '') # 「準備作業」を`report`に追加 + report.expects = docstring.get("Expects", '') # 「期待結果」を`report`に追加 diff --git a/ecs/crm-datafetch/tests/docstring_parser.py b/ecs/crm-datafetch/tests/docstring_parser.py new file mode 100644 index 00000000..040c9b5d --- /dev/null +++ b/ecs/crm-datafetch/tests/docstring_parser.py @@ -0,0 +1,45 @@ +"""pytest-htmlでレポート出力するため、各テスト関数のドキュメントコメントのセクションを抜き出して、辞書にする +Examples: + <セクション名>:となっている部分が対象になる + \"\"\" + Cases: + テストケース + Arranges: + 準備作業 + Expects: + 期待値 + \"\"\" +""" + +import re +from itertools import takewhile + +_section_rgx = re.compile(r"^\s*[a-zA-Z]+:\s*$") +_lspace_rgx = re.compile(r"^\s*") + + +def _parse_section(lines: list) -> list: + matches = map(lambda x: _section_rgx.match(x), lines) + indexes = [i for i, x in enumerate(matches) if x is not None] + return list(map(lambda x: (x, lines[x].strip()[: -1]), indexes)) + + +def _count_lspace(s: str) -> int: + rgx = _lspace_rgx.match(s) + if rgx is not None: + return rgx.end() + return 0 + + +def _parse_content(index: int, lines: list) -> str: + lspace = _count_lspace(lines[index]) + i = index + 1 + contents = takewhile(lambda x: _count_lspace(x) > lspace, lines[i:]) + return "\n".join(map(lambda x: x.strip(), contents)) + + +def parse(docstring: str) -> dict: + """🚧sloppy docstring parser🚧""" + lines = docstring.splitlines() + sections = _parse_section(lines) + return dict(map(lambda x: (x[1], _parse_content(x[0], lines)), sections))