From c0899e428bf8045a079b27d249b22f6f2bf4c02d Mon Sep 17 00:00:00 2001 From: Louis GERARD <186476141+lgerard-pass@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:26:56 +0100 Subject: [PATCH] (BSR)[API] feat: gate celery usage behind FF --- api/poetry.lock | 47 +-- api/src/pcapi/core/external/sendinblue.py | 15 +- .../pcapi/core/mails/backends/sendinblue.py | 15 +- .../transactional/send_transactional_email.py | 1 - api/src/pcapi/models/feature.py | 4 +- api/src/pcapi/tasks/sendinblue_tasks.py | 2 +- api/tests/core/mails/mails_celery_test.py | 342 ++++++++++++++++++ api/tests/core/mails/mails_test.py | 88 ++--- .../test_send_transactional_email.py | 15 +- 9 files changed, 431 insertions(+), 98 deletions(-) create mode 100644 api/tests/core/mails/mails_celery_test.py diff --git a/api/poetry.lock b/api/poetry.lock index 7f8152af09c..d314cbe982d 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -457,10 +457,6 @@ files = [ {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5dab0844f2cf82be357a0eb11a9087f70c5430b2c241493fc122bb6f2bb0917c"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e4fe605b917c70283db7dfe5ada75e04561479075761a0b3866c081d035b01c1"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:1e9a65b5736232e7a7f91ff3d02277f11d339bf34099a56cdab6a8b3410a02b2"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:58d4b711689366d4a03ac7957ab8c28890415e267f9b6589969e74b6e42225ec"}, {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, @@ -473,14 +469,8 @@ files = [ {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c247dd99d39e0338a604f8c2b3bc7061d5c2e9e2ac7ba9cc1be5a69cb6cd832f"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1b2c248cd517c222d89e74669a4adfa5577e06ab68771a529060cf5a156e9757"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:2a24c50840d89ded6c9a8fdc7b6ed3692ed4e86f1c4a4a938e1e92def92933e0"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f31859074d57b4639318523d6ffdca586ace54271a73ad23ad021acd807eb14b"}, {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:32d95b80260d79926f5fab3c41701dbb818fde1c9da590e77e571eefd14abe28"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b760c65308ff1e462f65d69c12e4ae085cff3b332d894637f6273a12a482d09f"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, @@ -491,24 +481,8 @@ files = [ {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:87a3044c3a35055527ac75e419dfa9f4f3667a1e887ee80360589eb8c90aabb9"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c5529b34c1c9d937168297f2c1fde7ebe9ebdd5e121297ff9c043bdb2ae3d6fb"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:ca63e1890ede90b2e4454f9a65135a4d387a4585ff8282bb72964fab893f2111"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e79e6520141d792237c70bcd7a3b122d00f2613769ae0cb61c52e89fd3443839"}, {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8bf32b98b75c13ec7cf774164172683d6e7891088f6316e54425fde1efc276d5"}, - {file = "Brotli-1.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bc37c4d6b87fb1017ea28c9508b36bbcb0c3d18b4260fcdf08b200c74a6aee8"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c0ef38c7a7014ffac184db9e04debe495d317cc9c6fb10071f7fefd93100a4f"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91d7cc2a76b5567591d12c01f019dd7afce6ba8cba6571187e21e2fc418ae648"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a93dde851926f4f2678e704fadeb39e16c35d8baebd5252c9fd94ce8ce68c4a0"}, - {file = "Brotli-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0db75f47be8b8abc8d9e31bc7aad0547ca26f24a54e6fd10231d623f183d089"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6967ced6730aed543b8673008b5a391c3b1076d834ca438bbd70635c73775368"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7eedaa5d036d9336c95915035fb57422054014ebdeb6f3b42eac809928e40d0c"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:d487f5432bf35b60ed625d7e1b448e2dc855422e87469e3f450aa5552b0eb284"}, - {file = "Brotli-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:832436e59afb93e1836081a20f324cb185836c617659b07b129141a8426973c7"}, - {file = "Brotli-1.1.0-cp313-cp313-win32.whl", hash = "sha256:43395e90523f9c23a3d5bdf004733246fba087f2948f87ab28015f12359ca6a0"}, - {file = "Brotli-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9011560a466d2eb3f5a6e4929cf4a09be405c64154e12df0dd72713f6500e32b"}, {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, @@ -518,10 +492,6 @@ files = [ {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_aarch64.whl", hash = "sha256:aea440a510e14e818e67bfc4027880e2fb500c2ccb20ab21c7a7c8b5b4703d75"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_i686.whl", hash = "sha256:6974f52a02321b36847cd19d1b8e381bf39939c21efd6ee2fc13a28b0d99348c"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_ppc64le.whl", hash = "sha256:a7e53012d2853a07a4a79c00643832161a910674a893d296c9f1259859a289d2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_2_x86_64.whl", hash = "sha256:d7702622a8b40c49bffb46e1e3ba2e81268d5c04a34f460978c6b5517a34dd52"}, {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, @@ -533,10 +503,6 @@ files = [ {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:cb1dac1770878ade83f2ccdf7d25e494f05c9165f5246b46a621cc849341dc01"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:3ee8a80d67a4334482d9712b8e83ca6b1d9bc7e351931252ebef5d8f7335a547"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:5e55da2c8724191e5b557f8e18943b1b4839b8efc3ef60d65985bcf6f587dd38"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:d342778ef319e1026af243ed0a07c97acf3bad33b9f29e7ae6a1f68fd083e90c"}, {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, @@ -549,10 +515,6 @@ files = [ {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:d2b35ca2c7f81d173d2fadc2f4f31e88cc5f7a39ae5b6db5513cf3383b0e0ec7"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:af6fa6817889314555aede9a919612b23739395ce767fe7fcbea9a80bf140fe5"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:2feb1d960f760a575dbc5ab3b1c00504b24caaf6986e2dc2b01c09c87866a943"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:4410f84b33374409552ac9b6903507cdb31cd30d2501fc5ca13d18f73548444a"}, {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, @@ -565,10 +527,6 @@ files = [ {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0737ddb3068957cf1b054899b0883830bb1fec522ec76b1098f9b6e0f02d9419"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:4f3607b129417e111e30637af1b56f24f7a49e64763253bbc275c75fa887d4b2"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:6c6e0c425f22c1c719c42670d561ad682f7bfeeef918edea971a79ac5252437f"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:494994f807ba0b92092a163a0a283961369a65f6cbe01e8891132b7a320e61eb"}, {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, @@ -1302,7 +1260,6 @@ files = [ {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"}, - {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"}, {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"}, @@ -1313,7 +1270,6 @@ files = [ {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"}, - {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"}, {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"}, @@ -3941,6 +3897,7 @@ files = [ {file = "psycopg2-2.9.10-cp311-cp311-win_amd64.whl", hash = "sha256:0435034157049f6846e95103bd8f5a668788dd913a7c30162ca9503fdf542cb4"}, {file = "psycopg2-2.9.10-cp312-cp312-win32.whl", hash = "sha256:65a63d7ab0e067e2cdb3cf266de39663203d38d6a8ed97f5ca0cb315c73fe067"}, {file = "psycopg2-2.9.10-cp312-cp312-win_amd64.whl", hash = "sha256:4a579d6243da40a7b3182e0430493dbd55950c493d8c68f4eec0b302f6bbf20e"}, + {file = "psycopg2-2.9.10-cp313-cp313-win_amd64.whl", hash = "sha256:91fd603a2155da8d0cfcdbf8ab24a2d54bca72795b90d2a3ed2b6da8d979dee2"}, {file = "psycopg2-2.9.10-cp39-cp39-win32.whl", hash = "sha256:9d5b3b94b79a844a986d029eee38998232451119ad653aea42bb9220a8c5066b"}, {file = "psycopg2-2.9.10-cp39-cp39-win_amd64.whl", hash = "sha256:88138c8dedcbfa96408023ea2b0c369eda40fe5d75002c0964c78f46f11fa442"}, {file = "psycopg2-2.9.10.tar.gz", hash = "sha256:12ec0b40b0273f95296233e8750441339298e6a572f7039da5b260e3c8b60e11"}, @@ -6599,4 +6556,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.11,<4.0" -content-hash = "e07d2cc5ff70750dbae6c3ac5e8e33b079719e00706c01e2edad230a3cb69a0f" +content-hash = "66408a8b5d714965415546f91758359f301ac4ed7479514782e13c57f3353039" diff --git a/api/src/pcapi/core/external/sendinblue.py b/api/src/pcapi/core/external/sendinblue.py index e280cf6adce..6af39df6a1c 100644 --- a/api/src/pcapi/core/external/sendinblue.py +++ b/api/src/pcapi/core/external/sendinblue.py @@ -122,9 +122,15 @@ def update_contact_email(user: users_models.User, old_email: str, new_email: str ) if asynchronous: - update_contact_attributes_task_cloud_tasks.delay(contact_request) + if FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS.is_active(): + update_contact_attributes_task_celery.delay(contact_request) + else: + update_contact_attributes_task_cloud_tasks.delay(contact_request) else: - update_contact_attributes_task_cloud_tasks(contact_request) + if FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS.is_active(): + update_contact_attributes_task_celery(contact_request) + else: + update_contact_attributes_task_cloud_tasks(contact_request) def update_contact_attributes( @@ -152,7 +158,10 @@ def update_contact_attributes( ) if asynchronous: - update_contact_attributes_task_cloud_tasks.delay(contact_request) + if FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS.is_active(): + update_contact_attributes_task_celery.delay(contact_request) + else: + update_contact_attributes_task_cloud_tasks.delay(contact_request) else: make_update_request(contact_request) diff --git a/api/src/pcapi/core/mails/backends/sendinblue.py b/api/src/pcapi/core/mails/backends/sendinblue.py index c491d905cf5..67003829a30 100644 --- a/api/src/pcapi/core/mails/backends/sendinblue.py +++ b/api/src/pcapi/core/mails/backends/sendinblue.py @@ -57,9 +57,15 @@ def send_mail( use_pro_subaccount=data.template.use_pro_subaccount, ) if data.template.use_priority_queue: - send_transactional_email_primary_task_cloud_tasks.delay(payload) + if FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS.is_active(): + send_transactional_email_primary_task_celery.delay(payload) + else: + send_transactional_email_primary_task_cloud_tasks.delay(payload) else: - send_transactional_email_secondary_task_cloud_tasks.delay(payload) + if FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS.is_active(): + send_transactional_email_secondary_task_celery.delay(payload) + else: + send_transactional_email_secondary_task_cloud_tasks.delay(payload) elif isinstance(data, models.TransactionalWithoutTemplateEmailData): payload = serializers.SendTransactionalEmailRequest( @@ -74,7 +80,10 @@ def send_mail( params=None, tags=None, ) - send_transactional_email_secondary_task_cloud_tasks.delay(payload) + if FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS.is_active(): + send_transactional_email_secondary_task_celery.delay(payload) + else: + send_transactional_email_secondary_task_cloud_tasks.delay(payload) else: raise ValueError(f"Tried sending an email via sendinblue, but received incorrectly formatted data: {data}") diff --git a/api/src/pcapi/core/mails/transactional/send_transactional_email.py b/api/src/pcapi/core/mails/transactional/send_transactional_email.py index 5a6670ad7e1..85f7193961f 100644 --- a/api/src/pcapi/core/mails/transactional/send_transactional_email.py +++ b/api/src/pcapi/core/mails/transactional/send_transactional_email.py @@ -58,7 +58,6 @@ def send_transactional_email(payload: SendTransactionalEmailRequest) -> None: try: configuration = brevo_python.Configuration() - configuration = sib_api_v3_sdk.Configuration() if settings.PROXY_CERT_BUNDLE is not None: configuration.ssl_ca_cert = settings.PROXY_CERT_BUNDLE if payload.use_pro_subaccount: diff --git a/api/src/pcapi/models/feature.py b/api/src/pcapi/models/feature.py index 14f9945188c..8166ce3c18b 100644 --- a/api/src/pcapi/models/feature.py +++ b/api/src/pcapi/models/feature.py @@ -107,6 +107,8 @@ class FeatureToggle(enum.Enum): ENABLE_PRO_FEEDBACK = "Activer l'envoi des commentaires du portail pro vers Harvestr" ENABLE_MOVIE_FESTIVAL_RATE = "Activer les tarifs spéciaux pour un festival cinéma" # For features under construction, a temporary feature flag must be named with the `WIP_` prefix + WIP_ASYNCHRONOUS_CELERY_TASKS = "Activer le backend de tâches asynchrone Celery pour les tâches qui le supporte" + WIP_ENABLE_BREVO_PRO_SUBACCOUNT = "Activer l'utilisation du sous-compte Brevo pour les mails pros" WIP_ENABLE_REMINDER_MARKETING_MAIL_METADATA_DISPLAY = "Changer le template d'email de confirmation de réservation" WIP_ENABLE_NEW_FINANCE_WORKFLOW = "Active le nouveau workflow finance" WIP_ENABLE_MARSEILLE = "Activer Marseille en grand" @@ -200,7 +202,7 @@ def nameKey(self) -> str: FeatureToggle.ID_CHECK_ADDRESS_AUTOCOMPLETION, FeatureToggle.LOG_EMS_CINEMAS_AVAILABLE_FOR_SYNC, FeatureToggle.SYNCHRONIZE_TITELIVE_API_MUSIC_PRODUCTS, - FeatureToggle.WIP_2025_SIGN_UP, + FeatureToggle.WIP_ASYNCHRONOUS_CELERY_TASKS, FeatureToggle.WIP_COLLAPSED_MEMORIZED_FILTERS, FeatureToggle.WIP_DISABLE_CANCEL_BOOKING_NOTIFICATION, FeatureToggle.WIP_DISABLE_NOTIFY_USERS_BOOKINGS_NOT_RETRIEVED, diff --git a/api/src/pcapi/tasks/sendinblue_tasks.py b/api/src/pcapi/tasks/sendinblue_tasks.py index a3ade672b3b..7d1c885f383 100644 --- a/api/src/pcapi/tasks/sendinblue_tasks.py +++ b/api/src/pcapi/tasks/sendinblue_tasks.py @@ -23,7 +23,7 @@ def update_contact_attributes_task_cloud_tasks(payload: UpdateSendinblueContactR @task(SENDINBLUE_TRANSACTIONAL_EMAILS_PRIMARY_QUEUE_NAME, "/sendinblue/send-transactional-email-primary") # type: ignore[arg-type] -def send_transactional_email_primary_cloud_tasks(payload: SendTransactionalEmailRequest) -> None: +def send_transactional_email_primary_task_cloud_tasks(payload: SendTransactionalEmailRequest) -> None: send_transactional_email(payload) diff --git a/api/tests/core/mails/mails_celery_test.py b/api/tests/core/mails/mails_celery_test.py new file mode 100644 index 00000000000..ed56bae5042 --- /dev/null +++ b/api/tests/core/mails/mails_celery_test.py @@ -0,0 +1,342 @@ +import logging +from unittest.mock import patch + +from brevo_python.rest import ApiException as SendinblueApiException +import pytest +from urllib3.response import HTTPResponse + +from pcapi.core.mails import models +from pcapi.core.mails import send +from pcapi.core.users import factories as users_factories +from pcapi.core.users import models as users_models +from pcapi.tasks.serialization import sendinblue_tasks +from pcapi.utils.module_loading import import_string + + +@pytest.mark.usefixtures("db_session") +@pytest.mark.features(WIP_ASYNCHRONOUS_CELERY_TASKS=True) +class SendinblueBackendTest: + recipients = ["lucy.ellingson@example.com", "avery.kelly@example.com"] + bcc_recipients = ["catherine.clark@example.com", "tate.walker@example.com"] + mock_template = models.Template( + id_prod=1, id_not_prod=10, tags=["this_is_such_a_great_tag", "it_would_be_a_pity_if_anything_happened_to_it"] + ) + mock_reply_to = models.EmailInfo(email="reply_to@example.com", name="Tom S.") + params = {"Name": "Lucy", "City": "Kennet", "OtherCharacteristics": "Awsomeness"} + data = models.TransactionalEmailData(template=mock_template, params=params, reply_to=mock_reply_to) + + expected_sent_data = sendinblue_tasks.SendTransactionalEmailRequest( + recipients=recipients, + bcc_recipients=bcc_recipients, + params=params, + template_id=data.template.id, + tags=data.template.tags, + reply_to={"email": "reply_to@example.com", "name": "Tom S."}, + enable_unsubscribe=False, + ) + + def _get_backend_for_test(self): + return import_string("pcapi.core.mails.backends.sendinblue.SendinblueBackend") + + @pytest.mark.settings( + WHITELISTED_EMAIL_RECIPIENTS=[ + "lucy.ellingson@example.com", + "avery.kelly@example.com", + "catherine.clark@example.com", + "tate.walker@example.com", + ] + ) + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail(self, mock_send_transactional_email_secondary_task_celery): + backend = self._get_backend_for_test() + backend(use_pro_subaccount=False).send_mail( + recipients=self.recipients, bcc_recipients=self.bcc_recipients, data=self.data + ) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + + assert set(task_param.recipients) == set(self.expected_sent_data.recipients) + assert set(task_param.bcc_recipients) == set(self.expected_sent_data.bcc_recipients) + assert task_param.params == self.expected_sent_data.params + assert task_param.template_id == self.expected_sent_data.template_id + assert task_param.tags == self.expected_sent_data.tags + assert task_param.reply_to == self.expected_sent_data.reply_to + assert task_param.enable_unsubscribe == self.expected_sent_data.enable_unsubscribe + + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_with_no_sender(self, mock_send_transactional_email_secondary_task_celery): + self.mock_template = models.TemplatePro( + id_prod=1, + id_not_prod=10, + tags=["this_is_such_a_great_tag", "it_would_be_a_pity_if_anything_happened_to_it"], + ) + self.data = models.TransactionalEmailData(template=self.mock_template, params=self.params, reply_to=None) + + expected_sent_data = sendinblue_tasks.SendTransactionalEmailRequest( + recipients=self.recipients, + params=self.params, + template_id=self.data.template.id, + tags=self.data.template.tags, + sender=None, + reply_to=None, + enable_unsubscribe=False, + ) + + backend = self._get_backend_for_test() + backend(use_pro_subaccount=True).send_mail( + recipients=self.recipients, bcc_recipients=self.bcc_recipients, data=self.data + ) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + + assert task_param.params == expected_sent_data.params + assert task_param.template_id == expected_sent_data.template_id + assert task_param.tags == expected_sent_data.tags + assert task_param.sender == expected_sent_data.sender + assert task_param.reply_to == expected_sent_data.reply_to + assert task_param.enable_unsubscribe == self.expected_sent_data.enable_unsubscribe + + @patch("pcapi.core.external.sendinblue.brevo_python.api.contacts_api.ContactsApi.delete_contact") + @patch("pcapi.core.external.sendinblue.brevo_python.api.contacts_api.ContactsApi.create_contact") + def test_create_contact(self, mock_create_contact, mock_delete_contact): + payload = sendinblue_tasks.UpdateSendinblueContactRequest( + email="old.email@example.com", + use_pro_subaccount=True, + attributes={"EMAIL": "new.email@example.com"}, + contact_list_ids=[123], + emailBlacklisted=False, + ) + + backend = self._get_backend_for_test() + backend(use_pro_subaccount=True).create_contact(payload) + + mock_create_contact.assert_called_once() + mock_delete_contact.assert_not_called() + + @patch("pcapi.core.external.sendinblue.brevo_python.api.contacts_api.ContactsApi.delete_contact") + @patch("pcapi.core.external.sendinblue.brevo_python.api.contacts_api.ContactsApi.create_contact") + def test_create_contact_duplicate_email(self, mock_create_contact, mock_delete_contact): + payload = sendinblue_tasks.UpdateSendinblueContactRequest( + email="old.email@example.com", + use_pro_subaccount=True, + attributes={"EMAIL": "new.email@example.com"}, + contact_list_ids=[123], + emailBlacklisted=False, + ) + + mock_create_contact.side_effect = SendinblueApiException( + http_resp=HTTPResponse( + status=400, + reason="Bad Request", + headers={"Content-Type": "application/json"}, + body='{"code":"duplicate_parameter",' + '"message":"Unable to update contact, email is already associated with another Contact",' + '"metadata":{"duplicate_identifiers":["email"]}}', + ) + ) + + backend = self._get_backend_for_test() + backend(use_pro_subaccount=True).create_contact(payload) + + mock_create_contact.assert_called_once() + mock_delete_contact.assert_called_once_with("old.email@example.com") + + +@pytest.mark.usefixtures("db_session") +@pytest.mark.features(WIP_ASYNCHRONOUS_CELERY_TASKS=True) +class ToDevSendinblueBackendTest(SendinblueBackendTest): + expected_sent_data_to_dev = sendinblue_tasks.SendTransactionalEmailRequest( + recipients=["dev@example.com"], + bcc_recipients=["test@example.com"], + params=SendinblueBackendTest.params, + template_id=SendinblueBackendTest.data.template.id, + tags=SendinblueBackendTest.data.template.tags, + sender=None, + reply_to={"email": "reply_to@example.com", "name": "Tom S."}, + enable_unsubscribe=SendinblueBackendTest.data.template.enable_unsubscribe, + ) + + def _get_backend_for_test(self): + return import_string("pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") + + @pytest.mark.settings(WHITELISTED_EMAIL_RECIPIENTS=["test@example.com"]) + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_to_dev(self, mock_send_transactional_email_secondary_task_celery): + backend = self._get_backend_for_test() + backend(use_pro_subaccount=False).send_mail( + recipients=self.recipients, bcc_recipients=["test@example.com"], data=self.data + ) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + assert set(task_param.recipients) == set(self.expected_sent_data_to_dev.recipients) + assert set(task_param.bcc_recipients) == set(self.expected_sent_data_to_dev.bcc_recipients) + assert task_param.params == self.expected_sent_data_to_dev.params + assert task_param.template_id == self.expected_sent_data_to_dev.template_id + assert task_param.tags == self.expected_sent_data_to_dev.tags + assert task_param.sender == self.expected_sent_data_to_dev.sender + assert task_param.reply_to == self.expected_sent_data_to_dev.reply_to + + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_test_user(self, mock_send_transactional_email_secondary_task_celery): + users_factories.UserFactory(email=self.recipients[0], roles=[users_models.UserRole.TEST]) + + backend = self._get_backend_for_test() + backend(use_pro_subaccount=False).send_mail(recipients=self.recipients, data=self.data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + assert list(task_param.recipients) == list(self.recipients[0:1]) + + @pytest.mark.parametrize( + "recipient", + ["avery.kelly@example.com", "sandy.zuko@passculture-test.app"], + ) + @pytest.mark.settings(WHITELISTED_EMAIL_RECIPIENTS=["avery.kelly@example.com", "*@passculture-test.app"]) + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_whitelisted(self, mock_send_transactional_email_secondary_task_celery, recipient): + backend = self._get_backend_for_test() + backend(use_pro_subaccount=False).send_mail( + recipients=[recipient, "lucy.ellingson@example.com"], data=self.data + ) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + assert list(task_param.recipients) == [recipient] + + @pytest.mark.settings(IS_STAGING=True, IS_E2E_TESTS=True, END_TO_END_TESTS_EMAIL_ADDRESS="qa-test@passculture.app") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_whitelisted_qa_staging(self, mock_send_transactional_email_secondary_task_celery): + recipient = "qa-test+123@passculture.app" + users_factories.UserFactory(email=recipient) + + backend = self._get_backend_for_test() + backend(use_pro_subaccount=False).send_mail(recipients=[recipient], data=self.data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + assert list(task_param.recipients) == [recipient] + + @pytest.mark.settings(IS_TESTING=True, IS_E2E_TESTS=True, END_TO_END_TESTS_EMAIL_ADDRESS="qa-test@passculture.app") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_whitelisted_qa_testing( + self, mock_send_transactional_email_secondary_task_celery, recipient="qa-test+123@passculture.app" + ): + users_factories.UserFactory(email=recipient) + + backend = self._get_backend_for_test() + backend(use_pro_subaccount=False).send_mail(recipients=[recipient], data=self.data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_celery.call_args[0][0] + assert list(task_param.recipients) == [recipient] + + +@pytest.mark.features(WIP_ASYNCHRONOUS_CELERY_TASKS=True) +class SendTest: + @pytest.mark.settings(IS_TESTING=True, EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_to_ehp_false_in_testing(self, mock_send_transactional_email_secondary_task_celery, caplog): + mock_template_send_ehp_false = models.Template( + id_prod=11, id_not_prod=12, tags=["some", "stuff"], send_to_ehp=False + ) + mock_reply_to = models.EmailInfo(email="reply_to@example.com", name="Tom S.") + data = models.TransactionalEmailData(template=mock_template_send_ehp_false, params={}, reply_to=mock_reply_to) + recipients = ["lucy.ellingson@example.com", "avery.kelly@example.com"] + + with caplog.at_level(logging.INFO): + send(recipients=recipients, data=data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 0 + + assert caplog.messages[0] == ( + "An email would be sent via Sendinblue to=lucy.ellingson@example.com, avery.kelly@example.com, bcc=(): " + "{'template': {'id_prod': 11, 'id_not_prod': 12, 'tags': ['some', 'stuff'], 'use_priority_queue': False, " + "'send_to_ehp': False, 'enable_unsubscribe': False}, " + "'reply_to': {'email': 'reply_to@example.com', 'name': 'Tom S.'}, 'params': {}}" + ) + + @pytest.mark.settings(IS_TESTING=True, EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_to_ehp_true_in_testing(self, mock_send_transactional_email_secondary_task_celery, caplog): + mock_template_send_ehp_true = models.Template( + id_prod=11, id_not_prod=12, tags=["some", "stuff"], send_to_ehp=True + ) + mock_reply_to = models.EmailInfo(email="reply_to@example.com", name="Tom S.") + data = models.TransactionalEmailData(template=mock_template_send_ehp_true, params={}, reply_to=mock_reply_to) + recipients = ["lucy.ellingson@example.com", "avery.kelly@example.com"] + + with caplog.at_level(logging.INFO): + send(recipients=recipients, data=data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + assert caplog.records == [] + + @pytest.mark.parametrize( + "template_class,expected_use_pro_subaccount", + [ + (models.TemplatePro, True), + (models.Template, False), + ], + ) + @pytest.mark.settings(EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.SendinblueBackend") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_to_pro_with_FF( + self, + mock_send_transactional_email_secondary_task_celery, + features, + caplog, + template_class, + expected_use_pro_subaccount, + ): + + mock_template = template_class(id_prod=1, id_not_prod=10, send_to_ehp=False) + data = models.TransactionalEmailData(template=mock_template) + recipients = ["lucy.ellingson@example.com", "avery.kelly@example.com"] + + with caplog.at_level(logging.INFO): + send(recipients=recipients, data=data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 1 + assert ( + mock_send_transactional_email_secondary_task_celery.call_args[0][0].use_pro_subaccount + is expected_use_pro_subaccount + ) + + @pytest.mark.parametrize( + "template_class,enable_unsubscribe,expected_use_pro_subaccount", + [ + (models.TemplatePro, True, True), + (models.Template, False, False), + ], + ) + @pytest.mark.settings(IS_TESTING=True, EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.SendinblueBackend") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_celery.delay") + def test_send_mail_to_pro_with_FF_in_ehp( + self, + mock_send_transactional_email_secondary_task_celery, + features, + caplog, + template_class, + enable_unsubscribe, + expected_use_pro_subaccount, + ): + + mock_template = template_class( + id_prod=1, id_not_prod=10, send_to_ehp=False, enable_unsubscribe=enable_unsubscribe + ) + data = models.TransactionalEmailData(template=mock_template) + recipients = ["lucy.ellingson@example.com", "avery.kelly@example.com"] + + with caplog.at_level(logging.INFO): + send(recipients=recipients, data=data) + + assert mock_send_transactional_email_secondary_task_celery.call_count == 0 + assert caplog.messages[0] == ( + f"An email would be sent via Sendinblue {'using the PRO subaccount ' if expected_use_pro_subaccount else ''}to=lucy.ellingson@example.com, " + "avery.kelly@example.com, bcc=(): {'template': {'id_prod': 1, 'id_not_prod': 10, 'tags': [], 'use_priority_queue': False, " + f"'send_to_ehp': False, 'enable_unsubscribe': {enable_unsubscribe}" + "}, 'reply_to': None, 'params': {}}" + ) diff --git a/api/tests/core/mails/mails_test.py b/api/tests/core/mails/mails_test.py index 127174d8084..1f4741ca294 100644 --- a/api/tests/core/mails/mails_test.py +++ b/api/tests/core/mails/mails_test.py @@ -45,15 +45,15 @@ def _get_backend_for_test(self): "tate.walker@example.com", ] ) - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail(self, mock_send_transactional_email_secondary_task): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail(self, mock_send_transactional_email_secondary_task_cloud_tasks): backend = self._get_backend_for_test() backend(use_pro_subaccount=False).send_mail( recipients=self.recipients, bcc_recipients=self.bcc_recipients, data=self.data ) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert set(task_param.recipients) == set(self.expected_sent_data.recipients) assert set(task_param.bcc_recipients) == set(self.expected_sent_data.bcc_recipients) @@ -63,8 +63,8 @@ def test_send_mail(self, mock_send_transactional_email_secondary_task): assert task_param.reply_to == self.expected_sent_data.reply_to assert task_param.enable_unsubscribe == self.expected_sent_data.enable_unsubscribe - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_with_no_sender(self, mock_send_transactional_email_secondary_task): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_with_no_sender(self, mock_send_transactional_email_secondary_task_cloud_tasks): self.mock_template = models.TemplatePro( id_prod=1, id_not_prod=10, @@ -87,8 +87,8 @@ def test_send_mail_with_no_sender(self, mock_send_transactional_email_secondary_ recipients=self.recipients, bcc_recipients=self.bcc_recipients, data=self.data ) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert task_param.params == expected_sent_data.params assert task_param.template_id == expected_sent_data.template_id @@ -160,15 +160,15 @@ def _get_backend_for_test(self): return import_string("pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") @pytest.mark.settings(WHITELISTED_EMAIL_RECIPIENTS=["test@example.com"]) - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_to_dev(self, mock_send_transactional_email_secondary_task): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_to_dev(self, mock_send_transactional_email_secondary_task_cloud_tasks): backend = self._get_backend_for_test() backend(use_pro_subaccount=False).send_mail( recipients=self.recipients, bcc_recipients=["test@example.com"], data=self.data ) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert set(task_param.recipients) == set(self.expected_sent_data_to_dev.recipients) assert set(task_param.bcc_recipients) == set(self.expected_sent_data_to_dev.bcc_recipients) assert task_param.params == self.expected_sent_data_to_dev.params @@ -177,15 +177,15 @@ def test_send_mail_to_dev(self, mock_send_transactional_email_secondary_task): assert task_param.sender == self.expected_sent_data_to_dev.sender assert task_param.reply_to == self.expected_sent_data_to_dev.reply_to - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_test_user(self, mock_send_transactional_email_secondary_task): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_test_user(self, mock_send_transactional_email_secondary_task_cloud_tasks): users_factories.UserFactory(email=self.recipients[0], roles=[users_models.UserRole.TEST]) backend = self._get_backend_for_test() backend(use_pro_subaccount=False).send_mail(recipients=self.recipients, data=self.data) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert list(task_param.recipients) == list(self.recipients[0:1]) @pytest.mark.parametrize( @@ -193,49 +193,49 @@ def test_send_mail_test_user(self, mock_send_transactional_email_secondary_task) ["avery.kelly@example.com", "sandy.zuko@passculture-test.app"], ) @pytest.mark.settings(WHITELISTED_EMAIL_RECIPIENTS=["avery.kelly@example.com", "*@passculture-test.app"]) - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_whitelisted(self, mock_send_transactional_email_secondary_task, recipient): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_whitelisted(self, mock_send_transactional_email_secondary_task_cloud_tasks, recipient): backend = self._get_backend_for_test() backend(use_pro_subaccount=False).send_mail( recipients=[recipient, "lucy.ellingson@example.com"], data=self.data ) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert list(task_param.recipients) == [recipient] @pytest.mark.settings(IS_STAGING=True, IS_E2E_TESTS=True, END_TO_END_TESTS_EMAIL_ADDRESS="qa-test@passculture.app") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_whitelisted_qa_staging(self, mock_send_transactional_email_secondary_task): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_whitelisted_qa_staging(self, mock_send_transactional_email_secondary_task_cloud_tasks): recipient = "qa-test+123@passculture.app" users_factories.UserFactory(email=recipient) backend = self._get_backend_for_test() backend(use_pro_subaccount=False).send_mail(recipients=[recipient], data=self.data) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert list(task_param.recipients) == [recipient] @pytest.mark.settings(IS_TESTING=True, IS_E2E_TESTS=True, END_TO_END_TESTS_EMAIL_ADDRESS="qa-test@passculture.app") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") def test_send_mail_whitelisted_qa_testing( - self, mock_send_transactional_email_secondary_task, recipient="qa-test+123@passculture.app" + self, mock_send_transactional_email_secondary_task_cloud_tasks, recipient="qa-test+123@passculture.app" ): users_factories.UserFactory(email=recipient) backend = self._get_backend_for_test() backend(use_pro_subaccount=False).send_mail(recipients=[recipient], data=self.data) - assert mock_send_transactional_email_secondary_task.call_count == 1 - task_param = mock_send_transactional_email_secondary_task.call_args[0][0] + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 + task_param = mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0] assert list(task_param.recipients) == [recipient] class SendTest: @pytest.mark.settings(IS_TESTING=True, EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_to_ehp_false_in_testing(self, mock_send_transactional_email_secondary_task, caplog): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_to_ehp_false_in_testing(self, mock_send_transactional_email_secondary_task_cloud_tasks, caplog): mock_template_send_ehp_false = models.Template( id_prod=11, id_not_prod=12, tags=["some", "stuff"], send_to_ehp=False ) @@ -246,7 +246,7 @@ def test_send_to_ehp_false_in_testing(self, mock_send_transactional_email_second with caplog.at_level(logging.INFO): send(recipients=recipients, data=data) - assert mock_send_transactional_email_secondary_task.call_count == 0 + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 0 assert caplog.messages[0] == ( "An email would be sent via Sendinblue to=lucy.ellingson@example.com, avery.kelly@example.com, bcc=(): " @@ -256,8 +256,8 @@ def test_send_to_ehp_false_in_testing(self, mock_send_transactional_email_second ) @pytest.mark.settings(IS_TESTING=True, EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_to_ehp_true_in_testing(self, mock_send_transactional_email_secondary_task, caplog): + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_to_ehp_true_in_testing(self, mock_send_transactional_email_secondary_task_cloud_tasks, caplog): mock_template_send_ehp_true = models.Template( id_prod=11, id_not_prod=12, tags=["some", "stuff"], send_to_ehp=True ) @@ -268,7 +268,7 @@ def test_send_to_ehp_true_in_testing(self, mock_send_transactional_email_seconda with caplog.at_level(logging.INFO): send(recipients=recipients, data=data) - assert mock_send_transactional_email_secondary_task.call_count == 1 + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 assert caplog.records == [] @pytest.mark.parametrize( @@ -279,10 +279,11 @@ def test_send_to_ehp_true_in_testing(self, mock_send_transactional_email_seconda ], ) @pytest.mark.settings(EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.SendinblueBackend") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_to_pro( + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_to_pro_with_FF( self, - mock_send_transactional_email_secondary_task, + mock_send_transactional_email_secondary_task_cloud_tasks, + features, caplog, template_class, expected_use_pro_subaccount, @@ -295,9 +296,9 @@ def test_send_mail_to_pro( with caplog.at_level(logging.INFO): send(recipients=recipients, data=data) - assert mock_send_transactional_email_secondary_task.call_count == 1 + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 1 assert ( - mock_send_transactional_email_secondary_task.call_args[0][0].use_pro_subaccount + mock_send_transactional_email_secondary_task_cloud_tasks.call_args[0][0].use_pro_subaccount is expected_use_pro_subaccount ) @@ -309,10 +310,11 @@ def test_send_mail_to_pro( ], ) @pytest.mark.settings(IS_TESTING=True, EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.SendinblueBackend") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task.delay") - def test_send_mail_to_pro_in_ehp( + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_secondary_task_cloud_tasks.delay") + def test_send_mail_to_pro_with_FF_in_ehp( self, - mock_send_transactional_email_secondary_task, + mock_send_transactional_email_secondary_task_cloud_tasks, + features, caplog, template_class, enable_unsubscribe, @@ -328,7 +330,7 @@ def test_send_mail_to_pro_in_ehp( with caplog.at_level(logging.INFO): send(recipients=recipients, data=data) - assert mock_send_transactional_email_secondary_task.call_count == 0 + assert mock_send_transactional_email_secondary_task_cloud_tasks.call_count == 0 assert caplog.messages[0] == ( f"An email would be sent via Sendinblue {'using the PRO subaccount ' if expected_use_pro_subaccount else ''}to=lucy.ellingson@example.com, " "avery.kelly@example.com, bcc=(): {'template': {'id_prod': 1, 'id_not_prod': 10, 'tags': [], 'use_priority_queue': False, " diff --git a/api/tests/core/mails/transactional/test_send_transactional_email.py b/api/tests/core/mails/transactional/test_send_transactional_email.py index 43b30aa078f..a297f1579f8 100644 --- a/api/tests/core/mails/transactional/test_send_transactional_email.py +++ b/api/tests/core/mails/transactional/test_send_transactional_email.py @@ -193,7 +193,7 @@ def test_send_transactional_email_with_template_id_success_empty_params(self, mo assert mock_send_transac_email.call_args[0][0].headers == {"X-List-Unsub": "disabled"} @pytest.mark.settings(EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") - @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_primary_task.delay") + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_primary_task_cloud_tasks.delay") def test_to_dev_send_email_confirmation_email(self, mock_send_transactional_email_task, db_session): user = users_factories.UserFactory(email="john.stiles@gmail.com") token = token_utils.Token.create( @@ -204,6 +204,19 @@ def test_to_dev_send_email_confirmation_email(self, mock_send_transactional_emai send_email_confirmation_email(user.email, token=token) mock_send_transactional_email_task.assert_called_once() + @pytest.mark.settings(EMAIL_BACKEND="pcapi.core.mails.backends.sendinblue.ToDevSendinblueBackend") + @pytest.mark.features(WIP_ASYNCHRONOUS_CELERY_TASKS=True) + @patch("pcapi.core.mails.backends.sendinblue.send_transactional_email_primary_task_celery.delay") + def test_to_dev_send_email_confirmation_email_through_celery(self, mock_send_transactional_email_task, db_session): + user = users_factories.UserFactory(email="john.celery@gmail.com") + token = token_utils.Token.create( + type_=token_utils.TokenType.EMAIL_VALIDATION, + ttl=users_constants.EMAIL_VALIDATION_TOKEN_LIFE_TIME, + user_id=user.id, + ) + send_email_confirmation_email(user.email, token=token) + mock_send_transactional_email_task.assert_called_once() + class TransactionalEmailWithoutTemplateTest: data = models.TransactionalWithoutTemplateEmailData(