mirror of
https://github.com/kristoferssolo/zero2prod.git
synced 2025-10-21 20:10:40 +00:00
test(wiremoc): add tests
This commit is contained in:
parent
a68c95d51b
commit
241fe4953f
324
Cargo.lock
generated
324
Cargo.lock
generated
@ -60,6 +60,16 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "assert-json-diff"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.79"
|
||||
@ -265,16 +275,6 @@ version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
@ -330,6 +330,24 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deadpool"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"deadpool-runtime",
|
||||
"num_cpus",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deadpool-runtime"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49"
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.8"
|
||||
@ -377,15 +395,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
@ -467,21 +476,6 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
@ -491,6 +485,21 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.30"
|
||||
@ -535,6 +544,17 @@ version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.30"
|
||||
@ -553,8 +573,10 @@ version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
@ -765,18 +787,19 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.6.0"
|
||||
name = "hyper-rustls"
|
||||
version = "0.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
||||
checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http-body-util",
|
||||
"futures-util",
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"native-tls",
|
||||
"rustls 0.22.3",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
]
|
||||
|
||||
@ -1000,24 +1023,6 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"openssl",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"schannel",
|
||||
"security-framework",
|
||||
"security-framework-sys",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
@ -1116,50 +1121,6 @@ version = "1.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.64"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f"
|
||||
dependencies = [
|
||||
"bitflags 2.5.0",
|
||||
"cfg-if",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"openssl-macros",
|
||||
"openssl-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.55",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "overload"
|
||||
version = "0.1.1"
|
||||
@ -1460,37 +1421,36 @@ checksum = "e333b1eb9fe677f6893a9efcb0d277a2d3edd83f358a236b657c32301dc6e5f6"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustls 0.22.3",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"webpki-roots 0.26.1",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
@ -1555,10 +1515,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-webpki",
|
||||
"rustls-webpki 0.101.7",
|
||||
"sct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.22.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99008d7ad0bbbea527ec27bddbc0e432c5b87d8175178cee68d2eec9c4a1813c"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki 0.102.2",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pemfile"
|
||||
version = "1.0.4"
|
||||
@ -1568,6 +1542,12 @@ dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247"
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.101.7"
|
||||
@ -1578,6 +1558,17 @@ dependencies = [
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.102.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
@ -1590,15 +1581,6 @@ version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
@ -1625,29 +1607,6 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"security-framework-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.197"
|
||||
@ -1752,15 +1711,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@ -1873,7 +1823,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"paste",
|
||||
"percent-encoding",
|
||||
"rustls",
|
||||
"rustls 0.21.10",
|
||||
"rustls-pemfile",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -1886,7 +1836,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
"webpki-roots",
|
||||
"webpki-roots 0.25.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2082,27 +2032,6 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"core-foundation",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.10.1"
|
||||
@ -2202,11 +2131,10 @@ dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"tracing",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -2222,12 +2150,13 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-native-tls"
|
||||
version = "0.3.1"
|
||||
name = "tokio-rustls"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
|
||||
checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"rustls 0.22.3",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
@ -2646,6 +2575,15 @@ version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "0.26.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "whoami"
|
||||
version = "1.5.1"
|
||||
@ -2838,6 +2776,30 @@ dependencies = [
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wiremock"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec874e1eef0df2dcac546057fe5e29186f09c378181cd7b635b4b7bcc98e9d81"
|
||||
dependencies = [
|
||||
"assert-json-diff",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"deadpool",
|
||||
"futures",
|
||||
"http",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"log",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zero2prod"
|
||||
version = "0.1.0"
|
||||
@ -2854,6 +2816,7 @@ dependencies = [
|
||||
"secrecy",
|
||||
"serde",
|
||||
"serde-aux",
|
||||
"serde_json",
|
||||
"sqlx",
|
||||
"tokio",
|
||||
"tower-http",
|
||||
@ -2864,6 +2827,7 @@ dependencies = [
|
||||
"unicode-segmentation",
|
||||
"uuid",
|
||||
"validator",
|
||||
"wiremock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
14
Cargo.toml
14
Cargo.toml
@ -27,7 +27,12 @@ sqlx = { version = "0.7", default-features = false, features = [
|
||||
"chrono",
|
||||
"migrate",
|
||||
] }
|
||||
tokio = { version = "1.36", features = ["full"] }
|
||||
tokio = { version = "1.36", features = [
|
||||
"rt",
|
||||
"macros",
|
||||
"tracing",
|
||||
"rt-multi-thread",
|
||||
] }
|
||||
uuid = { version = "1.8", features = ["v4", "serde"] }
|
||||
tracing = { version = "0.1", features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["registry", "env-filter"] }
|
||||
@ -39,13 +44,18 @@ serde-aux = "4"
|
||||
unicode-segmentation = "1"
|
||||
claims = "0.7"
|
||||
validator = "0.16"
|
||||
reqwest = { version = "0.12", default-features = false, features = [
|
||||
"json",
|
||||
"rustls-tls",
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
reqwest = "0.12"
|
||||
once_cell = "1.19"
|
||||
fake = "~2.3"
|
||||
quickcheck = "0.9"
|
||||
quickcheck_macros = "0.9"
|
||||
wiremock = "0.6"
|
||||
serde_json = "1"
|
||||
|
||||
[package.metadata.clippy]
|
||||
warn = [
|
||||
|
||||
@ -7,3 +7,8 @@ port = 5432
|
||||
username = "postgres"
|
||||
password = "password"
|
||||
database_name = "newsletter"
|
||||
|
||||
[email_client]
|
||||
base_url = "localhost"
|
||||
sender_email = "test@gmail.com"
|
||||
auth_token = "super-secret-token"
|
||||
|
||||
@ -3,3 +3,7 @@ host = "0.0.0.0"
|
||||
|
||||
[database]
|
||||
require_ssl = true
|
||||
|
||||
[email_client]
|
||||
base_url = "localhost"
|
||||
sender_email = "zero2prod@kristofers.xyz" # FIX: swap to postmark
|
||||
|
||||
@ -8,10 +8,13 @@ use sqlx::{
|
||||
ConnectOptions,
|
||||
};
|
||||
|
||||
use crate::domain::SubscriberEmail;
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Settings {
|
||||
pub database: DatabaseSettings,
|
||||
pub application: ApplicationSettings,
|
||||
pub email_client: EmailClientSettings,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
@ -37,6 +40,18 @@ pub enum Environment {
|
||||
Local,
|
||||
Production,
|
||||
}
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct EmailClientSettings {
|
||||
pub base_url: String,
|
||||
pub sender_email: String,
|
||||
pub auth_token: Secret<String>,
|
||||
}
|
||||
|
||||
impl EmailClientSettings {
|
||||
pub fn sender(&self) -> Result<SubscriberEmail, String> {
|
||||
self.sender_email.clone().try_into()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_config() -> Result<Settings, config::ConfigError> {
|
||||
let base_path = std::env::current_dir().expect("Failed to determine current directory");
|
||||
|
||||
@ -2,7 +2,7 @@ use std::str::FromStr;
|
||||
|
||||
use validator::validate_email;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SubscriberEmail(String);
|
||||
|
||||
impl TryFrom<String> for SubscriberEmail {
|
||||
|
||||
121
src/email_client.rs
Normal file
121
src/email_client.rs
Normal file
@ -0,0 +1,121 @@
|
||||
use reqwest::{Client, Url};
|
||||
use secrecy::{ExposeSecret, Secret};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::domain::SubscriberEmail;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EmailClient {
|
||||
http_client: Client,
|
||||
base_url: String,
|
||||
sender: SubscriberEmail,
|
||||
auth_token: Secret<String>,
|
||||
}
|
||||
|
||||
impl EmailClient {
|
||||
pub fn new(base_url: String, sender: SubscriberEmail, auth_token: Secret<String>) -> Self {
|
||||
Self {
|
||||
http_client: Client::new(),
|
||||
base_url,
|
||||
sender,
|
||||
auth_token,
|
||||
}
|
||||
}
|
||||
pub async fn send_email(
|
||||
&self,
|
||||
recipient: SubscriberEmail,
|
||||
subject: &str,
|
||||
html_content: &str,
|
||||
text_content: &str,
|
||||
) -> Result<(), reqwest::Error> {
|
||||
let url = Url::parse(&self.base_url)
|
||||
.and_then(|path| path.join("email"))
|
||||
.unwrap();
|
||||
let request_body = SendEmailRequest {
|
||||
from: self.sender.as_ref().to_owned(),
|
||||
to: recipient.as_ref().to_owned(),
|
||||
subject: subject.to_owned(),
|
||||
html_body: html_content.to_owned(),
|
||||
text_body: text_content.to_owned(),
|
||||
};
|
||||
let builder = self
|
||||
.http_client
|
||||
.post(url)
|
||||
.header("X-Postmark-Server-Token", self.auth_token.expose_secret())
|
||||
.json(&request_body)
|
||||
.send()
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(rename_all = "PascalCase")]
|
||||
struct SendEmailRequest {
|
||||
from: String,
|
||||
to: String,
|
||||
subject: String,
|
||||
html_body: String,
|
||||
text_body: String,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use fake::{
|
||||
faker::{
|
||||
internet::en::SafeEmail,
|
||||
lorem::en::{Paragraph, Sentence},
|
||||
},
|
||||
Fake, Faker,
|
||||
};
|
||||
use serde_json;
|
||||
use wiremock::{
|
||||
matchers::{header, header_exists, method, path},
|
||||
Match, Mock, MockServer, ResponseTemplate,
|
||||
};
|
||||
|
||||
struct SendEmailBodyMatcher;
|
||||
|
||||
impl Match for SendEmailBodyMatcher {
|
||||
fn matches(&self, request: &wiremock::Request) -> bool {
|
||||
match serde_json::from_slice::<serde_json::Value>(&request.body) {
|
||||
Err(_) => false,
|
||||
Ok(body) => {
|
||||
dbg!(&body);
|
||||
body.get("From").is_some()
|
||||
&& body.get("To").is_some()
|
||||
&& body.get("Subject").is_some()
|
||||
&& body.get("HtmlBody").is_some()
|
||||
&& body.get("TextBody").is_some()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use super::*;
|
||||
#[tokio::test]
|
||||
async fn send_email_sends_the_expectred_request() {
|
||||
let mock_server = MockServer::start().await;
|
||||
let sender = SafeEmail().fake::<String>().try_into().unwrap();
|
||||
let email_client = EmailClient::new(mock_server.uri(), sender, Secret::new(Faker.fake()));
|
||||
|
||||
Mock::given(header_exists("X-Postmark-Server-Token"))
|
||||
.and(header("Content-Type", "application/json"))
|
||||
.and(path("/email"))
|
||||
.and(method("POST"))
|
||||
.and(SendEmailBodyMatcher)
|
||||
.respond_with(ResponseTemplate::new(200))
|
||||
.expect(1)
|
||||
.mount(&mock_server)
|
||||
.await;
|
||||
|
||||
let subscriber_email = SafeEmail().fake::<String>().try_into().unwrap();
|
||||
let subject = Sentence(1..2).fake::<String>();
|
||||
dbg!(&subject);
|
||||
let content = Paragraph(1..10).fake::<String>();
|
||||
let _ = email_client
|
||||
.send_email(subscriber_email, &subject, &content, &content)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod config;
|
||||
pub mod domain;
|
||||
pub mod email_client;
|
||||
pub mod routes;
|
||||
pub mod telemetry;
|
||||
|
||||
13
src/main.rs
13
src/main.rs
@ -2,6 +2,7 @@ use sqlx::postgres::PgPoolOptions;
|
||||
use tokio::net::TcpListener;
|
||||
use zero2prod::{
|
||||
config::get_config,
|
||||
email_client::EmailClient,
|
||||
routes::route,
|
||||
telemetry::{get_subscriber, init_subscriber},
|
||||
};
|
||||
@ -17,5 +18,15 @@ async fn main() -> Result<(), std::io::Error> {
|
||||
.await
|
||||
.expect("Failed to bind port 8000.");
|
||||
|
||||
axum::serve(listener, route(pool)).await
|
||||
let sender_email = config
|
||||
.email_client
|
||||
.sender()
|
||||
.expect("Invalid sender email adress");
|
||||
let email_client = EmailClient::new(
|
||||
config.email_client.base_url,
|
||||
sender_email,
|
||||
config.email_client.auth_token,
|
||||
);
|
||||
|
||||
axum::serve(listener, route(pool, email_client)).await
|
||||
}
|
||||
|
||||
@ -19,11 +19,14 @@ use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer};
|
||||
use tracing::{info_span, Span};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub fn route(state: PgPool) -> Router {
|
||||
use crate::email_client::EmailClient;
|
||||
|
||||
pub fn route(pool: PgPool, email_client: EmailClient) -> Router {
|
||||
Router::new()
|
||||
.route("/health_check", get(health_check))
|
||||
.route("/subscriptions", post(subscribe))
|
||||
.with_state(state)
|
||||
.with_state(pool)
|
||||
.with_state(email_client)
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.make_span_with(|request: &Request<_>| {
|
||||
|
||||
@ -5,7 +5,7 @@ use sqlx::PgPool;
|
||||
use tracing::error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::domain::{NewSubscriber, SubscriberEmail, SubscriberName};
|
||||
use crate::domain::NewSubscriber;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct FormData {
|
||||
|
||||
@ -5,6 +5,7 @@ use tokio::net::TcpListener;
|
||||
use uuid::Uuid;
|
||||
use zero2prod::{
|
||||
config::{get_config, DatabaseSettings},
|
||||
email_client::EmailClient,
|
||||
routes::route,
|
||||
telemetry::{get_subscriber, init_subscriber},
|
||||
};
|
||||
@ -140,8 +141,19 @@ async fn spawn_app() -> TestApp {
|
||||
|
||||
let pool = configure_database(&config.database).await;
|
||||
let pool_clone = pool.clone();
|
||||
|
||||
let sender_email = config
|
||||
.email_client
|
||||
.sender()
|
||||
.expect("Invalid sender email adress");
|
||||
let email_client = EmailClient::new(
|
||||
config.email_client.base_url,
|
||||
sender_email,
|
||||
config.email_client.auth_token,
|
||||
);
|
||||
|
||||
let _ = tokio::spawn(async move {
|
||||
axum::serve(listener, route(pool_clone))
|
||||
axum::serve(listener, route(pool_clone, email_client))
|
||||
.await
|
||||
.expect("Failed to bind address.")
|
||||
});
|
||||
|
||||
Loading…
Reference in New Issue
Block a user