tests for new post notifications and dashboard stats
This commit is contained in:
311
Cargo.lock
generated
311
Cargo.lock
generated
@@ -529,6 +529,29 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cssparser"
|
||||||
|
version = "0.35.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e901edd733a1472f944a45116df3f846f54d37e67e68640ac8bb69689aca2aa"
|
||||||
|
dependencies = [
|
||||||
|
"cssparser-macros",
|
||||||
|
"dtoa-short",
|
||||||
|
"itoa",
|
||||||
|
"phf",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cssparser-macros"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||||
|
dependencies = [
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.11"
|
version = "0.20.11"
|
||||||
@@ -603,6 +626,26 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "deunicode"
|
name = "deunicode"
|
||||||
version = "1.6.2"
|
version = "1.6.2"
|
||||||
@@ -656,6 +699,27 @@ version = "0.15.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtoa-short"
|
||||||
|
version = "0.3.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87"
|
||||||
|
dependencies = [
|
||||||
|
"dtoa",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ego-tree"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -811,6 +875,16 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futf"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843"
|
||||||
|
dependencies = [
|
||||||
|
"mac",
|
||||||
|
"new_debug_unreachable",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@@ -911,6 +985,15 @@ dependencies = [
|
|||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxhash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.7"
|
version = "0.14.7"
|
||||||
@@ -921,6 +1004,15 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -1044,6 +1136,17 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "html5ever"
|
||||||
|
version = "0.35.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"markup5ever",
|
||||||
|
"match_token",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "1.3.1"
|
version = "1.3.1"
|
||||||
@@ -1447,6 +1550,12 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mac"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markdown"
|
name = "markdown"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
@@ -1456,6 +1565,28 @@ dependencies = [
|
|||||||
"unicode-id",
|
"unicode-id",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markup5ever"
|
||||||
|
version = "0.35.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"tendril",
|
||||||
|
"web_atoms",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "match_token"
|
||||||
|
version = "0.35.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "matchers"
|
name = "matchers"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1529,6 +1660,12 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "new_debug_unreachable"
|
||||||
|
version = "1.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nom"
|
name = "nom"
|
||||||
version = "7.1.3"
|
version = "7.1.3"
|
||||||
@@ -1763,6 +1900,58 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||||
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_codegen"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
"rand 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_macros"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -1826,6 +2015,12 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "precomputed-hash"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro-error-attr2"
|
name = "proc-macro-error-attr2"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@@ -1908,7 +2103,7 @@ dependencies = [
|
|||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls",
|
"rustls",
|
||||||
"socket2 0.5.10",
|
"socket2 0.6.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -1945,9 +2140,9 @@ dependencies = [
|
|||||||
"cfg_aliases",
|
"cfg_aliases",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"socket2 0.5.10",
|
"socket2 0.6.0",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2275,6 +2470,21 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scraper"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5f3a24d916e78954af99281a455168d4a9515d65eca99a18da1b813689c4ad9"
|
||||||
|
dependencies = [
|
||||||
|
"cssparser",
|
||||||
|
"ego-tree",
|
||||||
|
"getopts",
|
||||||
|
"html5ever",
|
||||||
|
"precomputed-hash",
|
||||||
|
"selectors",
|
||||||
|
"tendril",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "secrecy"
|
name = "secrecy"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
@@ -2285,6 +2495,25 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "selectors"
|
||||||
|
version = "0.31.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5685b6ae43bfcf7d2e7dfcfb5d8e8f61b46442c902531e41a32a9a8bf0ee0fb6"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cssparser",
|
||||||
|
"derive_more",
|
||||||
|
"fxhash",
|
||||||
|
"log",
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"phf",
|
||||||
|
"phf_codegen",
|
||||||
|
"precomputed-hash",
|
||||||
|
"servo_arc",
|
||||||
|
"smallvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "1.0.26"
|
version = "1.0.26"
|
||||||
@@ -2387,6 +2616,15 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "servo_arc"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "204ea332803bd95a0b60388590d59cf6468ec9becf626e2451f1d26a1d972de4"
|
||||||
|
dependencies = [
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -2434,6 +2672,12 @@ dependencies = [
|
|||||||
"rand_core 0.6.4",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.11"
|
version = "0.4.11"
|
||||||
@@ -2692,6 +2936,31 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string_cache"
|
||||||
|
version = "0.8.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f"
|
||||||
|
dependencies = [
|
||||||
|
"new_debug_unreachable",
|
||||||
|
"parking_lot",
|
||||||
|
"phf_shared",
|
||||||
|
"precomputed-hash",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "string_cache_codegen"
|
||||||
|
version = "0.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stringprep"
|
name = "stringprep"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -2746,6 +3015,17 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tendril"
|
||||||
|
version = "0.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0"
|
||||||
|
dependencies = [
|
||||||
|
"futf",
|
||||||
|
"mac",
|
||||||
|
"utf-8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.16"
|
version = "2.0.16"
|
||||||
@@ -3208,6 +3488,12 @@ version = "1.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@@ -3231,6 +3517,12 @@ version = "2.1.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf-8"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "utf8_iter"
|
name = "utf8_iter"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
@@ -3418,6 +3710,18 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "web_atoms"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57ffde1dc01240bdf9992e3205668b235e59421fd085e8a317ed98da0178d414"
|
||||||
|
dependencies = [
|
||||||
|
"phf",
|
||||||
|
"phf_codegen",
|
||||||
|
"string_cache",
|
||||||
|
"string_cache_codegen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webpki-roots"
|
name = "webpki-roots"
|
||||||
version = "0.26.11"
|
version = "0.26.11"
|
||||||
@@ -3778,6 +4082,7 @@ dependencies = [
|
|||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"rand 0.9.2",
|
"rand 0.9.2",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"scraper",
|
||||||
"secrecy",
|
"secrecy",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-aux",
|
"serde-aux",
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ linkify = "0.10.0"
|
|||||||
once_cell = "1.21.3"
|
once_cell = "1.21.3"
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.1.0"
|
quickcheck_macros = "1.1.0"
|
||||||
|
scraper = "0.24.0"
|
||||||
serde_json = "1.0.143"
|
serde_json = "1.0.143"
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
wiremock = "0.6.4"
|
wiremock = "0.6.4"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<p class="text-sm font-medium text-gray-500">Subscribers</p>
|
<p class="text-sm font-medium text-gray-500">Subscribers</p>
|
||||||
<p class="text-2xl font-semibold text-gray-900">{{ stats.subscribers }}</p>
|
<p class="text-2xl font-semibold text-gray-900" id="subscribers-count">{{ stats.subscribers }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<p class="text-sm font-medium text-gray-500">Posts</p>
|
<p class="text-sm font-medium text-gray-500">Posts</p>
|
||||||
<p class="text-2xl font-semibold text-gray-900">{{ stats.posts }}</p>
|
<p class="text-2xl font-semibold text-gray-900" id="posts-count">{{ stats.posts }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<p class="text-sm font-medium text-gray-500">Notifications</p>
|
<p class="text-sm font-medium text-gray-500">Notifications</p>
|
||||||
<p class="text-2xl font-semibold text-gray-900">{{ stats.notifications_sent }}</p>
|
<p class="text-2xl font-semibold text-gray-900" id="notifications-sent">{{ stats.notifications_sent }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="ml-4">
|
<div class="ml-4">
|
||||||
<p class="text-sm font-medium text-gray-500">Open rate</p>
|
<p class="text-sm font-medium text-gray-500">Open rate</p>
|
||||||
<p class="text-2xl font-semibold text-gray-900">{{ stats.formatted_rate() }}</p>
|
<p class="text-2xl font-semibold text-gray-900" id="open-rate">{{ stats.formatted_rate() }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use crate::helpers::{TestApp, assert_is_redirect_to};
|
use crate::helpers::{TestApp, assert_is_redirect_to, fake_post_body, when_sending_an_email};
|
||||||
|
use scraper::{Html, Selector};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
use wiremock::ResponseTemplate;
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
async fn you_must_be_logged_in_to_access_the_admin_dashboard(connection_pool: PgPool) {
|
async fn you_must_be_logged_in_to_access_the_admin_dashboard(connection_pool: PgPool) {
|
||||||
@@ -54,3 +56,68 @@ async fn subscribers_are_visible_on_the_dashboard(connection_pool: PgPool) {
|
|||||||
assert!(response.contains("No data available"));
|
assert!(response.contains("No data available"));
|
||||||
assert!(!response.contains(&subscriber.email));
|
assert!(!response.contains(&subscriber.email));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
async fn dashboard_shows_correct_stats(connection_pool: PgPool) {
|
||||||
|
let app = TestApp::spawn(connection_pool).await;
|
||||||
|
app.admin_login().await;
|
||||||
|
|
||||||
|
app.create_confirmed_subscriber().await;
|
||||||
|
app.create_confirmed_subscriber().await;
|
||||||
|
app.create_unconfirmed_subscriber().await;
|
||||||
|
app.post_create_post(&fake_post_body()).await;
|
||||||
|
app.create_confirmed_subscriber().await;
|
||||||
|
|
||||||
|
when_sending_an_email()
|
||||||
|
.respond_with(ResponseTemplate::new(200))
|
||||||
|
.mount(&app.email_server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
app.dispatch_all_pending_emails().await;
|
||||||
|
|
||||||
|
let html = app.get_admin_dashboard_html().await;
|
||||||
|
let document = Html::parse_document(&html);
|
||||||
|
|
||||||
|
assert_element_is(&document, "p#subscribers-count", "3");
|
||||||
|
assert_element_is(&document, "p#posts-count", "1");
|
||||||
|
assert_element_is(&document, "p#notifications-sent", "2");
|
||||||
|
assert_element_is(&document, "p#open-rate", "0.0%");
|
||||||
|
|
||||||
|
let email_request = app
|
||||||
|
.email_server
|
||||||
|
.received_requests()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.pop()
|
||||||
|
.unwrap();
|
||||||
|
let links = app.get_post_urls(&email_request);
|
||||||
|
reqwest::get(links.html).await.unwrap();
|
||||||
|
|
||||||
|
let html = app.get_admin_dashboard_html().await;
|
||||||
|
let document = Html::parse_document(&html);
|
||||||
|
assert_element_is(&document, "p#open-rate", "50.0%");
|
||||||
|
|
||||||
|
app.post_create_post(&fake_post_body()).await;
|
||||||
|
app.dispatch_all_pending_emails().await;
|
||||||
|
let email_request = app
|
||||||
|
.email_server
|
||||||
|
.received_requests()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.pop()
|
||||||
|
.unwrap();
|
||||||
|
let links = app.get_post_urls(&email_request);
|
||||||
|
reqwest::get(links.html).await.unwrap();
|
||||||
|
|
||||||
|
let html = app.get_admin_dashboard_html().await;
|
||||||
|
let document = Html::parse_document(&html);
|
||||||
|
assert_element_is(&document, "p#posts-count", "2");
|
||||||
|
assert_element_is(&document, "p#notifications-sent", "5");
|
||||||
|
assert_element_is(&document, "p#open-rate", "40.0%");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assert_element_is(document: &Html, selectors: &str, value: &str) {
|
||||||
|
let selector = Selector::parse(selectors).unwrap();
|
||||||
|
let mut element = document.select(&selector);
|
||||||
|
assert_eq!(element.next().unwrap().text().collect::<String>(), value);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ use argon2::{
|
|||||||
Algorithm, Argon2, Params, PasswordHasher, Version,
|
Algorithm, Argon2, Params, PasswordHasher, Version,
|
||||||
password_hash::{SaltString, rand_core::OsRng},
|
password_hash::{SaltString, rand_core::OsRng},
|
||||||
};
|
};
|
||||||
use fake::{Fake, faker::internet::en::SafeEmail};
|
use fake::{
|
||||||
|
Fake,
|
||||||
|
faker::{
|
||||||
|
internet::en::SafeEmail,
|
||||||
|
lorem::en::{Paragraph, Sentence},
|
||||||
|
},
|
||||||
|
};
|
||||||
use linkify::{Link, LinkFinder};
|
use linkify::{Link, LinkFinder};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
@@ -183,6 +189,22 @@ impl TestApp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_post_urls(&self, request: &wiremock::Request) -> ConfirmationLinks {
|
||||||
|
let body: serde_json::Value = serde_json::from_slice(&request.body).unwrap();
|
||||||
|
let get_link = |s: &str| {
|
||||||
|
let links = get_links(s);
|
||||||
|
assert!(!links.is_empty());
|
||||||
|
let mut confirmation_link = reqwest::Url::parse(links[0].as_str()).unwrap();
|
||||||
|
assert_eq!(confirmation_link.host_str().unwrap(), "127.0.0.1");
|
||||||
|
confirmation_link.set_port(Some(self.port)).unwrap();
|
||||||
|
confirmation_link
|
||||||
|
};
|
||||||
|
|
||||||
|
let html = get_link(body["html"].as_str().unwrap());
|
||||||
|
let text = get_link(body["text"].as_str().unwrap());
|
||||||
|
ConfirmationLinks { html, text }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_unsubscribe_links(&self, request: &wiremock::Request) -> ConfirmationLinks {
|
pub fn get_unsubscribe_links(&self, request: &wiremock::Request) -> ConfirmationLinks {
|
||||||
let body: serde_json::Value = serde_json::from_slice(&request.body).unwrap();
|
let body: serde_json::Value = serde_json::from_slice(&request.body).unwrap();
|
||||||
let get_link = |s: &str| {
|
let get_link = |s: &str| {
|
||||||
@@ -395,3 +417,11 @@ pub fn get_links(s: &'_ str) -> Vec<Link<'_>> {
|
|||||||
.filter(|l| *l.kind() == linkify::LinkKind::Url)
|
.filter(|l| *l.kind() == linkify::LinkKind::Url)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn subject() -> String {
|
||||||
|
Sentence(1..2).fake()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn content() -> String {
|
||||||
|
Paragraph(1..10).fake()
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,20 +1,10 @@
|
|||||||
use crate::helpers::{TestApp, assert_is_redirect_to, when_sending_an_email};
|
use crate::helpers::{
|
||||||
use fake::{
|
TestApp, assert_is_redirect_to, content, fake_post_body, subject, when_sending_an_email,
|
||||||
Fake,
|
|
||||||
faker::lorem::en::{Paragraph, Sentence},
|
|
||||||
};
|
};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use wiremock::ResponseTemplate;
|
use wiremock::ResponseTemplate;
|
||||||
|
|
||||||
fn subject() -> String {
|
|
||||||
Sentence(1..2).fake()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn content() -> String {
|
|
||||||
Paragraph(1..10).fake()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
async fn you_must_be_logged_in_to_create_a_new_post(connection_pool: PgPool) {
|
async fn you_must_be_logged_in_to_create_a_new_post(connection_pool: PgPool) {
|
||||||
let app = TestApp::spawn(connection_pool).await;
|
let app = TestApp::spawn(connection_pool).await;
|
||||||
@@ -83,6 +73,51 @@ async fn confirmed_subscribers_are_notified_when_a_new_post_is_published(connect
|
|||||||
app.dispatch_all_pending_emails().await;
|
app.dispatch_all_pending_emails().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
async fn notification_contains_the_blog_post_url(connection_pool: PgPool) {
|
||||||
|
let app = TestApp::spawn(connection_pool).await;
|
||||||
|
app.create_confirmed_subscriber().await;
|
||||||
|
app.admin_login().await;
|
||||||
|
|
||||||
|
let title = subject();
|
||||||
|
let content = content();
|
||||||
|
let body = serde_json::json!({
|
||||||
|
"title": title,
|
||||||
|
"content": content,
|
||||||
|
"idempotency_key": Uuid::new_v4(),
|
||||||
|
|
||||||
|
});
|
||||||
|
app.post_create_post(&body).await;
|
||||||
|
|
||||||
|
when_sending_an_email()
|
||||||
|
.respond_with(ResponseTemplate::new(200))
|
||||||
|
.expect(1)
|
||||||
|
.mount(&app.email_server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
app.dispatch_all_pending_emails().await;
|
||||||
|
|
||||||
|
let email_request = app
|
||||||
|
.email_server
|
||||||
|
.received_requests()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.pop()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let post_id = sqlx::query!("SELECT post_id FROM posts")
|
||||||
|
.fetch_one(&app.connection_pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.post_id;
|
||||||
|
|
||||||
|
let links = app.get_post_urls(&email_request);
|
||||||
|
let text = String::from_utf8(email_request.body).unwrap();
|
||||||
|
|
||||||
|
assert!(text.contains(&title));
|
||||||
|
assert!(links.html.as_str().contains(&post_id.to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
#[sqlx::test]
|
#[sqlx::test]
|
||||||
async fn new_posts_are_visible_on_the_website(connection_pool: PgPool) {
|
async fn new_posts_are_visible_on_the_website(connection_pool: PgPool) {
|
||||||
let app = TestApp::spawn(connection_pool).await;
|
let app = TestApp::spawn(connection_pool).await;
|
||||||
@@ -165,3 +200,37 @@ async fn a_deleted_blog_post_returns_404(connection_pool: PgPool) {
|
|||||||
let html = app.get_post_html(post.post_id).await;
|
let html = app.get_post_html(post.post_id).await;
|
||||||
assert!(html.contains("Not Found"));
|
assert!(html.contains("Not Found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sqlx::test]
|
||||||
|
async fn clicking_the_notification_link_marks_the_email_as_opened(connection_pool: PgPool) {
|
||||||
|
let app = TestApp::spawn(connection_pool).await;
|
||||||
|
app.admin_login().await;
|
||||||
|
app.create_confirmed_subscriber().await;
|
||||||
|
app.post_create_post(&fake_post_body()).await;
|
||||||
|
|
||||||
|
when_sending_an_email()
|
||||||
|
.respond_with(ResponseTemplate::new(200))
|
||||||
|
.expect(1)
|
||||||
|
.mount(&app.email_server)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
app.dispatch_all_pending_emails().await;
|
||||||
|
|
||||||
|
let email_request = app
|
||||||
|
.email_server
|
||||||
|
.received_requests()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.pop()
|
||||||
|
.unwrap();
|
||||||
|
let links = app.get_post_urls(&email_request);
|
||||||
|
reqwest::get(links.html.as_str()).await.unwrap();
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
sqlx::query!("SELECT opened FROM notifications_delivered")
|
||||||
|
.fetch_one(&app.connection_pool)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.opened
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ async fn subscribe_persists_the_new_subscriber(connection_pool: PgPool) {
|
|||||||
let response = app.post_subscriptions(body).await;
|
let response = app.post_subscriptions(body).await;
|
||||||
|
|
||||||
assert!(response.status().is_success());
|
assert!(response.status().is_success());
|
||||||
let html_fragment = dbg!(response.text().await.unwrap());
|
let html_fragment = response.text().await.unwrap();
|
||||||
assert!(html_fragment.contains("You'll receive a confirmation email shortly"));
|
assert!(html_fragment.contains("You'll receive a confirmation email shortly"));
|
||||||
|
|
||||||
let saved = sqlx::query!("SELECT email, status FROM subscriptions")
|
let saved = sqlx::query!("SELECT email, status FROM subscriptions")
|
||||||
|
|||||||
@@ -161,7 +161,6 @@ async fn subscription_works_after_unsubscribe(connection_pool: PgPool) {
|
|||||||
let requests = app.email_server.received_requests().await.unwrap();
|
let requests = app.email_server.received_requests().await.unwrap();
|
||||||
let confirmation_request = requests.last().unwrap();
|
let confirmation_request = requests.last().unwrap();
|
||||||
let confirmation_links = app.get_confirmation_links(confirmation_request);
|
let confirmation_links = app.get_confirmation_links(confirmation_request);
|
||||||
dbg!(&confirmation_links.html.as_str());
|
|
||||||
|
|
||||||
let response = reqwest::get(confirmation_links.html).await.unwrap();
|
let response = reqwest::get(confirmation_links.html).await.unwrap();
|
||||||
assert_eq!(response.status().as_u16(), 200);
|
assert_eq!(response.status().as_u16(), 200);
|
||||||
|
|||||||
Reference in New Issue
Block a user