mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-03-28 06:05:29 +00:00
Merge branch 'main' into no-individual-inboxes
This commit is contained in:
commit
e6f219aa1a
127 changed files with 2871 additions and 2205 deletions
|
@ -42,14 +42,14 @@ steps:
|
||||||
- event: [pull_request, tag]
|
- event: [pull_request, tag]
|
||||||
|
|
||||||
prettier_check:
|
prettier_check:
|
||||||
image: tmknom/prettier:3.0.0
|
image: tmknom/prettier:3.2.5
|
||||||
commands:
|
commands:
|
||||||
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations' '!api_tests/pnpm-lock.yaml'
|
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations' '!api_tests/pnpm-lock.yaml'
|
||||||
when:
|
when:
|
||||||
- event: pull_request
|
- event: pull_request
|
||||||
|
|
||||||
toml_fmt:
|
toml_fmt:
|
||||||
image: tamasfe/taplo:0.8.1
|
image: tamasfe/taplo:0.9.3
|
||||||
commands:
|
commands:
|
||||||
- taplo format --check
|
- taplo format --check
|
||||||
when:
|
when:
|
||||||
|
|
136
Cargo.lock
generated
136
Cargo.lock
generated
|
@ -42,7 +42,7 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"rsa",
|
"rsa",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -298,9 +298,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-web-prom"
|
name = "actix-web-prom"
|
||||||
version = "0.8.0"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76743e67d4e7efa9fc2ac7123de0dd7b2ca592668e19334f1d81a3b077afc6ac"
|
checksum = "56a34f1825c3ae06567a9d632466809bbf34963c86002e8921b64f32d48d289d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -839,9 +839,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.17"
|
version = "4.5.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac"
|
checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
|
@ -849,9 +849,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.17"
|
version = "4.5.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73"
|
checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
|
@ -861,9 +861,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_derive"
|
name = "clap_derive"
|
||||||
version = "4.5.13"
|
version = "4.5.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0"
|
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -1645,9 +1645,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures"
|
name = "futures"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -1660,9 +1660,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
|
@ -1670,15 +1670,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-core"
|
name = "futures-core"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-executor"
|
name = "futures-executor"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
|
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-task",
|
"futures-task",
|
||||||
|
@ -1687,15 +1687,15 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-io"
|
name = "futures-io"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -1704,21 +1704,21 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-sink"
|
name = "futures-sink"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
|
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-task"
|
name = "futures-task"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
|
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.30"
|
version = "0.3.31"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
@ -1875,9 +1875,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html2text"
|
name = "html2text"
|
||||||
version = "0.12.5"
|
version = "0.12.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c66ee488a63a92237d5b48875b7e05bb293be8fb2894641c8118b60c08ab5ef"
|
checksum = "042a9677c258ac2952dd026bb0cd21972f00f644a5a38f5a215cb22cdaf6834e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"html5ever 0.27.0",
|
"html5ever 0.27.0",
|
||||||
"markup5ever 0.12.1",
|
"markup5ever 0.12.1",
|
||||||
|
@ -1989,7 +1989,7 @@ dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"http-signature-normalization",
|
"http-signature-normalization",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"sha2",
|
"sha2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -2075,7 +2075,7 @@ dependencies = [
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"hyper 1.4.1",
|
"hyper 1.4.1",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.26.0",
|
||||||
|
@ -2514,7 +2514,7 @@ dependencies = [
|
||||||
"moka",
|
"moka",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"rosetta-i18n",
|
"rosetta-i18n",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2579,7 +2579,7 @@ dependencies = [
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"moka",
|
"moka",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
@ -2630,7 +2630,7 @@ dependencies = [
|
||||||
"moka",
|
"moka",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
|
@ -2718,7 +2718,7 @@ dependencies = [
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"mockall",
|
"mockall",
|
||||||
"moka",
|
"moka",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"test-context",
|
"test-context",
|
||||||
|
@ -2745,7 +2745,7 @@ dependencies = [
|
||||||
"lemmy_db_views",
|
"lemmy_db_views",
|
||||||
"lemmy_db_views_actor",
|
"lemmy_db_views_actor",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"rss",
|
"rss",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -2778,10 +2778,10 @@ dependencies = [
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"prometheus",
|
"prometheus",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"reqwest-tracing",
|
"reqwest-tracing",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -2811,7 +2811,7 @@ dependencies = [
|
||||||
"markdown-it",
|
"markdown-it",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"rosetta-build",
|
"rosetta-build",
|
||||||
"rosetta-i18n",
|
"rosetta-i18n",
|
||||||
|
@ -2847,7 +2847,7 @@ dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"quoted_printable",
|
"quoted_printable",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"rustls-pemfile 2.1.3",
|
"rustls-pemfile 2.1.3",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"socket2",
|
"socket2",
|
||||||
|
@ -3762,7 +3762,7 @@ dependencies = [
|
||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash 2.0.0",
|
"rustc-hash 2.0.0",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -3779,7 +3779,7 @@ dependencies = [
|
||||||
"rand",
|
"rand",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash 2.0.0",
|
"rustc-hash 2.0.0",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
|
@ -3875,14 +3875,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex"
|
name = "regex"
|
||||||
version = "1.10.6"
|
version = "1.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
|
checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-automata 0.4.7",
|
"regex-automata 0.4.8",
|
||||||
"regex-syntax 0.8.4",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3896,13 +3896,13 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-automata"
|
name = "regex-automata"
|
||||||
version = "0.4.7"
|
version = "0.4.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
|
checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aho-corasick",
|
"aho-corasick",
|
||||||
"memchr",
|
"memchr",
|
||||||
"regex-syntax 0.8.4",
|
"regex-syntax 0.8.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -3919,9 +3919,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "regex-syntax"
|
name = "regex-syntax"
|
||||||
version = "0.8.4"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
|
@ -3966,9 +3966,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.7"
|
version = "0.12.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8f4955649ef5c38cc7f9e8aa41761d48fb9677197daea9984dc54f56aad5e63"
|
checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-compression",
|
"async-compression",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
|
@ -3990,7 +3990,7 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"rustls-pemfile 2.1.3",
|
"rustls-pemfile 2.1.3",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -4019,7 +4019,7 @@ dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
@ -4036,7 +4036,7 @@ dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
"matchit",
|
"matchit",
|
||||||
"reqwest 0.12.7",
|
"reqwest 0.12.8",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
@ -4177,9 +4177,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.13"
|
version = "0.23.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
|
checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aws-lc-rs",
|
"aws-lc-rs",
|
||||||
"log",
|
"log",
|
||||||
|
@ -4212,9 +4212,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.8.0"
|
version = "1.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
|
checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
|
@ -4373,9 +4373,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "3.9.0"
|
version = "3.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857"
|
checksum = "8e28bdad6db2b8340e449f7108f020b3b092e8583a9e3fb82713e1d4e71fe817"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -4391,9 +4391,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "3.9.0"
|
version = "3.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8fee4991ef4f274617a51ad4af30519438dacb2f56ac773b08a1922ff743350"
|
checksum = "9d846214a9854ef724f3da161b426242d8de7c1fc7de2f89bb1efcb154dca79d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling 0.20.10",
|
"darling 0.20.10",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
|
@ -4737,7 +4737,7 @@ dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"plist",
|
"plist",
|
||||||
"regex-syntax 0.8.4",
|
"regex-syntax 0.8.5",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -4974,7 +4974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab"
|
checksum = "04fb792ccd6bbcd4bba408eb8a292f70fc4a3589e5d793626f45190e6454b6ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ring",
|
"ring",
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls 0.26.0",
|
||||||
|
@ -4997,7 +4997,7 @@ version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls 0.23.13",
|
"rustls 0.23.14",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
@ -5579,7 +5579,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -90,7 +90,7 @@ lemmy_db_views = { version = "=0.19.6-beta.7", path = "./crates/db_views" }
|
||||||
lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" }
|
lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" }
|
||||||
lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" }
|
lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" }
|
||||||
lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" }
|
lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" }
|
||||||
activitypub_federation = { version = "0.6.0-alpha1", default-features = false, features = [
|
activitypub_federation = { version = "0.6.0-alpha2", default-features = false, features = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
] }
|
] }
|
||||||
diesel = "2.1.6"
|
diesel = "2.1.6"
|
||||||
|
@ -188,7 +188,7 @@ chrono = { workspace = true }
|
||||||
prometheus = { version = "0.13.4", features = ["process"] }
|
prometheus = { version = "0.13.4", features = ["process"] }
|
||||||
serial_test = { workspace = true }
|
serial_test = { workspace = true }
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
actix-web-prom = "0.8.0"
|
actix-web-prom = "0.9.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = { workspace = true }
|
pretty_assertions = { workspace = true }
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"repository": "https://github.com/LemmyNet/lemmy",
|
"repository": "https://github.com/LemmyNet/lemmy",
|
||||||
"author": "Dessalines",
|
"author": "Dessalines",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"packageManager": "pnpm@9.9.0",
|
"packageManager": "pnpm@9.12.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
|
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
|
||||||
"fix": "prettier --write src && eslint --fix src",
|
"fix": "prettier --write src && eslint --fix src",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -858,3 +858,26 @@ test("Dont send a comment reply to a blocked community", async () => {
|
||||||
blockRes = await blockCommunity(beta, newCommunityId, false);
|
blockRes = await blockCommunity(beta, newCommunityId, false);
|
||||||
expect(blockRes.blocked).toBe(false);
|
expect(blockRes.blocked).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Fetching a deeply nested comment can lead to stack overflow as all parent comments are also
|
||||||
|
/// fetched recursively. Ensure that it works properly.
|
||||||
|
test("Fetch a deeply nested comment", async () => {
|
||||||
|
let lastComment;
|
||||||
|
for (let i = 0; i < 50; i++) {
|
||||||
|
let commentRes = await createComment(
|
||||||
|
alpha,
|
||||||
|
postOnAlphaRes.post_view.post.id,
|
||||||
|
lastComment?.comment_view.comment.id,
|
||||||
|
);
|
||||||
|
expect(commentRes.comment_view.comment).toBeDefined();
|
||||||
|
lastComment = commentRes;
|
||||||
|
}
|
||||||
|
|
||||||
|
let betaComment = await resolveComment(
|
||||||
|
beta,
|
||||||
|
lastComment!.comment_view.comment,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(betaComment!.comment!.comment).toBeDefined();
|
||||||
|
expect(betaComment?.comment?.post).toBeDefined();
|
||||||
|
});
|
||||||
|
|
|
@ -794,3 +794,29 @@ test("Fetch post with redirect", async () => {
|
||||||
let gammaPost2 = await gamma.resolveObject(form);
|
let gammaPost2 = await gamma.resolveObject(form);
|
||||||
expect(gammaPost2.post).toBeDefined();
|
expect(gammaPost2.post).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Rewrite markdown links", async () => {
|
||||||
|
const community = (await resolveBetaCommunity(beta)).community!;
|
||||||
|
|
||||||
|
// create a post
|
||||||
|
let postRes1 = await createPost(beta, community.community.id);
|
||||||
|
|
||||||
|
// link to this post in markdown
|
||||||
|
let postRes2 = await createPost(
|
||||||
|
beta,
|
||||||
|
community.community.id,
|
||||||
|
"https://example.com/",
|
||||||
|
`[link](${postRes1.post_view.post.ap_id})`,
|
||||||
|
);
|
||||||
|
console.log(postRes2.post_view.post.body);
|
||||||
|
expect(postRes2.post_view.post).toBeDefined();
|
||||||
|
|
||||||
|
// fetch both posts from another instance
|
||||||
|
const alphaPost1 = await resolvePost(alpha, postRes1.post_view.post);
|
||||||
|
const alphaPost2 = await resolvePost(alpha, postRes2.post_view.post);
|
||||||
|
|
||||||
|
// remote markdown link is replaced with local link
|
||||||
|
expect(alphaPost2.post?.post.body).toBe(
|
||||||
|
`[link](http://lemmy-alpha:8541/post/${alphaPost1.post?.post.id})`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
|
@ -75,6 +75,8 @@
|
||||||
"ProxyAllImages"
|
"ProxyAllImages"
|
||||||
# Timeout for uploading images to pictrs (in seconds)
|
# Timeout for uploading images to pictrs (in seconds)
|
||||||
upload_timeout: 30
|
upload_timeout: 30
|
||||||
|
# Resize post thumbnails to this maximum width/height.
|
||||||
|
max_thumbnail_size: 256
|
||||||
}
|
}
|
||||||
# Email sending configuration. All options except login/password are mandatory
|
# Email sending configuration. All options except login/password are mandatory
|
||||||
email: {
|
email: {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
comment::{CommentResponse, CreateCommentLike},
|
comment::{CommentResponse, CreateCommentLike},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_bot_account, check_community_user_action, check_downvotes_enabled},
|
utils::{check_bot_account, check_community_user_action, check_local_vote_mode, VoteItem},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::LocalUserId,
|
newtypes::LocalUserId,
|
||||||
|
@ -27,14 +27,20 @@ pub async fn like_comment(
|
||||||
local_user_view: LocalUserView,
|
local_user_view: LocalUserView,
|
||||||
) -> LemmyResult<Json<CommentResponse>> {
|
) -> LemmyResult<Json<CommentResponse>> {
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
let comment_id = data.comment_id;
|
||||||
|
|
||||||
let mut recipient_ids = Vec::<LocalUserId>::new();
|
let mut recipient_ids = Vec::<LocalUserId>::new();
|
||||||
|
|
||||||
// Don't do a downvote if site has downvotes disabled
|
check_local_vote_mode(
|
||||||
check_downvotes_enabled(data.score, &local_site)?;
|
data.score,
|
||||||
|
VoteItem::Comment(comment_id),
|
||||||
|
&local_site,
|
||||||
|
local_user_view.person.id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
check_bot_account(&local_user_view.person)?;
|
check_bot_account(&local_user_view.person)?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
|
||||||
let orig_comment = CommentView::read(
|
let orig_comment = CommentView::read(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
comment_id,
|
comment_id,
|
||||||
|
|
|
@ -92,8 +92,10 @@ pub async fn ban_from_community(
|
||||||
let remove_data = data.ban;
|
let remove_data = data.ban;
|
||||||
remove_or_restore_user_data_in_community(
|
remove_or_restore_user_data_in_community(
|
||||||
data.community_id,
|
data.community_id,
|
||||||
|
local_user_view.person.id,
|
||||||
banned_person_id,
|
banned_person_id,
|
||||||
remove_data,
|
remove_data,
|
||||||
|
&data.reason,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -3,4 +3,5 @@ pub mod ban;
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod follow;
|
pub mod follow;
|
||||||
pub mod hide;
|
pub mod hide;
|
||||||
|
pub mod random;
|
||||||
pub mod transfer;
|
pub mod transfer;
|
||||||
|
|
55
crates/api/src/community/random.rs
Normal file
55
crates/api/src/community/random.rs
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::{Json, Query};
|
||||||
|
use lemmy_api_common::{
|
||||||
|
community::{CommunityResponse, GetRandomCommunity},
|
||||||
|
context::LemmyContext,
|
||||||
|
utils::{check_private_instance, is_mod_or_admin_opt},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
actor_language::CommunityLanguage,
|
||||||
|
community::Community,
|
||||||
|
local_site::LocalSite,
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
|
use lemmy_db_views_actor::structs::CommunityView;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
|
#[tracing::instrument(skip(context))]
|
||||||
|
pub async fn get_random_community(
|
||||||
|
data: Query<GetRandomCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
local_user_view: Option<LocalUserView>,
|
||||||
|
) -> LemmyResult<Json<CommunityResponse>> {
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
|
check_private_instance(&local_user_view, &local_site)?;
|
||||||
|
|
||||||
|
let local_user = local_user_view.as_ref().map(|u| &u.local_user);
|
||||||
|
|
||||||
|
let random_community_id =
|
||||||
|
Community::get_random_community_id(&mut context.pool(), &data.type_).await?;
|
||||||
|
|
||||||
|
let is_mod_or_admin = is_mod_or_admin_opt(
|
||||||
|
&mut context.pool(),
|
||||||
|
local_user_view.as_ref(),
|
||||||
|
Some(random_community_id),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.is_ok();
|
||||||
|
|
||||||
|
let community_view = CommunityView::read(
|
||||||
|
&mut context.pool(),
|
||||||
|
random_community_id,
|
||||||
|
local_user,
|
||||||
|
is_mod_or_admin,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let discussion_languages =
|
||||||
|
CommunityLanguage::read(&mut context.pool(), random_community_id).await?;
|
||||||
|
|
||||||
|
Ok(Json(CommunityResponse {
|
||||||
|
community_view,
|
||||||
|
discussion_languages,
|
||||||
|
}))
|
||||||
|
}
|
|
@ -265,8 +265,6 @@ pub async fn local_user_view_from_jwt(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::{BanPerson, BanPersonResponse},
|
person::{BanPerson, BanPersonResponse},
|
||||||
send_activity::{ActivityChannel, SendActivityData},
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_expire_time, is_admin, remove_user_data, restore_user_data},
|
utils::{check_expire_time, is_admin, remove_or_restore_user_data},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -66,11 +66,15 @@ pub async fn ban_from_site(
|
||||||
|
|
||||||
// Remove their data if that's desired
|
// Remove their data if that's desired
|
||||||
if data.remove_or_restore_data.unwrap_or(false) {
|
if data.remove_or_restore_data.unwrap_or(false) {
|
||||||
if data.ban {
|
let removed = data.ban;
|
||||||
remove_user_data(person.id, &context).await?;
|
remove_or_restore_user_data(
|
||||||
} else {
|
local_user_view.person.id,
|
||||||
restore_user_data(person.id, &context).await?;
|
person.id,
|
||||||
}
|
removed,
|
||||||
|
&data.reason,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mod tables
|
// Mod tables
|
||||||
|
|
|
@ -130,7 +130,6 @@ pub async fn save_user_settings(
|
||||||
send_notifications_to_email: data.send_notifications_to_email,
|
send_notifications_to_email: data.send_notifications_to_email,
|
||||||
show_nsfw: data.show_nsfw,
|
show_nsfw: data.show_nsfw,
|
||||||
blur_nsfw: data.blur_nsfw,
|
blur_nsfw: data.blur_nsfw,
|
||||||
auto_expand: data.auto_expand,
|
|
||||||
show_bot_accounts: data.show_bot_accounts,
|
show_bot_accounts: data.show_bot_accounts,
|
||||||
default_post_sort_type,
|
default_post_sort_type,
|
||||||
default_comment_sort_type,
|
default_comment_sort_type,
|
||||||
|
|
|
@ -8,8 +8,9 @@ use lemmy_api_common::{
|
||||||
utils::{
|
utils::{
|
||||||
check_bot_account,
|
check_bot_account,
|
||||||
check_community_user_action,
|
check_community_user_action,
|
||||||
check_downvotes_enabled,
|
check_local_vote_mode,
|
||||||
mark_post_as_read,
|
mark_post_as_read,
|
||||||
|
VoteItem,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -31,13 +32,19 @@ pub async fn like_post(
|
||||||
local_user_view: LocalUserView,
|
local_user_view: LocalUserView,
|
||||||
) -> LemmyResult<Json<PostResponse>> {
|
) -> LemmyResult<Json<PostResponse>> {
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
let post_id = data.post_id;
|
||||||
|
|
||||||
// Don't do a downvote if site has downvotes disabled
|
check_local_vote_mode(
|
||||||
check_downvotes_enabled(data.score, &local_site)?;
|
data.score,
|
||||||
|
VoteItem::Post(post_id),
|
||||||
|
&local_site,
|
||||||
|
local_user_view.person.id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
check_bot_account(&local_user_view.person)?;
|
check_bot_account(&local_user_view.person)?;
|
||||||
|
|
||||||
// Check for a community ban
|
// Check for a community ban
|
||||||
let post_id = data.post_id;
|
|
||||||
let post = Post::read(&mut context.pool(), post_id).await?;
|
let post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
|
|
||||||
check_community_user_action(
|
check_community_user_action(
|
||||||
|
|
|
@ -63,7 +63,7 @@ pub async fn leave_admin(
|
||||||
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
||||||
let oauth_providers = OAuthProvider::get_all_public(&mut context.pool()).await?;
|
let oauth_providers = OAuthProvider::get_all_public(&mut context.pool()).await?;
|
||||||
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
|
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
|
||||||
let tagline = Tagline::get_random(&mut context.pool()).await?;
|
let tagline = Tagline::get_random(&mut context.pool()).await.ok();
|
||||||
|
|
||||||
Ok(Json(GetSiteResponse {
|
Ok(Json(GetSiteResponse {
|
||||||
site_view,
|
site_view,
|
||||||
|
@ -76,5 +76,7 @@ pub async fn leave_admin(
|
||||||
admin_oauth_providers: None,
|
admin_oauth_providers: None,
|
||||||
blocked_urls,
|
blocked_urls,
|
||||||
tagline,
|
tagline,
|
||||||
|
taglines: vec![],
|
||||||
|
custom_emojis: vec![],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,13 +34,10 @@ use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::{error::LemmyResult, LemmyErrorType, CACHE_DURATION_API};
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType, CACHE_DURATION_API};
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance, LocalUserView)> {
|
async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance, LocalUserView)> {
|
||||||
let pool = &mut context.pool();
|
let pool = &mut context.pool();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.expect("Create test instance");
|
|
||||||
|
|
||||||
let admin_person = Person::create(
|
let admin_person = Person::create(
|
||||||
pool,
|
pool,
|
||||||
|
@ -57,7 +54,7 @@ async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance
|
||||||
let admin_local_user_view = LocalUserView::read_person(pool, admin_person.id).await?;
|
let admin_local_user_view = LocalUserView::read_person(pool, admin_person.id).await?;
|
||||||
|
|
||||||
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
||||||
let site = Site::create(pool, &site_form).await.unwrap();
|
let site = Site::create(pool, &site_form).await?;
|
||||||
|
|
||||||
// Create a local site, since this is necessary for determining if email verification is
|
// Create a local site, since this is necessary for determining if email verification is
|
||||||
// required
|
// required
|
||||||
|
@ -68,14 +65,12 @@ async fn create_test_site(context: &Data<LemmyContext>) -> LemmyResult<(Instance
|
||||||
site_setup: Some(true),
|
site_setup: Some(true),
|
||||||
..LocalSiteInsertForm::new(site.id)
|
..LocalSiteInsertForm::new(site.id)
|
||||||
};
|
};
|
||||||
let local_site = LocalSite::create(pool, &local_site_form).await.unwrap();
|
let local_site = LocalSite::create(pool, &local_site_form).await?;
|
||||||
|
|
||||||
// Required to have a working local SiteView when updating the site to change email verification
|
// Required to have a working local SiteView when updating the site to change email verification
|
||||||
// requirement or registration mode
|
// requirement or registration mode
|
||||||
let rate_limit_form = LocalSiteRateLimitInsertForm::new(local_site.id);
|
let rate_limit_form = LocalSiteRateLimitInsertForm::new(local_site.id);
|
||||||
LocalSiteRateLimit::create(pool, &rate_limit_form)
|
LocalSiteRateLimit::create(pool, &rate_limit_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok((inserted_instance, admin_local_user_view))
|
Ok((inserted_instance, admin_local_user_view))
|
||||||
}
|
}
|
||||||
|
@ -109,7 +104,6 @@ async fn signup(
|
||||||
Ok((local_user, application))
|
Ok((local_user, application))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
async fn get_application_statuses(
|
async fn get_application_statuses(
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
admin: LocalUserView,
|
admin: LocalUserView,
|
||||||
|
@ -122,14 +116,14 @@ async fn get_application_statuses(
|
||||||
get_unread_registration_application_count(context.reset_request_count(), admin.clone()).await?;
|
get_unread_registration_application_count(context.reset_request_count(), admin.clone()).await?;
|
||||||
|
|
||||||
let unread_applications = list_registration_applications(
|
let unread_applications = list_registration_applications(
|
||||||
Query::from_query("unread_only=true").unwrap(),
|
Query::from_query("unread_only=true")?,
|
||||||
context.reset_request_count(),
|
context.reset_request_count(),
|
||||||
admin.clone(),
|
admin.clone(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let all_applications = list_registration_applications(
|
let all_applications = list_registration_applications(
|
||||||
Query::from_query("unread_only=false").unwrap(),
|
Query::from_query("unread_only=false")?,
|
||||||
context.reset_request_count(),
|
context.reset_request_count(),
|
||||||
admin,
|
admin,
|
||||||
)
|
)
|
||||||
|
@ -138,10 +132,9 @@ async fn get_application_statuses(
|
||||||
Ok((application_count, unread_applications, all_applications))
|
Ok((application_count, unread_applications, all_applications))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
#[serial]
|
||||||
|
#[tokio::test]
|
||||||
|
#[expect(clippy::indexing_slicing)]
|
||||||
async fn test_application_approval() -> LemmyResult<()> {
|
async fn test_application_approval() -> LemmyResult<()> {
|
||||||
let context = LemmyContext::init_test_context().await;
|
let context = LemmyContext::init_test_context().await;
|
||||||
let pool = &mut context.pool();
|
let pool = &mut context.pool();
|
||||||
|
|
|
@ -42,44 +42,40 @@ pub async fn get_sitemap(context: Data<LemmyContext>) -> LemmyResult<HttpRespons
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use crate::sitemap::generate_urlset;
|
use crate::sitemap::generate_urlset;
|
||||||
use chrono::{DateTime, NaiveDate, Utc};
|
use chrono::{DateTime, NaiveDate, Utc};
|
||||||
use elementtree::Element;
|
use elementtree::Element;
|
||||||
use lemmy_db_schema::newtypes::DbUrl;
|
use lemmy_db_schema::newtypes::DbUrl;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_generate_urlset() {
|
async fn test_generate_urlset() -> LemmyResult<()> {
|
||||||
let posts: Vec<(DbUrl, DateTime<Utc>)> = vec![
|
let posts: Vec<(DbUrl, DateTime<Utc>)> = vec![
|
||||||
(
|
(
|
||||||
Url::parse("https://example.com").unwrap().into(),
|
Url::parse("https://example.com")?.into(),
|
||||||
NaiveDate::from_ymd_opt(2022, 12, 1)
|
NaiveDate::from_ymd_opt(2022, 12, 1)
|
||||||
.unwrap()
|
.unwrap_or_default()
|
||||||
.and_hms_opt(9, 10, 11)
|
.and_hms_opt(9, 10, 11)
|
||||||
.unwrap()
|
.unwrap_or_default()
|
||||||
.and_utc(),
|
.and_utc(),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
Url::parse("https://lemmy.ml").unwrap().into(),
|
Url::parse("https://lemmy.ml")?.into(),
|
||||||
NaiveDate::from_ymd_opt(2023, 1, 1)
|
NaiveDate::from_ymd_opt(2023, 1, 1)
|
||||||
.unwrap()
|
.unwrap_or_default()
|
||||||
.and_hms_opt(1, 2, 3)
|
.and_hms_opt(1, 2, 3)
|
||||||
.unwrap()
|
.unwrap_or_default()
|
||||||
.and_utc(),
|
.and_utc(),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut buf = Vec::<u8>::new();
|
let mut buf = Vec::<u8>::new();
|
||||||
generate_urlset(posts)
|
generate_urlset(posts).await?.write(&mut buf)?;
|
||||||
.await
|
let root = Element::from_reader(buf.as_slice())?;
|
||||||
.unwrap()
|
|
||||||
.write(&mut buf)
|
|
||||||
.unwrap();
|
|
||||||
let root = Element::from_reader(buf.as_slice()).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(root.tag().name(), "urlset");
|
assert_eq!(root.tag().name(), "urlset");
|
||||||
assert_eq!(root.child_count(), 2);
|
assert_eq!(root.child_count(), 2);
|
||||||
|
@ -99,45 +95,43 @@ pub(crate) mod tests {
|
||||||
root
|
root
|
||||||
.children()
|
.children()
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.and_then(|n| n.children().find(|element| element.tag().name() == "loc"))
|
||||||
.children()
|
.map(Element::text)
|
||||||
.find(|element| element.tag().name() == "loc")
|
.unwrap_or_default(),
|
||||||
.unwrap()
|
|
||||||
.text(),
|
|
||||||
"https://example.com/"
|
"https://example.com/"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
root
|
root
|
||||||
.children()
|
.children()
|
||||||
.next()
|
.next()
|
||||||
.unwrap()
|
.and_then(|n| n
|
||||||
.children()
|
.children()
|
||||||
.find(|element| element.tag().name() == "lastmod")
|
.find(|element| element.tag().name() == "lastmod"))
|
||||||
.unwrap()
|
.map(Element::text)
|
||||||
.text(),
|
.unwrap_or_default(),
|
||||||
"2022-12-01T09:10:11+00:00"
|
"2022-12-01T09:10:11+00:00"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
root
|
root
|
||||||
.children()
|
.children()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.unwrap()
|
.and_then(|n| n.children().find(|element| element.tag().name() == "loc"))
|
||||||
.children()
|
.map(Element::text)
|
||||||
.find(|element| element.tag().name() == "loc")
|
.unwrap_or_default(),
|
||||||
.unwrap()
|
|
||||||
.text(),
|
|
||||||
"https://lemmy.ml/"
|
"https://lemmy.ml/"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
root
|
root
|
||||||
.children()
|
.children()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.unwrap()
|
.and_then(|n| n
|
||||||
.children()
|
.children()
|
||||||
.find(|element| element.tag().name() == "lastmod")
|
.find(|element| element.tag().name() == "lastmod"))
|
||||||
.unwrap()
|
.map(Element::text)
|
||||||
.text(),
|
.unwrap_or_default(),
|
||||||
"2023-01-01T01:02:03+00:00"
|
"2023-01-01T01:02:03+00:00"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,8 +69,6 @@ impl Claims {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{claims::Claims, context::LemmyContext};
|
use crate::{claims::Claims, context::LemmyContext};
|
||||||
|
@ -85,7 +83,7 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
use lemmy_utils::rate_limit::RateLimitCell;
|
use lemmy_utils::{error::LemmyResult, rate_limit::RateLimitCell};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use reqwest_middleware::ClientBuilder;
|
use reqwest_middleware::ClientBuilder;
|
||||||
|
@ -93,10 +91,10 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_should_not_validate_user_token_after_password_change() {
|
async fn test_should_not_validate_user_token_after_password_change() -> LemmyResult<()> {
|
||||||
let pool_ = build_db_pool_for_tests().await;
|
let pool_ = build_db_pool_for_tests().await;
|
||||||
let pool = &mut (&pool_).into();
|
let pool = &mut (&pool_).into();
|
||||||
let secret = Secret::init(pool).await.unwrap().unwrap();
|
let secret = Secret::init(pool).await?;
|
||||||
let context = LemmyContext::create(
|
let context = LemmyContext::create(
|
||||||
pool_.clone(),
|
pool_.clone(),
|
||||||
ClientBuilder::new(Client::default()).build(),
|
ClientBuilder::new(Client::default()).build(),
|
||||||
|
@ -104,29 +102,25 @@ mod tests {
|
||||||
RateLimitCell::with_test_config(),
|
RateLimitCell::with_test_config(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "Gerry9812");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "Gerry9812");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
|
let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
|
||||||
|
|
||||||
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![])
|
let inserted_local_user = LocalUser::create(pool, &local_user_form, vec![]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let req = TestRequest::default().to_http_request();
|
let req = TestRequest::default().to_http_request();
|
||||||
let jwt = Claims::generate(inserted_local_user.id, req, &context)
|
let jwt = Claims::generate(inserted_local_user.id, req, &context).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let valid = Claims::validate(&jwt, &context).await;
|
let valid = Claims::validate(&jwt, &context).await;
|
||||||
assert!(valid.is_ok());
|
assert!(valid.is_ok());
|
||||||
|
|
||||||
let num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
|
let num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
assert_eq!(1, num_deleted);
|
assert_eq!(1, num_deleted);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,13 @@ use lemmy_db_schema::{
|
||||||
source::site::Site,
|
source::site::Site,
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
ListingType,
|
ListingType,
|
||||||
PostSortType,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView, PersonView};
|
use lemmy_db_views_actor::structs::{
|
||||||
|
CommunityModeratorView,
|
||||||
|
CommunitySortType,
|
||||||
|
CommunityView,
|
||||||
|
PersonView,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
|
@ -74,7 +78,7 @@ pub struct CommunityResponse {
|
||||||
/// Fetches a list of communities.
|
/// Fetches a list of communities.
|
||||||
pub struct ListCommunities {
|
pub struct ListCommunities {
|
||||||
pub type_: Option<ListingType>,
|
pub type_: Option<ListingType>,
|
||||||
pub sort: Option<PostSortType>,
|
pub sort: Option<CommunitySortType>,
|
||||||
pub show_nsfw: Option<bool>,
|
pub show_nsfw: Option<bool>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
|
@ -225,3 +229,12 @@ pub struct TransferCommunity {
|
||||||
pub community_id: CommunityId,
|
pub community_id: CommunityId,
|
||||||
pub person_id: PersonId,
|
pub person_id: PersonId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
|
#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)]
|
||||||
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
/// Fetches a random community
|
||||||
|
pub struct GetRandomCommunity {
|
||||||
|
pub type_: Option<ListingType>,
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ use serde_with::skip_serializing_none;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
@ -19,11 +20,12 @@ pub struct CreateOAuthProvider {
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
pub client_secret: String,
|
pub client_secret: String,
|
||||||
pub scopes: String,
|
pub scopes: String,
|
||||||
pub auto_verify_email: bool,
|
pub auto_verify_email: Option<bool>,
|
||||||
pub account_linking_enabled: bool,
|
pub account_linking_enabled: Option<bool>,
|
||||||
pub enabled: bool,
|
pub enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(TS))]
|
#[cfg_attr(feature = "full", derive(TS))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
|
|
@ -84,8 +84,8 @@ pub struct CaptchaResponse {
|
||||||
pub struct SaveUserSettings {
|
pub struct SaveUserSettings {
|
||||||
/// Show nsfw posts.
|
/// Show nsfw posts.
|
||||||
pub show_nsfw: Option<bool>,
|
pub show_nsfw: Option<bool>,
|
||||||
|
/// Blur nsfw posts.
|
||||||
pub blur_nsfw: Option<bool>,
|
pub blur_nsfw: Option<bool>,
|
||||||
pub auto_expand: Option<bool>,
|
|
||||||
/// Your user's theme.
|
/// Your user's theme.
|
||||||
pub theme: Option<String>,
|
pub theme: Option<String>,
|
||||||
/// The default post listing type, usually "local"
|
/// The default post listing type, usually "local"
|
||||||
|
|
|
@ -354,9 +354,10 @@ async fn generate_pictrs_thumbnail(image_url: &Url, context: &LemmyContext) -> L
|
||||||
// fetch remote non-pictrs images for persistent thumbnail link
|
// fetch remote non-pictrs images for persistent thumbnail link
|
||||||
// TODO: should limit size once supported by pictrs
|
// TODO: should limit size once supported by pictrs
|
||||||
let fetch_url = format!(
|
let fetch_url = format!(
|
||||||
"{}image/download?url={}",
|
"{}image/download?url={}&resize={}",
|
||||||
pictrs_config.url,
|
pictrs_config.url,
|
||||||
encode(image_url.as_str())
|
encode(image_url.as_str()),
|
||||||
|
context.settings().pictrs_config()?.max_thumbnail_size
|
||||||
);
|
);
|
||||||
|
|
||||||
let res = context
|
let res = context
|
||||||
|
@ -471,14 +472,13 @@ pub async fn replace_image(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
request::{extract_opengraph_data, fetch_link_metadata},
|
request::{extract_opengraph_data, fetch_link_metadata},
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -486,10 +486,10 @@ mod tests {
|
||||||
// These helped with testing
|
// These helped with testing
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_link_metadata() {
|
async fn test_link_metadata() -> LemmyResult<()> {
|
||||||
let context = LemmyContext::init_test_context().await;
|
let context = LemmyContext::init_test_context().await;
|
||||||
let sample_url = Url::parse("https://gitlab.com/IzzyOnDroid/repo/-/wikis/FAQ").unwrap();
|
let sample_url = Url::parse("https://gitlab.com/IzzyOnDroid/repo/-/wikis/FAQ")?;
|
||||||
let sample_res = fetch_link_metadata(&sample_url, &context).await.unwrap();
|
let sample_res = fetch_link_metadata(&sample_url, &context).await?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some("FAQ · Wiki · IzzyOnDroid / repo · GitLab".to_string()),
|
Some("FAQ · Wiki · IzzyOnDroid / repo · GitLab".to_string()),
|
||||||
sample_res.opengraph_data.title
|
sample_res.opengraph_data.title
|
||||||
|
@ -500,8 +500,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(
|
Some(
|
||||||
Url::parse("https://gitlab.com/uploads/-/system/project/avatar/4877469/iod_logo.png")
|
Url::parse("https://gitlab.com/uploads/-/system/project/avatar/4877469/iod_logo.png")?
|
||||||
.unwrap()
|
|
||||||
.into()
|
.into()
|
||||||
),
|
),
|
||||||
sample_res.opengraph_data.image
|
sample_res.opengraph_data.image
|
||||||
|
@ -511,19 +510,21 @@ mod tests {
|
||||||
Some(mime::TEXT_HTML_UTF_8.to_string()),
|
Some(mime::TEXT_HTML_UTF_8.to_string()),
|
||||||
sample_res.content_type
|
sample_res.content_type
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_resolve_image_url() {
|
fn test_resolve_image_url() -> LemmyResult<()> {
|
||||||
// url that lists the opengraph fields
|
// url that lists the opengraph fields
|
||||||
let url = Url::parse("https://example.com/one/two.html").unwrap();
|
let url = Url::parse("https://example.com/one/two.html")?;
|
||||||
|
|
||||||
// root relative url
|
// root relative url
|
||||||
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='/image.jpg'></head><body></body></html>";
|
let html_bytes = b"<!DOCTYPE html><html><head><meta property='og:image' content='/image.jpg'></head><body></body></html>";
|
||||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metadata.image,
|
metadata.image,
|
||||||
Some(Url::parse("https://example.com/image.jpg").unwrap().into())
|
Some(Url::parse("https://example.com/image.jpg")?.into())
|
||||||
);
|
);
|
||||||
|
|
||||||
// base relative url
|
// base relative url
|
||||||
|
@ -531,11 +532,7 @@ mod tests {
|
||||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metadata.image,
|
metadata.image,
|
||||||
Some(
|
Some(Url::parse("https://example.com/one/image.jpg")?.into())
|
||||||
Url::parse("https://example.com/one/image.jpg")
|
|
||||||
.unwrap()
|
|
||||||
.into()
|
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// absolute url
|
// absolute url
|
||||||
|
@ -543,7 +540,7 @@ mod tests {
|
||||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metadata.image,
|
metadata.image,
|
||||||
Some(Url::parse("https://cdn.host.com/image.jpg").unwrap().into())
|
Some(Url::parse("https://cdn.host.com/image.jpg")?.into())
|
||||||
);
|
);
|
||||||
|
|
||||||
// protocol relative url
|
// protocol relative url
|
||||||
|
@ -551,7 +548,9 @@ mod tests {
|
||||||
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
let metadata = extract_opengraph_data(html_bytes, &url).expect("Unable to parse metadata");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
metadata.image,
|
metadata.image,
|
||||||
Some(Url::parse("https://example.com/image.jpg").unwrap().into())
|
Some(Url::parse("https://example.com/image.jpg")?.into())
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ use lemmy_db_schema::{
|
||||||
tagline::Tagline,
|
tagline::Tagline,
|
||||||
},
|
},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
|
FederationMode,
|
||||||
ListingType,
|
ListingType,
|
||||||
ModlogActionType,
|
ModlogActionType,
|
||||||
PostListingMode,
|
PostListingMode,
|
||||||
|
@ -78,7 +79,7 @@ pub struct Search {
|
||||||
pub listing_type: Option<ListingType>,
|
pub listing_type: Option<ListingType>,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
pub limit: Option<i64>,
|
pub limit: Option<i64>,
|
||||||
pub post_title_only: Option<bool>,
|
pub title_only: Option<bool>,
|
||||||
pub post_url_only: Option<bool>,
|
pub post_url_only: Option<bool>,
|
||||||
pub saved_only: Option<bool>,
|
pub saved_only: Option<bool>,
|
||||||
pub liked_only: Option<bool>,
|
pub liked_only: Option<bool>,
|
||||||
|
@ -170,7 +171,6 @@ pub struct CreateSite {
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
pub enable_downvotes: Option<bool>,
|
|
||||||
pub enable_nsfw: Option<bool>,
|
pub enable_nsfw: Option<bool>,
|
||||||
pub community_creation_admin_only: Option<bool>,
|
pub community_creation_admin_only: Option<bool>,
|
||||||
pub require_email_verification: Option<bool>,
|
pub require_email_verification: Option<bool>,
|
||||||
|
@ -208,6 +208,10 @@ pub struct CreateSite {
|
||||||
pub registration_mode: Option<RegistrationMode>,
|
pub registration_mode: Option<RegistrationMode>,
|
||||||
pub oauth_registration: Option<bool>,
|
pub oauth_registration: Option<bool>,
|
||||||
pub content_warning: Option<String>,
|
pub content_warning: Option<String>,
|
||||||
|
pub post_upvotes: Option<FederationMode>,
|
||||||
|
pub post_downvotes: Option<FederationMode>,
|
||||||
|
pub comment_upvotes: Option<FederationMode>,
|
||||||
|
pub comment_downvotes: Option<FederationMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
|
@ -224,8 +228,6 @@ pub struct EditSite {
|
||||||
pub icon: Option<String>,
|
pub icon: Option<String>,
|
||||||
/// A url for your site's banner.
|
/// A url for your site's banner.
|
||||||
pub banner: Option<String>,
|
pub banner: Option<String>,
|
||||||
/// Whether to enable downvotes.
|
|
||||||
pub enable_downvotes: Option<bool>,
|
|
||||||
/// Whether to enable NSFW.
|
/// Whether to enable NSFW.
|
||||||
pub enable_nsfw: Option<bool>,
|
pub enable_nsfw: Option<bool>,
|
||||||
/// Limits community creation to admins only.
|
/// Limits community creation to admins only.
|
||||||
|
@ -291,13 +293,21 @@ pub struct EditSite {
|
||||||
/// A list of blocked URLs
|
/// A list of blocked URLs
|
||||||
pub blocked_urls: Option<Vec<String>>,
|
pub blocked_urls: Option<Vec<String>>,
|
||||||
pub registration_mode: Option<RegistrationMode>,
|
pub registration_mode: Option<RegistrationMode>,
|
||||||
/// Whether or not external auth methods can auto-register users.
|
|
||||||
pub oauth_registration: Option<bool>,
|
|
||||||
/// Whether to email admins for new reports.
|
/// Whether to email admins for new reports.
|
||||||
pub reports_email_admins: Option<bool>,
|
pub reports_email_admins: Option<bool>,
|
||||||
/// If present, nsfw content is visible by default. Should be displayed by frontends/clients
|
/// If present, nsfw content is visible by default. Should be displayed by frontends/clients
|
||||||
/// when the site is first opened by a user.
|
/// when the site is first opened by a user.
|
||||||
pub content_warning: Option<String>,
|
pub content_warning: Option<String>,
|
||||||
|
/// Whether or not external auth methods can auto-register users.
|
||||||
|
pub oauth_registration: Option<bool>,
|
||||||
|
/// What kind of post upvotes your site allows.
|
||||||
|
pub post_upvotes: Option<FederationMode>,
|
||||||
|
/// What kind of post downvotes your site allows.
|
||||||
|
pub post_downvotes: Option<FederationMode>,
|
||||||
|
/// What kind of comment upvotes your site allows.
|
||||||
|
pub comment_upvotes: Option<FederationMode>,
|
||||||
|
/// What kind of comment downvotes your site allows.
|
||||||
|
pub comment_downvotes: Option<FederationMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
|
@ -306,6 +316,8 @@ pub struct EditSite {
|
||||||
/// The response for a site.
|
/// The response for a site.
|
||||||
pub struct SiteResponse {
|
pub struct SiteResponse {
|
||||||
pub site_view: SiteView,
|
pub site_view: SiteView,
|
||||||
|
/// deprecated, use field `tagline` or /api/v3/tagline/list
|
||||||
|
pub taglines: Vec<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
|
@ -320,6 +332,10 @@ pub struct GetSiteResponse {
|
||||||
pub my_user: Option<MyUserInfo>,
|
pub my_user: Option<MyUserInfo>,
|
||||||
pub all_languages: Vec<Language>,
|
pub all_languages: Vec<Language>,
|
||||||
pub discussion_languages: Vec<LanguageId>,
|
pub discussion_languages: Vec<LanguageId>,
|
||||||
|
/// deprecated, use field `tagline` or /api/v3/tagline/list
|
||||||
|
pub taglines: Vec<()>,
|
||||||
|
/// deprecated, use /api/v3/custom_emoji/list
|
||||||
|
pub custom_emojis: Vec<()>,
|
||||||
/// If the site has any taglines, a random one is included here for displaying
|
/// If the site has any taglines, a random one is included here for displaying
|
||||||
pub tagline: Option<Tagline>,
|
pub tagline: Option<Tagline>,
|
||||||
/// A list of external auth methods your site supports.
|
/// A list of external auth methods your site supports.
|
||||||
|
|
|
@ -11,9 +11,9 @@ use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
||||||
use enum_map::{enum_map, EnumMap};
|
use enum_map::{enum_map, EnumMap};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
||||||
newtypes::{CommunityId, DbUrl, InstanceId, PersonId, PostId},
|
newtypes::{CommentId, CommunityId, DbUrl, InstanceId, PersonId, PostId},
|
||||||
source::{
|
source::{
|
||||||
comment::{Comment, CommentUpdateForm},
|
comment::{Comment, CommentLike, CommentUpdateForm},
|
||||||
community::{Community, CommunityModerator, CommunityUpdateForm},
|
community::{Community, CommunityModerator, CommunityUpdateForm},
|
||||||
community_block::CommunityBlock,
|
community_block::CommunityBlock,
|
||||||
email_verification::{EmailVerification, EmailVerificationForm},
|
email_verification::{EmailVerification, EmailVerificationForm},
|
||||||
|
@ -23,16 +23,18 @@ use lemmy_db_schema::{
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
local_site_rate_limit::LocalSiteRateLimit,
|
local_site_rate_limit::LocalSiteRateLimit,
|
||||||
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
local_site_url_blocklist::LocalSiteUrlBlocklist,
|
||||||
|
moderator::{ModRemoveComment, ModRemoveCommentForm, ModRemovePost, ModRemovePostForm},
|
||||||
oauth_account::OAuthAccount,
|
oauth_account::OAuthAccount,
|
||||||
password_reset_request::PasswordResetRequest,
|
password_reset_request::PasswordResetRequest,
|
||||||
person::{Person, PersonUpdateForm},
|
person::{Person, PersonUpdateForm},
|
||||||
person_block::PersonBlock,
|
person_block::PersonBlock,
|
||||||
post::{Post, PostRead},
|
post::{Post, PostLike, PostRead},
|
||||||
registration_application::RegistrationApplication,
|
registration_application::RegistrationApplication,
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::{Crud, Likeable},
|
||||||
utils::DbPool,
|
utils::DbPool,
|
||||||
|
FederationMode,
|
||||||
RegistrationMode,
|
RegistrationMode,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::{
|
use lemmy_db_views::{
|
||||||
|
@ -50,7 +52,7 @@ use lemmy_utils::{
|
||||||
rate_limit::{ActionType, BucketConfig},
|
rate_limit::{ActionType, BucketConfig},
|
||||||
settings::structs::{PictrsImageMode, Settings},
|
settings::structs::{PictrsImageMode, Settings},
|
||||||
utils::{
|
utils::{
|
||||||
markdown::{markdown_check_for_blocked_urls, markdown_rewrite_image_links},
|
markdown::{image_links::markdown_rewrite_image_links, markdown_check_for_blocked_urls},
|
||||||
slurs::{build_slur_regex, remove_slurs},
|
slurs::{build_slur_regex, remove_slurs},
|
||||||
validation::clean_urls_in_text,
|
validation::clean_urls_in_text,
|
||||||
},
|
},
|
||||||
|
@ -296,13 +298,36 @@ pub async fn check_person_instance_community_block(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A vote item type used to check the vote mode.
|
||||||
|
pub enum VoteItem {
|
||||||
|
Post(PostId),
|
||||||
|
Comment(CommentId),
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> LemmyResult<()> {
|
pub async fn check_local_vote_mode(
|
||||||
if score == -1 && !local_site.enable_downvotes {
|
score: i16,
|
||||||
Err(LemmyErrorType::DownvotesAreDisabled)?
|
vote_item: VoteItem,
|
||||||
} else {
|
local_site: &LocalSite,
|
||||||
Ok(())
|
person_id: PersonId,
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let (downvote_setting, upvote_setting) = match vote_item {
|
||||||
|
VoteItem::Post(_) => (local_site.post_downvotes, local_site.post_upvotes),
|
||||||
|
VoteItem::Comment(_) => (local_site.comment_downvotes, local_site.comment_upvotes),
|
||||||
|
};
|
||||||
|
|
||||||
|
let downvote_fail = score == -1 && downvote_setting == FederationMode::Disable;
|
||||||
|
let upvote_fail = score == 1 && upvote_setting == FederationMode::Disable;
|
||||||
|
|
||||||
|
// Undo previous vote for item if new vote fails
|
||||||
|
if downvote_fail || upvote_fail {
|
||||||
|
match vote_item {
|
||||||
|
VoteItem::Post(post_id) => PostLike::remove(pool, person_id, post_id).await?,
|
||||||
|
VoteItem::Comment(comment_id) => CommentLike::remove(pool, person_id, comment_id).await?,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dont allow bots to do certain actions, like voting
|
/// Dont allow bots to do certain actions, like voting
|
||||||
|
@ -667,112 +692,179 @@ pub async fn purge_image_posts_for_community(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_user_data(
|
/// Removes or restores user data.
|
||||||
|
pub async fn remove_or_restore_user_data(
|
||||||
|
mod_person_id: PersonId,
|
||||||
banned_person_id: PersonId,
|
banned_person_id: PersonId,
|
||||||
|
removed: bool,
|
||||||
|
reason: &Option<String>,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
let pool = &mut context.pool();
|
let pool = &mut context.pool();
|
||||||
// Purge user images
|
|
||||||
let person = Person::read(pool, banned_person_id).await?;
|
// Only these actions are possible when removing, not restoring
|
||||||
if let Some(avatar) = person.avatar {
|
if removed {
|
||||||
purge_image_from_pictrs(&avatar, context).await.ok();
|
// Purge user images
|
||||||
}
|
let person = Person::read(pool, banned_person_id).await?;
|
||||||
if let Some(banner) = person.banner {
|
if let Some(avatar) = person.avatar {
|
||||||
purge_image_from_pictrs(&banner, context).await.ok();
|
purge_image_from_pictrs(&avatar, context).await.ok();
|
||||||
|
}
|
||||||
|
if let Some(banner) = person.banner {
|
||||||
|
purge_image_from_pictrs(&banner, context).await.ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the fields to None
|
||||||
|
Person::update(
|
||||||
|
pool,
|
||||||
|
banned_person_id,
|
||||||
|
&PersonUpdateForm {
|
||||||
|
avatar: Some(None),
|
||||||
|
banner: Some(None),
|
||||||
|
bio: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Purge image posts
|
||||||
|
purge_image_posts_for_person(banned_person_id, context).await?;
|
||||||
|
|
||||||
|
// Communities
|
||||||
|
// Remove all communities where they're the top mod
|
||||||
|
// for now, remove the communities manually
|
||||||
|
let first_mod_communities = CommunityModeratorView::get_community_first_mods(pool).await?;
|
||||||
|
|
||||||
|
// Filter to only this banned users top communities
|
||||||
|
let banned_user_first_communities: Vec<CommunityModeratorView> = first_mod_communities
|
||||||
|
.into_iter()
|
||||||
|
.filter(|fmc| fmc.moderator.id == banned_person_id)
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for first_mod_community in banned_user_first_communities {
|
||||||
|
let community_id = first_mod_community.community.id;
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
community_id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
removed: Some(removed),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Delete the community images
|
||||||
|
if let Some(icon) = first_mod_community.community.icon {
|
||||||
|
purge_image_from_pictrs(&icon, context).await.ok();
|
||||||
|
}
|
||||||
|
if let Some(banner) = first_mod_community.community.banner {
|
||||||
|
purge_image_from_pictrs(&banner, context).await.ok();
|
||||||
|
}
|
||||||
|
// Update the fields to None
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
community_id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
icon: Some(None),
|
||||||
|
banner: Some(None),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the fields to None
|
// Posts
|
||||||
Person::update(
|
let removed_or_restored_posts =
|
||||||
|
Post::update_removed_for_creator(pool, banned_person_id, None, removed).await?;
|
||||||
|
create_modlog_entries_for_removed_or_restored_posts(
|
||||||
pool,
|
pool,
|
||||||
banned_person_id,
|
mod_person_id,
|
||||||
&PersonUpdateForm {
|
removed_or_restored_posts.iter().map(|r| r.id).collect(),
|
||||||
avatar: Some(None),
|
removed,
|
||||||
banner: Some(None),
|
reason,
|
||||||
bio: Some(None),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Posts
|
|
||||||
Post::update_removed_for_creator(pool, banned_person_id, None, true).await?;
|
|
||||||
|
|
||||||
// Purge image posts
|
|
||||||
purge_image_posts_for_person(banned_person_id, context).await?;
|
|
||||||
|
|
||||||
// Communities
|
|
||||||
// Remove all communities where they're the top mod
|
|
||||||
// for now, remove the communities manually
|
|
||||||
let first_mod_communities = CommunityModeratorView::get_community_first_mods(pool).await?;
|
|
||||||
|
|
||||||
// Filter to only this banned users top communities
|
|
||||||
let banned_user_first_communities: Vec<CommunityModeratorView> = first_mod_communities
|
|
||||||
.into_iter()
|
|
||||||
.filter(|fmc| fmc.moderator.id == banned_person_id)
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
for first_mod_community in banned_user_first_communities {
|
|
||||||
let community_id = first_mod_community.community.id;
|
|
||||||
Community::update(
|
|
||||||
pool,
|
|
||||||
community_id,
|
|
||||||
&CommunityUpdateForm {
|
|
||||||
removed: Some(true),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Delete the community images
|
|
||||||
if let Some(icon) = first_mod_community.community.icon {
|
|
||||||
purge_image_from_pictrs(&icon, context).await.ok();
|
|
||||||
}
|
|
||||||
if let Some(banner) = first_mod_community.community.banner {
|
|
||||||
purge_image_from_pictrs(&banner, context).await.ok();
|
|
||||||
}
|
|
||||||
// Update the fields to None
|
|
||||||
Community::update(
|
|
||||||
pool,
|
|
||||||
community_id,
|
|
||||||
&CommunityUpdateForm {
|
|
||||||
icon: Some(None),
|
|
||||||
banner: Some(None),
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
Comment::update_removed_for_creator(pool, banned_person_id, true).await?;
|
let removed_or_restored_comments =
|
||||||
|
Comment::update_removed_for_creator(pool, banned_person_id, removed).await?;
|
||||||
|
create_modlog_entries_for_removed_or_restored_comments(
|
||||||
|
pool,
|
||||||
|
mod_person_id,
|
||||||
|
removed_or_restored_comments.iter().map(|r| r.id).collect(),
|
||||||
|
removed,
|
||||||
|
reason,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// We can't restore their images, but we can unremove their posts and comments
|
async fn create_modlog_entries_for_removed_or_restored_posts(
|
||||||
pub async fn restore_user_data(
|
pool: &mut DbPool<'_>,
|
||||||
banned_person_id: PersonId,
|
mod_person_id: PersonId,
|
||||||
context: &LemmyContext,
|
post_ids: Vec<PostId>,
|
||||||
|
removed: bool,
|
||||||
|
reason: &Option<String>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
let pool = &mut context.pool();
|
// Build the forms
|
||||||
|
let forms = post_ids
|
||||||
|
.iter()
|
||||||
|
.map(|&post_id| ModRemovePostForm {
|
||||||
|
mod_person_id,
|
||||||
|
post_id,
|
||||||
|
removed: Some(removed),
|
||||||
|
reason: reason.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Posts
|
ModRemovePost::create_multiple(pool, &forms).await?;
|
||||||
Post::update_removed_for_creator(pool, banned_person_id, None, false).await?;
|
|
||||||
|
|
||||||
// Comments
|
Ok(())
|
||||||
Comment::update_removed_for_creator(pool, banned_person_id, false).await?;
|
}
|
||||||
|
|
||||||
|
async fn create_modlog_entries_for_removed_or_restored_comments(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
mod_person_id: PersonId,
|
||||||
|
comment_ids: Vec<CommentId>,
|
||||||
|
removed: bool,
|
||||||
|
reason: &Option<String>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
// Build the forms
|
||||||
|
let forms = comment_ids
|
||||||
|
.iter()
|
||||||
|
.map(|&comment_id| ModRemoveCommentForm {
|
||||||
|
mod_person_id,
|
||||||
|
comment_id,
|
||||||
|
removed: Some(removed),
|
||||||
|
reason: reason.clone(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
ModRemoveComment::create_multiple(pool, &forms).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove_or_restore_user_data_in_community(
|
pub async fn remove_or_restore_user_data_in_community(
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
|
mod_person_id: PersonId,
|
||||||
banned_person_id: PersonId,
|
banned_person_id: PersonId,
|
||||||
remove: bool,
|
remove: bool,
|
||||||
|
reason: &Option<String>,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
// Posts
|
// Posts
|
||||||
Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), remove).await?;
|
let posts =
|
||||||
|
Post::update_removed_for_creator(pool, banned_person_id, Some(community_id), remove).await?;
|
||||||
|
create_modlog_entries_for_removed_or_restored_posts(
|
||||||
|
pool,
|
||||||
|
mod_person_id,
|
||||||
|
posts.iter().map(|r| r.id).collect(),
|
||||||
|
remove,
|
||||||
|
reason,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Comments
|
// Comments
|
||||||
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
// TODO Diesel doesn't allow updates with joins, so this has to be a loop
|
||||||
|
@ -798,6 +890,15 @@ pub async fn remove_or_restore_user_data_in_community(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
create_modlog_entries_for_removed_or_restored_comments(
|
||||||
|
pool,
|
||||||
|
mod_person_id,
|
||||||
|
comments.iter().map(|r| r.comment.id).collect(),
|
||||||
|
remove,
|
||||||
|
reason,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1067,11 +1168,20 @@ fn build_proxied_image_url(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
comment::CommentInsertForm,
|
||||||
|
community::CommunityInsertForm,
|
||||||
|
person::PersonInsertForm,
|
||||||
|
post::PostInsertForm,
|
||||||
|
};
|
||||||
|
use lemmy_db_views_moderator::structs::{
|
||||||
|
ModRemoveCommentView,
|
||||||
|
ModRemovePostView,
|
||||||
|
ModlogListParams,
|
||||||
|
};
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
|
@ -1093,48 +1203,42 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_limit_ban_term() {
|
fn test_limit_ban_term() -> LemmyResult<()> {
|
||||||
// Ban expires in past, should throw error
|
// Ban expires in past, should throw error
|
||||||
assert!(limit_expire_time(Utc::now() - Days::new(5)).is_err());
|
assert!(limit_expire_time(Utc::now() - Days::new(5)).is_err());
|
||||||
|
|
||||||
// Legitimate ban term, return same value
|
// Legitimate ban term, return same value
|
||||||
let fourteen_days = Utc::now() + Days::new(14);
|
let fourteen_days = Utc::now() + Days::new(14);
|
||||||
assert_eq!(
|
assert_eq!(limit_expire_time(fourteen_days)?, Some(fourteen_days));
|
||||||
limit_expire_time(fourteen_days).unwrap(),
|
|
||||||
Some(fourteen_days)
|
|
||||||
);
|
|
||||||
let nine_years = Utc::now() + Days::new(365 * 9);
|
let nine_years = Utc::now() + Days::new(365 * 9);
|
||||||
assert_eq!(limit_expire_time(nine_years).unwrap(), Some(nine_years));
|
assert_eq!(limit_expire_time(nine_years)?, Some(nine_years));
|
||||||
|
|
||||||
// Too long ban term, changes to None (permanent ban)
|
// Too long ban term, changes to None (permanent ban)
|
||||||
assert_eq!(
|
assert_eq!(limit_expire_time(Utc::now() + Days::new(365 * 11))?, None);
|
||||||
limit_expire_time(Utc::now() + Days::new(365 * 11)).unwrap(),
|
|
||||||
None
|
Ok(())
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_proxy_image_link() {
|
async fn test_proxy_image_link() -> LemmyResult<()> {
|
||||||
let context = LemmyContext::init_test_context().await;
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
|
||||||
// image from local domain is unchanged
|
// image from local domain is unchanged
|
||||||
let local_url = Url::parse("http://lemmy-alpha/image.png").unwrap();
|
let local_url = Url::parse("http://lemmy-alpha/image.png")?;
|
||||||
let proxied =
|
let proxied =
|
||||||
proxy_image_link_internal(local_url.clone(), PictrsImageMode::ProxyAllImages, &context)
|
proxy_image_link_internal(local_url.clone(), PictrsImageMode::ProxyAllImages, &context)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(&local_url, proxied.inner());
|
assert_eq!(&local_url, proxied.inner());
|
||||||
|
|
||||||
// image from remote domain is proxied
|
// image from remote domain is proxied
|
||||||
let remote_image = Url::parse("http://lemmy-beta/image.png").unwrap();
|
let remote_image = Url::parse("http://lemmy-beta/image.png")?;
|
||||||
let proxied = proxy_image_link_internal(
|
let proxied = proxy_image_link_internal(
|
||||||
remote_image.clone(),
|
remote_image.clone(),
|
||||||
PictrsImageMode::ProxyAllImages,
|
PictrsImageMode::ProxyAllImages,
|
||||||
&context,
|
&context,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
"https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png",
|
"https://lemmy-alpha/api/v3/image_proxy?url=http%3A%2F%2Flemmy-beta%2Fimage.png",
|
||||||
proxied.as_str()
|
proxied.as_str()
|
||||||
|
@ -1147,5 +1251,159 @@ mod tests {
|
||||||
.await
|
.await
|
||||||
.is_ok()
|
.is_ok()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn test_mod_remove_or_restore_data() -> LemmyResult<()> {
|
||||||
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
let pool = &mut context.pool();
|
||||||
|
|
||||||
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
|
|
||||||
|
let new_mod = PersonInsertForm::test_form(inserted_instance.id, "modder");
|
||||||
|
let inserted_mod = Person::create(pool, &new_mod).await?;
|
||||||
|
|
||||||
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "chrimbus");
|
||||||
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
|
let new_community = CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"mod_community crepes".to_string(),
|
||||||
|
"nada".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
);
|
||||||
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
|
let post_form_1 = PostInsertForm::new(
|
||||||
|
"A test post tubular".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post_1 = Post::create(pool, &post_form_1).await?;
|
||||||
|
|
||||||
|
let post_form_2 = PostInsertForm::new(
|
||||||
|
"A test post radical".into(),
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_community.id,
|
||||||
|
);
|
||||||
|
let inserted_post_2 = Post::create(pool, &post_form_2).await?;
|
||||||
|
|
||||||
|
let comment_form_1 = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post_1.id,
|
||||||
|
"A test comment tubular".into(),
|
||||||
|
);
|
||||||
|
let _inserted_comment_1 = Comment::create(pool, &comment_form_1, None).await?;
|
||||||
|
|
||||||
|
let comment_form_2 = CommentInsertForm::new(
|
||||||
|
inserted_person.id,
|
||||||
|
inserted_post_2.id,
|
||||||
|
"A test comment radical".into(),
|
||||||
|
);
|
||||||
|
let _inserted_comment_2 = Comment::create(pool, &comment_form_2, None).await?;
|
||||||
|
|
||||||
|
// Remove the user data
|
||||||
|
remove_or_restore_user_data(
|
||||||
|
inserted_mod.id,
|
||||||
|
inserted_person.id,
|
||||||
|
true,
|
||||||
|
&Some("a remove reason".to_string()),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Verify that their posts and comments are removed.
|
||||||
|
let params = ModlogListParams {
|
||||||
|
community_id: None,
|
||||||
|
mod_person_id: None,
|
||||||
|
other_person_id: None,
|
||||||
|
post_id: None,
|
||||||
|
comment_id: None,
|
||||||
|
page: None,
|
||||||
|
limit: None,
|
||||||
|
hide_modlog_names: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Posts
|
||||||
|
let post_modlog = ModRemovePostView::list(pool, params).await?;
|
||||||
|
assert_eq!(2, post_modlog.len());
|
||||||
|
|
||||||
|
let mod_removed_posts = post_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.mod_remove_post.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![true, true], mod_removed_posts);
|
||||||
|
|
||||||
|
let removed_posts = post_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.post.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![true, true], removed_posts);
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
let comment_modlog = ModRemoveCommentView::list(pool, params).await?;
|
||||||
|
assert_eq!(2, comment_modlog.len());
|
||||||
|
|
||||||
|
let mod_removed_comments = comment_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.mod_remove_comment.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![true, true], mod_removed_comments);
|
||||||
|
|
||||||
|
let removed_comments = comment_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.comment.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![true, true], removed_comments);
|
||||||
|
|
||||||
|
// Now restore the content, and make sure it got appended
|
||||||
|
remove_or_restore_user_data(
|
||||||
|
inserted_mod.id,
|
||||||
|
inserted_person.id,
|
||||||
|
false,
|
||||||
|
&Some("a restore reason".to_string()),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Posts
|
||||||
|
let post_modlog = ModRemovePostView::list(pool, params).await?;
|
||||||
|
assert_eq!(4, post_modlog.len());
|
||||||
|
|
||||||
|
let mod_restored_posts = post_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.mod_remove_post.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![false, false, true, true], mod_restored_posts);
|
||||||
|
|
||||||
|
let restored_posts = post_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.post.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
// All of these will be false, cause its the current state of the post
|
||||||
|
assert_eq!(vec![false, false, false, false], restored_posts);
|
||||||
|
|
||||||
|
// Comments
|
||||||
|
let comment_modlog = ModRemoveCommentView::list(pool, params).await?;
|
||||||
|
assert_eq!(4, comment_modlog.len());
|
||||||
|
|
||||||
|
let mod_restored_comments = comment_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.mod_remove_comment.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![false, false, true, true], mod_restored_comments);
|
||||||
|
|
||||||
|
let restored_comments = comment_modlog
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.comment.removed)
|
||||||
|
.collect::<Vec<bool>>();
|
||||||
|
assert_eq!(vec![false, false, false, false], restored_comments);
|
||||||
|
|
||||||
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,9 @@ use lemmy_db_views::structs::{LocalUserView, PostView};
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||||
utils::{mention::scrape_text_for_mentions, validation::is_valid_body_field},
|
utils::{mention::scrape_text_for_mentions, validation::is_valid_body_field},
|
||||||
|
MAX_COMMENT_DEPTH_LIMIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
pub async fn create_comment(
|
pub async fn create_comment(
|
||||||
data: Json<CreateComment>,
|
data: Json<CreateComment>,
|
||||||
|
@ -89,16 +88,9 @@ pub async fn create_comment(
|
||||||
check_comment_depth(parent)?;
|
check_comment_depth(parent)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
CommunityLanguage::is_allowed_community_language(
|
|
||||||
&mut context.pool(),
|
|
||||||
data.language_id,
|
|
||||||
community_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// attempt to set default language if none was provided
|
// attempt to set default language if none was provided
|
||||||
let language_id = match data.language_id {
|
let language_id = match data.language_id {
|
||||||
Some(lid) => Some(lid),
|
Some(lid) => lid,
|
||||||
None => {
|
None => {
|
||||||
default_post_language(
|
default_post_language(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
|
@ -109,8 +101,11 @@ pub async fn create_comment(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
CommunityLanguage::is_allowed_community_language(&mut context.pool(), language_id, community_id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm {
|
let comment_form = CommentInsertForm {
|
||||||
language_id,
|
language_id: Some(language_id),
|
||||||
..CommentInsertForm::new(local_user_view.person.id, data.post_id, content.clone())
|
..CommentInsertForm::new(local_user_view.person.id, data.post_id, content.clone())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -55,13 +55,14 @@ pub async fn update_comment(
|
||||||
Err(LemmyErrorType::NoCommentEditAllowed)?
|
Err(LemmyErrorType::NoCommentEditAllowed)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let language_id = data.language_id;
|
if let Some(language_id) = data.language_id {
|
||||||
CommunityLanguage::is_allowed_community_language(
|
CommunityLanguage::is_allowed_community_language(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
language_id,
|
language_id,
|
||||||
orig_comment.community.id,
|
orig_comment.community.id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(&context).await?;
|
let url_blocklist = get_url_blocklist(&context).await?;
|
||||||
|
|
|
@ -104,18 +104,9 @@ pub async fn create_post(
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only need to check if language is allowed in case user set it explicitly. When using default
|
|
||||||
// language, it already only returns allowed languages.
|
|
||||||
CommunityLanguage::is_allowed_community_language(
|
|
||||||
&mut context.pool(),
|
|
||||||
data.language_id,
|
|
||||||
community_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// attempt to set default language if none was provided
|
// attempt to set default language if none was provided
|
||||||
let language_id = match data.language_id {
|
let language_id = match data.language_id {
|
||||||
Some(lid) => Some(lid),
|
Some(lid) => lid,
|
||||||
None => {
|
None => {
|
||||||
default_post_language(
|
default_post_language(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
|
@ -126,6 +117,11 @@ pub async fn create_post(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only need to check if language is allowed in case user set it explicitly. When using default
|
||||||
|
// language, it already only returns allowed languages.
|
||||||
|
CommunityLanguage::is_allowed_community_language(&mut context.pool(), language_id, community_id)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let scheduled_publish_time =
|
let scheduled_publish_time =
|
||||||
convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?;
|
convert_published_time(data.scheduled_publish_time, &local_user_view, &context).await?;
|
||||||
let post_form = PostInsertForm {
|
let post_form = PostInsertForm {
|
||||||
|
@ -133,7 +129,7 @@ pub async fn create_post(
|
||||||
body,
|
body,
|
||||||
alt_text: data.alt_text.clone(),
|
alt_text: data.alt_text.clone(),
|
||||||
nsfw: data.nsfw,
|
nsfw: data.nsfw,
|
||||||
language_id,
|
language_id: Some(language_id),
|
||||||
scheduled_publish_time,
|
scheduled_publish_time,
|
||||||
..PostInsertForm::new(
|
..PostInsertForm::new(
|
||||||
data.name.trim().to_string(),
|
data.name.trim().to_string(),
|
||||||
|
|
|
@ -101,13 +101,14 @@ pub async fn update_post(
|
||||||
Err(LemmyErrorType::NoPostEditAllowed)?
|
Err(LemmyErrorType::NoPostEditAllowed)?
|
||||||
}
|
}
|
||||||
|
|
||||||
let language_id = data.language_id;
|
if let Some(language_id) = data.language_id {
|
||||||
CommunityLanguage::is_allowed_community_language(
|
CommunityLanguage::is_allowed_community_language(
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
language_id,
|
language_id,
|
||||||
orig_post.community_id,
|
orig_post.community_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
// handle changes to scheduled_publish_time
|
// handle changes to scheduled_publish_time
|
||||||
let scheduled_publish_time = match (
|
let scheduled_publish_time = match (
|
||||||
|
|
|
@ -90,7 +90,6 @@ pub async fn create_site(
|
||||||
let local_site_form = LocalSiteUpdateForm {
|
let local_site_form = LocalSiteUpdateForm {
|
||||||
// Set the site setup to true
|
// Set the site setup to true
|
||||||
site_setup: Some(true),
|
site_setup: Some(true),
|
||||||
enable_downvotes: data.enable_downvotes,
|
|
||||||
registration_mode: data.registration_mode,
|
registration_mode: data.registration_mode,
|
||||||
community_creation_admin_only: data.community_creation_admin_only,
|
community_creation_admin_only: data.community_creation_admin_only,
|
||||||
require_email_verification: data.require_email_verification,
|
require_email_verification: data.require_email_verification,
|
||||||
|
@ -110,6 +109,10 @@ pub async fn create_site(
|
||||||
captcha_enabled: data.captcha_enabled,
|
captcha_enabled: data.captcha_enabled,
|
||||||
captcha_difficulty: data.captcha_difficulty.clone(),
|
captcha_difficulty: data.captcha_difficulty.clone(),
|
||||||
default_post_listing_mode: data.default_post_listing_mode,
|
default_post_listing_mode: data.default_post_listing_mode,
|
||||||
|
post_upvotes: data.post_upvotes,
|
||||||
|
post_downvotes: data.post_downvotes,
|
||||||
|
comment_upvotes: data.comment_upvotes,
|
||||||
|
comment_downvotes: data.comment_downvotes,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -139,7 +142,10 @@ pub async fn create_site(
|
||||||
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
||||||
context.rate_limit_cell().set_config(rate_limit_config);
|
context.rate_limit_cell().set_config(rate_limit_config);
|
||||||
|
|
||||||
Ok(Json(SiteResponse { site_view }))
|
Ok(Json(SiteResponse {
|
||||||
|
site_view,
|
||||||
|
taglines: vec![],
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> {
|
fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) -> LemmyResult<()> {
|
||||||
|
@ -189,8 +195,6 @@ fn validate_create_payload(local_site: &LocalSite, create_site: &CreateSite) ->
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::site::create::validate_create_payload;
|
use crate::site::create::validate_create_payload;
|
||||||
|
|
|
@ -48,8 +48,6 @@ fn not_zero(val: Option<i32>) -> Option<i32> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::site::{application_question_check, not_zero, site_default_post_listing_type_check};
|
use crate::site::{application_question_check, not_zero, site_default_post_listing_type_check};
|
||||||
|
|
|
@ -43,7 +43,7 @@ pub async fn get_site(
|
||||||
let all_languages = Language::read_all(&mut context.pool()).await?;
|
let all_languages = Language::read_all(&mut context.pool()).await?;
|
||||||
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
||||||
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
|
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
|
||||||
let tagline = Tagline::get_random(&mut context.pool()).await?;
|
let tagline = Tagline::get_random(&mut context.pool()).await.ok();
|
||||||
let admin_oauth_providers = OAuthProvider::get_all(&mut context.pool()).await?;
|
let admin_oauth_providers = OAuthProvider::get_all(&mut context.pool()).await?;
|
||||||
let oauth_providers =
|
let oauth_providers =
|
||||||
OAuthProvider::convert_providers_to_public(admin_oauth_providers.clone());
|
OAuthProvider::convert_providers_to_public(admin_oauth_providers.clone());
|
||||||
|
@ -59,6 +59,8 @@ pub async fn get_site(
|
||||||
tagline,
|
tagline,
|
||||||
oauth_providers: Some(oauth_providers),
|
oauth_providers: Some(oauth_providers),
|
||||||
admin_oauth_providers: Some(admin_oauth_providers),
|
admin_oauth_providers: Some(admin_oauth_providers),
|
||||||
|
taglines: vec![],
|
||||||
|
custom_emojis: vec![],
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -99,7 +99,6 @@ pub async fn update_site(
|
||||||
.ok();
|
.ok();
|
||||||
|
|
||||||
let local_site_form = LocalSiteUpdateForm {
|
let local_site_form = LocalSiteUpdateForm {
|
||||||
enable_downvotes: data.enable_downvotes,
|
|
||||||
registration_mode: data.registration_mode,
|
registration_mode: data.registration_mode,
|
||||||
community_creation_admin_only: data.community_creation_admin_only,
|
community_creation_admin_only: data.community_creation_admin_only,
|
||||||
require_email_verification: data.require_email_verification,
|
require_email_verification: data.require_email_verification,
|
||||||
|
@ -121,6 +120,10 @@ pub async fn update_site(
|
||||||
reports_email_admins: data.reports_email_admins,
|
reports_email_admins: data.reports_email_admins,
|
||||||
default_post_listing_mode: data.default_post_listing_mode,
|
default_post_listing_mode: data.default_post_listing_mode,
|
||||||
oauth_registration: data.oauth_registration,
|
oauth_registration: data.oauth_registration,
|
||||||
|
post_upvotes: data.post_upvotes,
|
||||||
|
post_downvotes: data.post_downvotes,
|
||||||
|
comment_upvotes: data.comment_upvotes,
|
||||||
|
comment_downvotes: data.comment_downvotes,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -193,7 +196,10 @@ pub async fn update_site(
|
||||||
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit);
|
||||||
context.rate_limit_cell().set_config(rate_limit_config);
|
context.rate_limit_cell().set_config(rate_limit_config);
|
||||||
|
|
||||||
Ok(Json(SiteResponse { site_view }))
|
Ok(Json(SiteResponse {
|
||||||
|
site_view,
|
||||||
|
taglines: vec![],
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> LemmyResult<()> {
|
fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> LemmyResult<()> {
|
||||||
|
@ -241,8 +247,6 @@ fn validate_update_payload(local_site: &LocalSite, edit_site: &EditSite) -> Lemm
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::site::update::validate_update_payload;
|
use crate::site::update::validate_update_payload;
|
||||||
|
|
|
@ -107,9 +107,7 @@ pub async fn register(
|
||||||
check_slurs(&data.username, &slur_regex)?;
|
check_slurs(&data.username, &slur_regex)?;
|
||||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||||
|
|
||||||
if Person::is_username_taken(&mut context.pool(), &data.username).await? {
|
Person::check_username_taken(&mut context.pool(), &data.username).await?;
|
||||||
return Err(LemmyErrorType::UsernameAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(email) = &data.email {
|
if let Some(email) = &data.email {
|
||||||
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
|
||||||
|
@ -329,9 +327,7 @@ pub async fn authenticate_with_oauth(
|
||||||
check_slurs(username, &slur_regex)?;
|
check_slurs(username, &slur_regex)?;
|
||||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||||
|
|
||||||
if Person::is_username_taken(&mut context.pool(), username).await? {
|
Person::check_username_taken(&mut context.pool(), username).await?;
|
||||||
return Err(LemmyErrorType::UsernameAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to create a person, a local_user, and an oauth_account
|
// We have to create a person, a local_user, and an oauth_account
|
||||||
person = create_person(
|
person = create_person(
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
"type": "Group",
|
"type": "Group",
|
||||||
"preferredUsername": "tenforward",
|
"preferredUsername": "tenforward",
|
||||||
"name": "Ten Forward",
|
"name": "Ten Forward",
|
||||||
"summary": "<p>Lounge and recreation facility</p>\n<hr />\n<p>Welcome to the <a href=\"https://memory-alpha.fandom.com/wiki/USS_Enterprise_(NCC-1701-D)\">Enterprise</a>!.</p>\n",
|
"summary": "<p>Lounge and recreation facility</p>\n<hr />\n<p>Welcome to the Enterprise!.</p>\n",
|
||||||
"source": {
|
"source": {
|
||||||
"content": "Lounge and recreation facility\n\n---\n\nWelcome to the [Enterprise](https://memory-alpha.fandom.com/wiki/USS_Enterprise_(NCC-1701-D))!.",
|
"content": "Lounge and recreation facility\n\n---\n\nWelcome to the Enterprise!",
|
||||||
"mediaType": "text/markdown"
|
"mediaType": "text/markdown"
|
||||||
},
|
},
|
||||||
"sensitive": false,
|
"sensitive": false,
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"attachment": [],
|
"attachment": [],
|
||||||
"attributedTo": "https://queer.hacktivis.me/users/lanodan",
|
"attributedTo": "https://queer.hacktivis.me/users/lanodan",
|
||||||
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
|
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
"content": "<span class=\"h-card\"><a class=\"u-url mention\" data-user=\"9zkUX4o3WxGM8vGPfU\" href=\"https://pleroma.popolon.org/users/popolon\" rel=\"ugc\">@<span>popolon</span></a></span> Have what?",
|
"content": "Have what?",
|
||||||
"context": "https://queer.hacktivis.me/contexts/34cba3d2-2f35-4169-aeff-56af9bfeb753",
|
"context": "https://queer.hacktivis.me/contexts/34cba3d2-2f35-4169-aeff-56af9bfeb753",
|
||||||
"conversation": "https://queer.hacktivis.me/contexts/34cba3d2-2f35-4169-aeff-56af9bfeb753",
|
"conversation": "https://queer.hacktivis.me/contexts/34cba3d2-2f35-4169-aeff-56af9bfeb753",
|
||||||
"id": "https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2",
|
"id": "https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2",
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"owner": "https://queer.hacktivis.me/users/lanodan",
|
"owner": "https://queer.hacktivis.me/users/lanodan",
|
||||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsWOgdjSMc010qvxC3njI\nXJlFWMJ5gJ8QXCW/PajYdsHPM6d+jxBNJ6zp9/tIRa2m7bWHTSkuHQ7QthOpt6vu\n+dAWpKRLS607SPLItn/qUcyXvgN+H8shfyhMxvkVs9jXdtlBsLUVE7UNpN0dxzqe\nI79QWbf7o4amgaIWGRYB+OYMnIxKt+GzIkivZdSVSYjfxNnBYkMCeUxm5EpPIxKS\nP5bBHAVRRambD5NUmyKILuC60/rYuc/C+vmgpY2HCWFS2q6o34dPr9enwL6t4b3m\nS1t/EJHk9rGaaDqSGkDEfyQI83/7SDebWKuETMKKFLZi1vMgQIFuOYCIhN6bIiZm\npQIDAQAB\n-----END PUBLIC KEY-----\n\n"
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsWOgdjSMc010qvxC3njI\nXJlFWMJ5gJ8QXCW/PajYdsHPM6d+jxBNJ6zp9/tIRa2m7bWHTSkuHQ7QthOpt6vu\n+dAWpKRLS607SPLItn/qUcyXvgN+H8shfyhMxvkVs9jXdtlBsLUVE7UNpN0dxzqe\nI79QWbf7o4amgaIWGRYB+OYMnIxKt+GzIkivZdSVSYjfxNnBYkMCeUxm5EpPIxKS\nP5bBHAVRRambD5NUmyKILuC60/rYuc/C+vmgpY2HCWFS2q6o34dPr9enwL6t4b3m\nS1t/EJHk9rGaaDqSGkDEfyQI83/7SDebWKuETMKKFLZi1vMgQIFuOYCIhN6bIiZm\npQIDAQAB\n-----END PUBLIC KEY-----\n\n"
|
||||||
},
|
},
|
||||||
"summary": "---<br/>Website: <a href=\"https://hacktivis.me/\">https://hacktivis.me/</a><br/>Lang: Français(natif), English(fluent), LSF(🤏~👌), русский (еле-еле), <br/>Politics: Anarchist as in DIY/DIWO, freedom of association, anti-authoritarian, anti-identitarianism<br/><br/>Pronouns: meh, pick any, have fun<br/>Timezone: Let's say Mars, I have a non-24h cycle<br/>```<br/>🦊🦄⚧🂡ⓥ :anarchy: 👿🐧 :gentoo:<br/>Pleroma maintainer (mostly backend)<br/>BadWolf developer<br/>Gentoo contributor<br/><br/>Dayjob: yogoko.fr<br/><br/>That person which uses HJKL in games<br/><br/>Just because computer bad: X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*<br/><br/>banner from: <a href=\"https://soc.flyingcube.tech/objects/56f79be2-9013-4559-9826-f7dc392417db\">https://soc.flyingcube.tech/objects/56f79be2-9013-4559-9826-f7dc392417db</a><br/>Federation-bots: <a class=\"hashtag\" data-tag=\"nobot\" href=\"https://queer.hacktivis.me/tag/nobot\">#nobot</a>",
|
"summary": "---Lang: Français(natif), English(fluent), LSF(🤏~👌), русский (еле-еле), <br/>Politics: Anarchist as in DIY/DIWO, freedom of association, anti-authoritarian, anti-identitarianism<br/><br/>Pronouns: meh, pick any, have fun<br/>Timezone: Let's say Mars, I have a non-24h cycle<br/>```<br/>🦊🦄⚧🂡ⓥ :anarchy: 👿🐧 :gentoo:<br/>Pleroma maintainer (mostly backend)<br/>BadWolf developer<br/>Gentoo contributor<br/><br/>Dayjob: yogoko.fr<br/><br/>That person which uses HJKL in games<br/><br/>Just because computer bad: X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*<br/><br/>banner from: <a href=\"https://soc.flyingcube.tech/objects/56f79be2-9013-4559-9826-f7dc392417db\">https://soc.flyingcube.tech/objects/56f79be2-9013-4559-9826-f7dc392417db</a><br/>Federation-bots: <a class=\"hashtag\" data-tag=\"nobot\" href=\"https://queer.hacktivis.me/tag/nobot\">#nobot</a>",
|
||||||
"tag": [
|
"tag": [
|
||||||
{
|
{
|
||||||
"icon": {
|
"icon": {
|
||||||
|
|
|
@ -23,7 +23,7 @@ use anyhow::anyhow;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
utils::{remove_or_restore_user_data_in_community, remove_user_data},
|
utils::{remove_or_restore_user_data, remove_or_restore_user_data_in_community},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -160,6 +160,7 @@ impl ActivityHandler for BlockUser {
|
||||||
let mod_person = self.actor.dereference(context).await?;
|
let mod_person = self.actor.dereference(context).await?;
|
||||||
let blocked_person = self.object.dereference(context).await?;
|
let blocked_person = self.object.dereference(context).await?;
|
||||||
let target = self.target.dereference(context).await?;
|
let target = self.target.dereference(context).await?;
|
||||||
|
let reason = self.summary;
|
||||||
match target {
|
match target {
|
||||||
SiteOrCommunity::Site(_site) => {
|
SiteOrCommunity::Site(_site) => {
|
||||||
let blocked_person = Person::update(
|
let blocked_person = Person::update(
|
||||||
|
@ -173,14 +174,15 @@ impl ActivityHandler for BlockUser {
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
if self.remove_data.unwrap_or(false) {
|
if self.remove_data.unwrap_or(false) {
|
||||||
remove_user_data(blocked_person.id, context).await?;
|
remove_or_restore_user_data(mod_person.id, blocked_person.id, true, &reason, context)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write mod log
|
// write mod log
|
||||||
let form = ModBanForm {
|
let form = ModBanForm {
|
||||||
mod_person_id: mod_person.id,
|
mod_person_id: mod_person.id,
|
||||||
other_person_id: blocked_person.id,
|
other_person_id: blocked_person.id,
|
||||||
reason: self.summary,
|
reason,
|
||||||
banned: Some(true),
|
banned: Some(true),
|
||||||
expires,
|
expires,
|
||||||
};
|
};
|
||||||
|
@ -207,8 +209,10 @@ impl ActivityHandler for BlockUser {
|
||||||
if self.remove_data.unwrap_or(false) {
|
if self.remove_data.unwrap_or(false) {
|
||||||
remove_or_restore_user_data_in_community(
|
remove_or_restore_user_data_in_community(
|
||||||
community.id,
|
community.id,
|
||||||
|
mod_person.id,
|
||||||
blocked_person.id,
|
blocked_person.id,
|
||||||
true,
|
true,
|
||||||
|
&reason,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -219,7 +223,7 @@ impl ActivityHandler for BlockUser {
|
||||||
mod_person_id: mod_person.id,
|
mod_person_id: mod_person.id,
|
||||||
other_person_id: blocked_person.id,
|
other_person_id: blocked_person.id,
|
||||||
community_id: community.id,
|
community_id: community.id,
|
||||||
reason: self.summary,
|
reason,
|
||||||
banned: Some(true),
|
banned: Some(true),
|
||||||
expires,
|
expires,
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,7 +19,7 @@ use activitypub_federation::{
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
utils::{remove_or_restore_user_data_in_community, restore_user_data},
|
utils::{remove_or_restore_user_data, remove_or_restore_user_data_in_community},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -120,7 +120,8 @@ impl ActivityHandler for UndoBlockUser {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if self.restore_data.unwrap_or(false) {
|
if self.restore_data.unwrap_or(false) {
|
||||||
restore_user_data(blocked_person.id, context).await?;
|
remove_or_restore_user_data(mod_person.id, blocked_person.id, false, &None, context)
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write mod log
|
// write mod log
|
||||||
|
@ -144,8 +145,10 @@ impl ActivityHandler for UndoBlockUser {
|
||||||
if self.restore_data.unwrap_or(false) {
|
if self.restore_data.unwrap_or(false) {
|
||||||
remove_or_restore_user_data_in_community(
|
remove_or_restore_user_data_in_community(
|
||||||
community.id,
|
community.id,
|
||||||
|
mod_person.id,
|
||||||
blocked_person.id,
|
blocked_person.id,
|
||||||
false,
|
false,
|
||||||
|
&None,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
|
@ -18,7 +18,7 @@ use activitypub_federation::{
|
||||||
traits::{ActivityHandler, Actor},
|
traits::{ActivityHandler, Actor},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{context::LemmyContext, utils::check_bot_account};
|
use lemmy_api_common::{context::LemmyContext, utils::check_bot_account};
|
||||||
use lemmy_db_schema::source::local_site::LocalSite;
|
use lemmy_db_schema::{source::local_site::LocalSite, FederationMode};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -68,12 +68,22 @@ impl ActivityHandler for Vote {
|
||||||
|
|
||||||
check_bot_account(&actor.0)?;
|
check_bot_account(&actor.0)?;
|
||||||
|
|
||||||
let enable_downvotes = LocalSite::read(&mut context.pool())
|
// Check for enabled federation votes
|
||||||
|
let local_site = LocalSite::read(&mut context.pool())
|
||||||
.await
|
.await
|
||||||
.map(|l| l.enable_downvotes)
|
.unwrap_or_default();
|
||||||
.unwrap_or(true);
|
|
||||||
if self.kind == VoteType::Dislike && !enable_downvotes {
|
let (downvote_setting, upvote_setting) = match object {
|
||||||
// If this is a downvote but downvotes are ignored, only undo any existing vote
|
PostOrComment::Post(_) => (local_site.post_downvotes, local_site.post_upvotes),
|
||||||
|
PostOrComment::Comment(_) => (local_site.comment_downvotes, local_site.comment_upvotes),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't allow dislikes for either disabled, or local only votes
|
||||||
|
let downvote_fail = self.kind == VoteType::Dislike && downvote_setting != FederationMode::All;
|
||||||
|
let upvote_fail = self.kind == VoteType::Like && upvote_setting != FederationMode::All;
|
||||||
|
|
||||||
|
if downvote_fail || upvote_fail {
|
||||||
|
// If this is a rejection, undo the vote
|
||||||
match object {
|
match object {
|
||||||
PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
|
PostOrComment::Post(p) => undo_vote_post(actor, &p, context).await,
|
||||||
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
|
PostOrComment::Comment(c) => undo_vote_comment(actor, &c, context).await,
|
||||||
|
|
|
@ -123,7 +123,6 @@ impl InCommunity for AnnouncableActivities {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::fetcher::{
|
use crate::fetcher::{
|
||||||
|
post_or_comment::PostOrComment,
|
||||||
search::{search_query_to_object_id, search_query_to_object_id_local, SearchableObjects},
|
search::{search_query_to_object_id, search_query_to_object_id_local, SearchableObjects},
|
||||||
user_or_community::UserOrCommunity,
|
user_or_community::UserOrCommunity,
|
||||||
};
|
};
|
||||||
|
@ -46,21 +47,22 @@ async fn convert_response(
|
||||||
local_user_view: Option<LocalUserView>,
|
local_user_view: Option<LocalUserView>,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<Json<ResolveObjectResponse>> {
|
) -> LemmyResult<Json<ResolveObjectResponse>> {
|
||||||
use SearchableObjects::*;
|
|
||||||
let removed_or_deleted;
|
let removed_or_deleted;
|
||||||
let mut res = ResolveObjectResponse::default();
|
let mut res = ResolveObjectResponse::default();
|
||||||
let local_user = local_user_view.map(|l| l.local_user);
|
let local_user = local_user_view.map(|l| l.local_user);
|
||||||
|
|
||||||
match object {
|
match object {
|
||||||
Post(p) => {
|
SearchableObjects::PostOrComment(pc) => match *pc {
|
||||||
removed_or_deleted = p.deleted || p.removed;
|
PostOrComment::Post(p) => {
|
||||||
res.post = Some(PostView::read(pool, p.id, local_user.as_ref(), false).await?)
|
removed_or_deleted = p.deleted || p.removed;
|
||||||
}
|
res.post = Some(PostView::read(pool, p.id, local_user.as_ref(), false).await?)
|
||||||
Comment(c) => {
|
}
|
||||||
removed_or_deleted = c.deleted || c.removed;
|
PostOrComment::Comment(c) => {
|
||||||
res.comment = Some(CommentView::read(pool, c.id, local_user.as_ref()).await?)
|
removed_or_deleted = c.deleted || c.removed;
|
||||||
}
|
res.comment = Some(CommentView::read(pool, c.id, local_user.as_ref()).await?)
|
||||||
PersonOrCommunity(p) => match *p {
|
}
|
||||||
|
},
|
||||||
|
SearchableObjects::PersonOrCommunity(pc) => match *pc {
|
||||||
UserOrCommunity::User(u) => {
|
UserOrCommunity::User(u) => {
|
||||||
removed_or_deleted = u.deleted;
|
removed_or_deleted = u.deleted;
|
||||||
res.person = Some(PersonView::read(pool, u.id).await?)
|
res.person = Some(PersonView::read(pool, u.id).await?)
|
||||||
|
|
|
@ -12,7 +12,11 @@ use lemmy_db_views::{
|
||||||
post_view::PostQuery,
|
post_view::PostQuery,
|
||||||
structs::{LocalUserView, SiteView},
|
structs::{LocalUserView, SiteView},
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::{community_view::CommunityQuery, person_view::PersonQuery};
|
use lemmy_db_views_actor::{
|
||||||
|
community_view::CommunityQuery,
|
||||||
|
person_view::PersonQuery,
|
||||||
|
structs::CommunitySortType,
|
||||||
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::error::LemmyResult;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
|
@ -47,7 +51,7 @@ pub async fn search(
|
||||||
listing_type,
|
listing_type,
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
post_title_only,
|
title_only,
|
||||||
post_url_only,
|
post_url_only,
|
||||||
saved_only,
|
saved_only,
|
||||||
liked_only,
|
liked_only,
|
||||||
|
@ -78,7 +82,7 @@ pub async fn search(
|
||||||
search_term: Some(q.clone()),
|
search_term: Some(q.clone()),
|
||||||
page,
|
page,
|
||||||
limit,
|
limit,
|
||||||
title_only: post_title_only,
|
title_only,
|
||||||
url_only: post_url_only,
|
url_only: post_url_only,
|
||||||
liked_only,
|
liked_only,
|
||||||
disliked_only,
|
disliked_only,
|
||||||
|
@ -102,9 +106,10 @@ pub async fn search(
|
||||||
};
|
};
|
||||||
|
|
||||||
let community_query = CommunityQuery {
|
let community_query = CommunityQuery {
|
||||||
sort,
|
sort: sort.map(CommunitySortType::from),
|
||||||
listing_type,
|
listing_type,
|
||||||
search_term: Some(q.clone()),
|
search_term: Some(q.clone()),
|
||||||
|
title_only,
|
||||||
local_user,
|
local_user,
|
||||||
is_mod_or_admin: is_admin,
|
is_mod_or_admin: is_admin,
|
||||||
page,
|
page,
|
||||||
|
|
|
@ -103,13 +103,16 @@ pub async fn import_settings(
|
||||||
context: Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
) -> LemmyResult<Json<SuccessResponse>> {
|
) -> LemmyResult<Json<SuccessResponse>> {
|
||||||
let person_form = PersonUpdateForm {
|
let person_form = PersonUpdateForm {
|
||||||
display_name: Some(data.display_name.clone()),
|
display_name: data.display_name.clone().map(Some),
|
||||||
bio: Some(data.bio.clone()),
|
bio: data.bio.clone().map(Some),
|
||||||
matrix_user_id: Some(data.matrix_id.clone()),
|
matrix_user_id: data.bio.clone().map(Some),
|
||||||
bot_account: data.bot_account,
|
bot_account: data.bot_account,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
Person::update(&mut context.pool(), local_user_view.person.id, &person_form).await?;
|
// ignore error in case form is empty
|
||||||
|
Person::update(&mut context.pool(), local_user_view.person.id, &person_form)
|
||||||
|
.await
|
||||||
|
.ok();
|
||||||
|
|
||||||
let local_user_form = LocalUserUpdateForm {
|
let local_user_form = LocalUserUpdateForm {
|
||||||
show_nsfw: data.settings.as_ref().map(|s| s.show_nsfw),
|
show_nsfw: data.settings.as_ref().map(|s| s.show_nsfw),
|
||||||
|
@ -127,7 +130,6 @@ pub async fn import_settings(
|
||||||
show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts),
|
show_read_posts: data.settings.as_ref().map(|s| s.show_read_posts),
|
||||||
open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab),
|
open_links_in_new_tab: data.settings.as_ref().map(|s| s.open_links_in_new_tab),
|
||||||
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
blur_nsfw: data.settings.as_ref().map(|s| s.blur_nsfw),
|
||||||
auto_expand: data.settings.as_ref().map(|s| s.auto_expand),
|
|
||||||
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
infinite_scroll_enabled: data.settings.as_ref().map(|s| s.infinite_scroll_enabled),
|
||||||
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
post_listing_mode: data.settings.as_ref().map(|s| s.post_listing_mode),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
@ -308,12 +310,14 @@ where
|
||||||
});
|
});
|
||||||
Ok(failed_items.into_iter().join(","))
|
Ok(failed_items.into_iter().join(","))
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use crate::api::user_settings_backup::{export_settings, import_settings, UserSettingsBackup};
|
#[cfg(test)]
|
||||||
|
#[expect(clippy::indexing_slicing)]
|
||||||
|
pub(crate) mod tests {
|
||||||
|
|
||||||
|
use crate::api::user_settings_backup::{export_settings, import_settings};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::context::LemmyContext;
|
use lemmy_api_common::context::LemmyContext;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
@ -332,7 +336,7 @@ mod tests {
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
async fn create_user(
|
pub(crate) async fn create_user(
|
||||||
name: String,
|
name: String,
|
||||||
bio: Option<String>,
|
bio: Option<String>,
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
|
@ -401,45 +405,6 @@ mod tests {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
#[serial]
|
|
||||||
async fn test_settings_partial_import() -> LemmyResult<()> {
|
|
||||||
let context = LemmyContext::init_test_context().await;
|
|
||||||
|
|
||||||
let export_user =
|
|
||||||
create_user("hanna".to_string(), Some("my bio".to_string()), &context).await?;
|
|
||||||
|
|
||||||
let community_form = CommunityInsertForm::new(
|
|
||||||
export_user.person.instance_id,
|
|
||||||
"testcom".to_string(),
|
|
||||||
"testcom".to_string(),
|
|
||||||
"pubkey".to_string(),
|
|
||||||
);
|
|
||||||
let community = Community::create(&mut context.pool(), &community_form).await?;
|
|
||||||
let follower_form = CommunityFollowerForm {
|
|
||||||
community_id: community.id,
|
|
||||||
person_id: export_user.person.id,
|
|
||||||
pending: false,
|
|
||||||
};
|
|
||||||
CommunityFollower::follow(&mut context.pool(), &follower_form).await?;
|
|
||||||
|
|
||||||
let backup = export_settings(export_user.clone(), context.reset_request_count()).await?;
|
|
||||||
|
|
||||||
let import_user = create_user("charles".to_string(), None, &context).await?;
|
|
||||||
|
|
||||||
let backup2 = UserSettingsBackup {
|
|
||||||
followed_communities: backup.followed_communities.clone(),
|
|
||||||
..Default::default()
|
|
||||||
};
|
|
||||||
import_settings(
|
|
||||||
actix_web::web::Json(backup2),
|
|
||||||
import_user.clone(),
|
|
||||||
context.reset_request_count(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn disallow_large_backup() -> LemmyResult<()> {
|
async fn disallow_large_backup() -> LemmyResult<()> {
|
||||||
|
@ -475,4 +440,33 @@ mod tests {
|
||||||
LocalUser::delete(&mut context.pool(), import_user.local_user.id).await?;
|
LocalUser::delete(&mut context.pool(), import_user.local_user.id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn import_partial_backup() -> LemmyResult<()> {
|
||||||
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
|
||||||
|
let import_user =
|
||||||
|
create_user("hanna".to_string(), Some("my bio".to_string()), &context).await?;
|
||||||
|
|
||||||
|
let backup =
|
||||||
|
serde_json::from_str("{\"bot_account\": true, \"settings\": {\"theme\": \"my_theme\"}}")?;
|
||||||
|
import_settings(
|
||||||
|
Json(backup),
|
||||||
|
import_user.clone(),
|
||||||
|
context.reset_request_count(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let import_user_updated =
|
||||||
|
LocalUserView::read(&mut context.pool(), import_user.local_user.id).await?;
|
||||||
|
// mark as bot account
|
||||||
|
assert!(import_user_updated.person.bot_account);
|
||||||
|
// dont remove existing bio
|
||||||
|
assert_eq!(import_user.person.bio, import_user_updated.person.bio);
|
||||||
|
// local_user can be deserialized without id/person_id fields
|
||||||
|
assert_eq!("my_theme", import_user_updated.local_user.theme);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,7 +98,7 @@ impl Collection for ApubCommunityModerators {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
191
crates/apub/src/fetcher/markdown_links.rs
Normal file
191
crates/apub/src/fetcher/markdown_links.rs
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
use super::{search::SearchableObjects, user_or_community::UserOrCommunity};
|
||||||
|
use crate::fetcher::post_or_comment::PostOrComment;
|
||||||
|
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||||
|
use lemmy_api_common::{
|
||||||
|
context::LemmyContext,
|
||||||
|
utils::{generate_local_apub_endpoint, EndpointType},
|
||||||
|
};
|
||||||
|
use lemmy_db_schema::{newtypes::InstanceId, source::instance::Instance};
|
||||||
|
use lemmy_utils::{
|
||||||
|
error::LemmyResult,
|
||||||
|
utils::markdown::image_links::{markdown_find_links, markdown_handle_title},
|
||||||
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
|
pub async fn markdown_rewrite_remote_links_opt(
|
||||||
|
src: Option<String>,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> Option<String> {
|
||||||
|
match src {
|
||||||
|
Some(t) => Some(markdown_rewrite_remote_links(t, context).await),
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Goes through all remote markdown links and attempts to resolve them as Activitypub objects.
|
||||||
|
/// If successful, the link is rewritten to a local link, so it can be viewed without leaving the
|
||||||
|
/// local instance.
|
||||||
|
///
|
||||||
|
/// As it relies on ObjectId::dereference, it can only be used for incoming federated objects, not
|
||||||
|
/// for the API.
|
||||||
|
pub async fn markdown_rewrite_remote_links(
|
||||||
|
mut src: String,
|
||||||
|
context: &Data<LemmyContext>,
|
||||||
|
) -> String {
|
||||||
|
let links_offsets = markdown_find_links(&src);
|
||||||
|
|
||||||
|
// Go through the collected links in reverse order
|
||||||
|
for (start, end) in links_offsets.into_iter().rev() {
|
||||||
|
let (url, extra) = markdown_handle_title(&src, start, end);
|
||||||
|
|
||||||
|
if let Some(local_url) = to_local_url(url, context).await {
|
||||||
|
let mut local_url = local_url.to_string();
|
||||||
|
// restore title
|
||||||
|
if let Some(extra) = extra {
|
||||||
|
local_url = format!("{local_url} {extra}");
|
||||||
|
}
|
||||||
|
src.replace_range(start..end, local_url.as_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn to_local_url(url: &str, context: &Data<LemmyContext>) -> Option<Url> {
|
||||||
|
let local_domain = &context.settings().get_protocol_and_hostname();
|
||||||
|
let object_id = ObjectId::<SearchableObjects>::parse(url).ok()?;
|
||||||
|
if object_id.inner().domain() == Some(local_domain) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let dereferenced = object_id.dereference(context).await.ok()?;
|
||||||
|
match dereferenced {
|
||||||
|
SearchableObjects::PostOrComment(pc) => match *pc {
|
||||||
|
PostOrComment::Post(post) => {
|
||||||
|
generate_local_apub_endpoint(EndpointType::Post, &post.id.to_string(), local_domain)
|
||||||
|
}
|
||||||
|
PostOrComment::Comment(comment) => {
|
||||||
|
generate_local_apub_endpoint(EndpointType::Comment, &comment.id.to_string(), local_domain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ok()
|
||||||
|
.map(Into::into),
|
||||||
|
SearchableObjects::PersonOrCommunity(pc) => match *pc {
|
||||||
|
UserOrCommunity::User(user) => {
|
||||||
|
format_actor_url(&user.name, "u", user.instance_id, context).await
|
||||||
|
}
|
||||||
|
UserOrCommunity::Community(community) => {
|
||||||
|
format_actor_url(&community.name, "c", community.instance_id, context).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.ok(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn format_actor_url(
|
||||||
|
name: &str,
|
||||||
|
kind: &str,
|
||||||
|
instance_id: InstanceId,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> LemmyResult<Url> {
|
||||||
|
let local_protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
|
let local_hostname = &context.settings().hostname;
|
||||||
|
let instance = Instance::read(&mut context.pool(), instance_id).await?;
|
||||||
|
let url = if &instance.domain != local_hostname {
|
||||||
|
format!(
|
||||||
|
"{local_protocol_and_hostname}/{kind}/{name}@{}",
|
||||||
|
instance.domain
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
format!("{local_protocol_and_hostname}/{kind}/{name}")
|
||||||
|
};
|
||||||
|
Ok(Url::parse(&url)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::api::user_settings_backup::tests::create_user;
|
||||||
|
use lemmy_db_schema::{
|
||||||
|
source::{
|
||||||
|
community::{Community, CommunityInsertForm},
|
||||||
|
post::{Post, PostInsertForm},
|
||||||
|
},
|
||||||
|
traits::Crud,
|
||||||
|
};
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
use serial_test::serial;
|
||||||
|
|
||||||
|
#[serial]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_markdown_rewrite_remote_links() -> LemmyResult<()> {
|
||||||
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
|
||||||
|
let community = Community::create(
|
||||||
|
&mut context.pool(),
|
||||||
|
&CommunityInsertForm::new(
|
||||||
|
instance.id,
|
||||||
|
"my_community".to_string(),
|
||||||
|
"My Community".to_string(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let user = create_user("john".to_string(), None, &context).await?;
|
||||||
|
|
||||||
|
// insert a remote post which is already fetched
|
||||||
|
let post_form = PostInsertForm {
|
||||||
|
ap_id: Some(Url::parse("https://example.com/post/123")?.into()),
|
||||||
|
..PostInsertForm::new("My post".to_string(), user.person.id, community.id)
|
||||||
|
};
|
||||||
|
let post = Post::create(&mut context.pool(), &post_form).await?;
|
||||||
|
let markdown_local_post_url = format!("[link](https://lemmy-alpha/post/{})", post.id);
|
||||||
|
|
||||||
|
let tests: Vec<_> = vec", post.ap_id),
|
||||||
|
markdown_local_post_url.as_ref(),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"rewrite community link",
|
||||||
|
format!("[link]({})", community.actor_id),
|
||||||
|
"[link](https://lemmy-alpha/c/my_community@example.com)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite local post link",
|
||||||
|
"[link](https://lemmy-alpha/post/2)".to_string(),
|
||||||
|
"[link](https://lemmy-alpha/post/2)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite local community link",
|
||||||
|
"[link](https://lemmy-alpha/c/test)".to_string(),
|
||||||
|
"[link](https://lemmy-alpha/c/test)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite non-fediverse link",
|
||||||
|
"[link](https://example.com/)".to_string(),
|
||||||
|
"[link](https://example.com/)",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"dont rewrite invalid url",
|
||||||
|
"[link](example-com)".to_string(),
|
||||||
|
"[link](example-com)",
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
let context = LemmyContext::init_test_context().await;
|
||||||
|
for (msg, input, expected) in &tests {
|
||||||
|
let result = markdown_rewrite_remote_links(input.to_string(), &context).await;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
&result, expected,
|
||||||
|
"Testing {}, with original input '{}'",
|
||||||
|
msg, input
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Instance::delete(&mut context.pool(), instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ use lemmy_db_schema::traits::ApubActor;
|
||||||
use lemmy_db_views::structs::LocalUserView;
|
use lemmy_db_views::structs::LocalUserView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyResult};
|
use lemmy_utils::error::{LemmyError, LemmyResult};
|
||||||
|
|
||||||
|
pub(crate) mod markdown_links;
|
||||||
pub mod post_or_comment;
|
pub mod post_or_comment;
|
||||||
pub mod search;
|
pub mod search;
|
||||||
pub mod site_or_community_or_user;
|
pub mod site_or_community_or_user;
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
use crate::{
|
use super::post_or_comment::{PageOrNote, PostOrComment};
|
||||||
fetcher::user_or_community::{PersonOrGroup, UserOrCommunity},
|
use crate::fetcher::user_or_community::{PersonOrGroup, UserOrCommunity};
|
||||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
|
||||||
protocol::objects::{note::Note, page::Page},
|
|
||||||
};
|
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
fetch::{object_id::ObjectId, webfinger::webfinger_resolve_actor},
|
||||||
|
@ -54,16 +51,14 @@ pub(crate) async fn search_query_to_object_id_local(
|
||||||
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
/// The types of ActivityPub objects that can be fetched directly by searching for their ID.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum SearchableObjects {
|
pub(crate) enum SearchableObjects {
|
||||||
Post(ApubPost),
|
PostOrComment(Box<PostOrComment>),
|
||||||
Comment(ApubComment),
|
|
||||||
PersonOrCommunity(Box<UserOrCommunity>),
|
PersonOrCommunity(Box<UserOrCommunity>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
pub(crate) enum SearchableKinds {
|
pub(crate) enum SearchableKinds {
|
||||||
Page(Box<Page>),
|
PageOrNote(Box<PageOrNote>),
|
||||||
Note(Note),
|
|
||||||
PersonOrGroup(Box<PersonOrGroup>),
|
PersonOrGroup(Box<PersonOrGroup>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +70,7 @@ impl Object for SearchableObjects {
|
||||||
|
|
||||||
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
|
fn last_refreshed_at(&self) -> Option<DateTime<Utc>> {
|
||||||
match self {
|
match self {
|
||||||
SearchableObjects::Post(p) => p.last_refreshed_at(),
|
SearchableObjects::PostOrComment(p) => p.last_refreshed_at(),
|
||||||
SearchableObjects::Comment(c) => c.last_refreshed_at(),
|
|
||||||
SearchableObjects::PersonOrCommunity(p) => p.last_refreshed_at(),
|
SearchableObjects::PersonOrCommunity(p) => p.last_refreshed_at(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,13 +89,9 @@ impl Object for SearchableObjects {
|
||||||
if let Some(uc) = uc {
|
if let Some(uc) = uc {
|
||||||
return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc))));
|
return Ok(Some(SearchableObjects::PersonOrCommunity(Box::new(uc))));
|
||||||
}
|
}
|
||||||
let p = ApubPost::read_from_id(object_id.clone(), context).await?;
|
let pc = PostOrComment::read_from_id(object_id.clone(), context).await?;
|
||||||
if let Some(p) = p {
|
if let Some(pc) = pc {
|
||||||
return Ok(Some(SearchableObjects::Post(p)));
|
return Ok(Some(SearchableObjects::PostOrComment(Box::new(pc))));
|
||||||
}
|
|
||||||
let c = ApubComment::read_from_id(object_id, context).await?;
|
|
||||||
if let Some(c) = c {
|
|
||||||
return Ok(Some(SearchableObjects::Comment(c)));
|
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
@ -109,25 +99,16 @@ impl Object for SearchableObjects {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn delete(self, data: &Data<Self::DataType>) -> LemmyResult<()> {
|
async fn delete(self, data: &Data<Self::DataType>) -> LemmyResult<()> {
|
||||||
match self {
|
match self {
|
||||||
SearchableObjects::Post(p) => p.delete(data).await,
|
SearchableObjects::PostOrComment(pc) => pc.delete(data).await,
|
||||||
SearchableObjects::Comment(c) => c.delete(data).await,
|
SearchableObjects::PersonOrCommunity(pc) => pc.delete(data).await,
|
||||||
SearchableObjects::PersonOrCommunity(pc) => match *pc {
|
|
||||||
UserOrCommunity::User(p) => p.delete(data).await,
|
|
||||||
UserOrCommunity::Community(c) => c.delete(data).await,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn into_json(self, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> {
|
async fn into_json(self, data: &Data<Self::DataType>) -> LemmyResult<Self::Kind> {
|
||||||
|
use SearchableObjects::*;
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
SearchableObjects::Post(p) => SearchableKinds::Page(Box::new(p.into_json(data).await?)),
|
PostOrComment(pc) => SearchableKinds::PageOrNote(Box::new(pc.into_json(data).await?)),
|
||||||
SearchableObjects::Comment(c) => SearchableKinds::Note(c.into_json(data).await?),
|
PersonOrCommunity(pc) => SearchableKinds::PersonOrGroup(Box::new(pc.into_json(data).await?)),
|
||||||
SearchableObjects::PersonOrCommunity(pc) => {
|
|
||||||
SearchableKinds::PersonOrGroup(Box::new(match *pc {
|
|
||||||
UserOrCommunity::User(p) => PersonOrGroup::Person(p.into_json(data).await?),
|
|
||||||
UserOrCommunity::Community(c) => PersonOrGroup::Group(c.into_json(data).await?),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -137,24 +118,20 @@ impl Object for SearchableObjects {
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
data: &Data<Self::DataType>,
|
data: &Data<Self::DataType>,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
|
use SearchableKinds::*;
|
||||||
match apub {
|
match apub {
|
||||||
SearchableKinds::Page(a) => ApubPost::verify(a, expected_domain, data).await,
|
PageOrNote(pn) => PostOrComment::verify(pn, expected_domain, data).await,
|
||||||
SearchableKinds::Note(a) => ApubComment::verify(a, expected_domain, data).await,
|
PersonOrGroup(pg) => UserOrCommunity::verify(pg, expected_domain, data).await,
|
||||||
SearchableKinds::PersonOrGroup(pg) => match pg.as_ref() {
|
|
||||||
PersonOrGroup::Person(a) => ApubPerson::verify(a, expected_domain, data).await,
|
|
||||||
PersonOrGroup::Group(a) => ApubCommunity::verify(a, expected_domain, data).await,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn from_json(apub: Self::Kind, context: &Data<LemmyContext>) -> LemmyResult<Self> {
|
async fn from_json(apub: Self::Kind, context: &Data<LemmyContext>) -> LemmyResult<Self> {
|
||||||
use SearchableKinds as SAT;
|
use SearchableKinds::*;
|
||||||
use SearchableObjects as SO;
|
use SearchableObjects as SO;
|
||||||
Ok(match apub {
|
Ok(match apub {
|
||||||
SAT::Page(p) => SO::Post(ApubPost::from_json(*p, context).await?),
|
PageOrNote(pg) => SO::PostOrComment(Box::new(PostOrComment::from_json(*pg, context).await?)),
|
||||||
SAT::Note(n) => SO::Comment(ApubComment::from_json(n, context).await?),
|
PersonOrGroup(pg) => {
|
||||||
SAT::PersonOrGroup(pg) => {
|
|
||||||
SO::PersonOrCommunity(Box::new(UserOrCommunity::from_json(*pg, context).await?))
|
SO::PersonOrCommunity(Box::new(UserOrCommunity::from_json(*pg, context).await?))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -104,8 +104,6 @@ pub(crate) async fn get_apub_community_featured(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
pub(crate) mod tests {
|
pub(crate) mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -167,7 +165,7 @@ pub(crate) mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn decode_response<T: DeserializeOwned>(res: HttpResponse) -> LemmyResult<T> {
|
async fn decode_response<T: DeserializeOwned>(res: HttpResponse) -> LemmyResult<T> {
|
||||||
let body = to_bytes(res.into_body()).await.unwrap();
|
let body = to_bytes(res.into_body()).await.unwrap_or_default();
|
||||||
let body = std::str::from_utf8(&body)?;
|
let body = std::str::from_utf8(&body)?;
|
||||||
Ok(serde_json::from_str(body)?)
|
Ok(serde_json::from_str(body)?)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
activities::{verify_is_public, verify_person_in_community},
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links,
|
||||||
mentions::collect_non_local_mentions,
|
mentions::collect_non_local_mentions,
|
||||||
objects::{read_from_string_or_source, verify_is_remote_object},
|
objects::{read_from_string_or_source, verify_is_remote_object},
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -104,7 +105,7 @@ impl Object for ApubComment {
|
||||||
} else {
|
} else {
|
||||||
post.ap_id.into()
|
post.ap_id.into()
|
||||||
};
|
};
|
||||||
let language = LanguageTag::new_single(self.language_id, &mut context.pool()).await?;
|
let language = Some(LanguageTag::new_single(self.language_id, &mut context.pool()).await?);
|
||||||
let maa = collect_non_local_mentions(&self, community.actor_id.clone().into(), context).await?;
|
let maa = collect_non_local_mentions(&self, community.actor_id.clone().into(), context).await?;
|
||||||
|
|
||||||
let note = Note {
|
let note = Note {
|
||||||
|
@ -128,6 +129,8 @@ impl Object for ApubComment {
|
||||||
Ok(note)
|
Ok(note)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Recursively fetches all parent comments. This can lead to a stack overflow so we need to
|
||||||
|
/// Box::pin all large futures on the heap.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn verify(
|
async fn verify(
|
||||||
note: &Note,
|
note: &Note,
|
||||||
|
@ -137,14 +140,24 @@ impl Object for ApubComment {
|
||||||
verify_domains_match(note.id.inner(), expected_domain)?;
|
verify_domains_match(note.id.inner(), expected_domain)?;
|
||||||
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
||||||
verify_is_public(¬e.to, ¬e.cc)?;
|
verify_is_public(¬e.to, ¬e.cc)?;
|
||||||
let community = note.community(context).await?;
|
let community = Box::pin(note.community(context)).await?;
|
||||||
|
|
||||||
check_apub_id_valid_with_strictness(note.id.inner(), community.local, context).await?;
|
Box::pin(check_apub_id_valid_with_strictness(
|
||||||
|
note.id.inner(),
|
||||||
|
community.local,
|
||||||
|
context,
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
verify_is_remote_object(¬e.id, context)?;
|
verify_is_remote_object(¬e.id, context)?;
|
||||||
verify_person_in_community(¬e.attributed_to, &community, context).await?;
|
Box::pin(verify_person_in_community(
|
||||||
|
¬e.attributed_to,
|
||||||
|
&community,
|
||||||
|
context,
|
||||||
|
))
|
||||||
|
.await?;
|
||||||
|
|
||||||
let (post, _) = note.get_parents(context).await?;
|
let (post, _) = Box::pin(note.get_parents(context)).await?;
|
||||||
let creator = note.attributed_to.dereference(context).await?;
|
let creator = Box::pin(note.attributed_to.dereference(context)).await?;
|
||||||
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &creator, community.id)
|
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), &creator, community.id)
|
||||||
.await
|
.await
|
||||||
.is_ok();
|
.is_ok();
|
||||||
|
@ -169,8 +182,11 @@ impl Object for ApubComment {
|
||||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
||||||
let language_id =
|
let content = markdown_rewrite_remote_links(content, context).await;
|
||||||
LanguageTag::to_language_id_single(note.language, &mut context.pool()).await?;
|
let language_id = Some(
|
||||||
|
LanguageTag::to_language_id_single(note.language.unwrap_or_default(), &mut context.pool())
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
|
||||||
let form = CommentInsertForm {
|
let form = CommentInsertForm {
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
|
@ -284,7 +300,7 @@ pub(crate) mod tests {
|
||||||
let comment = ApubComment::from_json(json, &context).await?;
|
let comment = ApubComment::from_json(json, &context).await?;
|
||||||
|
|
||||||
assert_eq!(comment.ap_id, pleroma_url.into());
|
assert_eq!(comment.ap_id, pleroma_url.into());
|
||||||
assert_eq!(comment.content.len(), 64);
|
assert_eq!(comment.content.len(), 10);
|
||||||
assert!(!comment.local);
|
assert!(!comment.local);
|
||||||
assert_eq!(context.request_count(), 1);
|
assert_eq!(context.request_count(), 1);
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::GetActorType,
|
activities::GetActorType,
|
||||||
check_apub_id_valid,
|
check_apub_id_valid,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links_opt,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -148,6 +149,7 @@ impl Object for ApubCommunity {
|
||||||
let description = read_from_string_or_source_opt(&group.summary, &None, &group.source);
|
let description = read_from_string_or_source_opt(&group.summary, &None, &group.source);
|
||||||
let description =
|
let description =
|
||||||
process_markdown_opt(&description, slur_regex, &url_blocklist, context).await?;
|
process_markdown_opt(&description, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let description = markdown_rewrite_remote_links_opt(description, context).await;
|
||||||
let icon = proxy_image_link_opt_apub(group.icon.map(|i| i.url), context).await?;
|
let icon = proxy_image_link_opt_apub(group.icon.map(|i| i.url), context).await?;
|
||||||
let banner = proxy_image_link_opt_apub(group.image.map(|i| i.url), context).await?;
|
let banner = proxy_image_link_opt_apub(group.image.map(|i| i.url), context).await?;
|
||||||
|
|
||||||
|
@ -296,7 +298,7 @@ pub(crate) mod tests {
|
||||||
assert!(!community.local);
|
assert!(!community.local);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
community.description.as_ref().map(std::string::String::len),
|
community.description.as_ref().map(std::string::String::len),
|
||||||
Some(132)
|
Some(63)
|
||||||
);
|
);
|
||||||
|
|
||||||
Community::delete(&mut context.pool(), community.id).await?;
|
Community::delete(&mut context.pool(), community.id).await?;
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::verify_is_remote_object;
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::GetActorType,
|
activities::GetActorType,
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links_opt,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::read_from_string_or_source_opt,
|
objects::read_from_string_or_source_opt,
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -151,6 +152,7 @@ impl Object for ApubSite {
|
||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let sidebar = read_from_string_or_source_opt(&apub.content, &None, &apub.source);
|
let sidebar = read_from_string_or_source_opt(&apub.content, &None, &apub.source);
|
||||||
let sidebar = process_markdown_opt(&sidebar, slur_regex, &url_blocklist, context).await?;
|
let sidebar = process_markdown_opt(&sidebar, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let sidebar = markdown_rewrite_remote_links_opt(sidebar, context).await;
|
||||||
let icon = proxy_image_link_opt_apub(apub.icon.map(|i| i.url), context).await?;
|
let icon = proxy_image_link_opt_apub(apub.icon.map(|i| i.url), context).await?;
|
||||||
let banner = proxy_image_link_opt_apub(apub.image.map(|i| i.url), context).await?;
|
let banner = proxy_image_link_opt_apub(apub.image.map(|i| i.url), context).await?;
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ use super::verify_is_remote_object;
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::GetActorType,
|
activities::GetActorType,
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links_opt,
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -156,6 +157,7 @@ impl Object for ApubPerson {
|
||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
|
let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
|
||||||
let bio = process_markdown_opt(&bio, slur_regex, &url_blocklist, context).await?;
|
let bio = process_markdown_opt(&bio, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let bio = markdown_rewrite_remote_links_opt(bio, context).await;
|
||||||
let avatar = proxy_image_link_opt_apub(person.icon.map(|i| i.url), context).await?;
|
let avatar = proxy_image_link_opt_apub(person.icon.map(|i| i.url), context).await?;
|
||||||
let banner = proxy_image_link_opt_apub(person.image.map(|i| i.url), context).await?;
|
let banner = proxy_image_link_opt_apub(person.image.map(|i| i.url), context).await?;
|
||||||
|
|
||||||
|
@ -277,7 +279,7 @@ pub(crate) mod tests {
|
||||||
assert_eq!(person.name, "lanodan");
|
assert_eq!(person.name, "lanodan");
|
||||||
assert!(!person.local);
|
assert!(!person.local);
|
||||||
assert_eq!(context.request_count(), 0);
|
assert_eq!(context.request_count(), 0);
|
||||||
assert_eq!(person.bio.as_ref().map(std::string::String::len), Some(873));
|
assert_eq!(person.bio.as_ref().map(std::string::String::len), Some(812));
|
||||||
|
|
||||||
cleanup((person, site), &context).await?;
|
cleanup((person, site), &context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::{verify_is_public, verify_person_in_community},
|
activities::{verify_is_public, verify_person_in_community},
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::{markdown_rewrite_remote_links_opt, to_local_url},
|
||||||
local_site_data_cached,
|
local_site_data_cached,
|
||||||
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
||||||
protocol::{
|
protocol::{
|
||||||
|
@ -110,7 +111,7 @@ impl Object for ApubPost {
|
||||||
let creator = Person::read(&mut context.pool(), creator_id).await?;
|
let creator = Person::read(&mut context.pool(), creator_id).await?;
|
||||||
let community_id = self.community_id;
|
let community_id = self.community_id;
|
||||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||||
let language = LanguageTag::new_single(self.language_id, &mut context.pool()).await?;
|
let language = Some(LanguageTag::new_single(self.language_id, &mut context.pool()).await?);
|
||||||
|
|
||||||
let attachment = self
|
let attachment = self
|
||||||
.url
|
.url
|
||||||
|
@ -226,10 +227,13 @@ impl Object for ApubPost {
|
||||||
|
|
||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
|
|
||||||
if let Some(url) = &url {
|
let url = if let Some(url) = url {
|
||||||
is_url_blocked(url, &url_blocklist)?;
|
is_url_blocked(&url, &url_blocklist)?;
|
||||||
is_valid_url(url)?;
|
is_valid_url(&url)?;
|
||||||
}
|
to_local_url(url.as_str(), context).await.or(Some(url))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let alt_text = first_attachment.cloned().and_then(Attachment::alt_text);
|
let alt_text = first_attachment.cloned().and_then(Attachment::alt_text);
|
||||||
|
|
||||||
|
@ -237,8 +241,11 @@ impl Object for ApubPost {
|
||||||
|
|
||||||
let body = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source);
|
let body = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source);
|
||||||
let body = process_markdown_opt(&body, slur_regex, &url_blocklist, context).await?;
|
let body = process_markdown_opt(&body, slur_regex, &url_blocklist, context).await?;
|
||||||
let language_id =
|
let body = markdown_rewrite_remote_links_opt(body, context).await;
|
||||||
LanguageTag::to_language_id_single(page.language, &mut context.pool()).await?;
|
let language_id = Some(
|
||||||
|
LanguageTag::to_language_id_single(page.language.unwrap_or_default(), &mut context.pool())
|
||||||
|
.await?,
|
||||||
|
);
|
||||||
|
|
||||||
let form = PostInsertForm {
|
let form = PostInsertForm {
|
||||||
url: url.map(Into::into),
|
url: url.map(Into::into),
|
||||||
|
@ -301,7 +308,7 @@ mod tests {
|
||||||
assert_eq!(post.body.as_ref().map(std::string::String::len), Some(45));
|
assert_eq!(post.body.as_ref().map(std::string::String::len), Some(45));
|
||||||
assert!(!post.locked);
|
assert!(!post.locked);
|
||||||
assert!(!post.featured_community);
|
assert!(!post.featured_community);
|
||||||
assert_eq!(context.request_count(), 0);
|
assert_eq!(context.request_count(), 1);
|
||||||
|
|
||||||
Post::delete(&mut context.pool(), post.id).await?;
|
Post::delete(&mut context.pool(), post.id).await?;
|
||||||
Person::delete(&mut context.pool(), person.id).await?;
|
Person::delete(&mut context.pool(), person.id).await?;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use super::verify_is_remote_object;
|
use super::verify_is_remote_object;
|
||||||
use crate::{
|
use crate::{
|
||||||
check_apub_id_valid_with_strictness,
|
check_apub_id_valid_with_strictness,
|
||||||
|
fetcher::markdown_links::markdown_rewrite_remote_links,
|
||||||
objects::read_from_string_or_source,
|
objects::read_from_string_or_source,
|
||||||
protocol::{
|
protocol::{
|
||||||
objects::chat_message::{ChatMessage, ChatMessageType},
|
objects::chat_message::{ChatMessage, ChatMessageType},
|
||||||
|
@ -134,6 +135,7 @@ impl Object for ApubPrivateMessage {
|
||||||
let url_blocklist = get_url_blocklist(context).await?;
|
let url_blocklist = get_url_blocklist(context).await?;
|
||||||
let content = read_from_string_or_source(¬e.content, &None, ¬e.source);
|
let content = read_from_string_or_source(¬e.content, &None, ¬e.source);
|
||||||
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
let content = process_markdown(&content, slur_regex, &url_blocklist, context).await?;
|
||||||
|
let content = markdown_rewrite_remote_links(content, context).await;
|
||||||
|
|
||||||
let form = PrivateMessageInsertForm {
|
let form = PrivateMessageInsertForm {
|
||||||
creator_id: creator.id,
|
creator_id: creator.id,
|
||||||
|
|
|
@ -30,21 +30,30 @@ pub(crate) struct LanguageTag {
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for LanguageTag {
|
||||||
|
fn default() -> Self {
|
||||||
|
LanguageTag {
|
||||||
|
identifier: "und".to_string(),
|
||||||
|
name: "Undetermined".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl LanguageTag {
|
impl LanguageTag {
|
||||||
pub(crate) async fn new_single(
|
pub(crate) async fn new_single(
|
||||||
lang: LanguageId,
|
lang: LanguageId,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<Option<LanguageTag>> {
|
) -> LemmyResult<LanguageTag> {
|
||||||
let lang = Language::read_from_id(pool, lang).await?;
|
let lang = Language::read_from_id(pool, lang).await?;
|
||||||
|
|
||||||
// undetermined
|
// undetermined
|
||||||
if lang.id == UNDETERMINED_ID {
|
if lang.id == UNDETERMINED_ID {
|
||||||
Ok(None)
|
Ok(LanguageTag::default())
|
||||||
} else {
|
} else {
|
||||||
Ok(Some(LanguageTag {
|
Ok(LanguageTag {
|
||||||
identifier: lang.code,
|
identifier: lang.code,
|
||||||
name: lang.name,
|
name: lang.name,
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,13 +78,10 @@ impl LanguageTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn to_language_id_single(
|
pub(crate) async fn to_language_id_single(
|
||||||
lang: Option<Self>,
|
lang: Self,
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> LemmyResult<Option<LanguageId>> {
|
) -> LemmyResult<LanguageId> {
|
||||||
let identifier = lang.map(|l| l.identifier);
|
Ok(Language::read_id_from_code(pool, &lang.identifier).await?)
|
||||||
let language = Language::read_id_from_code(pool, identifier.as_deref()).await?;
|
|
||||||
|
|
||||||
Ok(language)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn to_language_id_multiple(
|
pub(crate) async fn to_language_id_multiple(
|
||||||
|
@ -86,10 +92,10 @@ impl LanguageTag {
|
||||||
|
|
||||||
for l in langs {
|
for l in langs {
|
||||||
let id = l.identifier;
|
let id = l.identifier;
|
||||||
language_ids.push(Language::read_id_from_code(pool, Some(&id)).await?);
|
language_ids.push(Language::read_id_from_code(pool, &id).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(language_ids.into_iter().flatten().collect())
|
Ok(language_ids.into_iter().collect())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,10 +20,9 @@ use lemmy_db_schema::{
|
||||||
source::{community::Community, post::Post},
|
source::{community::Community, post::Post},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyResult;
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType, MAX_COMMENT_DEPTH_LIMIT};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::skip_serializing_none;
|
use serde_with::skip_serializing_none;
|
||||||
use std::ops::Deref;
|
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
|
@ -58,9 +57,19 @@ impl Note {
|
||||||
&self,
|
&self,
|
||||||
context: &Data<LemmyContext>,
|
context: &Data<LemmyContext>,
|
||||||
) -> LemmyResult<(ApubPost, Option<ApubComment>)> {
|
) -> LemmyResult<(ApubPost, Option<ApubComment>)> {
|
||||||
// Fetch parent comment chain in a box, otherwise it can cause a stack overflow.
|
// We use recursion here to fetch the entire comment chain up to the top-level parent. This is
|
||||||
let parent = Box::pin(self.in_reply_to.dereference(context).await?);
|
// necessary because we need to know the post and parent comment in order to insert a new
|
||||||
match parent.deref() {
|
// comment. However it can also lead to stack overflow when fetching many comments recursively.
|
||||||
|
// To avoid this we check the request count against max comment depth, which based on testing
|
||||||
|
// can be handled without risking stack overflow. This is not a perfect solution, because in
|
||||||
|
// some cases we have to fetch user profiles too, and reach the limit after only 25 comments
|
||||||
|
// or so.
|
||||||
|
// A cleaner solution would be converting the recursion into a loop, but that is tricky.
|
||||||
|
if context.request_count() > MAX_COMMENT_DEPTH_LIMIT as u32 {
|
||||||
|
Err(LemmyErrorType::MaxCommentDepthReached)?;
|
||||||
|
}
|
||||||
|
let parent = self.in_reply_to.dereference(context).await?;
|
||||||
|
match parent {
|
||||||
PostOrComment::Post(p) => Ok((p.clone(), None)),
|
PostOrComment::Post(p) => Ok((p.clone(), None)),
|
||||||
PostOrComment::Comment(c) => {
|
PostOrComment::Comment(c) => {
|
||||||
let post_id = c.post_id;
|
let post_id = c.post_id;
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl<S: ValidGrouping<(), IsAggregate = is_aggregate::No>> ValidGrouping<()>
|
||||||
type IsAggregate = is_aggregate::No;
|
type IsAggregate = is_aggregate::No;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_camel_case_types)]
|
#[expect(non_camel_case_types)]
|
||||||
#[derive(QueryId, Clone, Copy, Debug)]
|
#[derive(QueryId, Clone, Copy, Debug)]
|
||||||
pub struct current_value;
|
pub struct current_value;
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,6 @@ impl CommentAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -46,26 +44,25 @@ mod tests {
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_comment_agg");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_comment_agg");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_comment_agg");
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_comment_agg");
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -73,21 +70,21 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
let child_comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -95,9 +92,7 @@ mod tests {
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let _inserted_child_comment =
|
let _inserted_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let comment_like = CommentLikeForm {
|
let comment_like = CommentLikeForm {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
|
@ -106,11 +101,9 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
CommentLike::like(pool, &comment_like).await.unwrap();
|
CommentLike::like(pool, &comment_like).await?;
|
||||||
|
|
||||||
let comment_aggs_before_delete = CommentAggregates::read(pool, inserted_comment.id)
|
let comment_aggs_before_delete = CommentAggregates::read(pool, inserted_comment.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(1, comment_aggs_before_delete.score);
|
assert_eq!(1, comment_aggs_before_delete.score);
|
||||||
assert_eq!(1, comment_aggs_before_delete.upvotes);
|
assert_eq!(1, comment_aggs_before_delete.upvotes);
|
||||||
|
@ -124,47 +117,39 @@ mod tests {
|
||||||
score: -1,
|
score: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
CommentLike::like(pool, &comment_dislike).await.unwrap();
|
CommentLike::like(pool, &comment_dislike).await?;
|
||||||
|
|
||||||
let comment_aggs_after_dislike = CommentAggregates::read(pool, inserted_comment.id)
|
let comment_aggs_after_dislike = CommentAggregates::read(pool, inserted_comment.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(0, comment_aggs_after_dislike.score);
|
assert_eq!(0, comment_aggs_after_dislike.score);
|
||||||
assert_eq!(1, comment_aggs_after_dislike.upvotes);
|
assert_eq!(1, comment_aggs_after_dislike.upvotes);
|
||||||
assert_eq!(1, comment_aggs_after_dislike.downvotes);
|
assert_eq!(1, comment_aggs_after_dislike.downvotes);
|
||||||
|
|
||||||
// Remove the first comment like
|
// Remove the first comment like
|
||||||
CommentLike::remove(pool, inserted_person.id, inserted_comment.id)
|
CommentLike::remove(pool, inserted_person.id, inserted_comment.id).await?;
|
||||||
.await
|
let after_like_remove = CommentAggregates::read(pool, inserted_comment.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_like_remove = CommentAggregates::read(pool, inserted_comment.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(-1, after_like_remove.score);
|
assert_eq!(-1, after_like_remove.score);
|
||||||
assert_eq!(0, after_like_remove.upvotes);
|
assert_eq!(0, after_like_remove.upvotes);
|
||||||
assert_eq!(1, after_like_remove.downvotes);
|
assert_eq!(1, after_like_remove.downvotes);
|
||||||
|
|
||||||
// Remove the parent post
|
// Remove the parent post
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
// Should be none found, since the post was deleted
|
// Should be none found, since the post was deleted
|
||||||
let after_delete = CommentAggregates::read(pool, inserted_comment.id).await;
|
let after_delete = CommentAggregates::read(pool, inserted_comment.id).await;
|
||||||
assert!(after_delete.is_err());
|
assert!(after_delete.is_err());
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
// This should delete all the associated rows, and fire triggers
|
||||||
Person::delete(pool, another_inserted_person.id)
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
.await
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
.unwrap();
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
|
|
||||||
assert_eq!(1, person_num_deleted);
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
// Delete the community
|
// Delete the community
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id)
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, community_num_deleted);
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::structs::CommunityAggregates,
|
aggregates::structs::CommunityAggregates,
|
||||||
diesel::OptionalExtension,
|
|
||||||
newtypes::CommunityId,
|
newtypes::CommunityId,
|
||||||
schema::{community_aggregates, community_aggregates::subscribers},
|
schema::{community_aggregates, community_aggregates::subscribers},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -9,16 +8,12 @@ use diesel::{result::Error, ExpressionMethods, QueryDsl};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
impl CommunityAggregates {
|
impl CommunityAggregates {
|
||||||
pub async fn read(
|
pub async fn read(pool: &mut DbPool<'_>, for_community_id: CommunityId) -> Result<Self, Error> {
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
for_community_id: CommunityId,
|
|
||||||
) -> Result<Option<Self>, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
community_aggregates::table
|
community_aggregates::table
|
||||||
.find(for_community_id)
|
.find(for_community_id)
|
||||||
.first(conn)
|
.first(conn)
|
||||||
.await
|
.await
|
||||||
.optional()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update_federated_followers(
|
pub async fn update_federated_followers(
|
||||||
|
@ -36,8 +31,6 @@ impl CommunityAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -52,26 +45,25 @@ mod tests {
|
||||||
traits::{Crud, Followable},
|
traits::{Crud, Followable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -79,7 +71,7 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let another_community = CommunityInsertForm::new(
|
let another_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -87,7 +79,7 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let another_inserted_community = Community::create(pool, &another_community).await.unwrap();
|
let another_inserted_community = Community::create(pool, &another_community).await?;
|
||||||
|
|
||||||
let first_person_follow = CommunityFollowerForm {
|
let first_person_follow = CommunityFollowerForm {
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
|
@ -95,9 +87,7 @@ mod tests {
|
||||||
pending: false,
|
pending: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
CommunityFollower::follow(pool, &first_person_follow)
|
CommunityFollower::follow(pool, &first_person_follow).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let second_person_follow = CommunityFollowerForm {
|
let second_person_follow = CommunityFollowerForm {
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
|
@ -105,9 +95,7 @@ mod tests {
|
||||||
pending: false,
|
pending: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
CommunityFollower::follow(pool, &second_person_follow)
|
CommunityFollower::follow(pool, &second_person_follow).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let another_community_follow = CommunityFollowerForm {
|
let another_community_follow = CommunityFollowerForm {
|
||||||
community_id: another_inserted_community.id,
|
community_id: another_inserted_community.id,
|
||||||
|
@ -115,23 +103,21 @@ mod tests {
|
||||||
pending: false,
|
pending: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
CommunityFollower::follow(pool, &another_community_follow)
|
CommunityFollower::follow(pool, &another_community_follow).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
let child_comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -139,14 +125,10 @@ mod tests {
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let _inserted_child_comment =
|
let _inserted_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let community_aggregates_before_delete = CommunityAggregates::read(pool, inserted_community.id)
|
let community_aggregates_before_delete =
|
||||||
.await
|
CommunityAggregates::read(pool, inserted_community.id).await?;
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(2, community_aggregates_before_delete.subscribers);
|
assert_eq!(2, community_aggregates_before_delete.subscribers);
|
||||||
assert_eq!(2, community_aggregates_before_delete.subscribers_local);
|
assert_eq!(2, community_aggregates_before_delete.subscribers_local);
|
||||||
|
@ -154,76 +136,53 @@ mod tests {
|
||||||
assert_eq!(2, community_aggregates_before_delete.comments);
|
assert_eq!(2, community_aggregates_before_delete.comments);
|
||||||
|
|
||||||
// Test the other community
|
// Test the other community
|
||||||
let another_community_aggs = CommunityAggregates::read(pool, another_inserted_community.id)
|
let another_community_aggs =
|
||||||
.await
|
CommunityAggregates::read(pool, another_inserted_community.id).await?;
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, another_community_aggs.subscribers);
|
assert_eq!(1, another_community_aggs.subscribers);
|
||||||
assert_eq!(1, another_community_aggs.subscribers_local);
|
assert_eq!(1, another_community_aggs.subscribers_local);
|
||||||
assert_eq!(0, another_community_aggs.posts);
|
assert_eq!(0, another_community_aggs.posts);
|
||||||
assert_eq!(0, another_community_aggs.comments);
|
assert_eq!(0, another_community_aggs.comments);
|
||||||
|
|
||||||
// Unfollow test
|
// Unfollow test
|
||||||
CommunityFollower::unfollow(pool, &second_person_follow)
|
CommunityFollower::unfollow(pool, &second_person_follow).await?;
|
||||||
.await
|
let after_unfollow = CommunityAggregates::read(pool, inserted_community.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_unfollow = CommunityAggregates::read(pool, inserted_community.id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, after_unfollow.subscribers);
|
assert_eq!(1, after_unfollow.subscribers);
|
||||||
assert_eq!(1, after_unfollow.subscribers_local);
|
assert_eq!(1, after_unfollow.subscribers_local);
|
||||||
|
|
||||||
// Follow again just for the later tests
|
// Follow again just for the later tests
|
||||||
CommunityFollower::follow(pool, &second_person_follow)
|
CommunityFollower::follow(pool, &second_person_follow).await?;
|
||||||
.await
|
let after_follow_again = CommunityAggregates::read(pool, inserted_community.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_follow_again = CommunityAggregates::read(pool, inserted_community.id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(2, after_follow_again.subscribers);
|
assert_eq!(2, after_follow_again.subscribers);
|
||||||
assert_eq!(2, after_follow_again.subscribers_local);
|
assert_eq!(2, after_follow_again.subscribers_local);
|
||||||
|
|
||||||
// Remove a parent post (the comment count should also be 0)
|
// Remove a parent post (the comment count should also be 0)
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
let after_parent_post_delete = CommunityAggregates::read(pool, inserted_community.id)
|
let after_parent_post_delete = CommunityAggregates::read(pool, inserted_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(0, after_parent_post_delete.comments);
|
assert_eq!(0, after_parent_post_delete.comments);
|
||||||
assert_eq!(0, after_parent_post_delete.posts);
|
assert_eq!(0, after_parent_post_delete.posts);
|
||||||
|
|
||||||
// Remove the 2nd person
|
// Remove the 2nd person
|
||||||
Person::delete(pool, another_inserted_person.id)
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
.await
|
let after_person_delete = CommunityAggregates::read(pool, inserted_community.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_person_delete = CommunityAggregates::read(pool, inserted_community.id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, after_person_delete.subscribers);
|
assert_eq!(1, after_person_delete.subscribers);
|
||||||
assert_eq!(1, after_person_delete.subscribers_local);
|
assert_eq!(1, after_person_delete.subscribers_local);
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
// This should delete all the associated rows, and fire triggers
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
assert_eq!(1, person_num_deleted);
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
// Delete the community
|
// Delete the community
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id)
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, community_num_deleted);
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
let another_community_num_deleted = Community::delete(pool, another_inserted_community.id)
|
let another_community_num_deleted =
|
||||||
.await
|
Community::delete(pool, another_inserted_community.id).await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, another_community_num_deleted);
|
assert_eq!(1, another_community_num_deleted);
|
||||||
|
|
||||||
// Should be none found, since the creator was deleted
|
// Should be none found, since the creator was deleted
|
||||||
let after_delete = CommunityAggregates::read(pool, inserted_community.id)
|
let after_delete = CommunityAggregates::read(pool, inserted_community.id).await;
|
||||||
.await
|
assert!(after_delete.is_err());
|
||||||
.unwrap();
|
|
||||||
assert!(after_delete.is_none());
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
pub(crate) use crate::diesel::OptionalExtension;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::structs::PersonAggregates,
|
aggregates::structs::PersonAggregates,
|
||||||
newtypes::PersonId,
|
newtypes::PersonId,
|
||||||
|
@ -9,19 +8,13 @@ use diesel::{result::Error, QueryDsl};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
impl PersonAggregates {
|
impl PersonAggregates {
|
||||||
pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Option<Self>, Error> {
|
pub async fn read(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
person_aggregates::table
|
person_aggregates::table.find(person_id).first(conn).await
|
||||||
.find(person_id)
|
|
||||||
.first(conn)
|
|
||||||
.await
|
|
||||||
.optional()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -36,26 +29,25 @@ mod tests {
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_user_agg");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_user_agg");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_user_agg");
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_user_agg");
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -64,28 +56,28 @@ mod tests {
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let post_like = PostLikeForm {
|
let post_like = PostLikeForm {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
person_id: inserted_person.id,
|
person_id: inserted_person.id,
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
let _inserted_post_like = PostLike::like(pool, &post_like).await.unwrap();
|
let _inserted_post_like = PostLike::like(pool, &post_like).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let mut comment_like = CommentLikeForm {
|
let mut comment_like = CommentLikeForm {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
|
@ -94,7 +86,7 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _inserted_comment_like = CommentLike::like(pool, &comment_like).await.unwrap();
|
let _inserted_comment_like = CommentLike::like(pool, &comment_like).await?;
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
let child_comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -102,9 +94,7 @@ mod tests {
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_child_comment =
|
let inserted_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let child_comment_like = CommentLikeForm {
|
let child_comment_like = CommentLikeForm {
|
||||||
comment_id: inserted_child_comment.id,
|
comment_id: inserted_child_comment.id,
|
||||||
|
@ -113,12 +103,9 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _inserted_child_comment_like = CommentLike::like(pool, &child_comment_like).await.unwrap();
|
let _inserted_child_comment_like = CommentLike::like(pool, &child_comment_like).await?;
|
||||||
|
|
||||||
let person_aggregates_before_delete = PersonAggregates::read(pool, inserted_person.id)
|
let person_aggregates_before_delete = PersonAggregates::read(pool, inserted_person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(1, person_aggregates_before_delete.post_count);
|
assert_eq!(1, person_aggregates_before_delete.post_count);
|
||||||
assert_eq!(1, person_aggregates_before_delete.post_score);
|
assert_eq!(1, person_aggregates_before_delete.post_score);
|
||||||
|
@ -126,13 +113,8 @@ mod tests {
|
||||||
assert_eq!(2, person_aggregates_before_delete.comment_score);
|
assert_eq!(2, person_aggregates_before_delete.comment_score);
|
||||||
|
|
||||||
// Remove a post like
|
// Remove a post like
|
||||||
PostLike::remove(pool, inserted_person.id, inserted_post.id)
|
PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
||||||
.await
|
let after_post_like_remove = PersonAggregates::read(pool, inserted_person.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_post_like_remove = PersonAggregates::read(pool, inserted_person.id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(0, after_post_like_remove.post_score);
|
assert_eq!(0, after_post_like_remove.post_score);
|
||||||
|
|
||||||
Comment::update(
|
Comment::update(
|
||||||
|
@ -143,8 +125,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
Comment::update(
|
Comment::update(
|
||||||
pool,
|
pool,
|
||||||
inserted_child_comment.id,
|
inserted_child_comment.id,
|
||||||
|
@ -153,51 +134,34 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let after_parent_comment_removed = PersonAggregates::read(pool, inserted_person.id)
|
let after_parent_comment_removed = PersonAggregates::read(pool, inserted_person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(0, after_parent_comment_removed.comment_count);
|
assert_eq!(0, after_parent_comment_removed.comment_count);
|
||||||
// TODO: fix person aggregate comment score calculation
|
// TODO: fix person aggregate comment score calculation
|
||||||
// assert_eq!(0, after_parent_comment_removed.comment_score);
|
// assert_eq!(0, after_parent_comment_removed.comment_score);
|
||||||
|
|
||||||
// Remove a parent comment (the scores should also be removed)
|
// Remove a parent comment (the scores should also be removed)
|
||||||
Comment::delete(pool, inserted_comment.id).await.unwrap();
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
Comment::delete(pool, inserted_child_comment.id)
|
Comment::delete(pool, inserted_child_comment.id).await?;
|
||||||
.await
|
let after_parent_comment_delete = PersonAggregates::read(pool, inserted_person.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_parent_comment_delete = PersonAggregates::read(pool, inserted_person.id)
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(0, after_parent_comment_delete.comment_count);
|
assert_eq!(0, after_parent_comment_delete.comment_count);
|
||||||
// TODO: fix person aggregate comment score calculation
|
// TODO: fix person aggregate comment score calculation
|
||||||
// assert_eq!(0, after_parent_comment_delete.comment_score);
|
// assert_eq!(0, after_parent_comment_delete.comment_score);
|
||||||
|
|
||||||
// Add in the two comments again, then delete the post.
|
// Add in the two comments again, then delete the post.
|
||||||
let new_parent_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let new_parent_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
let _new_child_comment =
|
let _new_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&new_parent_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
comment_like.comment_id = new_parent_comment.id;
|
comment_like.comment_id = new_parent_comment.id;
|
||||||
CommentLike::like(pool, &comment_like).await.unwrap();
|
CommentLike::like(pool, &comment_like).await?;
|
||||||
let after_comment_add = PersonAggregates::read(pool, inserted_person.id)
|
let after_comment_add = PersonAggregates::read(pool, inserted_person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(2, after_comment_add.comment_count);
|
assert_eq!(2, after_comment_add.comment_count);
|
||||||
// TODO: fix person aggregate comment score calculation
|
// TODO: fix person aggregate comment score calculation
|
||||||
// assert_eq!(1, after_comment_add.comment_score);
|
// assert_eq!(1, after_comment_add.comment_score);
|
||||||
|
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
let after_post_delete = PersonAggregates::read(pool, inserted_person.id)
|
let after_post_delete = PersonAggregates::read(pool, inserted_person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
// TODO: fix person aggregate comment score calculation
|
// TODO: fix person aggregate comment score calculation
|
||||||
// assert_eq!(0, after_post_delete.comment_score);
|
// assert_eq!(0, after_post_delete.comment_score);
|
||||||
assert_eq!(0, after_post_delete.comment_count);
|
assert_eq!(0, after_post_delete.comment_count);
|
||||||
|
@ -205,24 +169,20 @@ mod tests {
|
||||||
assert_eq!(0, after_post_delete.post_count);
|
assert_eq!(0, after_post_delete.post_count);
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
// This should delete all the associated rows, and fire triggers
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
assert_eq!(1, person_num_deleted);
|
assert_eq!(1, person_num_deleted);
|
||||||
Person::delete(pool, another_inserted_person.id)
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Delete the community
|
// Delete the community
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id)
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, community_num_deleted);
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
// Should be none found
|
// Should be none found
|
||||||
let after_delete = PersonAggregates::read(pool, inserted_person.id)
|
let after_delete = PersonAggregates::read(pool, inserted_person.id).await;
|
||||||
.await
|
assert!(after_delete.is_err());
|
||||||
.unwrap();
|
|
||||||
assert!(after_delete.is_none());
|
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,8 +49,6 @@ impl PostAggregates {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -65,26 +63,25 @@ mod tests {
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
let another_person = PersonInsertForm::test_form(inserted_instance.id, "jerry_community_agg");
|
||||||
|
|
||||||
let another_inserted_person = Person::create(pool, &another_person).await.unwrap();
|
let another_inserted_person = Person::create(pool, &another_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -92,21 +89,21 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
let child_comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -114,9 +111,7 @@ mod tests {
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_child_comment =
|
let inserted_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let post_like = PostLikeForm {
|
let post_like = PostLikeForm {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -124,9 +119,9 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
PostLike::like(pool, &post_like).await.unwrap();
|
PostLike::like(pool, &post_like).await?;
|
||||||
|
|
||||||
let post_aggs_before_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
let post_aggs_before_delete = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
assert_eq!(2, post_aggs_before_delete.comments);
|
assert_eq!(2, post_aggs_before_delete.comments);
|
||||||
assert_eq!(1, post_aggs_before_delete.score);
|
assert_eq!(1, post_aggs_before_delete.score);
|
||||||
|
@ -140,9 +135,9 @@ mod tests {
|
||||||
score: -1,
|
score: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
PostLike::like(pool, &post_dislike).await.unwrap();
|
PostLike::like(pool, &post_dislike).await?;
|
||||||
|
|
||||||
let post_aggs_after_dislike = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
let post_aggs_after_dislike = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
assert_eq!(2, post_aggs_after_dislike.comments);
|
assert_eq!(2, post_aggs_after_dislike.comments);
|
||||||
assert_eq!(0, post_aggs_after_dislike.score);
|
assert_eq!(0, post_aggs_after_dislike.score);
|
||||||
|
@ -150,59 +145,51 @@ mod tests {
|
||||||
assert_eq!(1, post_aggs_after_dislike.downvotes);
|
assert_eq!(1, post_aggs_after_dislike.downvotes);
|
||||||
|
|
||||||
// Remove the comments
|
// Remove the comments
|
||||||
Comment::delete(pool, inserted_comment.id).await.unwrap();
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
Comment::delete(pool, inserted_child_comment.id)
|
Comment::delete(pool, inserted_child_comment.id).await?;
|
||||||
.await
|
let after_comment_delete = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_comment_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
|
||||||
assert_eq!(0, after_comment_delete.comments);
|
assert_eq!(0, after_comment_delete.comments);
|
||||||
assert_eq!(0, after_comment_delete.score);
|
assert_eq!(0, after_comment_delete.score);
|
||||||
assert_eq!(1, after_comment_delete.upvotes);
|
assert_eq!(1, after_comment_delete.upvotes);
|
||||||
assert_eq!(1, after_comment_delete.downvotes);
|
assert_eq!(1, after_comment_delete.downvotes);
|
||||||
|
|
||||||
// Remove the first post like
|
// Remove the first post like
|
||||||
PostLike::remove(pool, inserted_person.id, inserted_post.id)
|
PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
||||||
.await
|
let after_like_remove = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
.unwrap();
|
|
||||||
let after_like_remove = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
|
||||||
assert_eq!(0, after_like_remove.comments);
|
assert_eq!(0, after_like_remove.comments);
|
||||||
assert_eq!(-1, after_like_remove.score);
|
assert_eq!(-1, after_like_remove.score);
|
||||||
assert_eq!(0, after_like_remove.upvotes);
|
assert_eq!(0, after_like_remove.upvotes);
|
||||||
assert_eq!(1, after_like_remove.downvotes);
|
assert_eq!(1, after_like_remove.downvotes);
|
||||||
|
|
||||||
// This should delete all the associated rows, and fire triggers
|
// This should delete all the associated rows, and fire triggers
|
||||||
Person::delete(pool, another_inserted_person.id)
|
Person::delete(pool, another_inserted_person.id).await?;
|
||||||
.await
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
.unwrap();
|
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
|
|
||||||
assert_eq!(1, person_num_deleted);
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
// Delete the community
|
// Delete the community
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id)
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, community_num_deleted);
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
// Should be none found, since the creator was deleted
|
// Should be none found, since the creator was deleted
|
||||||
let after_delete = PostAggregates::read(pool, inserted_post.id).await;
|
let after_delete = PostAggregates::read(pool, inserted_post.id).await;
|
||||||
assert!(after_delete.is_err());
|
assert!(after_delete.is_err());
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_soft_delete() {
|
async fn test_soft_delete() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_community_agg");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -210,14 +197,14 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -225,9 +212,9 @@ mod tests {
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let post_aggregates_before = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
let post_aggregates_before = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
assert_eq!(1, post_aggregates_before.comments);
|
assert_eq!(1, post_aggregates_before.comments);
|
||||||
|
|
||||||
Comment::update(
|
Comment::update(
|
||||||
|
@ -238,10 +225,9 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let post_aggregates_after_remove = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
let post_aggregates_after_remove = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
assert_eq!(0, post_aggregates_after_remove.comments);
|
assert_eq!(0, post_aggregates_after_remove.comments);
|
||||||
|
|
||||||
Comment::update(
|
Comment::update(
|
||||||
|
@ -252,8 +238,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Comment::update(
|
Comment::update(
|
||||||
pool,
|
pool,
|
||||||
|
@ -263,10 +248,9 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let post_aggregates_after_delete = PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
let post_aggregates_after_delete = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
assert_eq!(0, post_aggregates_after_delete.comments);
|
assert_eq!(0, post_aggregates_after_delete.comments);
|
||||||
|
|
||||||
Comment::update(
|
Comment::update(
|
||||||
|
@ -277,19 +261,17 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let post_aggregates_after_delete_remove =
|
let post_aggregates_after_delete_remove = PostAggregates::read(pool, inserted_post.id).await?;
|
||||||
PostAggregates::read(pool, inserted_post.id).await.unwrap();
|
|
||||||
assert_eq!(0, post_aggregates_after_delete_remove.comments);
|
assert_eq!(0, post_aggregates_after_delete_remove.comments);
|
||||||
|
|
||||||
Comment::delete(pool, inserted_comment.id).await.unwrap();
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
Person::delete(pool, inserted_person.id).await.unwrap();
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
Community::delete(pool, inserted_community.id)
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
aggregates::structs::SiteAggregates,
|
aggregates::structs::SiteAggregates,
|
||||||
diesel::OptionalExtension,
|
|
||||||
schema::site_aggregates,
|
schema::site_aggregates,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
|
@ -8,15 +7,13 @@ use diesel::result::Error;
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
impl SiteAggregates {
|
impl SiteAggregates {
|
||||||
pub async fn read(pool: &mut DbPool<'_>) -> Result<Option<Self>, Error> {
|
pub async fn read(pool: &mut DbPool<'_>) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
site_aggregates::table.first(conn).await.optional()
|
site_aggregates::table.first(conn).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -32,22 +29,21 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{build_db_pool_for_tests, DbPool},
|
utils::{build_db_pool_for_tests, DbPool},
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
async fn prepare_site_with_community(
|
async fn prepare_site_with_community(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
) -> (Instance, Person, Site, Community) {
|
) -> Result<(Instance, Person, Site, Community), Error> {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_site_agg");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "thommy_site_agg");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let site_form = SiteInsertForm::new("test_site".into(), inserted_instance.id);
|
let site_form = SiteInsertForm::new("test_site".into(), inserted_instance.id);
|
||||||
let inserted_site = Site::create(pool, &site_form).await.unwrap();
|
let inserted_site = Site::create(pool, &site_form).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -56,23 +52,24 @@ mod tests {
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
(
|
|
||||||
|
Ok((
|
||||||
inserted_instance,
|
inserted_instance,
|
||||||
inserted_person,
|
inserted_person,
|
||||||
inserted_site,
|
inserted_site,
|
||||||
inserted_community,
|
inserted_community,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
||||||
prepare_site_with_community(pool).await;
|
prepare_site_with_community(pool).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
|
@ -81,8 +78,8 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert two of those posts
|
// Insert two of those posts
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
let _inserted_post_again = Post::create(pool, &new_post).await.unwrap();
|
let _inserted_post_again = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -91,7 +88,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Insert two of those comments
|
// Insert two of those comments
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let child_comment_form = CommentInsertForm::new(
|
let child_comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
|
@ -99,11 +96,9 @@ mod tests {
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let _inserted_child_comment =
|
let _inserted_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let site_aggregates_before_delete = SiteAggregates::read(pool).await.unwrap().unwrap();
|
let site_aggregates_before_delete = SiteAggregates::read(pool).await?;
|
||||||
|
|
||||||
// TODO: this is unstable, sometimes it returns 0 users, sometimes 1
|
// TODO: this is unstable, sometimes it returns 0 users, sometimes 1
|
||||||
//assert_eq!(0, site_aggregates_before_delete.users);
|
//assert_eq!(0, site_aggregates_before_delete.users);
|
||||||
|
@ -112,42 +107,42 @@ mod tests {
|
||||||
assert_eq!(2, site_aggregates_before_delete.comments);
|
assert_eq!(2, site_aggregates_before_delete.comments);
|
||||||
|
|
||||||
// Try a post delete
|
// Try a post delete
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
let site_aggregates_after_post_delete = SiteAggregates::read(pool).await.unwrap().unwrap();
|
let site_aggregates_after_post_delete = SiteAggregates::read(pool).await?;
|
||||||
assert_eq!(1, site_aggregates_after_post_delete.posts);
|
assert_eq!(1, site_aggregates_after_post_delete.posts);
|
||||||
assert_eq!(0, site_aggregates_after_post_delete.comments);
|
assert_eq!(0, site_aggregates_after_post_delete.comments);
|
||||||
|
|
||||||
// This shouuld delete all the associated rows, and fire triggers
|
// This shouuld delete all the associated rows, and fire triggers
|
||||||
let person_num_deleted = Person::delete(pool, inserted_person.id).await.unwrap();
|
let person_num_deleted = Person::delete(pool, inserted_person.id).await?;
|
||||||
assert_eq!(1, person_num_deleted);
|
assert_eq!(1, person_num_deleted);
|
||||||
|
|
||||||
// Delete the community
|
// Delete the community
|
||||||
let community_num_deleted = Community::delete(pool, inserted_community.id)
|
let community_num_deleted = Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, community_num_deleted);
|
assert_eq!(1, community_num_deleted);
|
||||||
|
|
||||||
// Site should still exist, it can without a site creator.
|
// Site should still exist, it can without a site creator.
|
||||||
let after_delete_creator = SiteAggregates::read(pool).await;
|
let after_delete_creator = SiteAggregates::read(pool).await;
|
||||||
assert!(after_delete_creator.is_ok());
|
assert!(after_delete_creator.is_ok());
|
||||||
|
|
||||||
Site::delete(pool, inserted_site.id).await.unwrap();
|
Site::delete(pool, inserted_site.id).await?;
|
||||||
let after_delete_site = SiteAggregates::read(pool).await.unwrap();
|
let after_delete_site = SiteAggregates::read(pool).await;
|
||||||
assert!(after_delete_site.is_none());
|
assert!(after_delete_site.is_err());
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_soft_delete() {
|
async fn test_soft_delete() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
let (inserted_instance, inserted_person, inserted_site, inserted_community) =
|
||||||
prepare_site_with_community(pool).await;
|
prepare_site_with_community(pool).await?;
|
||||||
|
|
||||||
let site_aggregates_before = SiteAggregates::read(pool).await.unwrap().unwrap();
|
let site_aggregates_before = SiteAggregates::read(pool).await?;
|
||||||
assert_eq!(1, site_aggregates_before.communities);
|
assert_eq!(1, site_aggregates_before.communities);
|
||||||
|
|
||||||
Community::update(
|
Community::update(
|
||||||
|
@ -158,10 +153,9 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let site_aggregates_after_delete = SiteAggregates::read(pool).await.unwrap().unwrap();
|
let site_aggregates_after_delete = SiteAggregates::read(pool).await?;
|
||||||
assert_eq!(0, site_aggregates_after_delete.communities);
|
assert_eq!(0, site_aggregates_after_delete.communities);
|
||||||
|
|
||||||
Community::update(
|
Community::update(
|
||||||
|
@ -172,8 +166,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Community::update(
|
Community::update(
|
||||||
pool,
|
pool,
|
||||||
|
@ -183,10 +176,9 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let site_aggregates_after_remove = SiteAggregates::read(pool).await.unwrap().unwrap();
|
let site_aggregates_after_remove = SiteAggregates::read(pool).await?;
|
||||||
assert_eq!(0, site_aggregates_after_remove.communities);
|
assert_eq!(0, site_aggregates_after_remove.communities);
|
||||||
|
|
||||||
Community::update(
|
Community::update(
|
||||||
|
@ -197,17 +189,16 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let site_aggregates_after_remove_delete = SiteAggregates::read(pool).await.unwrap().unwrap();
|
let site_aggregates_after_remove_delete = SiteAggregates::read(pool).await?;
|
||||||
assert_eq!(0, site_aggregates_after_remove_delete.communities);
|
assert_eq!(0, site_aggregates_after_remove_delete.communities);
|
||||||
|
|
||||||
Community::delete(pool, inserted_community.id)
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
Site::delete(pool, inserted_site.id).await?;
|
||||||
.unwrap();
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
Site::delete(pool, inserted_site.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
Person::delete(pool, inserted_person.id).await.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,12 +58,11 @@ impl ReceivedActivity {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{source::activity::ActorType, utils::build_db_pool_for_tests};
|
use crate::{source::activity::ActorType, utils::build_db_pool_for_tests};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
@ -71,26 +70,25 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn receive_activity_duplicate() {
|
async fn receive_activity_duplicate() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let ap_id: DbUrl = Url::parse("http://example.com/activity/531")
|
let ap_id: DbUrl = Url::parse("http://example.com/activity/531")?.into();
|
||||||
.unwrap()
|
|
||||||
.into();
|
|
||||||
|
|
||||||
// inserting activity should only work once
|
// inserting activity should only work once
|
||||||
ReceivedActivity::create(pool, &ap_id).await.unwrap();
|
ReceivedActivity::create(pool, &ap_id).await?;
|
||||||
ReceivedActivity::create(pool, &ap_id).await.unwrap_err();
|
let second = ReceivedActivity::create(pool, &ap_id).await;
|
||||||
|
assert!(second.is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn sent_activity_write_read() {
|
async fn sent_activity_write_read() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let ap_id: DbUrl = Url::parse("http://example.com/activity/412")
|
let ap_id: DbUrl = Url::parse("http://example.com/activity/412")?.into();
|
||||||
.unwrap()
|
|
||||||
.into();
|
|
||||||
let data = json!({
|
let data = json!({
|
||||||
"key1": "0xF9BA143B95FF6D82",
|
"key1": "0xF9BA143B95FF6D82",
|
||||||
"key2": "42",
|
"key2": "42",
|
||||||
|
@ -101,20 +99,20 @@ mod tests {
|
||||||
ap_id: ap_id.clone(),
|
ap_id: ap_id.clone(),
|
||||||
data: data.clone(),
|
data: data.clone(),
|
||||||
sensitive,
|
sensitive,
|
||||||
actor_apub_id: Url::parse("http://example.com/u/exampleuser")
|
actor_apub_id: Url::parse("http://example.com/u/exampleuser")?.into(),
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
actor_type: ActorType::Person,
|
actor_type: ActorType::Person,
|
||||||
send_all_instances: false,
|
send_all_instances: false,
|
||||||
send_community_followers_of: None,
|
send_community_followers_of: None,
|
||||||
send_inboxes: vec![],
|
send_inboxes: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
SentActivity::create(pool, form).await.unwrap();
|
SentActivity::create(pool, form).await?;
|
||||||
|
|
||||||
let res = SentActivity::read_from_apub_id(pool, &ap_id).await.unwrap();
|
let res = SentActivity::read_from_apub_id(pool, &ap_id).await?;
|
||||||
assert_eq!(res.ap_id, ap_id);
|
assert_eq!(res.ap_id, ap_id);
|
||||||
assert_eq!(res.data, data);
|
assert_eq!(res.data, data);
|
||||||
assert_eq!(res.sensitive, sensitive);
|
assert_eq!(res.sensitive, sensitive);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,26 +199,22 @@ impl CommunityLanguage {
|
||||||
/// Returns true if the given language is one of configured languages for given community
|
/// Returns true if the given language is one of configured languages for given community
|
||||||
pub async fn is_allowed_community_language(
|
pub async fn is_allowed_community_language(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_language_id: Option<LanguageId>,
|
for_language_id: LanguageId,
|
||||||
for_community_id: CommunityId,
|
for_community_id: CommunityId,
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
use crate::schema::community_language::dsl::community_language;
|
use crate::schema::community_language::dsl::community_language;
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
if let Some(for_language_id) = for_language_id {
|
let is_allowed = select(exists(
|
||||||
let is_allowed = select(exists(
|
community_language.find((for_community_id, for_language_id)),
|
||||||
community_language.find((for_community_id, for_language_id)),
|
))
|
||||||
))
|
.get_result(conn)
|
||||||
.get_result(conn)
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
if is_allowed {
|
if is_allowed {
|
||||||
Ok(())
|
|
||||||
} else {
|
|
||||||
Err(LemmyErrorType::LanguageNotAllowed)?
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(LemmyErrorType::LanguageNotAllowed)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +323,7 @@ pub async fn default_post_language(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
local_user_id: LocalUserId,
|
local_user_id: LocalUserId,
|
||||||
) -> Result<Option<LanguageId>, Error> {
|
) -> Result<LanguageId, Error> {
|
||||||
use crate::schema::{community_language::dsl as cl, local_user_language::dsl as ul};
|
use crate::schema::{community_language::dsl as cl, local_user_language::dsl as ul};
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
let mut intersection = ul::local_user_language
|
let mut intersection = ul::local_user_language
|
||||||
|
@ -339,12 +335,12 @@ pub async fn default_post_language(
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
if intersection.len() == 1 {
|
if intersection.len() == 1 {
|
||||||
Ok(intersection.pop())
|
Ok(intersection.pop().unwrap_or(UNDETERMINED_ID))
|
||||||
} else if intersection.len() == 2 && intersection.contains(&UNDETERMINED_ID) {
|
} else if intersection.len() == 2 && intersection.contains(&UNDETERMINED_ID) {
|
||||||
intersection.retain(|i| i != &UNDETERMINED_ID);
|
intersection.retain(|i| i != &UNDETERMINED_ID);
|
||||||
Ok(intersection.pop())
|
Ok(intersection.pop().unwrap_or(UNDETERMINED_ID))
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(UNDETERMINED_ID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,8 +388,7 @@ async fn convert_read_languages(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -409,168 +404,148 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
async fn test_langs1(pool: &mut DbPool<'_>) -> Vec<LanguageId> {
|
async fn test_langs1(pool: &mut DbPool<'_>) -> Result<Vec<LanguageId>, Error> {
|
||||||
vec![
|
Ok(vec![
|
||||||
Language::read_id_from_code(pool, Some("en"))
|
Language::read_id_from_code(pool, "en").await?,
|
||||||
.await
|
Language::read_id_from_code(pool, "fr").await?,
|
||||||
.unwrap()
|
Language::read_id_from_code(pool, "ru").await?,
|
||||||
.unwrap(),
|
])
|
||||||
Language::read_id_from_code(pool, Some("fr"))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
Language::read_id_from_code(pool, Some("ru"))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
async fn test_langs2(pool: &mut DbPool<'_>) -> Vec<LanguageId> {
|
async fn test_langs2(pool: &mut DbPool<'_>) -> Result<Vec<LanguageId>, Error> {
|
||||||
vec![
|
Ok(vec![
|
||||||
Language::read_id_from_code(pool, Some("fi"))
|
Language::read_id_from_code(pool, "fi").await?,
|
||||||
.await
|
Language::read_id_from_code(pool, "se").await?,
|
||||||
.unwrap()
|
])
|
||||||
.unwrap(),
|
|
||||||
Language::read_id_from_code(pool, Some("se"))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn create_test_site(pool: &mut DbPool<'_>) -> (Site, Instance) {
|
async fn create_test_site(pool: &mut DbPool<'_>) -> Result<(Site, Instance), Error> {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
let site_form = SiteInsertForm::new("test site".to_string(), inserted_instance.id);
|
||||||
let site = Site::create(pool, &site_form).await.unwrap();
|
let site = Site::create(pool, &site_form).await?;
|
||||||
|
|
||||||
// Create a local site, since this is necessary for local languages
|
// Create a local site, since this is necessary for local languages
|
||||||
let local_site_form = LocalSiteInsertForm::new(site.id);
|
let local_site_form = LocalSiteInsertForm::new(site.id);
|
||||||
LocalSite::create(pool, &local_site_form).await.unwrap();
|
LocalSite::create(pool, &local_site_form).await?;
|
||||||
|
|
||||||
(site, inserted_instance)
|
Ok((site, inserted_instance))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_convert_update_languages() {
|
async fn test_convert_update_languages() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
// call with empty vec, returns all languages
|
// call with empty vec, returns all languages
|
||||||
let conn = &mut get_conn(pool).await.unwrap();
|
let conn = &mut get_conn(pool).await?;
|
||||||
let converted1 = convert_update_languages(conn, vec![]).await.unwrap();
|
let converted1 = convert_update_languages(conn, vec![]).await?;
|
||||||
assert_eq!(184, converted1.len());
|
assert_eq!(184, converted1.len());
|
||||||
|
|
||||||
// call with nonempty vec, returns same vec
|
// call with nonempty vec, returns same vec
|
||||||
let test_langs = test_langs1(&mut conn.into()).await;
|
let test_langs = test_langs1(&mut conn.into()).await?;
|
||||||
let converted2 = convert_update_languages(conn, test_langs.clone())
|
let converted2 = convert_update_languages(conn, test_langs.clone()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(test_langs, converted2);
|
assert_eq!(test_langs, converted2);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_convert_read_languages() {
|
async fn test_convert_read_languages() -> Result<(), Error> {
|
||||||
use crate::schema::language::dsl::{id, language};
|
use crate::schema::language::dsl::{id, language};
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
// call with all languages, returns empty vec
|
// call with all languages, returns empty vec
|
||||||
let conn = &mut get_conn(pool).await.unwrap();
|
let conn = &mut get_conn(pool).await?;
|
||||||
let all_langs = language.select(id).get_results(conn).await.unwrap();
|
let all_langs = language.select(id).get_results(conn).await?;
|
||||||
let converted1: Vec<LanguageId> = convert_read_languages(conn, all_langs).await.unwrap();
|
let converted1: Vec<LanguageId> = convert_read_languages(conn, all_langs).await?;
|
||||||
assert_eq!(0, converted1.len());
|
assert_eq!(0, converted1.len());
|
||||||
|
|
||||||
// call with nonempty vec, returns same vec
|
// call with nonempty vec, returns same vec
|
||||||
let test_langs = test_langs1(&mut conn.into()).await;
|
let test_langs = test_langs1(&mut conn.into()).await?;
|
||||||
let converted2 = convert_read_languages(conn, test_langs.clone())
|
let converted2 = convert_read_languages(conn, test_langs.clone()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(test_langs, converted2);
|
assert_eq!(test_langs, converted2);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_site_languages() {
|
async fn test_site_languages() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let (site, instance) = create_test_site(pool).await;
|
let (site, instance) = create_test_site(pool).await?;
|
||||||
let site_languages1 = SiteLanguage::read_local_raw(pool).await.unwrap();
|
let site_languages1 = SiteLanguage::read_local_raw(pool).await?;
|
||||||
// site is created with all languages
|
// site is created with all languages
|
||||||
assert_eq!(184, site_languages1.len());
|
assert_eq!(184, site_languages1.len());
|
||||||
|
|
||||||
let test_langs = test_langs1(pool).await;
|
let test_langs = test_langs1(pool).await?;
|
||||||
SiteLanguage::update(pool, test_langs.clone(), &site)
|
SiteLanguage::update(pool, test_langs.clone(), &site).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let site_languages2 = SiteLanguage::read_local_raw(pool).await.unwrap();
|
let site_languages2 = SiteLanguage::read_local_raw(pool).await?;
|
||||||
// after update, site only has new languages
|
// after update, site only has new languages
|
||||||
assert_eq!(test_langs, site_languages2);
|
assert_eq!(test_langs, site_languages2);
|
||||||
|
|
||||||
Site::delete(pool, site.id).await.unwrap();
|
Site::delete(pool, site.id).await?;
|
||||||
Instance::delete(pool, instance.id).await.unwrap();
|
Instance::delete(pool, instance.id).await?;
|
||||||
LocalSite::delete(pool).await.unwrap();
|
LocalSite::delete(pool).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_user_languages() {
|
async fn test_user_languages() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let (site, instance) = create_test_site(pool).await;
|
let (site, instance) = create_test_site(pool).await?;
|
||||||
|
|
||||||
let person_form = PersonInsertForm::test_form(instance.id, "my test person");
|
let person_form = PersonInsertForm::test_form(instance.id, "my test person");
|
||||||
let person = Person::create(pool, &person_form).await.unwrap();
|
let person = Person::create(pool, &person_form).await?;
|
||||||
let local_user_form = LocalUserInsertForm::test_form(person.id);
|
let local_user_form = LocalUserInsertForm::test_form(person.id);
|
||||||
|
|
||||||
let local_user = LocalUser::create(pool, &local_user_form, vec![])
|
let local_user = LocalUser::create(pool, &local_user_form, vec![]).await?;
|
||||||
.await
|
let local_user_langs1 = LocalUserLanguage::read(pool, local_user.id).await?;
|
||||||
.unwrap();
|
|
||||||
let local_user_langs1 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
|
|
||||||
|
|
||||||
// new user should be initialized with all languages
|
// new user should be initialized with all languages
|
||||||
assert_eq!(0, local_user_langs1.len());
|
assert_eq!(0, local_user_langs1.len());
|
||||||
|
|
||||||
// update user languages
|
// update user languages
|
||||||
let test_langs2 = test_langs2(pool).await;
|
let test_langs2 = test_langs2(pool).await?;
|
||||||
LocalUserLanguage::update(pool, test_langs2, local_user.id)
|
LocalUserLanguage::update(pool, test_langs2, local_user.id).await?;
|
||||||
.await
|
let local_user_langs2 = LocalUserLanguage::read(pool, local_user.id).await?;
|
||||||
.unwrap();
|
|
||||||
let local_user_langs2 = LocalUserLanguage::read(pool, local_user.id).await.unwrap();
|
|
||||||
assert_eq!(3, local_user_langs2.len());
|
assert_eq!(3, local_user_langs2.len());
|
||||||
|
|
||||||
Person::delete(pool, person.id).await.unwrap();
|
Person::delete(pool, person.id).await?;
|
||||||
LocalUser::delete(pool, local_user.id).await.unwrap();
|
LocalUser::delete(pool, local_user.id).await?;
|
||||||
Site::delete(pool, site.id).await.unwrap();
|
Site::delete(pool, site.id).await?;
|
||||||
LocalSite::delete(pool).await.unwrap();
|
LocalSite::delete(pool).await?;
|
||||||
Instance::delete(pool, instance.id).await.unwrap();
|
Instance::delete(pool, instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_community_languages() {
|
async fn test_community_languages() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let (site, instance) = create_test_site(pool).await;
|
let (site, instance) = create_test_site(pool).await?;
|
||||||
let test_langs = test_langs1(pool).await;
|
let test_langs = test_langs1(pool).await?;
|
||||||
SiteLanguage::update(pool, test_langs.clone(), &site)
|
SiteLanguage::update(pool, test_langs.clone(), &site).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_site_langs = SiteLanguage::read(pool, site.id).await.unwrap();
|
let read_site_langs = SiteLanguage::read(pool, site.id).await?;
|
||||||
assert_eq!(test_langs, read_site_langs);
|
assert_eq!(test_langs, read_site_langs);
|
||||||
|
|
||||||
// Test the local ones are the same
|
// Test the local ones are the same
|
||||||
let read_local_site_langs = SiteLanguage::read_local_raw(pool).await.unwrap();
|
let read_local_site_langs = SiteLanguage::read_local_raw(pool).await?;
|
||||||
assert_eq!(test_langs, read_local_site_langs);
|
assert_eq!(test_langs, read_local_site_langs);
|
||||||
|
|
||||||
let community_form = CommunityInsertForm::new(
|
let community_form = CommunityInsertForm::new(
|
||||||
|
@ -579,52 +554,48 @@ mod tests {
|
||||||
"test community".to_string(),
|
"test community".to_string(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let community = Community::create(pool, &community_form).await.unwrap();
|
let community = Community::create(pool, &community_form).await?;
|
||||||
let community_langs1 = CommunityLanguage::read(pool, community.id).await.unwrap();
|
let community_langs1 = CommunityLanguage::read(pool, community.id).await?;
|
||||||
|
|
||||||
// community is initialized with site languages
|
// community is initialized with site languages
|
||||||
assert_eq!(test_langs, community_langs1);
|
assert_eq!(test_langs, community_langs1);
|
||||||
|
|
||||||
let allowed_lang1 =
|
let allowed_lang1 =
|
||||||
CommunityLanguage::is_allowed_community_language(pool, Some(test_langs[0]), community.id)
|
CommunityLanguage::is_allowed_community_language(pool, test_langs[0], community.id).await;
|
||||||
.await;
|
|
||||||
assert!(allowed_lang1.is_ok());
|
assert!(allowed_lang1.is_ok());
|
||||||
|
|
||||||
let test_langs2 = test_langs2(pool).await;
|
let test_langs2 = test_langs2(pool).await?;
|
||||||
let allowed_lang2 =
|
let allowed_lang2 =
|
||||||
CommunityLanguage::is_allowed_community_language(pool, Some(test_langs2[0]), community.id)
|
CommunityLanguage::is_allowed_community_language(pool, test_langs2[0], community.id).await;
|
||||||
.await;
|
|
||||||
assert!(allowed_lang2.is_err());
|
assert!(allowed_lang2.is_err());
|
||||||
|
|
||||||
// limit site languages to en, fi. after this, community languages should be updated to
|
// limit site languages to en, fi. after this, community languages should be updated to
|
||||||
// intersection of old languages (en, fr, ru) and (en, fi), which is only fi.
|
// intersection of old languages (en, fr, ru) and (en, fi), which is only fi.
|
||||||
SiteLanguage::update(pool, vec![test_langs[0], test_langs2[0]], &site)
|
SiteLanguage::update(pool, vec![test_langs[0], test_langs2[0]], &site).await?;
|
||||||
.await
|
let community_langs2 = CommunityLanguage::read(pool, community.id).await?;
|
||||||
.unwrap();
|
|
||||||
let community_langs2 = CommunityLanguage::read(pool, community.id).await.unwrap();
|
|
||||||
assert_eq!(vec![test_langs[0]], community_langs2);
|
assert_eq!(vec![test_langs[0]], community_langs2);
|
||||||
|
|
||||||
// update community languages to different ones
|
// update community languages to different ones
|
||||||
CommunityLanguage::update(pool, test_langs2.clone(), community.id)
|
CommunityLanguage::update(pool, test_langs2.clone(), community.id).await?;
|
||||||
.await
|
let community_langs3 = CommunityLanguage::read(pool, community.id).await?;
|
||||||
.unwrap();
|
|
||||||
let community_langs3 = CommunityLanguage::read(pool, community.id).await.unwrap();
|
|
||||||
assert_eq!(test_langs2, community_langs3);
|
assert_eq!(test_langs2, community_langs3);
|
||||||
|
|
||||||
Community::delete(pool, community.id).await.unwrap();
|
Community::delete(pool, community.id).await?;
|
||||||
Site::delete(pool, site.id).await.unwrap();
|
Site::delete(pool, site.id).await?;
|
||||||
LocalSite::delete(pool).await.unwrap();
|
LocalSite::delete(pool).await?;
|
||||||
Instance::delete(pool, instance.id).await.unwrap();
|
Instance::delete(pool, instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_default_post_language() {
|
async fn test_default_post_language() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let (site, instance) = create_test_site(pool).await;
|
let (site, instance) = create_test_site(pool).await?;
|
||||||
let test_langs = test_langs1(pool).await;
|
let test_langs = test_langs1(pool).await?;
|
||||||
let test_langs2 = test_langs2(pool).await;
|
let test_langs2 = test_langs2(pool).await?;
|
||||||
|
|
||||||
let community_form = CommunityInsertForm::new(
|
let community_form = CommunityInsertForm::new(
|
||||||
instance.id,
|
instance.id,
|
||||||
|
@ -632,58 +603,39 @@ mod tests {
|
||||||
"test community".to_string(),
|
"test community".to_string(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let community = Community::create(pool, &community_form).await.unwrap();
|
let community = Community::create(pool, &community_form).await?;
|
||||||
CommunityLanguage::update(pool, test_langs, community.id)
|
CommunityLanguage::update(pool, test_langs, community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let person_form = PersonInsertForm::test_form(instance.id, "my test person");
|
let person_form = PersonInsertForm::test_form(instance.id, "my test person");
|
||||||
let person = Person::create(pool, &person_form).await.unwrap();
|
let person = Person::create(pool, &person_form).await?;
|
||||||
let local_user_form = LocalUserInsertForm::test_form(person.id);
|
let local_user_form = LocalUserInsertForm::test_form(person.id);
|
||||||
let local_user = LocalUser::create(pool, &local_user_form, vec![])
|
let local_user = LocalUser::create(pool, &local_user_form, vec![]).await?;
|
||||||
.await
|
LocalUserLanguage::update(pool, test_langs2, local_user.id).await?;
|
||||||
.unwrap();
|
|
||||||
LocalUserLanguage::update(pool, test_langs2, local_user.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// no overlap in user/community languages, so defaults to undetermined
|
// no overlap in user/community languages, so defaults to undetermined
|
||||||
let def1 = default_post_language(pool, community.id, local_user.id)
|
let def1 = default_post_language(pool, community.id, local_user.id).await?;
|
||||||
.await
|
assert_eq!(UNDETERMINED_ID, def1);
|
||||||
.unwrap();
|
|
||||||
assert_eq!(None, def1);
|
|
||||||
|
|
||||||
let ru = Language::read_id_from_code(pool, Some("ru"))
|
let ru = Language::read_id_from_code(pool, "ru").await?;
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
let test_langs3 = vec![
|
let test_langs3 = vec![
|
||||||
ru,
|
ru,
|
||||||
Language::read_id_from_code(pool, Some("fi"))
|
Language::read_id_from_code(pool, "fi").await?,
|
||||||
.await
|
Language::read_id_from_code(pool, "se").await?,
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
Language::read_id_from_code(pool, Some("se"))
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.unwrap(),
|
|
||||||
UNDETERMINED_ID,
|
UNDETERMINED_ID,
|
||||||
];
|
];
|
||||||
LocalUserLanguage::update(pool, test_langs3, local_user.id)
|
LocalUserLanguage::update(pool, test_langs3, local_user.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// this time, both have ru as common lang
|
// this time, both have ru as common lang
|
||||||
let def2 = default_post_language(pool, community.id, local_user.id)
|
let def2 = default_post_language(pool, community.id, local_user.id).await?;
|
||||||
.await
|
assert_eq!(ru, def2);
|
||||||
.unwrap();
|
|
||||||
assert_eq!(Some(ru), def2);
|
|
||||||
|
|
||||||
Person::delete(pool, person.id).await.unwrap();
|
Person::delete(pool, person.id).await?;
|
||||||
Community::delete(pool, community.id).await.unwrap();
|
Community::delete(pool, community.id).await?;
|
||||||
LocalUser::delete(pool, local_user.id).await.unwrap();
|
LocalUser::delete(pool, local_user.id).await?;
|
||||||
Site::delete(pool, site.id).await.unwrap();
|
Site::delete(pool, site.id).await?;
|
||||||
LocalSite::delete(pool).await.unwrap();
|
LocalSite::delete(pool).await?;
|
||||||
Instance::delete(pool, instance.id).await.unwrap();
|
Instance::delete(pool, instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,8 +51,6 @@ impl CaptchaAnswer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -40,12 +40,12 @@ impl Comment {
|
||||||
pub async fn update_removed_for_creator(
|
pub async fn update_removed_for_creator(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_creator_id: PersonId,
|
for_creator_id: PersonId,
|
||||||
new_removed: bool,
|
removed: bool,
|
||||||
) -> Result<Vec<Self>, Error> {
|
) -> Result<Vec<Self>, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
diesel::update(comment::table.filter(comment::creator_id.eq(for_creator_id)))
|
diesel::update(comment::table.filter(comment::creator_id.eq(for_creator_id)))
|
||||||
.set((
|
.set((
|
||||||
comment::removed.eq(new_removed),
|
comment::removed.eq(removed),
|
||||||
comment::updated.eq(naive_now()),
|
comment::updated.eq(naive_now()),
|
||||||
))
|
))
|
||||||
.get_results::<Self>(conn)
|
.get_results::<Self>(conn)
|
||||||
|
@ -196,8 +196,6 @@ impl Saveable for CommentSaved {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -221,23 +219,22 @@ mod tests {
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
use diesel_ltree::Ltree;
|
use diesel_ltree::Ltree;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "terry");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "terry");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -245,21 +242,21 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
let expected_comment = Comment {
|
let expected_comment = Comment {
|
||||||
id: inserted_comment.id,
|
id: inserted_comment.id,
|
||||||
|
@ -274,8 +271,7 @@ mod tests {
|
||||||
ap_id: Url::parse(&format!(
|
ap_id: Url::parse(&format!(
|
||||||
"https://lemmy-alpha/comment/{}",
|
"https://lemmy-alpha/comment/{}",
|
||||||
inserted_comment.id
|
inserted_comment.id
|
||||||
))
|
))?
|
||||||
.unwrap()
|
|
||||||
.into(),
|
.into(),
|
||||||
distinguished: false,
|
distinguished: false,
|
||||||
local: true,
|
local: true,
|
||||||
|
@ -288,9 +284,7 @@ mod tests {
|
||||||
"A child comment".into(),
|
"A child comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_child_comment =
|
let inserted_child_comment =
|
||||||
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path))
|
Comment::create(pool, &child_comment_form, Some(&inserted_comment.path)).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Comment Like
|
// Comment Like
|
||||||
let comment_like_form = CommentLikeForm {
|
let comment_like_form = CommentLikeForm {
|
||||||
|
@ -300,7 +294,7 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_comment_like = CommentLike::like(pool, &comment_like_form).await.unwrap();
|
let inserted_comment_like = CommentLike::like(pool, &comment_like_form).await?;
|
||||||
|
|
||||||
let expected_comment_like = CommentLike {
|
let expected_comment_like = CommentLike {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
|
@ -316,7 +310,7 @@ mod tests {
|
||||||
person_id: inserted_person.id,
|
person_id: inserted_person.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_comment_saved = CommentSaved::save(pool, &comment_saved_form).await.unwrap();
|
let inserted_comment_saved = CommentSaved::save(pool, &comment_saved_form).await?;
|
||||||
|
|
||||||
let expected_comment_saved = CommentSaved {
|
let expected_comment_saved = CommentSaved {
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
|
@ -329,27 +323,17 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
let updated_comment = Comment::update(pool, inserted_comment.id, &comment_update_form)
|
let updated_comment = Comment::update(pool, inserted_comment.id, &comment_update_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_comment = Comment::read(pool, inserted_comment.id).await.unwrap();
|
let read_comment = Comment::read(pool, inserted_comment.id).await?;
|
||||||
let like_removed = CommentLike::remove(pool, inserted_person.id, inserted_comment.id)
|
let like_removed = CommentLike::remove(pool, inserted_person.id, inserted_comment.id).await?;
|
||||||
.await
|
let saved_removed = CommentSaved::unsave(pool, &comment_saved_form).await?;
|
||||||
.unwrap();
|
let num_deleted = Comment::delete(pool, inserted_comment.id).await?;
|
||||||
let saved_removed = CommentSaved::unsave(pool, &comment_saved_form)
|
Comment::delete(pool, inserted_child_comment.id).await?;
|
||||||
.await
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
.unwrap();
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
let num_deleted = Comment::delete(pool, inserted_comment.id).await.unwrap();
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
Comment::delete(pool, inserted_child_comment.id)
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
|
||||||
Community::delete(pool, inserted_community.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
Person::delete(pool, inserted_person.id).await.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected_comment, read_comment);
|
assert_eq!(expected_comment, read_comment);
|
||||||
assert_eq!(expected_comment, inserted_comment);
|
assert_eq!(expected_comment, inserted_comment);
|
||||||
|
@ -363,5 +347,7 @@ mod tests {
|
||||||
assert_eq!(1, like_removed);
|
assert_eq!(1, like_removed);
|
||||||
assert_eq!(1, saved_removed);
|
assert_eq!(1, saved_removed);
|
||||||
assert_eq!(1, num_deleted);
|
assert_eq!(1, num_deleted);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,12 +30,13 @@ use crate::{
|
||||||
get_conn,
|
get_conn,
|
||||||
DbPool,
|
DbPool,
|
||||||
},
|
},
|
||||||
|
ListingType,
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
deserialize,
|
deserialize,
|
||||||
dsl::{self, exists, insert_into},
|
dsl::{self, exists, insert_into, not},
|
||||||
pg::Pg,
|
pg::Pg,
|
||||||
result::Error,
|
result::Error,
|
||||||
select,
|
select,
|
||||||
|
@ -193,6 +194,30 @@ impl Community {
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_random_community_id(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
type_: &Option<ListingType>,
|
||||||
|
) -> Result<CommunityId, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
sql_function!(fn random() -> Text);
|
||||||
|
|
||||||
|
let mut query = community::table
|
||||||
|
.filter(not(community::deleted))
|
||||||
|
.filter(not(community::removed))
|
||||||
|
.into_boxed();
|
||||||
|
|
||||||
|
if let Some(ListingType::Local) = type_ {
|
||||||
|
query = query.filter(community::local);
|
||||||
|
}
|
||||||
|
|
||||||
|
query
|
||||||
|
.select(community::id)
|
||||||
|
.order(random())
|
||||||
|
.limit(1)
|
||||||
|
.first::<CommunityId>(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommunityModerator {
|
impl CommunityModerator {
|
||||||
|
@ -431,7 +456,6 @@ impl ApubActor for Community {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -48,20 +48,19 @@ impl FederationAllowList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{federation_allowlist::FederationAllowList, instance::Instance},
|
source::{federation_allowlist::FederationAllowList, instance::Instance},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_allowlist_insert_and_clear() {
|
async fn test_allowlist_insert_and_clear() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let domains = vec![
|
let domains = vec![
|
||||||
|
@ -72,9 +71,9 @@ mod tests {
|
||||||
|
|
||||||
let allowed = Some(domains.clone());
|
let allowed = Some(domains.clone());
|
||||||
|
|
||||||
FederationAllowList::replace(pool, allowed).await.unwrap();
|
FederationAllowList::replace(pool, allowed).await?;
|
||||||
|
|
||||||
let allows = Instance::allowlist(pool).await.unwrap();
|
let allows = Instance::allowlist(pool).await?;
|
||||||
let allows_domains = allows
|
let allows_domains = allows
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| i.domain.clone())
|
.map(|i| i.domain.clone())
|
||||||
|
@ -86,13 +85,13 @@ mod tests {
|
||||||
// Now test clearing them via Some(empty vec)
|
// Now test clearing them via Some(empty vec)
|
||||||
let clear_allows = Some(Vec::new());
|
let clear_allows = Some(Vec::new());
|
||||||
|
|
||||||
FederationAllowList::replace(pool, clear_allows)
|
FederationAllowList::replace(pool, clear_allows).await?;
|
||||||
.await
|
let allows = Instance::allowlist(pool).await?;
|
||||||
.unwrap();
|
|
||||||
let allows = Instance::allowlist(pool).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(0, allows.len());
|
assert_eq!(0, allows.len());
|
||||||
|
|
||||||
Instance::delete_all(pool).await.unwrap();
|
Instance::delete_all(pool).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,6 +67,11 @@ impl Instance {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub async fn read(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<Self, Error> {
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
instance::table.find(instance_id).first(conn).await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn update(
|
pub async fn update(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
instance_id: InstanceId,
|
instance_id: InstanceId,
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use super::actor_language::UNDETERMINED_ID;
|
||||||
use crate::{
|
use crate::{
|
||||||
diesel::ExpressionMethods,
|
diesel::ExpressionMethods,
|
||||||
newtypes::LanguageId,
|
newtypes::LanguageId,
|
||||||
|
@ -19,47 +20,42 @@ impl Language {
|
||||||
language::table.find(id_).first(conn).await
|
language::table.find(id_).first(conn).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to find the given language code and return its ID. If not found, returns none.
|
/// Attempts to find the given language code and return its ID.
|
||||||
pub async fn read_id_from_code(
|
pub async fn read_id_from_code(pool: &mut DbPool<'_>, code_: &str) -> Result<LanguageId, Error> {
|
||||||
pool: &mut DbPool<'_>,
|
let conn = &mut get_conn(pool).await?;
|
||||||
code_: Option<&str>,
|
let res = language::table
|
||||||
) -> Result<Option<LanguageId>, Error> {
|
.filter(language::code.eq(code_))
|
||||||
if let Some(code_) = code_ {
|
.first::<Self>(conn)
|
||||||
let conn = &mut get_conn(pool).await?;
|
.await
|
||||||
Ok(
|
.map(|l| l.id);
|
||||||
language::table
|
|
||||||
.filter(language::code.eq(code_))
|
// Return undetermined by default
|
||||||
.first::<Self>(conn)
|
Ok(res.unwrap_or(UNDETERMINED_ID))
|
||||||
.await
|
|
||||||
.map(|l| l.id)
|
|
||||||
.ok(),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{source::language::Language, utils::build_db_pool_for_tests};
|
use crate::{source::language::Language, utils::build_db_pool_for_tests};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_languages() {
|
async fn test_languages() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let all = Language::read_all(pool).await.unwrap();
|
let all = Language::read_all(pool).await?;
|
||||||
|
|
||||||
assert_eq!(184, all.len());
|
assert_eq!(184, all.len());
|
||||||
assert_eq!("ak", all[5].code);
|
assert_eq!("ak", all[5].code);
|
||||||
assert_eq!("lv", all[99].code);
|
assert_eq!("lv", all[99].code);
|
||||||
assert_eq!("yi", all[179].code);
|
assert_eq!("yi", all[179].code);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -369,7 +369,6 @@ pub struct UserBackupLists {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
source::{
|
source::{
|
||||||
|
|
|
@ -66,6 +66,20 @@ impl Crud for ModRemovePost {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ModRemovePost {
|
||||||
|
pub async fn create_multiple(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
forms: &Vec<ModRemovePostForm>,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
use crate::schema::mod_remove_post::dsl::mod_remove_post;
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
insert_into(mod_remove_post)
|
||||||
|
.values(forms)
|
||||||
|
.execute(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for ModLockPost {
|
impl Crud for ModLockPost {
|
||||||
type InsertForm = ModLockPostForm;
|
type InsertForm = ModLockPostForm;
|
||||||
|
@ -153,6 +167,20 @@ impl Crud for ModRemoveComment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ModRemoveComment {
|
||||||
|
pub async fn create_multiple(
|
||||||
|
pool: &mut DbPool<'_>,
|
||||||
|
forms: &Vec<ModRemoveCommentForm>,
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
use crate::schema::mod_remove_comment::dsl::mod_remove_comment;
|
||||||
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
insert_into(mod_remove_comment)
|
||||||
|
.values(forms)
|
||||||
|
.execute(conn)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for ModRemoveCommunity {
|
impl Crud for ModRemoveCommunity {
|
||||||
type InsertForm = ModRemoveCommunityForm;
|
type InsertForm = ModRemoveCommunityForm;
|
||||||
|
@ -465,8 +493,6 @@ impl Crud for AdminPurgeComment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -500,26 +526,25 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_mod = PersonInsertForm::test_form(inserted_instance.id, "the mod");
|
let new_mod = PersonInsertForm::test_form(inserted_instance.id, "the mod");
|
||||||
|
|
||||||
let inserted_mod = Person::create(pool, &new_mod).await.unwrap();
|
let inserted_mod = Person::create(pool, &new_mod).await?;
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim2");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim2");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -528,21 +553,21 @@ mod tests {
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post thweep".into(),
|
"A test post thweep".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment".into(),
|
"A test comment".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
// Now the actual tests
|
// Now the actual tests
|
||||||
|
|
||||||
|
@ -553,12 +578,8 @@ mod tests {
|
||||||
reason: None,
|
reason: None,
|
||||||
removed: None,
|
removed: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_remove_post = ModRemovePost::create(pool, &mod_remove_post_form)
|
let inserted_mod_remove_post = ModRemovePost::create(pool, &mod_remove_post_form).await?;
|
||||||
.await
|
let read_mod_remove_post = ModRemovePost::read(pool, inserted_mod_remove_post.id).await?;
|
||||||
.unwrap();
|
|
||||||
let read_mod_remove_post = ModRemovePost::read(pool, inserted_mod_remove_post.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_remove_post = ModRemovePost {
|
let expected_mod_remove_post = ModRemovePost {
|
||||||
id: inserted_mod_remove_post.id,
|
id: inserted_mod_remove_post.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -575,12 +596,8 @@ mod tests {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
locked: None,
|
locked: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_lock_post = ModLockPost::create(pool, &mod_lock_post_form)
|
let inserted_mod_lock_post = ModLockPost::create(pool, &mod_lock_post_form).await?;
|
||||||
.await
|
let read_mod_lock_post = ModLockPost::read(pool, inserted_mod_lock_post.id).await?;
|
||||||
.unwrap();
|
|
||||||
let read_mod_lock_post = ModLockPost::read(pool, inserted_mod_lock_post.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_lock_post = ModLockPost {
|
let expected_mod_lock_post = ModLockPost {
|
||||||
id: inserted_mod_lock_post.id,
|
id: inserted_mod_lock_post.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -597,12 +614,8 @@ mod tests {
|
||||||
featured: false,
|
featured: false,
|
||||||
is_featured_community: true,
|
is_featured_community: true,
|
||||||
};
|
};
|
||||||
let inserted_mod_feature_post = ModFeaturePost::create(pool, &mod_feature_post_form)
|
let inserted_mod_feature_post = ModFeaturePost::create(pool, &mod_feature_post_form).await?;
|
||||||
.await
|
let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id).await?;
|
||||||
.unwrap();
|
|
||||||
let read_mod_feature_post = ModFeaturePost::read(pool, inserted_mod_feature_post.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_feature_post = ModFeaturePost {
|
let expected_mod_feature_post = ModFeaturePost {
|
||||||
id: inserted_mod_feature_post.id,
|
id: inserted_mod_feature_post.id,
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -620,12 +633,10 @@ mod tests {
|
||||||
reason: None,
|
reason: None,
|
||||||
removed: None,
|
removed: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_remove_comment = ModRemoveComment::create(pool, &mod_remove_comment_form)
|
let inserted_mod_remove_comment =
|
||||||
.await
|
ModRemoveComment::create(pool, &mod_remove_comment_form).await?;
|
||||||
.unwrap();
|
let read_mod_remove_comment =
|
||||||
let read_mod_remove_comment = ModRemoveComment::read(pool, inserted_mod_remove_comment.id)
|
ModRemoveComment::read(pool, inserted_mod_remove_comment.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_remove_comment = ModRemoveComment {
|
let expected_mod_remove_comment = ModRemoveComment {
|
||||||
id: inserted_mod_remove_comment.id,
|
id: inserted_mod_remove_comment.id,
|
||||||
comment_id: inserted_comment.id,
|
comment_id: inserted_comment.id,
|
||||||
|
@ -644,13 +655,9 @@ mod tests {
|
||||||
removed: None,
|
removed: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_remove_community =
|
let inserted_mod_remove_community =
|
||||||
ModRemoveCommunity::create(pool, &mod_remove_community_form)
|
ModRemoveCommunity::create(pool, &mod_remove_community_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let read_mod_remove_community =
|
let read_mod_remove_community =
|
||||||
ModRemoveCommunity::read(pool, inserted_mod_remove_community.id)
|
ModRemoveCommunity::read(pool, inserted_mod_remove_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_remove_community = ModRemoveCommunity {
|
let expected_mod_remove_community = ModRemoveCommunity {
|
||||||
id: inserted_mod_remove_community.id,
|
id: inserted_mod_remove_community.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
|
@ -671,13 +678,9 @@ mod tests {
|
||||||
expires: None,
|
expires: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_ban_from_community =
|
let inserted_mod_ban_from_community =
|
||||||
ModBanFromCommunity::create(pool, &mod_ban_from_community_form)
|
ModBanFromCommunity::create(pool, &mod_ban_from_community_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let read_mod_ban_from_community =
|
let read_mod_ban_from_community =
|
||||||
ModBanFromCommunity::read(pool, inserted_mod_ban_from_community.id)
|
ModBanFromCommunity::read(pool, inserted_mod_ban_from_community.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_ban_from_community = ModBanFromCommunity {
|
let expected_mod_ban_from_community = ModBanFromCommunity {
|
||||||
id: inserted_mod_ban_from_community.id,
|
id: inserted_mod_ban_from_community.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
|
@ -698,8 +701,8 @@ mod tests {
|
||||||
banned: None,
|
banned: None,
|
||||||
expires: None,
|
expires: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_ban = ModBan::create(pool, &mod_ban_form).await.unwrap();
|
let inserted_mod_ban = ModBan::create(pool, &mod_ban_form).await?;
|
||||||
let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id).await.unwrap();
|
let read_mod_ban = ModBan::read(pool, inserted_mod_ban.id).await?;
|
||||||
let expected_mod_ban = ModBan {
|
let expected_mod_ban = ModBan {
|
||||||
id: inserted_mod_ban.id,
|
id: inserted_mod_ban.id,
|
||||||
mod_person_id: inserted_mod.id,
|
mod_person_id: inserted_mod.id,
|
||||||
|
@ -718,12 +721,8 @@ mod tests {
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_add_community = ModAddCommunity::create(pool, &mod_add_community_form)
|
let inserted_mod_add_community = ModAddCommunity::create(pool, &mod_add_community_form).await?;
|
||||||
.await
|
let read_mod_add_community = ModAddCommunity::read(pool, inserted_mod_add_community.id).await?;
|
||||||
.unwrap();
|
|
||||||
let read_mod_add_community = ModAddCommunity::read(pool, inserted_mod_add_community.id)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_mod_add_community = ModAddCommunity {
|
let expected_mod_add_community = ModAddCommunity {
|
||||||
id: inserted_mod_add_community.id,
|
id: inserted_mod_add_community.id,
|
||||||
community_id: inserted_community.id,
|
community_id: inserted_community.id,
|
||||||
|
@ -740,8 +739,8 @@ mod tests {
|
||||||
other_person_id: inserted_person.id,
|
other_person_id: inserted_person.id,
|
||||||
removed: None,
|
removed: None,
|
||||||
};
|
};
|
||||||
let inserted_mod_add = ModAdd::create(pool, &mod_add_form).await.unwrap();
|
let inserted_mod_add = ModAdd::create(pool, &mod_add_form).await?;
|
||||||
let read_mod_add = ModAdd::read(pool, inserted_mod_add.id).await.unwrap();
|
let read_mod_add = ModAdd::read(pool, inserted_mod_add.id).await?;
|
||||||
let expected_mod_add = ModAdd {
|
let expected_mod_add = ModAdd {
|
||||||
id: inserted_mod_add.id,
|
id: inserted_mod_add.id,
|
||||||
mod_person_id: inserted_mod.id,
|
mod_person_id: inserted_mod.id,
|
||||||
|
@ -750,14 +749,12 @@ mod tests {
|
||||||
when_: inserted_mod_add.when_,
|
when_: inserted_mod_add.when_,
|
||||||
};
|
};
|
||||||
|
|
||||||
Comment::delete(pool, inserted_comment.id).await.unwrap();
|
Comment::delete(pool, inserted_comment.id).await?;
|
||||||
Post::delete(pool, inserted_post.id).await.unwrap();
|
Post::delete(pool, inserted_post.id).await?;
|
||||||
Community::delete(pool, inserted_community.id)
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
.unwrap();
|
Person::delete(pool, inserted_mod.id).await?;
|
||||||
Person::delete(pool, inserted_person.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
Person::delete(pool, inserted_mod.id).await.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected_mod_remove_post, read_mod_remove_post);
|
assert_eq!(expected_mod_remove_post, read_mod_remove_post);
|
||||||
assert_eq!(expected_mod_lock_post, read_mod_lock_post);
|
assert_eq!(expected_mod_lock_post, read_mod_lock_post);
|
||||||
|
@ -768,5 +765,7 @@ mod tests {
|
||||||
assert_eq!(expected_mod_ban, read_mod_ban);
|
assert_eq!(expected_mod_ban, read_mod_ban);
|
||||||
assert_eq!(expected_mod_add_community, read_mod_add_community);
|
assert_eq!(expected_mod_add_community, read_mod_add_community);
|
||||||
assert_eq!(expected_mod_add, read_mod_add);
|
assert_eq!(expected_mod_add, read_mod_add);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{LocalUserId, OAuthProviderId},
|
newtypes::LocalUserId,
|
||||||
schema::{oauth_account, oauth_account::dsl::local_user_id},
|
schema::{oauth_account, oauth_account::dsl::local_user_id},
|
||||||
source::oauth_account::{OAuthAccount, OAuthAccountInsertForm},
|
source::oauth_account::{OAuthAccount, OAuthAccountInsertForm},
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{
|
use diesel::{insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||||
dsl::{exists, insert_into},
|
|
||||||
result::Error,
|
|
||||||
select,
|
|
||||||
ExpressionMethods,
|
|
||||||
QueryDsl,
|
|
||||||
};
|
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
impl OAuthAccount {
|
impl OAuthAccount {
|
||||||
pub async fn read(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
for_oauth_provider_id: OAuthProviderId,
|
|
||||||
for_local_user_id: LocalUserId,
|
|
||||||
) -> Result<bool, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
select(exists(
|
|
||||||
oauth_account::table.find((for_oauth_provider_id, for_local_user_id)),
|
|
||||||
))
|
|
||||||
.get_result(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn create(pool: &mut DbPool<'_>, form: &OAuthAccountInsertForm) -> Result<Self, Error> {
|
pub async fn create(pool: &mut DbPool<'_>, form: &OAuthAccountInsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(oauth_account::table)
|
insert_into(oauth_account::table)
|
||||||
|
@ -35,17 +16,6 @@ impl OAuthAccount {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn delete(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
for_oauth_provider_id: OAuthProviderId,
|
|
||||||
for_local_user_id: LocalUserId,
|
|
||||||
) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(oauth_account::table.find((for_oauth_provider_id, for_local_user_id)))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn delete_user_accounts(
|
pub async fn delete_user_accounts(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_local_user_id: LocalUserId,
|
for_local_user_id: LocalUserId,
|
||||||
|
|
|
@ -42,8 +42,6 @@ impl PasswordResetRequest {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -21,6 +21,7 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
use lemmy_utils::{error::LemmyResult, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for Person {
|
impl Crud for Person {
|
||||||
|
@ -121,16 +122,18 @@ impl Person {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn is_username_taken(pool: &mut DbPool<'_>, username: &str) -> Result<bool, Error> {
|
pub async fn check_username_taken(pool: &mut DbPool<'_>, username: &str) -> LemmyResult<()> {
|
||||||
use diesel::dsl::{exists, select};
|
use diesel::dsl::{exists, select};
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
select(exists(
|
select(not(exists(
|
||||||
person::table
|
person::table
|
||||||
.filter(lower(person::name).eq(username.to_lowercase()))
|
.filter(lower(person::name).eq(username.to_lowercase()))
|
||||||
.filter(person::local.eq(true)),
|
.filter(person::local.eq(true)),
|
||||||
))
|
)))
|
||||||
.get_result(conn)
|
.get_result::<bool>(conn)
|
||||||
.await
|
.await?
|
||||||
|
.then_some(())
|
||||||
|
.ok_or(LemmyErrorType::UsernameAlreadyExists.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +235,6 @@ impl PersonFollower {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl Post {
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
for_creator_id: PersonId,
|
for_creator_id: PersonId,
|
||||||
for_community_id: Option<CommunityId>,
|
for_community_id: Option<CommunityId>,
|
||||||
new_removed: bool,
|
removed: bool,
|
||||||
) -> Result<Vec<Self>, Error> {
|
) -> Result<Vec<Self>, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ impl Post {
|
||||||
}
|
}
|
||||||
|
|
||||||
update
|
update
|
||||||
.set((post::removed.eq(new_removed), post::updated.eq(naive_now())))
|
.set((post::removed.eq(removed), post::updated.eq(naive_now())))
|
||||||
.get_results::<Self>(conn)
|
.get_results::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -258,9 +258,9 @@ impl Post {
|
||||||
post::table
|
post::table
|
||||||
.inner_join(person::table)
|
.inner_join(person::table)
|
||||||
.inner_join(community::table)
|
.inner_join(community::table)
|
||||||
// find all posts which have scheduled_publish_time that is in the past
|
// find all posts which have scheduled_publish_time that is in the future
|
||||||
.filter(post::scheduled_publish_time.is_not_null())
|
.filter(post::scheduled_publish_time.is_not_null())
|
||||||
.filter(coalesce(post::scheduled_publish_time, now()).lt(now()))
|
.filter(coalesce(post::scheduled_publish_time, now()).gt(now()))
|
||||||
// make sure the post and community are still around
|
// make sure the post and community are still around
|
||||||
.filter(not(post::deleted.or(post::removed)))
|
.filter(not(post::deleted.or(post::removed)))
|
||||||
.filter(not(community::removed.or(community::deleted)))
|
.filter(not(community::removed.or(community::deleted)))
|
||||||
|
@ -392,8 +392,6 @@ impl PostHide {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -415,6 +413,8 @@ mod tests {
|
||||||
traits::{Crud, Likeable, Saveable},
|
traits::{Crud, Likeable, Saveable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use chrono::DateTime;
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
@ -422,17 +422,15 @@ mod tests {
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "jim");
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -441,21 +439,27 @@ mod tests {
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post".into(),
|
"A test post".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let new_post2 = PostInsertForm::new(
|
let new_post2 = PostInsertForm::new(
|
||||||
"A test post 2".into(),
|
"A test post 2".into(),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post2 = Post::create(pool, &new_post2).await.unwrap();
|
let inserted_post2 = Post::create(pool, &new_post2).await?;
|
||||||
|
|
||||||
|
let new_scheduled_post = PostInsertForm {
|
||||||
|
scheduled_publish_time: Some(DateTime::from_timestamp_nanos(i64::MAX)),
|
||||||
|
..PostInsertForm::new("beans".into(), inserted_person.id, inserted_community.id)
|
||||||
|
};
|
||||||
|
let inserted_scheduled_post = Post::create(pool, &new_scheduled_post).await?;
|
||||||
|
|
||||||
let expected_post = Post {
|
let expected_post = Post {
|
||||||
id: inserted_post.id,
|
id: inserted_post.id,
|
||||||
|
@ -475,9 +479,7 @@ mod tests {
|
||||||
embed_description: None,
|
embed_description: None,
|
||||||
embed_video_url: None,
|
embed_video_url: None,
|
||||||
thumbnail_url: None,
|
thumbnail_url: None,
|
||||||
ap_id: Url::parse(&format!("https://lemmy-alpha/post/{}", inserted_post.id))
|
ap_id: Url::parse(&format!("https://lemmy-alpha/post/{}", inserted_post.id))?.into(),
|
||||||
.unwrap()
|
|
||||||
.into(),
|
|
||||||
local: true,
|
local: true,
|
||||||
language_id: Default::default(),
|
language_id: Default::default(),
|
||||||
featured_community: false,
|
featured_community: false,
|
||||||
|
@ -493,7 +495,7 @@ mod tests {
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post_like = PostLike::like(pool, &post_like_form).await.unwrap();
|
let inserted_post_like = PostLike::like(pool, &post_like_form).await?;
|
||||||
|
|
||||||
let expected_post_like = PostLike {
|
let expected_post_like = PostLike {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -508,7 +510,7 @@ mod tests {
|
||||||
person_id: inserted_person.id,
|
person_id: inserted_person.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await.unwrap();
|
let inserted_post_saved = PostSaved::save(pool, &post_saved_form).await?;
|
||||||
|
|
||||||
let expected_post_saved = PostSaved {
|
let expected_post_saved = PostSaved {
|
||||||
post_id: inserted_post.id,
|
post_id: inserted_post.id,
|
||||||
|
@ -522,48 +524,47 @@ mod tests {
|
||||||
HashSet::from([inserted_post.id, inserted_post2.id]),
|
HashSet::from([inserted_post.id, inserted_post2.id]),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(2, marked_as_read);
|
assert_eq!(2, marked_as_read);
|
||||||
|
|
||||||
let read_post = Post::read(pool, inserted_post.id).await.unwrap();
|
let read_post = Post::read(pool, inserted_post.id).await?;
|
||||||
|
|
||||||
let new_post_update = PostUpdateForm {
|
let new_post_update = PostUpdateForm {
|
||||||
name: Some("A test post".into()),
|
name: Some("A test post".into()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let updated_post = Post::update(pool, inserted_post.id, &new_post_update)
|
let updated_post = Post::update(pool, inserted_post.id, &new_post_update).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let like_removed = PostLike::remove(pool, inserted_person.id, inserted_post.id)
|
// Scheduled post count
|
||||||
.await
|
let scheduled_post_count = Post::user_scheduled_post_count(inserted_person.id, pool).await?;
|
||||||
.unwrap();
|
assert_eq!(1, scheduled_post_count);
|
||||||
|
|
||||||
|
let like_removed = PostLike::remove(pool, inserted_person.id, inserted_post.id).await?;
|
||||||
assert_eq!(1, like_removed);
|
assert_eq!(1, like_removed);
|
||||||
let saved_removed = PostSaved::unsave(pool, &post_saved_form).await.unwrap();
|
let saved_removed = PostSaved::unsave(pool, &post_saved_form).await?;
|
||||||
assert_eq!(1, saved_removed);
|
assert_eq!(1, saved_removed);
|
||||||
let read_removed = PostRead::mark_as_unread(
|
let read_removed = PostRead::mark_as_unread(
|
||||||
pool,
|
pool,
|
||||||
HashSet::from([inserted_post.id, inserted_post2.id]),
|
HashSet::from([inserted_post.id, inserted_post2.id]),
|
||||||
inserted_person.id,
|
inserted_person.id,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(2, read_removed);
|
assert_eq!(2, read_removed);
|
||||||
|
|
||||||
let num_deleted = Post::delete(pool, inserted_post.id).await.unwrap()
|
let num_deleted = Post::delete(pool, inserted_post.id).await?
|
||||||
+ Post::delete(pool, inserted_post2.id).await.unwrap();
|
+ Post::delete(pool, inserted_post2.id).await?
|
||||||
assert_eq!(2, num_deleted);
|
+ Post::delete(pool, inserted_scheduled_post.id).await?;
|
||||||
Community::delete(pool, inserted_community.id)
|
assert_eq!(3, num_deleted);
|
||||||
.await
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
.unwrap();
|
Person::delete(pool, inserted_person.id).await?;
|
||||||
Person::delete(pool, inserted_person.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected_post, read_post);
|
assert_eq!(expected_post, read_post);
|
||||||
assert_eq!(expected_post, inserted_post);
|
assert_eq!(expected_post, inserted_post);
|
||||||
assert_eq!(expected_post, updated_post);
|
assert_eq!(expected_post, updated_post);
|
||||||
assert_eq!(expected_post_like, inserted_post_like);
|
assert_eq!(expected_post_like, inserted_post_like);
|
||||||
assert_eq!(expected_post_saved, inserted_post_saved);
|
assert_eq!(expected_post_saved, inserted_post_saved);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,8 +80,6 @@ impl Reportable for PostReport {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -95,14 +93,13 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use diesel::result::Error;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
async fn init(pool: &mut DbPool<'_>) -> (Person, PostReport) {
|
async fn init(pool: &mut DbPool<'_>) -> Result<(Person, PostReport), Error> {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let person_form = PersonInsertForm::test_form(inserted_instance.id, "jim");
|
let person_form = PersonInsertForm::test_form(inserted_instance.id, "jim");
|
||||||
let person = Person::create(pool, &person_form).await.unwrap();
|
let person = Person::create(pool, &person_form).await?;
|
||||||
|
|
||||||
let community_form = CommunityInsertForm::new(
|
let community_form = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -110,10 +107,10 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let community = Community::create(pool, &community_form).await.unwrap();
|
let community = Community::create(pool, &community_form).await?;
|
||||||
|
|
||||||
let form = PostInsertForm::new("A test post".into(), person.id, community.id);
|
let form = PostInsertForm::new("A test post".into(), person.id, community.id);
|
||||||
let post = Post::create(pool, &form).await.unwrap();
|
let post = Post::create(pool, &form).await?;
|
||||||
|
|
||||||
let report_form = PostReportForm {
|
let report_form = PostReportForm {
|
||||||
post_id: post.id,
|
post_id: post.id,
|
||||||
|
@ -121,46 +118,46 @@ mod tests {
|
||||||
reason: "my reason".to_string(),
|
reason: "my reason".to_string(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let report = PostReport::report(pool, &report_form).await.unwrap();
|
let report = PostReport::report(pool, &report_form).await?;
|
||||||
(person, report)
|
|
||||||
|
Ok((person, report))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_resolve_post_report() {
|
async fn test_resolve_post_report() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let (person, report) = init(pool).await;
|
let (person, report) = init(pool).await?;
|
||||||
|
|
||||||
let resolved_count = PostReport::resolve(pool, report.id, person.id)
|
let resolved_count = PostReport::resolve(pool, report.id, person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(resolved_count, 1);
|
assert_eq!(resolved_count, 1);
|
||||||
|
|
||||||
let unresolved_count = PostReport::unresolve(pool, report.id, person.id)
|
let unresolved_count = PostReport::unresolve(pool, report.id, person.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(unresolved_count, 1);
|
assert_eq!(unresolved_count, 1);
|
||||||
|
|
||||||
Person::delete(pool, person.id).await.unwrap();
|
Person::delete(pool, person.id).await?;
|
||||||
Post::delete(pool, report.post_id).await.unwrap();
|
Post::delete(pool, report.post_id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_resolve_all_post_reports() {
|
async fn test_resolve_all_post_reports() -> Result<(), Error> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let (person, report) = init(pool).await;
|
let (person, report) = init(pool).await?;
|
||||||
|
|
||||||
let resolved_count = PostReport::resolve_all_for_object(pool, report.post_id, person.id)
|
let resolved_count =
|
||||||
.await
|
PostReport::resolve_all_for_object(pool, report.post_id, person.id).await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(resolved_count, 1);
|
assert_eq!(resolved_count, 1);
|
||||||
|
|
||||||
Person::delete(pool, person.id).await.unwrap();
|
Person::delete(pool, person.id).await?;
|
||||||
Post::delete(pool, report.post_id).await.unwrap();
|
Post::delete(pool, report.post_id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,8 +85,6 @@ impl PrivateMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -98,27 +96,26 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let creator_form = PersonInsertForm::test_form(inserted_instance.id, "creator_pm");
|
let creator_form = PersonInsertForm::test_form(inserted_instance.id, "creator_pm");
|
||||||
|
|
||||||
let inserted_creator = Person::create(pool, &creator_form).await.unwrap();
|
let inserted_creator = Person::create(pool, &creator_form).await?;
|
||||||
|
|
||||||
let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "recipient_pm");
|
let recipient_form = PersonInsertForm::test_form(inserted_instance.id, "recipient_pm");
|
||||||
|
|
||||||
let inserted_recipient = Person::create(pool, &recipient_form).await.unwrap();
|
let inserted_recipient = Person::create(pool, &recipient_form).await?;
|
||||||
|
|
||||||
let private_message_form = PrivateMessageInsertForm::new(
|
let private_message_form = PrivateMessageInsertForm::new(
|
||||||
inserted_creator.id,
|
inserted_creator.id,
|
||||||
|
@ -126,9 +123,7 @@ mod tests {
|
||||||
"A test private message".into(),
|
"A test private message".into(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_private_message = PrivateMessage::create(pool, &private_message_form)
|
let inserted_private_message = PrivateMessage::create(pool, &private_message_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let expected_private_message = PrivateMessage {
|
let expected_private_message = PrivateMessage {
|
||||||
id: inserted_private_message.id,
|
id: inserted_private_message.id,
|
||||||
|
@ -142,15 +137,12 @@ mod tests {
|
||||||
ap_id: Url::parse(&format!(
|
ap_id: Url::parse(&format!(
|
||||||
"https://lemmy-alpha/private_message/{}",
|
"https://lemmy-alpha/private_message/{}",
|
||||||
inserted_private_message.id
|
inserted_private_message.id
|
||||||
))
|
))?
|
||||||
.unwrap()
|
|
||||||
.into(),
|
.into(),
|
||||||
local: true,
|
local: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let read_private_message = PrivateMessage::read(pool, inserted_private_message.id)
|
let read_private_message = PrivateMessage::read(pool, inserted_private_message.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let private_message_update_form = PrivateMessageUpdateForm {
|
let private_message_update_form = PrivateMessageUpdateForm {
|
||||||
content: Some("A test private message".into()),
|
content: Some("A test private message".into()),
|
||||||
|
@ -161,8 +153,7 @@ mod tests {
|
||||||
inserted_private_message.id,
|
inserted_private_message.id,
|
||||||
&private_message_update_form,
|
&private_message_update_form,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let deleted_private_message = PrivateMessage::update(
|
let deleted_private_message = PrivateMessage::update(
|
||||||
pool,
|
pool,
|
||||||
|
@ -172,8 +163,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
let marked_read_private_message = PrivateMessage::update(
|
let marked_read_private_message = PrivateMessage::update(
|
||||||
pool,
|
pool,
|
||||||
inserted_private_message.id,
|
inserted_private_message.id,
|
||||||
|
@ -182,16 +172,17 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
Person::delete(pool, inserted_creator.id).await?;
|
||||||
Person::delete(pool, inserted_creator.id).await.unwrap();
|
Person::delete(pool, inserted_recipient.id).await?;
|
||||||
Person::delete(pool, inserted_recipient.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(expected_private_message, read_private_message);
|
assert_eq!(expected_private_message, read_private_message);
|
||||||
assert_eq!(expected_private_message, updated_private_message);
|
assert_eq!(expected_private_message, updated_private_message);
|
||||||
assert_eq!(expected_private_message, inserted_private_message);
|
assert_eq!(expected_private_message, inserted_private_message);
|
||||||
assert!(deleted_private_message.deleted);
|
assert!(deleted_private_message.deleted);
|
||||||
assert!(marked_read_private_message.read);
|
assert!(marked_read_private_message.read);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
diesel::OptionalExtension,
|
|
||||||
schema::secret::dsl::secret,
|
schema::secret::dsl::secret,
|
||||||
source::secret::Secret,
|
source::secret::Secret,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -10,12 +9,12 @@ use diesel_async::RunQueryDsl;
|
||||||
impl Secret {
|
impl Secret {
|
||||||
/// Initialize the Secrets from the DB.
|
/// Initialize the Secrets from the DB.
|
||||||
/// Warning: You should only call this once.
|
/// Warning: You should only call this once.
|
||||||
pub async fn init(pool: &mut DbPool<'_>) -> Result<Option<Secret>, Error> {
|
pub async fn init(pool: &mut DbPool<'_>) -> Result<Secret, Error> {
|
||||||
Self::read_secrets(pool).await
|
Self::read_secrets(pool).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_secrets(pool: &mut DbPool<'_>) -> Result<Option<Self>, Error> {
|
async fn read_secrets(pool: &mut DbPool<'_>) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
secret.first(conn).await.optional()
|
secret.first(conn).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{get_conn, limit_and_offset, DbPool},
|
utils::{get_conn, limit_and_offset, DbPool},
|
||||||
};
|
};
|
||||||
use diesel::{insert_into, result::Error, ExpressionMethods, OptionalExtension, QueryDsl};
|
use diesel::{insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
@ -51,14 +51,9 @@ impl Tagline {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_random(pool: &mut DbPool<'_>) -> Result<Option<Self>, Error> {
|
pub async fn get_random(pool: &mut DbPool<'_>) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
sql_function!(fn random() -> Text);
|
sql_function!(fn random() -> Text);
|
||||||
tagline
|
tagline.order(random()).limit(1).first::<Self>(conn).await
|
||||||
.order(random())
|
|
||||||
.limit(1)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
.optional()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ pub mod newtypes;
|
||||||
pub mod sensitive;
|
pub mod sensitive;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
#[allow(clippy::wildcard_imports)]
|
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
pub mod aliases {
|
pub mod aliases {
|
||||||
|
@ -252,6 +251,27 @@ pub enum CommunityVisibility {
|
||||||
LocalOnly,
|
LocalOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
EnumString, Display, Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default, Hash,
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "full", derive(DbEnum, TS))]
|
||||||
|
#[cfg_attr(
|
||||||
|
feature = "full",
|
||||||
|
ExistingTypePath = "crate::schema::sql_types::FederationModeEnum"
|
||||||
|
)]
|
||||||
|
#[cfg_attr(feature = "full", DbValueStyle = "verbatim")]
|
||||||
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
/// The federation mode for an item
|
||||||
|
pub enum FederationMode {
|
||||||
|
#[default]
|
||||||
|
/// Allows all
|
||||||
|
All,
|
||||||
|
/// Allows only local
|
||||||
|
Local,
|
||||||
|
/// Disables
|
||||||
|
Disable,
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrapper for assert_eq! macro. Checks that vec matches the given length, and prints the
|
/// Wrapper for assert_eq! macro. Checks that vec matches the given length, and prints the
|
||||||
/// vec on failure.
|
/// vec on failure.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
|
|
@ -191,13 +191,13 @@ impl Display for DbUrl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the project doesn't compile with From
|
// the project doesn't compile with From
|
||||||
#[allow(clippy::from_over_into)]
|
#[expect(clippy::from_over_into)]
|
||||||
impl Into<DbUrl> for Url {
|
impl Into<DbUrl> for Url {
|
||||||
fn into(self) -> DbUrl {
|
fn into(self) -> DbUrl {
|
||||||
DbUrl(Box::new(self))
|
DbUrl(Box::new(self))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[allow(clippy::from_over_into)]
|
#[expect(clippy::from_over_into)]
|
||||||
impl Into<Url> for DbUrl {
|
impl Into<Url> for DbUrl {
|
||||||
fn into(self) -> Url {
|
fn into(self) -> Url {
|
||||||
*self.0
|
*self.0
|
||||||
|
|
|
@ -13,6 +13,10 @@ pub mod sql_types {
|
||||||
#[diesel(postgres_type(name = "community_visibility"))]
|
#[diesel(postgres_type(name = "community_visibility"))]
|
||||||
pub struct CommunityVisibility;
|
pub struct CommunityVisibility;
|
||||||
|
|
||||||
|
#[derive(diesel::sql_types::SqlType)]
|
||||||
|
#[diesel(postgres_type(name = "federation_mode_enum"))]
|
||||||
|
pub struct FederationModeEnum;
|
||||||
|
|
||||||
#[derive(diesel::sql_types::SqlType)]
|
#[derive(diesel::sql_types::SqlType)]
|
||||||
#[diesel(postgres_type(name = "listing_type_enum"))]
|
#[diesel(postgres_type(name = "listing_type_enum"))]
|
||||||
pub struct ListingTypeEnum;
|
pub struct ListingTypeEnum;
|
||||||
|
@ -376,12 +380,12 @@ diesel::table! {
|
||||||
use super::sql_types::PostListingModeEnum;
|
use super::sql_types::PostListingModeEnum;
|
||||||
use super::sql_types::PostSortTypeEnum;
|
use super::sql_types::PostSortTypeEnum;
|
||||||
use super::sql_types::CommentSortTypeEnum;
|
use super::sql_types::CommentSortTypeEnum;
|
||||||
|
use super::sql_types::FederationModeEnum;
|
||||||
|
|
||||||
local_site (id) {
|
local_site (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
site_id -> Int4,
|
site_id -> Int4,
|
||||||
site_setup -> Bool,
|
site_setup -> Bool,
|
||||||
enable_downvotes -> Bool,
|
|
||||||
community_creation_admin_only -> Bool,
|
community_creation_admin_only -> Bool,
|
||||||
require_email_verification -> Bool,
|
require_email_verification -> Bool,
|
||||||
application_question -> Nullable<Text>,
|
application_question -> Nullable<Text>,
|
||||||
|
@ -406,6 +410,10 @@ diesel::table! {
|
||||||
default_post_sort_type -> PostSortTypeEnum,
|
default_post_sort_type -> PostSortTypeEnum,
|
||||||
default_comment_sort_type -> CommentSortTypeEnum,
|
default_comment_sort_type -> CommentSortTypeEnum,
|
||||||
oauth_registration -> Bool,
|
oauth_registration -> Bool,
|
||||||
|
post_upvotes -> FederationModeEnum,
|
||||||
|
post_downvotes -> FederationModeEnum,
|
||||||
|
comment_upvotes -> FederationModeEnum,
|
||||||
|
comment_downvotes -> FederationModeEnum,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -467,7 +475,6 @@ diesel::table! {
|
||||||
totp_2fa_secret -> Nullable<Text>,
|
totp_2fa_secret -> Nullable<Text>,
|
||||||
open_links_in_new_tab -> Bool,
|
open_links_in_new_tab -> Bool,
|
||||||
blur_nsfw -> Bool,
|
blur_nsfw -> Bool,
|
||||||
auto_expand -> Bool,
|
|
||||||
infinite_scroll_enabled -> Bool,
|
infinite_scroll_enabled -> Bool,
|
||||||
admin -> Bool,
|
admin -> Bool,
|
||||||
post_listing_mode -> PostListingModeEnum,
|
post_listing_mode -> PostListingModeEnum,
|
||||||
|
|
|
@ -3,6 +3,7 @@ use crate::schema::local_site;
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{LocalSiteId, SiteId},
|
newtypes::{LocalSiteId, SiteId},
|
||||||
CommentSortType,
|
CommentSortType,
|
||||||
|
FederationMode,
|
||||||
ListingType,
|
ListingType,
|
||||||
PostListingMode,
|
PostListingMode,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
|
@ -27,8 +28,6 @@ pub struct LocalSite {
|
||||||
pub site_id: SiteId,
|
pub site_id: SiteId,
|
||||||
/// True if the site is set up.
|
/// True if the site is set up.
|
||||||
pub site_setup: bool,
|
pub site_setup: bool,
|
||||||
/// Whether downvotes are enabled.
|
|
||||||
pub enable_downvotes: bool,
|
|
||||||
/// Whether only admins can create communities.
|
/// Whether only admins can create communities.
|
||||||
pub community_creation_admin_only: bool,
|
pub community_creation_admin_only: bool,
|
||||||
/// Whether emails are required.
|
/// Whether emails are required.
|
||||||
|
@ -72,6 +71,14 @@ pub struct LocalSite {
|
||||||
pub default_comment_sort_type: CommentSortType,
|
pub default_comment_sort_type: CommentSortType,
|
||||||
/// Whether or not external auth methods can auto-register users.
|
/// Whether or not external auth methods can auto-register users.
|
||||||
pub oauth_registration: bool,
|
pub oauth_registration: bool,
|
||||||
|
/// What kind of post upvotes your site allows.
|
||||||
|
pub post_upvotes: FederationMode,
|
||||||
|
/// What kind of post downvotes your site allows.
|
||||||
|
pub post_downvotes: FederationMode,
|
||||||
|
/// What kind of comment upvotes your site allows.
|
||||||
|
pub comment_upvotes: FederationMode,
|
||||||
|
/// What kind of comment downvotes your site allows.
|
||||||
|
pub comment_downvotes: FederationMode,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, derive_new::new)]
|
#[derive(Clone, derive_new::new)]
|
||||||
|
@ -82,8 +89,6 @@ pub struct LocalSiteInsertForm {
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub site_setup: Option<bool>,
|
pub site_setup: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub enable_downvotes: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub community_creation_admin_only: Option<bool>,
|
pub community_creation_admin_only: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub require_email_verification: Option<bool>,
|
pub require_email_verification: Option<bool>,
|
||||||
|
@ -114,8 +119,6 @@ pub struct LocalSiteInsertForm {
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub registration_mode: Option<RegistrationMode>,
|
pub registration_mode: Option<RegistrationMode>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub oauth_registration: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub reports_email_admins: Option<bool>,
|
pub reports_email_admins: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub federation_signed_fetch: Option<bool>,
|
pub federation_signed_fetch: Option<bool>,
|
||||||
|
@ -125,6 +128,16 @@ pub struct LocalSiteInsertForm {
|
||||||
pub default_post_sort_type: Option<PostSortType>,
|
pub default_post_sort_type: Option<PostSortType>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub default_comment_sort_type: Option<CommentSortType>,
|
pub default_comment_sort_type: Option<CommentSortType>,
|
||||||
|
#[new(default)]
|
||||||
|
pub oauth_registration: Option<bool>,
|
||||||
|
#[new(default)]
|
||||||
|
pub post_upvotes: Option<FederationMode>,
|
||||||
|
#[new(default)]
|
||||||
|
pub post_downvotes: Option<FederationMode>,
|
||||||
|
#[new(default)]
|
||||||
|
pub comment_upvotes: Option<FederationMode>,
|
||||||
|
#[new(default)]
|
||||||
|
pub comment_downvotes: Option<FederationMode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
|
@ -132,7 +145,6 @@ pub struct LocalSiteInsertForm {
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = local_site))]
|
#[cfg_attr(feature = "full", diesel(table_name = local_site))]
|
||||||
pub struct LocalSiteUpdateForm {
|
pub struct LocalSiteUpdateForm {
|
||||||
pub site_setup: Option<bool>,
|
pub site_setup: Option<bool>,
|
||||||
pub enable_downvotes: Option<bool>,
|
|
||||||
pub community_creation_admin_only: Option<bool>,
|
pub community_creation_admin_only: Option<bool>,
|
||||||
pub require_email_verification: Option<bool>,
|
pub require_email_verification: Option<bool>,
|
||||||
pub application_question: Option<Option<String>>,
|
pub application_question: Option<Option<String>>,
|
||||||
|
@ -148,11 +160,15 @@ pub struct LocalSiteUpdateForm {
|
||||||
pub captcha_enabled: Option<bool>,
|
pub captcha_enabled: Option<bool>,
|
||||||
pub captcha_difficulty: Option<String>,
|
pub captcha_difficulty: Option<String>,
|
||||||
pub registration_mode: Option<RegistrationMode>,
|
pub registration_mode: Option<RegistrationMode>,
|
||||||
pub oauth_registration: Option<bool>,
|
|
||||||
pub reports_email_admins: Option<bool>,
|
pub reports_email_admins: Option<bool>,
|
||||||
pub updated: Option<Option<DateTime<Utc>>>,
|
pub updated: Option<Option<DateTime<Utc>>>,
|
||||||
pub federation_signed_fetch: Option<bool>,
|
pub federation_signed_fetch: Option<bool>,
|
||||||
pub default_post_listing_mode: Option<PostListingMode>,
|
pub default_post_listing_mode: Option<PostListingMode>,
|
||||||
pub default_post_sort_type: Option<PostSortType>,
|
pub default_post_sort_type: Option<PostSortType>,
|
||||||
pub default_comment_sort_type: Option<CommentSortType>,
|
pub default_comment_sort_type: Option<CommentSortType>,
|
||||||
|
pub oauth_registration: Option<bool>,
|
||||||
|
pub post_upvotes: Option<FederationMode>,
|
||||||
|
pub post_downvotes: Option<FederationMode>,
|
||||||
|
pub comment_upvotes: Option<FederationMode>,
|
||||||
|
pub comment_downvotes: Option<FederationMode>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,11 +14,12 @@ use serde_with::skip_serializing_none;
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
|
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize, Default)]
|
||||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = local_user))]
|
#[cfg_attr(feature = "full", diesel(table_name = local_user))]
|
||||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
#[cfg_attr(feature = "full", ts(export))]
|
||||||
|
#[serde(default)]
|
||||||
/// A local user.
|
/// A local user.
|
||||||
pub struct LocalUser {
|
pub struct LocalUser {
|
||||||
pub id: LocalUserId,
|
pub id: LocalUserId,
|
||||||
|
@ -49,7 +50,6 @@ pub struct LocalUser {
|
||||||
/// Open links in a new tab.
|
/// Open links in a new tab.
|
||||||
pub open_links_in_new_tab: bool,
|
pub open_links_in_new_tab: bool,
|
||||||
pub blur_nsfw: bool,
|
pub blur_nsfw: bool,
|
||||||
pub auto_expand: bool,
|
|
||||||
/// Whether infinite scroll is enabled.
|
/// Whether infinite scroll is enabled.
|
||||||
pub infinite_scroll_enabled: bool,
|
pub infinite_scroll_enabled: bool,
|
||||||
/// Whether the person is an admin.
|
/// Whether the person is an admin.
|
||||||
|
@ -104,8 +104,6 @@ pub struct LocalUserInsertForm {
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub blur_nsfw: Option<bool>,
|
pub blur_nsfw: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub auto_expand: Option<bool>,
|
|
||||||
#[new(default)]
|
|
||||||
pub infinite_scroll_enabled: Option<bool>,
|
pub infinite_scroll_enabled: Option<bool>,
|
||||||
#[new(default)]
|
#[new(default)]
|
||||||
pub admin: Option<bool>,
|
pub admin: Option<bool>,
|
||||||
|
@ -143,7 +141,6 @@ pub struct LocalUserUpdateForm {
|
||||||
pub totp_2fa_secret: Option<Option<String>>,
|
pub totp_2fa_secret: Option<Option<String>>,
|
||||||
pub open_links_in_new_tab: Option<bool>,
|
pub open_links_in_new_tab: Option<bool>,
|
||||||
pub blur_nsfw: Option<bool>,
|
pub blur_nsfw: Option<bool>,
|
||||||
pub auto_expand: Option<bool>,
|
|
||||||
pub infinite_scroll_enabled: Option<bool>,
|
pub infinite_scroll_enabled: Option<bool>,
|
||||||
pub admin: Option<bool>,
|
pub admin: Option<bool>,
|
||||||
pub post_listing_mode: Option<PostListingMode>,
|
pub post_listing_mode: Option<PostListingMode>,
|
||||||
|
|
|
@ -87,39 +87,30 @@ impl Serialize for PublicOAuthProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset, TS))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
pub struct OAuthProviderInsertForm {
|
pub struct OAuthProviderInsertForm {
|
||||||
pub display_name: String,
|
pub display_name: String,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub issuer: DbUrl,
|
pub issuer: DbUrl,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub authorization_endpoint: DbUrl,
|
pub authorization_endpoint: DbUrl,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub token_endpoint: DbUrl,
|
pub token_endpoint: DbUrl,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub userinfo_endpoint: DbUrl,
|
pub userinfo_endpoint: DbUrl,
|
||||||
pub id_claim: String,
|
pub id_claim: String,
|
||||||
pub client_id: String,
|
pub client_id: String,
|
||||||
pub client_secret: String,
|
pub client_secret: String,
|
||||||
pub scopes: String,
|
pub scopes: String,
|
||||||
pub auto_verify_email: bool,
|
pub auto_verify_email: Option<bool>,
|
||||||
pub account_linking_enabled: bool,
|
pub account_linking_enabled: Option<bool>,
|
||||||
pub enabled: bool,
|
pub enabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset, TS))]
|
#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
#[cfg_attr(feature = "full", diesel(table_name = oauth_provider))]
|
||||||
#[cfg_attr(feature = "full", ts(export))]
|
|
||||||
pub struct OAuthProviderUpdateForm {
|
pub struct OAuthProviderUpdateForm {
|
||||||
pub display_name: Option<String>,
|
pub display_name: Option<String>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub authorization_endpoint: Option<DbUrl>,
|
pub authorization_endpoint: Option<DbUrl>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub token_endpoint: Option<DbUrl>,
|
pub token_endpoint: Option<DbUrl>,
|
||||||
#[cfg_attr(feature = "full", ts(type = "string"))]
|
|
||||||
pub userinfo_endpoint: Option<DbUrl>,
|
pub userinfo_endpoint: Option<DbUrl>,
|
||||||
pub id_claim: Option<String>,
|
pub id_claim: Option<String>,
|
||||||
pub client_secret: Option<String>,
|
pub client_secret: Option<String>,
|
||||||
|
|
|
@ -595,7 +595,6 @@ impl<RF, LF> Queries<RF, LF> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
|
@ -259,8 +259,7 @@ impl CommentReportQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -284,27 +283,24 @@ mod tests {
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
SubscribedType,
|
SubscribedType,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_crv");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_crv");
|
||||||
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id);
|
let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id);
|
||||||
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![])
|
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let timmy_view = LocalUserView {
|
let timmy_view = LocalUserView {
|
||||||
local_user: timmy_local_user,
|
local_user: timmy_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
||||||
|
@ -314,12 +310,12 @@ mod tests {
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_crv");
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_crv");
|
||||||
|
|
||||||
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_sara = Person::create(pool, &new_person_2).await?;
|
||||||
|
|
||||||
// Add a third person, since new ppl can only report something once.
|
// Add a third person, since new ppl can only report something once.
|
||||||
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_crv");
|
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_crv");
|
||||||
|
|
||||||
let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
|
let inserted_jessica = Person::create(pool, &new_person_3).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -327,7 +323,7 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
// Make timmy a mod
|
// Make timmy a mod
|
||||||
let timmy_moderator_form = CommunityModeratorForm {
|
let timmy_moderator_form = CommunityModeratorForm {
|
||||||
|
@ -335,9 +331,7 @@ mod tests {
|
||||||
person_id: inserted_timmy.id,
|
person_id: inserted_timmy.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form)
|
let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post crv".into(),
|
"A test post crv".into(),
|
||||||
|
@ -345,14 +339,14 @@ mod tests {
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_timmy.id,
|
inserted_timmy.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment 32".into(),
|
"A test comment 32".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
// sara reports
|
// sara reports
|
||||||
let sara_report_form = CommentReportForm {
|
let sara_report_form = CommentReportForm {
|
||||||
|
@ -362,9 +356,7 @@ mod tests {
|
||||||
reason: "from sara".into(),
|
reason: "from sara".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_sara_report = CommentReport::report(pool, &sara_report_form)
|
let inserted_sara_report = CommentReport::report(pool, &sara_report_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// jessica reports
|
// jessica reports
|
||||||
let jessica_report_form = CommentReportForm {
|
let jessica_report_form = CommentReportForm {
|
||||||
|
@ -374,18 +366,12 @@ mod tests {
|
||||||
reason: "from jessica".into(),
|
reason: "from jessica".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form)
|
let inserted_jessica_report = CommentReport::report(pool, &jessica_report_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let agg = CommentAggregates::read(pool, inserted_comment.id)
|
let agg = CommentAggregates::read(pool, inserted_comment.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_jessica_report_view =
|
let read_jessica_report_view =
|
||||||
CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
|
CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let expected_jessica_report_view = CommentReportView {
|
let expected_jessica_report_view = CommentReportView {
|
||||||
comment_report: inserted_jessica_report.clone(),
|
comment_report: inserted_jessica_report.clone(),
|
||||||
comment: inserted_comment.clone(),
|
comment: inserted_comment.clone(),
|
||||||
|
@ -514,8 +500,7 @@ mod tests {
|
||||||
// Do a batch read of timmys reports
|
// Do a batch read of timmys reports
|
||||||
let reports = CommentReportQuery::default()
|
let reports = CommentReportQuery::default()
|
||||||
.list(pool, &timmy_view)
|
.list(pool, &timmy_view)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
reports,
|
reports,
|
||||||
|
@ -526,19 +511,14 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure the counts are correct
|
// Make sure the counts are correct
|
||||||
let report_count = CommentReportView::get_report_count(pool, inserted_timmy.id, false, None)
|
let report_count =
|
||||||
.await
|
CommentReportView::get_report_count(pool, inserted_timmy.id, false, None).await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(2, report_count);
|
assert_eq!(2, report_count);
|
||||||
|
|
||||||
// Try to resolve the report
|
// Try to resolve the report
|
||||||
CommentReport::resolve(pool, inserted_jessica_report.id, inserted_timmy.id)
|
CommentReport::resolve(pool, inserted_jessica_report.id, inserted_timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let read_jessica_report_view_after_resolve =
|
let read_jessica_report_view_after_resolve =
|
||||||
CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
|
CommentReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view;
|
let mut expected_jessica_report_view_after_resolve = expected_jessica_report_view;
|
||||||
expected_jessica_report_view_after_resolve
|
expected_jessica_report_view_after_resolve
|
||||||
|
@ -588,24 +568,21 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, &timmy_view)
|
.list(pool, &timmy_view)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(reports_after_resolve[0], expected_sara_report_view);
|
assert_eq!(reports_after_resolve[0], expected_sara_report_view);
|
||||||
assert_eq!(reports_after_resolve.len(), 1);
|
assert_eq!(reports_after_resolve.len(), 1);
|
||||||
|
|
||||||
// Make sure the counts are correct
|
// Make sure the counts are correct
|
||||||
let report_count_after_resolved =
|
let report_count_after_resolved =
|
||||||
CommentReportView::get_report_count(pool, inserted_timmy.id, false, None)
|
CommentReportView::get_report_count(pool, inserted_timmy.id, false, None).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, report_count_after_resolved);
|
assert_eq!(1, report_count_after_resolved);
|
||||||
|
|
||||||
Person::delete(pool, inserted_timmy.id).await.unwrap();
|
Person::delete(pool, inserted_timmy.id).await?;
|
||||||
Person::delete(pool, inserted_sara.id).await.unwrap();
|
Person::delete(pool, inserted_sara.id).await?;
|
||||||
Person::delete(pool, inserted_jessica.id).await.unwrap();
|
Person::delete(pool, inserted_jessica.id).await?;
|
||||||
Community::delete(pool, inserted_community.id)
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,32 +216,30 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(post::community_id.eq(community_id));
|
query = query.filter(post::community_id.eq(community_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(listing_type) = options.listing_type {
|
let is_subscribed = exists(
|
||||||
let is_subscribed = exists(
|
community_follower::table.filter(
|
||||||
community_follower::table.filter(
|
post::community_id
|
||||||
post::community_id
|
.eq(community_follower::community_id)
|
||||||
.eq(community_follower::community_id)
|
.and(community_follower::person_id.eq(person_id_join)),
|
||||||
.and(community_follower::person_id.eq(person_id_join)),
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
match listing_type {
|
match options.listing_type.unwrap_or_default() {
|
||||||
ListingType::Subscribed => query = query.filter(is_subscribed), /* TODO could be this: and(community_follower::person_id.eq(person_id_join)), */
|
ListingType::Subscribed => query = query.filter(is_subscribed), /* TODO could be this: and(community_follower::person_id.eq(person_id_join)), */
|
||||||
ListingType::Local => {
|
ListingType::Local => {
|
||||||
query = query
|
query = query
|
||||||
.filter(community::local.eq(true))
|
.filter(community::local.eq(true))
|
||||||
.filter(community::hidden.eq(false).or(is_subscribed))
|
.filter(community::hidden.eq(false).or(is_subscribed))
|
||||||
}
|
}
|
||||||
ListingType::All => query = query.filter(community::hidden.eq(false).or(is_subscribed)),
|
ListingType::All => query = query.filter(community::hidden.eq(false).or(is_subscribed)),
|
||||||
ListingType::ModeratorView => {
|
ListingType::ModeratorView => {
|
||||||
query = query.filter(exists(
|
query = query.filter(exists(
|
||||||
community_moderator::table.filter(
|
community_moderator::table.filter(
|
||||||
post::community_id
|
post::community_id
|
||||||
.eq(community_moderator::community_id)
|
.eq(community_moderator::community_id)
|
||||||
.and(community_moderator::person_id.eq(person_id_join)),
|
.and(community_moderator::person_id.eq(person_id_join)),
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,8 +420,7 @@ impl<'a> CommentQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -511,7 +508,7 @@ mod tests {
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await?;
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
let english_id = Language::read_id_from_code(pool, Some("en")).await?;
|
let english_id = Language::read_id_from_code(pool, "en").await?;
|
||||||
|
|
||||||
// Create a comment tree with this hierarchy
|
// Create a comment tree with this hierarchy
|
||||||
// 0
|
// 0
|
||||||
|
@ -522,7 +519,7 @@ mod tests {
|
||||||
// \
|
// \
|
||||||
// 5
|
// 5
|
||||||
let comment_form_0 = CommentInsertForm {
|
let comment_form_0 = CommentInsertForm {
|
||||||
language_id: english_id,
|
language_id: Some(english_id),
|
||||||
..CommentInsertForm::new(
|
..CommentInsertForm::new(
|
||||||
inserted_timmy_person.id,
|
inserted_timmy_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
|
@ -533,7 +530,7 @@ mod tests {
|
||||||
let inserted_comment_0 = Comment::create(pool, &comment_form_0, None).await?;
|
let inserted_comment_0 = Comment::create(pool, &comment_form_0, None).await?;
|
||||||
|
|
||||||
let comment_form_1 = CommentInsertForm {
|
let comment_form_1 = CommentInsertForm {
|
||||||
language_id: english_id,
|
language_id: Some(english_id),
|
||||||
..CommentInsertForm::new(
|
..CommentInsertForm::new(
|
||||||
inserted_sara_person.id,
|
inserted_sara_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
|
@ -543,9 +540,9 @@ mod tests {
|
||||||
let inserted_comment_1 =
|
let inserted_comment_1 =
|
||||||
Comment::create(pool, &comment_form_1, Some(&inserted_comment_0.path)).await?;
|
Comment::create(pool, &comment_form_1, Some(&inserted_comment_0.path)).await?;
|
||||||
|
|
||||||
let finnish_id = Language::read_id_from_code(pool, Some("fi")).await?;
|
let finnish_id = Language::read_id_from_code(pool, "fi").await?;
|
||||||
let comment_form_2 = CommentInsertForm {
|
let comment_form_2 = CommentInsertForm {
|
||||||
language_id: finnish_id,
|
language_id: Some(finnish_id),
|
||||||
..CommentInsertForm::new(
|
..CommentInsertForm::new(
|
||||||
inserted_timmy_person.id,
|
inserted_timmy_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
|
@ -557,7 +554,7 @@ mod tests {
|
||||||
Comment::create(pool, &comment_form_2, Some(&inserted_comment_0.path)).await?;
|
Comment::create(pool, &comment_form_2, Some(&inserted_comment_0.path)).await?;
|
||||||
|
|
||||||
let comment_form_3 = CommentInsertForm {
|
let comment_form_3 = CommentInsertForm {
|
||||||
language_id: english_id,
|
language_id: Some(english_id),
|
||||||
..CommentInsertForm::new(
|
..CommentInsertForm::new(
|
||||||
inserted_timmy_person.id,
|
inserted_timmy_person.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
|
@ -567,9 +564,7 @@ mod tests {
|
||||||
let _inserted_comment_3 =
|
let _inserted_comment_3 =
|
||||||
Comment::create(pool, &comment_form_3, Some(&inserted_comment_1.path)).await?;
|
Comment::create(pool, &comment_form_3, Some(&inserted_comment_1.path)).await?;
|
||||||
|
|
||||||
let polish_id = Language::read_id_from_code(pool, Some("pl"))
|
let polish_id = Language::read_id_from_code(pool, "pl").await?;
|
||||||
.await?
|
|
||||||
.unwrap();
|
|
||||||
let comment_form_4 = CommentInsertForm {
|
let comment_form_4 = CommentInsertForm {
|
||||||
language_id: Some(polish_id),
|
language_id: Some(polish_id),
|
||||||
..CommentInsertForm::new(
|
..CommentInsertForm::new(
|
||||||
|
@ -655,8 +650,8 @@ mod tests {
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&expected_comment_view_no_person,
|
Some(&expected_comment_view_no_person),
|
||||||
read_comment_views_no_person.first().unwrap()
|
read_comment_views_no_person.first()
|
||||||
);
|
);
|
||||||
|
|
||||||
let read_comment_views_with_person = CommentQuery {
|
let read_comment_views_with_person = CommentQuery {
|
||||||
|
@ -832,9 +827,7 @@ mod tests {
|
||||||
assert_length!(5, all_languages);
|
assert_length!(5, all_languages);
|
||||||
|
|
||||||
// change user lang to finnish, should only show one post in finnish and one undetermined
|
// change user lang to finnish, should only show one post in finnish and one undetermined
|
||||||
let finnish_id = Language::read_id_from_code(pool, Some("fi"))
|
let finnish_id = Language::read_id_from_code(pool, "fi").await?;
|
||||||
.await?
|
|
||||||
.unwrap();
|
|
||||||
LocalUserLanguage::update(
|
LocalUserLanguage::update(
|
||||||
pool,
|
pool,
|
||||||
vec![finnish_id],
|
vec![finnish_id],
|
||||||
|
@ -853,8 +846,8 @@ mod tests {
|
||||||
.find(|c| c.comment.language_id == finnish_id);
|
.find(|c| c.comment.language_id == finnish_id);
|
||||||
assert!(finnish_comment.is_some());
|
assert!(finnish_comment.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
data.inserted_comment_2.content,
|
Some(&data.inserted_comment_2.content),
|
||||||
finnish_comment.unwrap().comment.content
|
finnish_comment.map(|c| &c.comment.content)
|
||||||
);
|
);
|
||||||
|
|
||||||
// now show all comments with undetermined language (which is the default value)
|
// now show all comments with undetermined language (which is the default value)
|
||||||
|
|
|
@ -284,8 +284,7 @@ impl PostReportQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -306,27 +305,24 @@ mod tests {
|
||||||
traits::{Crud, Joinable, Reportable},
|
traits::{Crud, Joinable, Reportable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_prv");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_prv");
|
||||||
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id);
|
let new_local_user = LocalUserInsertForm::test_form(inserted_timmy.id);
|
||||||
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![])
|
let timmy_local_user = LocalUser::create(pool, &new_local_user, vec![]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let timmy_view = LocalUserView {
|
let timmy_view = LocalUserView {
|
||||||
local_user: timmy_local_user,
|
local_user: timmy_local_user,
|
||||||
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
local_user_vote_display_mode: LocalUserVoteDisplayMode::default(),
|
||||||
|
@ -336,12 +332,12 @@ mod tests {
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_prv");
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_prv");
|
||||||
|
|
||||||
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_sara = Person::create(pool, &new_person_2).await?;
|
||||||
|
|
||||||
// Add a third person, since new ppl can only report something once.
|
// Add a third person, since new ppl can only report something once.
|
||||||
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_prv");
|
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "jessica_prv");
|
||||||
|
|
||||||
let inserted_jessica = Person::create(pool, &new_person_3).await.unwrap();
|
let inserted_jessica = Person::create(pool, &new_person_3).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -349,7 +345,7 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
// Make timmy a mod
|
// Make timmy a mod
|
||||||
let timmy_moderator_form = CommunityModeratorForm {
|
let timmy_moderator_form = CommunityModeratorForm {
|
||||||
|
@ -357,16 +353,14 @@ mod tests {
|
||||||
person_id: inserted_timmy.id,
|
person_id: inserted_timmy.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form)
|
let _inserted_moderator = CommunityModerator::join(pool, &timmy_moderator_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post crv".into(),
|
"A test post crv".into(),
|
||||||
inserted_timmy.id,
|
inserted_timmy.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
// sara reports
|
// sara reports
|
||||||
let sara_report_form = PostReportForm {
|
let sara_report_form = PostReportForm {
|
||||||
|
@ -378,14 +372,14 @@ mod tests {
|
||||||
reason: "from sara".into(),
|
reason: "from sara".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
PostReport::report(pool, &sara_report_form).await.unwrap();
|
PostReport::report(pool, &sara_report_form).await?;
|
||||||
|
|
||||||
let new_post_2 = PostInsertForm::new(
|
let new_post_2 = PostInsertForm::new(
|
||||||
"A test post crv 2".into(),
|
"A test post crv 2".into(),
|
||||||
inserted_timmy.id,
|
inserted_timmy.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post_2 = Post::create(pool, &new_post_2).await.unwrap();
|
let inserted_post_2 = Post::create(pool, &new_post_2).await?;
|
||||||
|
|
||||||
// jessica reports
|
// jessica reports
|
||||||
let jessica_report_form = PostReportForm {
|
let jessica_report_form = PostReportForm {
|
||||||
|
@ -397,14 +391,10 @@ mod tests {
|
||||||
reason: "from jessica".into(),
|
reason: "from jessica".into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_jessica_report = PostReport::report(pool, &jessica_report_form)
|
let inserted_jessica_report = PostReport::report(pool, &jessica_report_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_jessica_report_view =
|
let read_jessica_report_view =
|
||||||
PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
|
PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read_jessica_report_view.post_report,
|
read_jessica_report_view.post_report,
|
||||||
|
@ -418,30 +408,23 @@ mod tests {
|
||||||
assert_eq!(read_jessica_report_view.resolver, None);
|
assert_eq!(read_jessica_report_view.resolver, None);
|
||||||
|
|
||||||
// Do a batch read of timmys reports
|
// Do a batch read of timmys reports
|
||||||
let reports = PostReportQuery::default()
|
let reports = PostReportQuery::default().list(pool, &timmy_view).await?;
|
||||||
.list(pool, &timmy_view)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(reports[1].creator.id, inserted_sara.id);
|
assert_eq!(reports[1].creator.id, inserted_sara.id);
|
||||||
assert_eq!(reports[0].creator.id, inserted_jessica.id);
|
assert_eq!(reports[0].creator.id, inserted_jessica.id);
|
||||||
|
|
||||||
// Make sure the counts are correct
|
// Make sure the counts are correct
|
||||||
let report_count = PostReportView::get_report_count(pool, inserted_timmy.id, false, None)
|
let report_count =
|
||||||
.await
|
PostReportView::get_report_count(pool, inserted_timmy.id, false, None).await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(2, report_count);
|
assert_eq!(2, report_count);
|
||||||
|
|
||||||
// Pretend the post was removed, and resolve all reports for that object.
|
// Pretend the post was removed, and resolve all reports for that object.
|
||||||
// This is called manually in the API for post removals
|
// This is called manually in the API for post removals
|
||||||
PostReport::resolve_all_for_object(pool, inserted_jessica_report.post_id, inserted_timmy.id)
|
PostReport::resolve_all_for_object(pool, inserted_jessica_report.post_id, inserted_timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_jessica_report_view_after_resolve =
|
let read_jessica_report_view_after_resolve =
|
||||||
PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id)
|
PostReportView::read(pool, inserted_jessica_report.id, inserted_timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert!(read_jessica_report_view_after_resolve.post_report.resolved);
|
assert!(read_jessica_report_view_after_resolve.post_report.resolved);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read_jessica_report_view_after_resolve
|
read_jessica_report_view_after_resolve
|
||||||
|
@ -450,8 +433,10 @@ mod tests {
|
||||||
Some(inserted_timmy.id)
|
Some(inserted_timmy.id)
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
read_jessica_report_view_after_resolve.resolver.unwrap().id,
|
read_jessica_report_view_after_resolve
|
||||||
inserted_timmy.id
|
.resolver
|
||||||
|
.map(|r| r.id),
|
||||||
|
Some(inserted_timmy.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Do a batch read of timmys reports
|
// Do a batch read of timmys reports
|
||||||
|
@ -461,24 +446,21 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, &timmy_view)
|
.list(pool, &timmy_view)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_length!(1, reports_after_resolve);
|
assert_length!(1, reports_after_resolve);
|
||||||
assert_eq!(reports_after_resolve[0].creator.id, inserted_sara.id);
|
assert_eq!(reports_after_resolve[0].creator.id, inserted_sara.id);
|
||||||
|
|
||||||
// Make sure the counts are correct
|
// Make sure the counts are correct
|
||||||
let report_count_after_resolved =
|
let report_count_after_resolved =
|
||||||
PostReportView::get_report_count(pool, inserted_timmy.id, false, None)
|
PostReportView::get_report_count(pool, inserted_timmy.id, false, None).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(1, report_count_after_resolved);
|
assert_eq!(1, report_count_after_resolved);
|
||||||
|
|
||||||
Person::delete(pool, inserted_timmy.id).await.unwrap();
|
Person::delete(pool, inserted_timmy.id).await?;
|
||||||
Person::delete(pool, inserted_sara.id).await.unwrap();
|
Person::delete(pool, inserted_sara.id).await?;
|
||||||
Person::delete(pool, inserted_jessica.id).await.unwrap();
|
Person::delete(pool, inserted_jessica.id).await?;
|
||||||
Community::delete(pool, inserted_community.id)
|
Community::delete(pool, inserted_community.id).await?;
|
||||||
.await
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -346,47 +346,30 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(post_aggregates::creator_id.eq(creator_id));
|
query = query.filter(post_aggregates::creator_id.eq(creator_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(listing_type) = options.listing_type {
|
let is_subscribed = exists(
|
||||||
if let Some(person_id) = options.local_user.person_id() {
|
community_follower::table.filter(
|
||||||
let is_subscribed = exists(
|
post_aggregates::community_id
|
||||||
community_follower::table.filter(
|
.eq(community_follower::community_id)
|
||||||
post_aggregates::community_id
|
.and(community_follower::person_id.eq(person_id_join)),
|
||||||
.eq(community_follower::community_id)
|
),
|
||||||
.and(community_follower::person_id.eq(person_id)),
|
);
|
||||||
|
match options.listing_type.unwrap_or_default() {
|
||||||
|
ListingType::Subscribed => query = query.filter(is_subscribed),
|
||||||
|
ListingType::Local => {
|
||||||
|
query = query
|
||||||
|
.filter(community::local.eq(true))
|
||||||
|
.filter(community::hidden.eq(false).or(is_subscribed));
|
||||||
|
}
|
||||||
|
ListingType::All => query = query.filter(community::hidden.eq(false).or(is_subscribed)),
|
||||||
|
ListingType::ModeratorView => {
|
||||||
|
query = query.filter(exists(
|
||||||
|
community_moderator::table.filter(
|
||||||
|
post::community_id
|
||||||
|
.eq(community_moderator::community_id)
|
||||||
|
.and(community_moderator::person_id.eq(person_id_join)),
|
||||||
),
|
),
|
||||||
);
|
));
|
||||||
match listing_type {
|
|
||||||
ListingType::Subscribed => query = query.filter(is_subscribed),
|
|
||||||
ListingType::Local => {
|
|
||||||
query = query
|
|
||||||
.filter(community::local.eq(true))
|
|
||||||
.filter(community::hidden.eq(false).or(is_subscribed));
|
|
||||||
}
|
|
||||||
ListingType::All => query = query.filter(community::hidden.eq(false).or(is_subscribed)),
|
|
||||||
ListingType::ModeratorView => {
|
|
||||||
query = query.filter(exists(
|
|
||||||
community_moderator::table.filter(
|
|
||||||
post::community_id
|
|
||||||
.eq(community_moderator::community_id)
|
|
||||||
.and(community_moderator::person_id.eq(person_id)),
|
|
||||||
),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If your person_id is missing, only show local
|
|
||||||
else {
|
|
||||||
match listing_type {
|
|
||||||
ListingType::Local => {
|
|
||||||
query = query
|
|
||||||
.filter(community::local.eq(true))
|
|
||||||
.filter(community::hidden.eq(false));
|
|
||||||
}
|
|
||||||
_ => query = query.filter(community::hidden.eq(false)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
query = query.filter(community::hidden.eq(false));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(search_term) = &options.search_term {
|
if let Some(search_term) = &options.search_term {
|
||||||
|
@ -394,14 +377,12 @@ fn queries<'a>() -> Queries<
|
||||||
query = query.filter(post::url.eq(search_term));
|
query = query.filter(post::url.eq(search_term));
|
||||||
} else {
|
} else {
|
||||||
let searcher = fuzzy_search(search_term);
|
let searcher = fuzzy_search(search_term);
|
||||||
|
let name_filter = post::name.ilike(searcher.clone());
|
||||||
|
let body_filter = post::body.ilike(searcher.clone());
|
||||||
query = if options.title_only.unwrap_or_default() {
|
query = if options.title_only.unwrap_or_default() {
|
||||||
query.filter(post::name.ilike(searcher))
|
query.filter(name_filter)
|
||||||
} else {
|
} else {
|
||||||
query.filter(
|
query.filter(name_filter.or(body_filter))
|
||||||
post::name
|
|
||||||
.ilike(searcher.clone())
|
|
||||||
.or(post::body.ilike(searcher)),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
.filter(not(post::removed.or(post::deleted)));
|
.filter(not(post::removed.or(post::deleted)));
|
||||||
}
|
}
|
||||||
|
@ -741,7 +722,6 @@ impl<'a> PostQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
post_view::{PaginationCursorData, PostQuery, PostView},
|
post_view::{PaginationCursorData, PostQuery, PostView},
|
||||||
|
@ -757,6 +737,8 @@ mod tests {
|
||||||
comment::{Comment, CommentInsertForm},
|
comment::{Comment, CommentInsertForm},
|
||||||
community::{
|
community::{
|
||||||
Community,
|
Community,
|
||||||
|
CommunityFollower,
|
||||||
|
CommunityFollowerForm,
|
||||||
CommunityInsertForm,
|
CommunityInsertForm,
|
||||||
CommunityModerator,
|
CommunityModerator,
|
||||||
CommunityModeratorForm,
|
CommunityModeratorForm,
|
||||||
|
@ -785,7 +767,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
site::Site,
|
site::Site,
|
||||||
},
|
},
|
||||||
traits::{Bannable, Blockable, Crud, Joinable, Likeable, Saveable},
|
traits::{Bannable, Blockable, Crud, Followable, Joinable, Likeable, Saveable},
|
||||||
utils::{build_db_pool, build_db_pool_for_tests, DbPool, RANK_DEFAULT},
|
utils::{build_db_pool, build_db_pool_for_tests, DbPool, RANK_DEFAULT},
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
|
@ -1307,13 +1289,9 @@ mod tests {
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let data = init_data(pool).await?;
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
let spanish_id = Language::read_id_from_code(pool, Some("es"))
|
let spanish_id = Language::read_id_from_code(pool, "es").await?;
|
||||||
.await?
|
|
||||||
.expect("spanish should exist");
|
|
||||||
|
|
||||||
let french_id = Language::read_id_from_code(pool, Some("fr"))
|
let french_id = Language::read_id_from_code(pool, "fr").await?;
|
||||||
.await?
|
|
||||||
.expect("french should exist");
|
|
||||||
|
|
||||||
let post_spanish = PostInsertForm {
|
let post_spanish = PostInsertForm {
|
||||||
language_id: Some(spanish_id),
|
language_id: Some(spanish_id),
|
||||||
|
@ -1439,6 +1417,43 @@ mod tests {
|
||||||
cleanup(data, pool).await
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn post_listings_hidden_community() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool().await?;
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
|
Community::update(
|
||||||
|
pool,
|
||||||
|
data.inserted_community.id,
|
||||||
|
&CommunityUpdateForm {
|
||||||
|
hidden: Some(true),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let posts = PostQuery::default().list(&data.site, pool).await?;
|
||||||
|
assert!(posts.is_empty());
|
||||||
|
|
||||||
|
let posts = data.default_post_query().list(&data.site, pool).await?;
|
||||||
|
assert!(posts.is_empty());
|
||||||
|
|
||||||
|
// Follow the community
|
||||||
|
let form = CommunityFollowerForm {
|
||||||
|
community_id: data.inserted_community.id,
|
||||||
|
person_id: data.local_user_view.person.id,
|
||||||
|
pending: false,
|
||||||
|
};
|
||||||
|
CommunityFollower::follow(pool, &form).await?;
|
||||||
|
|
||||||
|
let posts = data.default_post_query().list(&data.site, pool).await?;
|
||||||
|
assert!(!posts.is_empty());
|
||||||
|
|
||||||
|
cleanup(data, pool).await
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn post_listing_instance_block() -> LemmyResult<()> {
|
async fn post_listing_instance_block() -> LemmyResult<()> {
|
||||||
|
@ -1692,7 +1707,7 @@ mod tests {
|
||||||
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_show_hidden));
|
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_show_hidden));
|
||||||
|
|
||||||
// Make sure that hidden field is true.
|
// Make sure that hidden field is true.
|
||||||
assert!(&post_listings_show_hidden.first().unwrap().hidden);
|
assert!(&post_listings_show_hidden.first().is_some_and(|p| p.hidden));
|
||||||
|
|
||||||
cleanup(data, pool).await
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
|
@ -1728,7 +1743,7 @@ mod tests {
|
||||||
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_show_nsfw));
|
assert_eq!(vec![POST_BY_BOT, POST], names(&post_listings_show_nsfw));
|
||||||
|
|
||||||
// Make sure that nsfw field is true.
|
// Make sure that nsfw field is true.
|
||||||
assert!(&post_listings_show_nsfw.first().unwrap().post.nsfw);
|
assert!(&post_listings_show_nsfw.first().is_some_and(|p| p.post.nsfw));
|
||||||
|
|
||||||
cleanup(data, pool).await
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,8 +111,7 @@ impl PrivateMessageReportQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::private_message_report_view::PrivateMessageReportQuery;
|
use crate::private_message_report_view::PrivateMessageReportQuery;
|
||||||
|
@ -127,24 +126,23 @@ mod tests {
|
||||||
traits::{Crud, Reportable},
|
traits::{Crud, Reportable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person_1 = PersonInsertForm::test_form(inserted_instance.id, "timmy_mrv");
|
let new_person_1 = PersonInsertForm::test_form(inserted_instance.id, "timmy_mrv");
|
||||||
let inserted_timmy = Person::create(pool, &new_person_1).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person_1).await?;
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "jessica_mrv");
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "jessica_mrv");
|
||||||
let inserted_jessica = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_jessica = Person::create(pool, &new_person_2).await?;
|
||||||
|
|
||||||
// timmy sends private message to jessica
|
// timmy sends private message to jessica
|
||||||
let pm_form = PrivateMessageInsertForm::new(
|
let pm_form = PrivateMessageInsertForm::new(
|
||||||
|
@ -152,7 +150,7 @@ mod tests {
|
||||||
inserted_jessica.id,
|
inserted_jessica.id,
|
||||||
"something offensive".to_string(),
|
"something offensive".to_string(),
|
||||||
);
|
);
|
||||||
let pm = PrivateMessage::create(pool, &pm_form).await.unwrap();
|
let pm = PrivateMessage::create(pool, &pm_form).await?;
|
||||||
|
|
||||||
// jessica reports private message
|
// jessica reports private message
|
||||||
let pm_report_form = PrivateMessageReportForm {
|
let pm_report_form = PrivateMessageReportForm {
|
||||||
|
@ -161,14 +159,9 @@ mod tests {
|
||||||
private_message_id: pm.id,
|
private_message_id: pm.id,
|
||||||
reason: "its offensive".to_string(),
|
reason: "its offensive".to_string(),
|
||||||
};
|
};
|
||||||
let pm_report = PrivateMessageReport::report(pool, &pm_report_form)
|
let pm_report = PrivateMessageReport::report(pool, &pm_report_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let reports = PrivateMessageReportQuery::default()
|
let reports = PrivateMessageReportQuery::default().list(pool).await?;
|
||||||
.list(pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_length!(1, reports);
|
assert_length!(1, reports);
|
||||||
assert!(!reports[0].private_message_report.resolved);
|
assert!(!reports[0].private_message_report.resolved);
|
||||||
assert_eq!(inserted_timmy.name, reports[0].private_message_creator.name);
|
assert_eq!(inserted_timmy.name, reports[0].private_message_creator.name);
|
||||||
|
@ -177,28 +170,27 @@ mod tests {
|
||||||
assert_eq!(pm.content, reports[0].private_message.content);
|
assert_eq!(pm.content, reports[0].private_message.content);
|
||||||
|
|
||||||
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "admin_mrv");
|
let new_person_3 = PersonInsertForm::test_form(inserted_instance.id, "admin_mrv");
|
||||||
let inserted_admin = Person::create(pool, &new_person_3).await.unwrap();
|
let inserted_admin = Person::create(pool, &new_person_3).await?;
|
||||||
|
|
||||||
// admin resolves the report (after taking appropriate action)
|
// admin resolves the report (after taking appropriate action)
|
||||||
PrivateMessageReport::resolve(pool, pm_report.id, inserted_admin.id)
|
PrivateMessageReport::resolve(pool, pm_report.id, inserted_admin.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let reports = PrivateMessageReportQuery {
|
let reports = PrivateMessageReportQuery {
|
||||||
unresolved_only: (false),
|
unresolved_only: (false),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_length!(1, reports);
|
assert_length!(1, reports);
|
||||||
assert!(reports[0].private_message_report.resolved);
|
assert!(reports[0].private_message_report.resolved);
|
||||||
assert!(reports[0].resolver.is_some());
|
assert!(reports[0].resolver.is_some());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
inserted_admin.name,
|
Some(&inserted_admin.name),
|
||||||
reports[0].resolver.as_ref().unwrap().name
|
reports[0].resolver.as_ref().map(|r| &r.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -173,8 +173,7 @@ impl PrivateMessageQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
#[expect(clippy::indexing_slicing)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
|
||||||
|
@ -205,45 +204,35 @@ mod tests {
|
||||||
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||||
let message_content = String::new();
|
let message_content = String::new();
|
||||||
|
|
||||||
let instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_rav");
|
let timmy_form = PersonInsertForm::test_form(instance.id, "timmy_rav");
|
||||||
|
|
||||||
let timmy = Person::create(pool, &timmy_form).await.unwrap();
|
let timmy = Person::create(pool, &timmy_form).await?;
|
||||||
|
|
||||||
let sara_form = PersonInsertForm::test_form(instance.id, "sara_rav");
|
let sara_form = PersonInsertForm::test_form(instance.id, "sara_rav");
|
||||||
|
|
||||||
let sara = Person::create(pool, &sara_form).await.unwrap();
|
let sara = Person::create(pool, &sara_form).await?;
|
||||||
|
|
||||||
let jess_form = PersonInsertForm::test_form(instance.id, "jess_rav");
|
let jess_form = PersonInsertForm::test_form(instance.id, "jess_rav");
|
||||||
|
|
||||||
let jess = Person::create(pool, &jess_form).await.unwrap();
|
let jess = Person::create(pool, &jess_form).await?;
|
||||||
|
|
||||||
let sara_timmy_message_form =
|
let sara_timmy_message_form =
|
||||||
PrivateMessageInsertForm::new(sara.id, timmy.id, message_content.clone());
|
PrivateMessageInsertForm::new(sara.id, timmy.id, message_content.clone());
|
||||||
PrivateMessage::create(pool, &sara_timmy_message_form)
|
PrivateMessage::create(pool, &sara_timmy_message_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let sara_jess_message_form =
|
let sara_jess_message_form =
|
||||||
PrivateMessageInsertForm::new(sara.id, jess.id, message_content.clone());
|
PrivateMessageInsertForm::new(sara.id, jess.id, message_content.clone());
|
||||||
PrivateMessage::create(pool, &sara_jess_message_form)
|
PrivateMessage::create(pool, &sara_jess_message_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let timmy_sara_message_form =
|
let timmy_sara_message_form =
|
||||||
PrivateMessageInsertForm::new(timmy.id, sara.id, message_content.clone());
|
PrivateMessageInsertForm::new(timmy.id, sara.id, message_content.clone());
|
||||||
PrivateMessage::create(pool, &timmy_sara_message_form)
|
PrivateMessage::create(pool, &timmy_sara_message_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let jess_timmy_message_form =
|
let jess_timmy_message_form =
|
||||||
PrivateMessageInsertForm::new(jess.id, timmy.id, message_content.clone());
|
PrivateMessageInsertForm::new(jess.id, timmy.id, message_content.clone());
|
||||||
PrivateMessage::create(pool, &jess_timmy_message_form)
|
PrivateMessage::create(pool, &jess_timmy_message_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(Data {
|
Ok(Data {
|
||||||
instance,
|
instance,
|
||||||
|
@ -255,7 +244,7 @@ mod tests {
|
||||||
|
|
||||||
async fn cleanup(instance_id: InstanceId, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
async fn cleanup(instance_id: InstanceId, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||||
// This also deletes all persons and private messages thanks to sql `on delete cascade`
|
// This also deletes all persons and private messages thanks to sql `on delete cascade`
|
||||||
Instance::delete(pool, instance_id).await.unwrap();
|
Instance::delete(pool, instance_id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,8 +266,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, timmy.id)
|
.list(pool, timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_length!(3, &timmy_messages);
|
assert_length!(3, &timmy_messages);
|
||||||
assert_eq!(timmy_messages[0].creator.id, jess.id);
|
assert_eq!(timmy_messages[0].creator.id, jess.id);
|
||||||
|
@ -294,8 +282,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, timmy.id)
|
.list(pool, timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_length!(2, &timmy_unread_messages);
|
assert_length!(2, &timmy_unread_messages);
|
||||||
assert_eq!(timmy_unread_messages[0].creator.id, jess.id);
|
assert_eq!(timmy_unread_messages[0].creator.id, jess.id);
|
||||||
|
@ -309,8 +296,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, timmy.id)
|
.list(pool, timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_length!(2, &timmy_sara_messages);
|
assert_length!(2, &timmy_sara_messages);
|
||||||
assert_eq!(timmy_sara_messages[0].creator.id, timmy.id);
|
assert_eq!(timmy_sara_messages[0].creator.id, timmy.id);
|
||||||
|
@ -324,8 +310,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, timmy.id)
|
.list(pool, timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_length!(1, &timmy_sara_unread_messages);
|
assert_length!(1, &timmy_sara_unread_messages);
|
||||||
assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id);
|
assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id);
|
||||||
|
@ -352,9 +337,7 @@ mod tests {
|
||||||
target_id: sara.id,
|
target_id: sara.id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_block = PersonBlock::block(pool, &timmy_blocks_sara_form)
|
let inserted_block = PersonBlock::block(pool, &timmy_blocks_sara_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let expected_block = PersonBlock {
|
let expected_block = PersonBlock {
|
||||||
person_id: timmy.id,
|
person_id: timmy.id,
|
||||||
|
@ -369,14 +352,11 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, timmy.id)
|
.list(pool, timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_length!(1, &timmy_messages);
|
assert_length!(1, &timmy_messages);
|
||||||
|
|
||||||
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id)
|
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(timmy_unread_messages, 1);
|
assert_eq!(timmy_unread_messages, 1);
|
||||||
|
|
||||||
cleanup(instance.id, pool).await
|
cleanup(instance.id, pool).await
|
||||||
|
@ -399,9 +379,7 @@ mod tests {
|
||||||
instance_id: sara.instance_id,
|
instance_id: sara.instance_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form)
|
let inserted_instance_block = InstanceBlock::block(pool, &timmy_blocks_instance_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let expected_instance_block = InstanceBlock {
|
let expected_instance_block = InstanceBlock {
|
||||||
person_id: timmy.id,
|
person_id: timmy.id,
|
||||||
|
@ -416,14 +394,11 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool, timmy.id)
|
.list(pool, timmy.id)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_length!(0, &timmy_messages);
|
assert_length!(0, &timmy_messages);
|
||||||
|
|
||||||
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id)
|
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(timmy_unread_messages, 0);
|
assert_eq!(timmy_unread_messages, 0);
|
||||||
cleanup(instance.id, pool).await
|
cleanup(instance.id, pool).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -135,8 +135,6 @@ impl RegistrationApplicationQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::registration_application_view::{
|
use crate::registration_application_view::{
|
||||||
|
@ -157,38 +155,34 @@ mod tests {
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn test_crud() {
|
async fn test_crud() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy_rav");
|
let timmy_person_form = PersonInsertForm::test_form(inserted_instance.id, "timmy_rav");
|
||||||
|
|
||||||
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await.unwrap();
|
let inserted_timmy_person = Person::create(pool, &timmy_person_form).await?;
|
||||||
|
|
||||||
let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id);
|
let timmy_local_user_form = LocalUserInsertForm::test_form_admin(inserted_timmy_person.id);
|
||||||
|
|
||||||
let _inserted_timmy_local_user = LocalUser::create(pool, &timmy_local_user_form, vec![])
|
let _inserted_timmy_local_user =
|
||||||
.await
|
LocalUser::create(pool, &timmy_local_user_form, vec![]).await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rav");
|
let sara_person_form = PersonInsertForm::test_form(inserted_instance.id, "sara_rav");
|
||||||
|
|
||||||
let inserted_sara_person = Person::create(pool, &sara_person_form).await.unwrap();
|
let inserted_sara_person = Person::create(pool, &sara_person_form).await?;
|
||||||
|
|
||||||
let sara_local_user_form = LocalUserInsertForm::test_form(inserted_sara_person.id);
|
let sara_local_user_form = LocalUserInsertForm::test_form(inserted_sara_person.id);
|
||||||
|
|
||||||
let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![])
|
let inserted_sara_local_user = LocalUser::create(pool, &sara_local_user_form, vec![]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Sara creates an application
|
// Sara creates an application
|
||||||
let sara_app_form = RegistrationApplicationInsertForm {
|
let sara_app_form = RegistrationApplicationInsertForm {
|
||||||
|
@ -196,23 +190,17 @@ mod tests {
|
||||||
answer: "LET ME IIIIINN".to_string(),
|
answer: "LET ME IIIIINN".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sara_app = RegistrationApplication::create(pool, &sara_app_form)
|
let sara_app = RegistrationApplication::create(pool, &sara_app_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_sara_app_view = RegistrationApplicationView::read(pool, sara_app.id)
|
let read_sara_app_view = RegistrationApplicationView::read(pool, sara_app.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let jess_person_form = PersonInsertForm::test_form(inserted_instance.id, "jess_rav");
|
let jess_person_form = PersonInsertForm::test_form(inserted_instance.id, "jess_rav");
|
||||||
|
|
||||||
let inserted_jess_person = Person::create(pool, &jess_person_form).await.unwrap();
|
let inserted_jess_person = Person::create(pool, &jess_person_form).await?;
|
||||||
|
|
||||||
let jess_local_user_form = LocalUserInsertForm::test_form(inserted_jess_person.id);
|
let jess_local_user_form = LocalUserInsertForm::test_form(inserted_jess_person.id);
|
||||||
|
|
||||||
let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![])
|
let inserted_jess_local_user = LocalUser::create(pool, &jess_local_user_form, vec![]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Sara creates an application
|
// Sara creates an application
|
||||||
let jess_app_form = RegistrationApplicationInsertForm {
|
let jess_app_form = RegistrationApplicationInsertForm {
|
||||||
|
@ -220,13 +208,9 @@ mod tests {
|
||||||
answer: "LET ME IIIIINN".to_string(),
|
answer: "LET ME IIIIINN".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let jess_app = RegistrationApplication::create(pool, &jess_app_form)
|
let jess_app = RegistrationApplication::create(pool, &jess_app_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_jess_app_view = RegistrationApplicationView::read(pool, jess_app.id)
|
let read_jess_app_view = RegistrationApplicationView::read(pool, jess_app.id).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut expected_sara_app_view = RegistrationApplicationView {
|
let mut expected_sara_app_view = RegistrationApplicationView {
|
||||||
registration_application: sara_app.clone(),
|
registration_application: sara_app.clone(),
|
||||||
|
@ -235,7 +219,6 @@ mod tests {
|
||||||
person_id: inserted_sara_local_user.person_id,
|
person_id: inserted_sara_local_user.person_id,
|
||||||
email: inserted_sara_local_user.email,
|
email: inserted_sara_local_user.email,
|
||||||
show_nsfw: inserted_sara_local_user.show_nsfw,
|
show_nsfw: inserted_sara_local_user.show_nsfw,
|
||||||
auto_expand: inserted_sara_local_user.auto_expand,
|
|
||||||
blur_nsfw: inserted_sara_local_user.blur_nsfw,
|
blur_nsfw: inserted_sara_local_user.blur_nsfw,
|
||||||
theme: inserted_sara_local_user.theme,
|
theme: inserted_sara_local_user.theme,
|
||||||
default_post_sort_type: inserted_sara_local_user.default_post_sort_type,
|
default_post_sort_type: inserted_sara_local_user.default_post_sort_type,
|
||||||
|
@ -293,8 +276,7 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
apps,
|
apps,
|
||||||
|
@ -302,9 +284,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Make sure the counts are correct
|
// Make sure the counts are correct
|
||||||
let unread_count = RegistrationApplicationView::get_unread_count(pool, false)
|
let unread_count = RegistrationApplicationView::get_unread_count(pool, false).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(unread_count, 2);
|
assert_eq!(unread_count, 2);
|
||||||
|
|
||||||
// Approve the application
|
// Approve the application
|
||||||
|
@ -313,9 +293,7 @@ mod tests {
|
||||||
deny_reason: None,
|
deny_reason: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
RegistrationApplication::update(pool, sara_app.id, &approve_form)
|
RegistrationApplication::update(pool, sara_app.id, &approve_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Update the local_user row
|
// Update the local_user row
|
||||||
let approve_local_user_form = LocalUserUpdateForm {
|
let approve_local_user_form = LocalUserUpdateForm {
|
||||||
|
@ -323,13 +301,10 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
LocalUser::update(pool, inserted_sara_local_user.id, &approve_local_user_form)
|
LocalUser::update(pool, inserted_sara_local_user.id, &approve_local_user_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let read_sara_app_view_after_approve = RegistrationApplicationView::read(pool, sara_app.id)
|
let read_sara_app_view_after_approve =
|
||||||
.await
|
RegistrationApplicationView::read(pool, sara_app.id).await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Make sure the columns changed
|
// Make sure the columns changed
|
||||||
expected_sara_app_view
|
expected_sara_app_view
|
||||||
|
@ -369,28 +344,23 @@ mod tests {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(pool)
|
.list(pool)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(apps_after_resolve, vec![read_jess_app_view]);
|
assert_eq!(apps_after_resolve, vec![read_jess_app_view]);
|
||||||
|
|
||||||
// Make sure the counts are correct
|
// Make sure the counts are correct
|
||||||
let unread_count_after_approve = RegistrationApplicationView::get_unread_count(pool, false)
|
let unread_count_after_approve =
|
||||||
.await
|
RegistrationApplicationView::get_unread_count(pool, false).await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(unread_count_after_approve, 1);
|
assert_eq!(unread_count_after_approve, 1);
|
||||||
|
|
||||||
// Make sure the not undenied_only has all the apps
|
// Make sure the not undenied_only has all the apps
|
||||||
let all_apps = RegistrationApplicationQuery::default()
|
let all_apps = RegistrationApplicationQuery::default().list(pool).await?;
|
||||||
.list(pool)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(all_apps.len(), 2);
|
assert_eq!(all_apps.len(), 2);
|
||||||
|
|
||||||
Person::delete(pool, inserted_timmy_person.id)
|
Person::delete(pool, inserted_timmy_person.id).await?;
|
||||||
.await
|
Person::delete(pool, inserted_sara_person.id).await?;
|
||||||
.unwrap();
|
Person::delete(pool, inserted_jess_person.id).await?;
|
||||||
Person::delete(pool, inserted_sara_person.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
Person::delete(pool, inserted_jess_person.id).await.unwrap();
|
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,8 +83,6 @@ impl VoteView {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::structs::VoteView;
|
use crate::structs::VoteView;
|
||||||
|
@ -99,26 +97,25 @@ mod tests {
|
||||||
traits::{Bannable, Crud, Likeable},
|
traits::{Bannable, Crud, Likeable},
|
||||||
utils::build_db_pool_for_tests,
|
utils::build_db_pool_for_tests,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn post_and_comment_vote_views() {
|
async fn post_and_comment_vote_views() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
|
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_vv");
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, "timmy_vv");
|
||||||
|
|
||||||
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
|
let inserted_timmy = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_vv");
|
let new_person_2 = PersonInsertForm::test_form(inserted_instance.id, "sara_vv");
|
||||||
|
|
||||||
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
|
let inserted_sara = Person::create(pool, &new_person_2).await?;
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let new_community = CommunityInsertForm::new(
|
||||||
inserted_instance.id,
|
inserted_instance.id,
|
||||||
|
@ -126,21 +123,21 @@ mod tests {
|
||||||
"nada".to_owned(),
|
"nada".to_owned(),
|
||||||
"pubkey".to_string(),
|
"pubkey".to_string(),
|
||||||
);
|
);
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
let inserted_community = Community::create(pool, &new_community).await?;
|
||||||
|
|
||||||
let new_post = PostInsertForm::new(
|
let new_post = PostInsertForm::new(
|
||||||
"A test post vv".into(),
|
"A test post vv".into(),
|
||||||
inserted_timmy.id,
|
inserted_timmy.id,
|
||||||
inserted_community.id,
|
inserted_community.id,
|
||||||
);
|
);
|
||||||
let inserted_post = Post::create(pool, &new_post).await.unwrap();
|
let inserted_post = Post::create(pool, &new_post).await?;
|
||||||
|
|
||||||
let comment_form = CommentInsertForm::new(
|
let comment_form = CommentInsertForm::new(
|
||||||
inserted_timmy.id,
|
inserted_timmy.id,
|
||||||
inserted_post.id,
|
inserted_post.id,
|
||||||
"A test comment vv".into(),
|
"A test comment vv".into(),
|
||||||
);
|
);
|
||||||
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
|
let inserted_comment = Comment::create(pool, &comment_form, None).await?;
|
||||||
|
|
||||||
// Timmy upvotes his own post
|
// Timmy upvotes his own post
|
||||||
let timmy_post_vote_form = PostLikeForm {
|
let timmy_post_vote_form = PostLikeForm {
|
||||||
|
@ -148,7 +145,7 @@ mod tests {
|
||||||
person_id: inserted_timmy.id,
|
person_id: inserted_timmy.id,
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
PostLike::like(pool, &timmy_post_vote_form).await.unwrap();
|
PostLike::like(pool, &timmy_post_vote_form).await?;
|
||||||
|
|
||||||
// Sara downvotes timmy's post
|
// Sara downvotes timmy's post
|
||||||
let sara_post_vote_form = PostLikeForm {
|
let sara_post_vote_form = PostLikeForm {
|
||||||
|
@ -156,7 +153,7 @@ mod tests {
|
||||||
person_id: inserted_sara.id,
|
person_id: inserted_sara.id,
|
||||||
score: -1,
|
score: -1,
|
||||||
};
|
};
|
||||||
PostLike::like(pool, &sara_post_vote_form).await.unwrap();
|
PostLike::like(pool, &sara_post_vote_form).await?;
|
||||||
|
|
||||||
let expected_post_vote_views = [
|
let expected_post_vote_views = [
|
||||||
VoteView {
|
VoteView {
|
||||||
|
@ -171,9 +168,7 @@ mod tests {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let read_post_vote_views = VoteView::list_for_post(pool, inserted_post.id, None, None)
|
let read_post_vote_views = VoteView::list_for_post(pool, inserted_post.id, None, None).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(read_post_vote_views, expected_post_vote_views);
|
assert_eq!(read_post_vote_views, expected_post_vote_views);
|
||||||
|
|
||||||
// Timothy votes down his own comment
|
// Timothy votes down his own comment
|
||||||
|
@ -183,9 +178,7 @@ mod tests {
|
||||||
person_id: inserted_timmy.id,
|
person_id: inserted_timmy.id,
|
||||||
score: -1,
|
score: -1,
|
||||||
};
|
};
|
||||||
CommentLike::like(pool, &timmy_comment_vote_form)
|
CommentLike::like(pool, &timmy_comment_vote_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Sara upvotes timmy's comment
|
// Sara upvotes timmy's comment
|
||||||
let sara_comment_vote_form = CommentLikeForm {
|
let sara_comment_vote_form = CommentLikeForm {
|
||||||
|
@ -194,9 +187,7 @@ mod tests {
|
||||||
person_id: inserted_sara.id,
|
person_id: inserted_sara.id,
|
||||||
score: 1,
|
score: 1,
|
||||||
};
|
};
|
||||||
CommentLike::like(pool, &sara_comment_vote_form)
|
CommentLike::like(pool, &sara_comment_vote_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let expected_comment_vote_views = [
|
let expected_comment_vote_views = [
|
||||||
VoteView {
|
VoteView {
|
||||||
|
@ -211,9 +202,8 @@ mod tests {
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
let read_comment_vote_views = VoteView::list_for_comment(pool, inserted_comment.id, None, None)
|
let read_comment_vote_views =
|
||||||
.await
|
VoteView::list_for_comment(pool, inserted_comment.id, None, None).await?;
|
||||||
.unwrap();
|
|
||||||
assert_eq!(read_comment_vote_views, expected_comment_vote_views);
|
assert_eq!(read_comment_vote_views, expected_comment_vote_views);
|
||||||
|
|
||||||
// Ban timmy from that community
|
// Ban timmy from that community
|
||||||
|
@ -222,36 +212,26 @@ mod tests {
|
||||||
person_id: inserted_timmy.id,
|
person_id: inserted_timmy.id,
|
||||||
expires: None,
|
expires: None,
|
||||||
};
|
};
|
||||||
CommunityPersonBan::ban(pool, &ban_timmy_form)
|
CommunityPersonBan::ban(pool, &ban_timmy_form).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// Make sure creator_banned_from_community is true
|
// Make sure creator_banned_from_community is true
|
||||||
let read_comment_vote_views_after_ban =
|
let read_comment_vote_views_after_ban =
|
||||||
VoteView::list_for_comment(pool, inserted_comment.id, None, None)
|
VoteView::list_for_comment(pool, inserted_comment.id, None, None).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(
|
assert!(read_comment_vote_views_after_ban
|
||||||
read_comment_vote_views_after_ban
|
.first()
|
||||||
.first()
|
.is_some_and(|c| c.creator_banned_from_community));
|
||||||
.unwrap()
|
|
||||||
.creator_banned_from_community
|
|
||||||
);
|
|
||||||
|
|
||||||
let read_post_vote_views_after_ban =
|
let read_post_vote_views_after_ban =
|
||||||
VoteView::list_for_post(pool, inserted_post.id, None, None)
|
VoteView::list_for_post(pool, inserted_post.id, None, None).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert!(
|
assert!(read_post_vote_views_after_ban
|
||||||
read_post_vote_views_after_ban
|
.get(1)
|
||||||
.get(1)
|
.is_some_and(|p| p.creator_banned_from_community));
|
||||||
.unwrap()
|
|
||||||
.creator_banned_from_community
|
|
||||||
);
|
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Instance::delete(pool, inserted_instance.id).await.unwrap();
|
Instance::delete(pool, inserted_instance.id).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,7 +303,6 @@ impl CommentReplyQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{comment_reply_view::CommentReplyQuery, structs::CommentReplyView};
|
use crate::{comment_reply_view::CommentReplyQuery, structs::CommentReplyView};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::structs::{CommunityModeratorView, CommunityView, PersonView};
|
use crate::structs::{CommunityModeratorView, CommunitySortType, CommunityView, PersonView};
|
||||||
use diesel::{
|
use diesel::{
|
||||||
pg::Pg,
|
pg::Pg,
|
||||||
result::Error,
|
result::Error,
|
||||||
|
@ -22,7 +22,16 @@ use lemmy_db_schema::{
|
||||||
instance_block,
|
instance_block,
|
||||||
},
|
},
|
||||||
source::{community::CommunityFollower, local_user::LocalUser, site::Site},
|
source::{community::CommunityFollower, local_user::LocalUser, site::Site},
|
||||||
utils::{fuzzy_search, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn},
|
utils::{
|
||||||
|
functions::lower,
|
||||||
|
fuzzy_search,
|
||||||
|
limit_and_offset,
|
||||||
|
DbConn,
|
||||||
|
DbPool,
|
||||||
|
ListFn,
|
||||||
|
Queries,
|
||||||
|
ReadFn,
|
||||||
|
},
|
||||||
ListingType,
|
ListingType,
|
||||||
PostSortType,
|
PostSortType,
|
||||||
};
|
};
|
||||||
|
@ -103,7 +112,7 @@ fn queries<'a>() -> Queries<
|
||||||
};
|
};
|
||||||
|
|
||||||
let list = move |mut conn: DbConn<'a>, (options, site): (CommunityQuery<'a>, &'a Site)| async move {
|
let list = move |mut conn: DbConn<'a>, (options, site): (CommunityQuery<'a>, &'a Site)| async move {
|
||||||
use PostSortType::*;
|
use CommunitySortType::*;
|
||||||
|
|
||||||
// The left join below will return None in this case
|
// The left join below will return None in this case
|
||||||
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
let person_id_join = options.local_user.person_id().unwrap_or(PersonId(-1));
|
||||||
|
@ -112,9 +121,14 @@ fn queries<'a>() -> Queries<
|
||||||
|
|
||||||
if let Some(search_term) = options.search_term {
|
if let Some(search_term) = options.search_term {
|
||||||
let searcher = fuzzy_search(&search_term);
|
let searcher = fuzzy_search(&search_term);
|
||||||
query = query
|
let name_filter = community::name.ilike(searcher.clone());
|
||||||
.filter(community::name.ilike(searcher.clone()))
|
let title_filter = community::title.ilike(searcher.clone());
|
||||||
.or_filter(community::title.ilike(searcher))
|
let description_filter = community::description.ilike(searcher.clone());
|
||||||
|
query = if options.title_only.unwrap_or_default() {
|
||||||
|
query.filter(name_filter.or(title_filter))
|
||||||
|
} else {
|
||||||
|
query.filter(name_filter.or(title_filter.or(description_filter)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hide deleted and removed for non-admins or mods
|
// Hide deleted and removed for non-admins or mods
|
||||||
|
@ -143,6 +157,8 @@ fn queries<'a>() -> Queries<
|
||||||
}
|
}
|
||||||
TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
|
TopMonth => query = query.order_by(community_aggregates::users_active_month.desc()),
|
||||||
TopWeek => query = query.order_by(community_aggregates::users_active_week.desc()),
|
TopWeek => query = query.order_by(community_aggregates::users_active_week.desc()),
|
||||||
|
NameAsc => query = query.order_by(lower(community::name).asc()),
|
||||||
|
NameDesc => query = query.order_by(lower(community::name).desc()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(listing_type) = options.listing_type {
|
if let Some(listing_type) = options.listing_type {
|
||||||
|
@ -223,12 +239,39 @@ impl CommunityView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PostSortType> for CommunitySortType {
|
||||||
|
fn from(value: PostSortType) -> Self {
|
||||||
|
match value {
|
||||||
|
PostSortType::Active => Self::Active,
|
||||||
|
PostSortType::Hot => Self::Hot,
|
||||||
|
PostSortType::New => Self::New,
|
||||||
|
PostSortType::Old => Self::Old,
|
||||||
|
PostSortType::TopDay => Self::TopDay,
|
||||||
|
PostSortType::TopWeek => Self::TopWeek,
|
||||||
|
PostSortType::TopMonth => Self::TopMonth,
|
||||||
|
PostSortType::TopYear => Self::TopYear,
|
||||||
|
PostSortType::TopAll => Self::TopAll,
|
||||||
|
PostSortType::MostComments => Self::MostComments,
|
||||||
|
PostSortType::NewComments => Self::NewComments,
|
||||||
|
PostSortType::TopHour => Self::TopHour,
|
||||||
|
PostSortType::TopSixHour => Self::TopSixHour,
|
||||||
|
PostSortType::TopTwelveHour => Self::TopTwelveHour,
|
||||||
|
PostSortType::TopThreeMonths => Self::TopThreeMonths,
|
||||||
|
PostSortType::TopSixMonths => Self::TopSixMonths,
|
||||||
|
PostSortType::TopNineMonths => Self::TopNineMonths,
|
||||||
|
PostSortType::Controversial => Self::Controversial,
|
||||||
|
PostSortType::Scaled => Self::Scaled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CommunityQuery<'a> {
|
pub struct CommunityQuery<'a> {
|
||||||
pub listing_type: Option<ListingType>,
|
pub listing_type: Option<ListingType>,
|
||||||
pub sort: Option<PostSortType>,
|
pub sort: Option<CommunitySortType>,
|
||||||
pub local_user: Option<&'a LocalUser>,
|
pub local_user: Option<&'a LocalUser>,
|
||||||
pub search_term: Option<String>,
|
pub search_term: Option<String>,
|
||||||
|
pub title_only: Option<bool>,
|
||||||
pub is_mod_or_admin: bool,
|
pub is_mod_or_admin: bool,
|
||||||
pub show_nsfw: bool,
|
pub show_nsfw: bool,
|
||||||
pub page: Option<i64>,
|
pub page: Option<i64>,
|
||||||
|
@ -242,11 +285,12 @@ impl<'a> CommunityQuery<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::unwrap_used)]
|
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{community_view::CommunityQuery, structs::CommunityView};
|
use crate::{
|
||||||
|
community_view::CommunityQuery,
|
||||||
|
structs::{CommunitySortType, CommunityView},
|
||||||
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
community::{Community, CommunityInsertForm, CommunityUpdateForm},
|
community::{Community, CommunityInsertForm, CommunityUpdateForm},
|
||||||
|
@ -259,41 +303,63 @@ mod tests {
|
||||||
utils::{build_db_pool_for_tests, DbPool},
|
utils::{build_db_pool_for_tests, DbPool},
|
||||||
CommunityVisibility,
|
CommunityVisibility,
|
||||||
};
|
};
|
||||||
|
use lemmy_utils::error::LemmyResult;
|
||||||
use serial_test::serial;
|
use serial_test::serial;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
struct Data {
|
struct Data {
|
||||||
inserted_instance: Instance,
|
inserted_instance: Instance,
|
||||||
local_user: LocalUser,
|
local_user: LocalUser,
|
||||||
inserted_community: Community,
|
inserted_communities: [Community; 3],
|
||||||
site: Site,
|
site: Site,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn init_data(pool: &mut DbPool<'_>) -> Data {
|
async fn init_data(pool: &mut DbPool<'_>) -> LemmyResult<Data> {
|
||||||
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
|
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string()).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let person_name = "tegan".to_string();
|
let person_name = "tegan".to_string();
|
||||||
|
|
||||||
let new_person = PersonInsertForm::test_form(inserted_instance.id, &person_name);
|
let new_person = PersonInsertForm::test_form(inserted_instance.id, &person_name);
|
||||||
|
|
||||||
let inserted_person = Person::create(pool, &new_person).await.unwrap();
|
let inserted_person = Person::create(pool, &new_person).await?;
|
||||||
|
|
||||||
let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
|
let local_user_form = LocalUserInsertForm::test_form(inserted_person.id);
|
||||||
let local_user = LocalUser::create(pool, &local_user_form, vec![])
|
let local_user = LocalUser::create(pool, &local_user_form, vec![]).await?;
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let new_community = CommunityInsertForm::new(
|
let inserted_communities = [
|
||||||
inserted_instance.id,
|
Community::create(
|
||||||
"test_community_3".to_string(),
|
pool,
|
||||||
"nada".to_owned(),
|
&CommunityInsertForm::new(
|
||||||
"pubkey".to_string(),
|
inserted_instance.id,
|
||||||
);
|
"test_community_1".to_string(),
|
||||||
let inserted_community = Community::create(pool, &new_community).await.unwrap();
|
"nada1".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
Community::create(
|
||||||
|
pool,
|
||||||
|
&CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"test_community_2".to_string(),
|
||||||
|
"nada2".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
Community::create(
|
||||||
|
pool,
|
||||||
|
&CommunityInsertForm::new(
|
||||||
|
inserted_instance.id,
|
||||||
|
"test_community_3".to_string(),
|
||||||
|
"nada3".to_owned(),
|
||||||
|
"pubkey".to_string(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?,
|
||||||
|
];
|
||||||
|
|
||||||
let url = Url::parse("http://example.com").unwrap();
|
let url = Url::parse("http://example.com")?;
|
||||||
let site = Site {
|
let site = Site {
|
||||||
id: Default::default(),
|
id: Default::default(),
|
||||||
name: String::new(),
|
name: String::new(),
|
||||||
|
@ -312,74 +378,102 @@ mod tests {
|
||||||
content_warning: None,
|
content_warning: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
Data {
|
Ok(Data {
|
||||||
inserted_instance,
|
inserted_instance,
|
||||||
local_user,
|
local_user,
|
||||||
inserted_community,
|
inserted_communities,
|
||||||
site,
|
site,
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn cleanup(data: Data, pool: &mut DbPool<'_>) {
|
async fn cleanup(data: Data, pool: &mut DbPool<'_>) -> LemmyResult<()> {
|
||||||
Community::delete(pool, data.inserted_community.id)
|
for Community { id, .. } in data.inserted_communities {
|
||||||
.await
|
Community::delete(pool, id).await?;
|
||||||
.unwrap();
|
}
|
||||||
Person::delete(pool, data.local_user.person_id)
|
Person::delete(pool, data.local_user.person_id).await?;
|
||||||
.await
|
Instance::delete(pool, data.inserted_instance.id).await?;
|
||||||
.unwrap();
|
|
||||||
Instance::delete(pool, data.inserted_instance.id)
|
Ok(())
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial]
|
#[serial]
|
||||||
async fn local_only_community() {
|
async fn local_only_community() -> LemmyResult<()> {
|
||||||
let pool = &build_db_pool_for_tests().await;
|
let pool = &build_db_pool_for_tests().await;
|
||||||
let pool = &mut pool.into();
|
let pool = &mut pool.into();
|
||||||
let data = init_data(pool).await;
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
Community::update(
|
Community::update(
|
||||||
pool,
|
pool,
|
||||||
data.inserted_community.id,
|
data.inserted_communities[0].id,
|
||||||
&CommunityUpdateForm {
|
&CommunityUpdateForm {
|
||||||
visibility: Some(CommunityVisibility::LocalOnly),
|
visibility: Some(CommunityVisibility::LocalOnly),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let unauthenticated_query = CommunityQuery {
|
let unauthenticated_query = CommunityQuery {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
assert_eq!(
|
||||||
assert_eq!(0, unauthenticated_query.len());
|
data.inserted_communities.len() - 1,
|
||||||
|
unauthenticated_query.len()
|
||||||
|
);
|
||||||
|
|
||||||
let authenticated_query = CommunityQuery {
|
let authenticated_query = CommunityQuery {
|
||||||
local_user: Some(&data.local_user),
|
local_user: Some(&data.local_user),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
.list(&data.site, pool)
|
.list(&data.site, pool)
|
||||||
.await
|
.await?;
|
||||||
.unwrap();
|
assert_eq!(data.inserted_communities.len(), authenticated_query.len());
|
||||||
assert_eq!(1, authenticated_query.len());
|
|
||||||
|
|
||||||
let unauthenticated_community =
|
let unauthenticated_community =
|
||||||
CommunityView::read(pool, data.inserted_community.id, None, false).await;
|
CommunityView::read(pool, data.inserted_communities[0].id, None, false).await;
|
||||||
assert!(unauthenticated_community.is_err());
|
assert!(unauthenticated_community.is_err());
|
||||||
|
|
||||||
let authenticated_community = CommunityView::read(
|
let authenticated_community = CommunityView::read(
|
||||||
pool,
|
pool,
|
||||||
data.inserted_community.id,
|
data.inserted_communities[0].id,
|
||||||
Some(&data.local_user),
|
Some(&data.local_user),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
assert!(authenticated_community.is_ok());
|
assert!(authenticated_community.is_ok());
|
||||||
|
|
||||||
cleanup(data, pool).await;
|
cleanup(data, pool).await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
#[serial]
|
||||||
|
async fn community_sort_name() -> LemmyResult<()> {
|
||||||
|
let pool = &build_db_pool_for_tests().await;
|
||||||
|
let pool = &mut pool.into();
|
||||||
|
let data = init_data(pool).await?;
|
||||||
|
|
||||||
|
let query = CommunityQuery {
|
||||||
|
sort: Some(CommunitySortType::NameAsc),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let communities = query.list(&data.site, pool).await?;
|
||||||
|
for (i, c) in communities.iter().enumerate().skip(1) {
|
||||||
|
let prev = communities.get(i - 1).expect("No previous community?");
|
||||||
|
assert!(c.community.title.cmp(&prev.community.title).is_ge());
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = CommunityQuery {
|
||||||
|
sort: Some(CommunitySortType::NameDesc),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let communities = query.list(&data.site, pool).await?;
|
||||||
|
for (i, c) in communities.iter().enumerate().skip(1) {
|
||||||
|
let prev = communities.get(i - 1).expect("No previous community?");
|
||||||
|
assert!(c.community.title.cmp(&prev.community.title).is_le());
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup(data, pool).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -303,7 +303,6 @@ impl PersonMentionQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[allow(clippy::indexing_slicing)]
|
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
use crate::{person_mention_view::PersonMentionQuery, structs::PersonMentionView};
|
use crate::{person_mention_view::PersonMentionQuery, structs::PersonMentionView};
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue