]> Untitled Git - lemmy.git/commitdiff
Merge branch 'drone-io-dess' into move_views_to_diesel_drone
authorDessalines <tyhou13@gmx.com>
Mon, 21 Dec 2020 02:48:29 +0000 (21:48 -0500)
committerDessalines <tyhou13@gmx.com>
Mon, 21 Dec 2020 02:48:29 +0000 (21:48 -0500)
14 files changed:
1  2 
Cargo.lock
Cargo.toml
api_tests/src/comment.spec.ts
api_tests/src/community.spec.ts
api_tests/src/follow.spec.ts
api_tests/src/post.spec.ts
api_tests/src/private_message.spec.ts
api_tests/src/shared.ts
api_tests/src/user.spec.ts
lemmy_apub/src/activity_queue.rs
lemmy_db/Cargo.toml
lemmy_db/src/lib.rs
test.sh
tests/integration_test.rs

diff --combined Cargo.lock
index 23622c17a7f783a7e09a467a3ab6ec336990d8c7,7144eac1bcdc5f5ba1c782a8f15f667cc9987b36..815cd20f69d4e55b5ca064fba84937a6302418a4
@@@ -8,7 -8,7 +8,7 @@@ checksum = "5e9fedbe571e267d9b93d071bdc
  dependencies = [
   "chrono",
   "mime",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "thiserror",
   "url",
@@@ -21,7 -21,7 +21,7 @@@ source = "registry+https://github.com/r
  checksum = "bb8e19a0810cc25df3535061a08b7d8f8a734d309ea4411c57a9767e4a2ffa0e"
  dependencies = [
   "activitystreams",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
  ]
  
@@@ -33,8 -33,8 +33,8 @@@ checksum = "1be241f88f3b1e7e9a3fbe3b5a8
  dependencies = [
   "actix-rt",
   "actix_derive",
 - "bitflags 1.2.1",
 - "bytes",
 + "bitflags",
 + "bytes 0.5.6",
   "crossbeam-channel 0.4.4",
   "derive_more",
   "futures-channel",
@@@ -44,7 -44,7 +44,7 @@@
   "parking_lot",
   "pin-project 0.4.27",
   "smallvec",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "tokio-util",
   "trust-dns-proto",
   "trust-dns-resolver",
@@@ -56,13 -56,13 +56,13 @@@ version = "0.3.0
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570"
  dependencies = [
 - "bitflags 1.2.1",
 - "bytes",
 + "bitflags",
 + "bytes 0.5.6",
   "futures-core",
   "futures-sink",
   "log",
   "pin-project 0.4.27",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "tokio-util",
  ]
  
@@@ -90,14 -90,14 +90,14 @@@ dependencies = 
  
  [[package]]
  name = "actix-files"
 -version = "0.4.0"
 +version = "0.4.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "5fc0a9181e93c91dc7eb401a0debaed5c8294e12019c307c72fd7a1731b672fc"
 +checksum = "d031468a7859f71674e5531bd05137e0ea5de05ec9a917314330b88c582e2e0a"
  dependencies = [
   "actix-service",
   "actix-web",
 - "bitflags 1.2.1",
 - "bytes",
 + "bitflags",
 + "bytes 0.5.6",
   "derive_more",
   "futures-core",
   "futures-util",
  
  [[package]]
  name = "actix-http"
 -version = "2.0.0"
 +version = "2.2.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "05dd80ba8f27c4a34357c07e338c8f5c38f8520e6d626ca1727d8fecc41b0cab"
 +checksum = "452299e87817ae5673910e53c243484ca38be3828db819b6011736fc6982e874"
  dependencies = [
   "actix-codec",
   "actix-connect",
   "actix-threadpool",
   "actix-tls",
   "actix-utils",
 - "base64 0.12.3",
 - "bitflags 1.2.1",
 + "base64 0.13.0",
 + "bitflags",
   "brotli2",
 - "bytes",
 + "bytes 0.5.6",
   "cookie",
   "copyless",
   "derive_more",
   "log",
   "mime",
   "percent-encoding",
 - "pin-project 0.4.27",
 - "rand 0.7.3",
 + "pin-project 1.0.2",
 + "rand",
   "regex",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "serde_urlencoded",
 - "sha-1 0.9.1",
 + "sha-1 0.9.2",
   "slab",
 - "time 0.2.22",
 + "time 0.2.23",
  ]
  
  [[package]]
  name = "actix-macros"
 -version = "0.1.2"
 +version = "0.1.3"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a60f9ba7c4e6df97f3aacb14bb5c0cd7d98a49dcbaed0d7f292912ad9a6a3ed2"
 +checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655"
  dependencies = [
   "quote",
   "syn",
@@@ -176,7 -176,7 +176,7 @@@ dependencies = 
   "http",
   "log",
   "regex",
 - "serde 1.0.117",
 + "serde 1.0.118",
  ]
  
  [[package]]
@@@ -191,7 -191,7 +191,7 @@@ dependencies = 
   "futures-channel",
   "futures-util",
   "smallvec",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
  ]
  
  [[package]]
@@@ -278,8 -278,8 +278,8 @@@ dependencies = 
   "actix-codec",
   "actix-rt",
   "actix-service",
 - "bitflags 1.2.1",
 - "bytes",
 + "bitflags",
 + "bytes 0.5.6",
   "either",
   "futures-channel",
   "futures-sink",
  
  [[package]]
  name = "actix-web"
 -version = "3.1.0"
 +version = "3.3.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "c1b12fe25e11cd9ed2ef2e428427eb6178a1b363f3f7f0dab8278572f11b2da1"
 +checksum = "e641d4a172e7faa0862241a20ff4f1f5ab0ab7c279f00c2d4587b77483477b86"
  dependencies = [
   "actix-codec",
   "actix-http",
   "actix-utils",
   "actix-web-codegen",
   "awc",
 - "bytes",
 + "bytes 0.5.6",
   "derive_more",
   "encoding_rs",
   "futures-channel",
   "fxhash",
   "log",
   "mime",
 - "pin-project 0.4.27",
 + "pin-project 1.0.2",
   "regex",
   "rustls",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "serde_urlencoded",
   "socket2",
 - "time 0.2.22",
 - "tinyvec 1.0.1",
 + "time 0.2.23",
 + "tinyvec",
   "url",
  ]
  
@@@ -339,7 -339,7 +339,7 @@@ dependencies = 
   "actix-codec",
   "actix-http",
   "actix-web",
 - "bytes",
 + "bytes 0.5.6",
   "futures-channel",
   "futures-core",
   "pin-project 0.4.27",
  
  [[package]]
  name = "actix-web-codegen"
 -version = "0.3.0"
 +version = "0.4.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "750ca8fb60bbdc79491991650ba5d2ae7cd75f3fc00ead51390cfe9efda0d4d8"
 +checksum = "ad26f77093333e0e7c6ffe54ebe3582d908a104e448723eec6d43d08b07143fb"
  dependencies = [
   "proc-macro2",
   "quote",
@@@ -369,9 -369,9 +369,9 @@@ dependencies = 
  
  [[package]]
  name = "addr2line"
 -version = "0.13.0"
 +version = "0.14.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
 +checksum = "7c0929d69e78dd9bf5408269919fcbcaeb2e35e5d43e5815517cdc6a8e11a423"
  dependencies = [
   "gimli",
  ]
@@@ -390,18 -390,24 +390,18 @@@ checksum = "aae1277d39aeec15cb388266ecc
  
  [[package]]
  name = "aho-corasick"
 -version = "0.7.14"
 +version = "0.7.15"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b476ce7103678b0c6d3d395dbbae31d48ff910bd28be979ba5d48c6351131d0d"
 +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5"
  dependencies = [
   "memchr",
  ]
  
  [[package]]
  name = "anyhow"
 -version = "1.0.33"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a1fd36ffbb1fb7c834eac128ea8d0e310c5aeb635548f9d58861e1308d46e71c"
 -
 -[[package]]
 -name = "arc-swap"
 -version = "0.4.7"
 +version = "1.0.35"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
 +checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4"
  
  [[package]]
  name = "arrayvec"
@@@ -420,9 -426,9 +420,9 @@@ dependencies = 
  
  [[package]]
  name = "async-trait"
 -version = "0.1.41"
 +version = "0.1.42"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b246867b8b3b6ae56035f1eb1ed557c1d8eae97f0d53696138a50fa0e3a3b8c0"
 +checksum = "8d3a45e77e34375a7923b1e8febb049bb011f064714a8e17a1a616fef01da13d"
  dependencies = [
   "proc-macro2",
   "quote",
@@@ -448,25 -454,24 +448,25 @@@ checksum = "cdb031dd78e28731d87d56cc8ff
  
  [[package]]
  name = "awc"
 -version = "2.0.0"
 +version = "2.0.3"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "150e00c06683ab44c5f97d033950e5d87a7a042d06d77f5eecb443cbd23d0575"
 +checksum = "b381e490e7b0cfc37ebc54079b0413d8093ef43d14a4e4747083f7fa47a9e691"
  dependencies = [
   "actix-codec",
   "actix-http",
   "actix-rt",
   "actix-service",
 - "base64 0.12.3",
 - "bytes",
 + "base64 0.13.0",
 + "bytes 0.5.6",
 + "cfg-if 1.0.0",
   "derive_more",
   "futures-core",
   "log",
   "mime",
   "percent-encoding",
 - "rand 0.7.3",
 + "rand",
   "rustls",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "serde_urlencoded",
  ]
@@@ -495,11 -500,11 +495,11 @@@ dependencies = 
   "chrono",
   "log",
   "num_cpus",
 - "rand 0.7.3",
 - "serde 1.0.117",
 + "rand",
 + "serde 1.0.118",
   "serde_json",
   "thiserror",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "uuid",
  ]
  
@@@ -515,32 -520,41 +515,32 @@@ dependencies = 
   "async-trait",
   "chrono",
   "log",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "thiserror",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "uuid",
  ]
  
  [[package]]
  name = "backtrace"
 -version = "0.3.53"
 +version = "0.3.55"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "707b586e0e2f247cbde68cdd2c3ce69ea7b7be43e1c5b426e37c9319c4b9838e"
 +checksum = "ef5140344c85b01f9bbb4d4b7288a8aa4b3287ccef913a14bcc78a1063623598"
  dependencies = [
   "addr2line",
   "cfg-if 1.0.0",
   "libc",
 - "miniz_oxide",
 + "miniz_oxide 0.4.3",
   "object",
   "rustc-demangle",
  ]
  
  [[package]]
  name = "base-x"
 -version = "0.2.6"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1b20b618342cf9891c292c4f5ac2cde7287cc5c87e87e9c769d617793607dec1"
 -
 -[[package]]
 -name = "base64"
 -version = "0.5.2"
 +version = "0.2.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
 -dependencies = [
 - "byteorder",
 -]
 +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b"
  
  [[package]]
  name = "base64"
@@@ -556,32 -570,27 +556,32 @@@ checksum = "904dfeac50f3cdaba28fc6f57fd
  
  [[package]]
  name = "bcrypt"
 -version = "0.8.2"
 +version = "0.9.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e2cab630912253fb9dc92c0e2fabd0a7b51f5a5a4007177cfa31e517015b7204"
 +checksum = "a4d0faafe9e089674fc3efdb311ff5253d445c79d85d1d28bd3ace76d45e7164"
  dependencies = [
 - "base64 0.12.3",
 + "base64 0.13.0",
   "blowfish",
 - "byteorder",
 - "getrandom",
 + "getrandom 0.2.0",
  ]
  
  [[package]]
  name = "bitflags"
 -version = "0.7.0"
 +version = "1.2.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
 +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
  
  [[package]]
 -name = "bitflags"
 -version = "1.2.1"
 +name = "bitvec"
 +version = "0.19.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 +checksum = "a7ba35e9565969edb811639dbebfe34edc0368e472c5018474c8eb2543397f81"
 +dependencies = [
 + "funty",
 + "radium",
 + "tap",
 + "wyz",
 +]
  
  [[package]]
  name = "block-buffer"
@@@ -604,6 -613,15 +604,6 @@@ dependencies = 
   "generic-array 0.14.4",
  ]
  
 -[[package]]
 -name = "block-cipher"
 -version = "0.8.0"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "f337a3e6da609650eb74e02bc9fac7b735049f7623ab12f2e4c719316fcc7e80"
 -dependencies = [
 - "generic-array 0.14.4",
 -]
 -
  [[package]]
  name = "block-padding"
  version = "0.1.5"
@@@ -615,12 -633,12 +615,12 @@@ dependencies = 
  
  [[package]]
  name = "blowfish"
 -version = "0.6.0"
 +version = "0.7.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "0f06850ba969bc59388b2cc0a4f186fc6d9d37208863b15b84ae3866ac90ac06"
 +checksum = "32fa6a061124e37baba002e496d203e23ba3d7b73750be82dbfbc92913048a5b"
  dependencies = [
 - "block-cipher",
   "byteorder",
 + "cipher",
   "opaque-debug 0.3.0",
  ]
  
@@@ -646,11 -664,11 +646,11 @@@ dependencies = 
  
  [[package]]
  name = "buf-min"
 -version = "0.1.1"
 +version = "0.2.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b6ae7069aad07c7cdefe6a22a671f00650728bd2331a4cc62e1e5d0becdf9ca4"
 +checksum = "881e704e61d0fb41d7c6c9ae2bd790eb8c13dc974ae102fb98c788b4fdea4349"
  dependencies = [
 - "bytes",
 + "bytes 0.6.0",
  ]
  
  [[package]]
@@@ -683,33 -701,33 +683,33 @@@ version = "0.5.6
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
  
 +[[package]]
 +name = "bytes"
 +version = "0.6.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "e0dcbc35f504eb6fc275a6d20e4ebcda18cf50d40ba6fabff8c711fa16cb3b16"
 +
  [[package]]
  name = "bytestring"
  version = "0.1.5"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "fc7c05fa5172da78a62d9949d662d2ac89d4cc7355d7b49adee5163f1fb3f363"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
  ]
  
 -[[package]]
 -name = "c_vec"
 -version = "1.3.3"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "f8a318911dce53b5f1ca6539c44f5342c632269f0fa7ea3e35f32458c27a7c30"
 -
  [[package]]
  name = "captcha"
 -version = "0.0.7"
 +version = "0.0.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "4d060a3be43adb2fe89d3448e9a193149806139b1ce99281865fcab7aeaf04ed"
 +checksum = "29256038744434f6d0e1328d7c9050f14aa5fc8562ff065b9e9481ac293ba5bc"
  dependencies = [
 - "base64 0.5.2",
 + "base64 0.13.0",
 + "hound",
   "image",
   "lodepng",
 - "rand 0.3.23",
 + "rand",
   "serde_json",
 - "time 0.1.44",
  ]
  
  [[package]]
@@@ -720,9 -738,9 +720,9 @@@ checksum = "7b02b629252fe8ef64604614095
  
  [[package]]
  name = "cc"
 -version = "1.0.61"
 +version = "1.0.66"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ed67cbde08356238e75fc4656be4749481eeffb09e19f320a25237d5221c985d"
 +checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
  
  [[package]]
  name = "cfg-if"
@@@ -744,19 -762,19 +744,19 @@@ checksum = "670ad68c9088c2a963aaa298cb3
  dependencies = [
   "libc",
   "num-integer",
 - "num-traits 0.2.12",
 - "serde 1.0.117",
 + "num-traits 0.2.14",
 + "serde 1.0.118",
   "time 0.1.44",
   "winapi 0.3.9",
  ]
  
  [[package]]
 -name = "cloudabi"
 -version = "0.1.0"
 +name = "cipher"
 +version = "0.2.5"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467"
 +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801"
  dependencies = [
 - "bitflags 1.2.1",
 + "generic-array 0.14.4",
  ]
  
  [[package]]
@@@ -767,9 -785,9 +767,9 @@@ checksum = "3d7b894f5411737b7867f482795
  
  [[package]]
  name = "comrak"
 -version = "0.8.2"
 +version = "0.9.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "0d325e4f2ffff52ca77d995bb675494d5364aa332499d5f7c7fbb28c25e671f6"
 +checksum = "bcfb8008d04126b176c76cbfdecf9a2ccb4bacc70af87c8da6136d63d7b7292a"
  dependencies = [
   "entities",
   "lazy_static",
@@@ -791,24 -809,24 +791,24 @@@ checksum = "19b076e143e1d9538dde65da30f
  dependencies = [
   "lazy_static",
   "nom 5.1.2",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde-hjson",
  ]
  
  [[package]]
  name = "const_fn"
 -version = "0.4.2"
 +version = "0.4.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ce90df4c658c62f12d78f7508cf92f9173e5184a539c10bfe54a3107b3ffd0f2"
 +checksum = "cd51eab21ab4fd6a3bf889e2d0958c0a6e3a61ad04260325e919e652a2a62826"
  
  [[package]]
  name = "cookie"
 -version = "0.14.2"
 +version = "0.14.3"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1373a16a4937bc34efec7b391f9c1500c30b8478a701a4f44c9165cc0475a6e0"
 +checksum = "784ad0fbab4f3e9cef09f20e0aea6000ae08d2cb98ac4c0abc53df18803d702f"
  dependencies = [
   "percent-encoding",
 - "time 0.2.22",
 + "time 0.2.23",
   "version_check 0.9.2",
  ]
  
@@@ -820,9 -838,9 +820,9 @@@ checksum = "a2df960f5d869b2dd8532793fde
  
  [[package]]
  name = "core-foundation"
 -version = "0.7.0"
 +version = "0.9.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171"
 +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62"
  dependencies = [
   "core-foundation-sys",
   "libc",
  
  [[package]]
  name = "core-foundation-sys"
 -version = "0.7.0"
 +version = "0.8.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac"
 +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b"
  
  [[package]]
  name = "cpuid-bool"
@@@ -866,7 -884,7 +866,7 @@@ source = "registry+https://github.com/r
  checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
  dependencies = [
   "cfg-if 1.0.0",
 - "crossbeam-utils 0.8.0",
 + "crossbeam-utils 0.8.1",
  ]
  
  [[package]]
@@@ -877,18 -895,18 +877,18 @@@ checksum = "94af6efb46fef72616855b036a6
  dependencies = [
   "cfg-if 1.0.0",
   "crossbeam-epoch",
 - "crossbeam-utils 0.8.0",
 + "crossbeam-utils 0.8.1",
  ]
  
  [[package]]
  name = "crossbeam-epoch"
 -version = "0.9.0"
 +version = "0.9.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ec0f606a85340376eef0d6d8fec399e6d4a544d648386c6645eb6d0653b27d9f"
 +checksum = "a1aaa739f95311c2c7887a76863f500026092fb1dce0161dab577e559ef3569d"
  dependencies = [
   "cfg-if 1.0.0",
   "const_fn",
 - "crossbeam-utils 0.8.0",
 + "crossbeam-utils 0.8.1",
   "lazy_static",
   "memoffset",
   "scopeguard",
@@@ -907,12 -925,13 +907,12 @@@ dependencies = 
  
  [[package]]
  name = "crossbeam-utils"
 -version = "0.8.0"
 +version = "0.8.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ec91540d98355f690a86367e566ecad2e9e579f230230eb7c21398372be73ea5"
 +checksum = "02d96d1e189ef58269ebe5b97953da3274d83a93af647c2ddd6f9dab28cedb8d"
  dependencies = [
   "autocfg",
   "cfg-if 1.0.0",
 - "const_fn",
   "lazy_static",
  ]
  
@@@ -953,9 -972,9 +953,9 @@@ dependencies = 
  
  [[package]]
  name = "deflate"
 -version = "0.7.20"
 +version = "0.8.6"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "707b6a7b384888a70c8d2e8650b3e60170dfc6a67bb4aa67b6dfca57af4bedb4"
 +checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
  dependencies = [
   "adler32",
   "byteorder",
@@@ -1003,7 -1022,7 +1003,7 @@@ version = "1.4.5
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "3e2de9deab977a153492a1468d1b1c0662c1cf39e5ea87d0c060ecd59ef18d8c"
  dependencies = [
 - "bitflags 1.2.1",
 + "bitflags",
   "byteorder",
   "chrono",
   "diesel_derives",
@@@ -1057,6 -1076,12 +1057,6 @@@ version = "1.0.4
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0"
  
 -[[package]]
 -name = "dtoa"
 -version = "0.4.6"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "134951f4028bdadb9b84baf4232681efbf277da25144b9b0ad65df75946c422b"
 -
  [[package]]
  name = "either"
  version = "1.6.1"
@@@ -1065,11 -1090,11 +1065,11 @@@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a
  
  [[package]]
  name = "encoding_rs"
 -version = "0.8.24"
 +version = "0.8.26"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a51b8cf747471cb9499b6d59e59b0444f4c90eba8968c4e44874e92b5b64ace2"
 +checksum = "801bbab217d7f79c0062f4f7205b5d4427c6d1a7bd7aafdd1475f7c59d62b283"
  dependencies = [
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
  ]
  
  [[package]]
@@@ -1090,11 -1115,20 +1090,11 @@@ dependencies = 
   "syn",
  ]
  
 -[[package]]
 -name = "enum_primitive"
 -version = "0.1.1"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180"
 -dependencies = [
 - "num-traits 0.1.43",
 -]
 -
  [[package]]
  name = "env_logger"
 -version = "0.8.1"
 +version = "0.8.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "54532e3223c5af90a6a757c90b5c5521564b07e5e7a958681bcd2afad421cdcd"
 +checksum = "f26ecb66b4bdca6c1409b40fb255eefc2bd4f6d135dab3c3124f80ffa2a9661e"
  dependencies = [
   "atty",
   "humantime",
@@@ -1117,14 -1151,14 +1117,14 @@@ checksum = "e88a8acf291dafb59c2d96e8f59
  
  [[package]]
  name = "flate2"
 -version = "1.0.18"
 +version = "1.0.19"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "da80be589a72651dcda34d8b35bcdc9b7254ad06325611074d9cc0fbb19f60ee"
 +checksum = "7411863d55df97a419aa64cb4d2f167103ea9d767e2c54a1868b7ac3f6b47129"
  dependencies = [
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
   "crc32fast",
   "libc",
 - "miniz_oxide",
 + "miniz_oxide 0.4.3",
  ]
  
  [[package]]
@@@ -1149,14 -1183,10 +1149,14 @@@ source = "registry+https://github.com/r
  checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
  
  [[package]]
 -name = "fuchsia-cprng"
 -version = "0.1.1"
 +name = "form_urlencoded"
 +version = "1.0.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
 +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00"
 +dependencies = [
 + "matches",
 + "percent-encoding",
 +]
  
  [[package]]
  name = "fuchsia-zircon"
@@@ -1164,7 -1194,7 +1164,7 @@@ version = "0.3.3
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
  dependencies = [
 - "bitflags 1.2.1",
 + "bitflags",
   "fuchsia-zircon-sys",
  ]
  
@@@ -1174,17 -1204,11 +1174,17 @@@ version = "0.3.3
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
  
 +[[package]]
 +name = "funty"
 +version = "1.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "0ba62103ce691c2fd80fbae2213dfdda9ce60804973ac6b6e97de818ea7f52c8"
 +
  [[package]]
  name = "futures"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "95314d38584ffbfda215621d723e0a3906f032e03ae5551e650058dac83d4797"
 +checksum = "9b3b0c040a1fe6529d30b3c5944b280c7f0dcb2930d2c3062bca967b602583d0"
  dependencies = [
   "futures-channel",
   "futures-core",
  
  [[package]]
  name = "futures-channel"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "0448174b01148032eed37ac4aed28963aaaa8cfa93569a08e5b479bbc6c2c151"
 +checksum = "4b7109687aa4e177ef6fe84553af6280ef2778bdb7783ba44c9dc3399110fe64"
  dependencies = [
   "futures-core",
   "futures-sink",
  
  [[package]]
  name = "futures-core"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "18eaa56102984bed2c88ea39026cff3ce3b4c7f508ca970cedf2450ea10d4e46"
 +checksum = "847ce131b72ffb13b6109a221da9ad97a64cbe48feb1028356b836b47b8f1748"
  
  [[package]]
  name = "futures-executor"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "f5f8e0c9258abaea85e78ebdda17ef9666d390e987f006be6080dfe354b708cb"
 +checksum = "4caa2b2b68b880003057c1dd49f1ed937e38f22fcf6c212188a121f08cf40a65"
  dependencies = [
   "futures-core",
   "futures-task",
  
  [[package]]
  name = "futures-io"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "6e1798854a4727ff944a7b12aa999f58ce7aa81db80d2dfaaf2ba06f065ddd2b"
 +checksum = "611834ce18aaa1bd13c4b374f5d653e1027cf99b6b502584ff8c9a64413b30bb"
  
  [[package]]
  name = "futures-macro"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e36fccf3fc58563b4a14d265027c627c3b665d7fed489427e88e7cc929559efe"
 +checksum = "77408a692f1f97bcc61dc001d752e00643408fbc922e4d634c655df50d595556"
  dependencies = [
   "proc-macro-hack",
   "proc-macro2",
  
  [[package]]
  name = "futures-sink"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "0e3ca3f17d6e8804ae5d3df7a7d35b2b3a6fe89dac84b31872720fc3060a0b11"
 +checksum = "f878195a49cee50e006b02b93cf7e0a95a38ac7b776b4c4d9cc1207cd20fcb3d"
  
  [[package]]
  name = "futures-task"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "96d502af37186c4fef99453df03e374683f8a1eec9dcc1e66b3b82dc8278ce3c"
 +checksum = "7c554eb5bf48b2426c4771ab68c6b14468b6e76cc90996f528c3338d761a4d0d"
  dependencies = [
   "once_cell",
  ]
  
  [[package]]
  name = "futures-util"
 -version = "0.3.7"
 +version = "0.3.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "abcb44342f62e6f3e8ac427b8aa815f724fd705dfad060b18ac7866c15bb8e34"
 +checksum = "d304cff4a7b99cfb7986f7d43fbe93d175e72e704a8860787cc95e9ffd85cbd2"
  dependencies = [
   "futures-channel",
   "futures-core",
   "futures-sink",
   "futures-task",
   "memchr",
 - "pin-project 1.0.1",
 + "pin-project 1.0.2",
   "pin-utils",
   "proc-macro-hack",
   "proc-macro-nested",
@@@ -1314,32 -1338,21 +1314,32 @@@ dependencies = 
   "wasi 0.9.0+wasi-snapshot-preview1",
  ]
  
 +[[package]]
 +name = "getrandom"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "ee8025cf36f917e6a52cce185b7c7177689b838b7ec138364e50cc2277a56cf4"
 +dependencies = [
 + "cfg-if 0.1.10",
 + "libc",
 + "wasi 0.9.0+wasi-snapshot-preview1",
 +]
 +
  [[package]]
  name = "gif"
 -version = "0.9.2"
 +version = "0.11.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e2e41945ba23db3bf51b24756d73d81acb4f28d85c3dccc32c6fae904438c25f"
 +checksum = "02efba560f227847cb41463a7395c514d127d4f74fff12ef0137fff1b84b96c4"
  dependencies = [
   "color_quant",
 - "lzw",
 + "weezl",
  ]
  
  [[package]]
  name = "gimli"
 -version = "0.22.0"
 +version = "0.23.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
 +checksum = "f6503fe142514ca4799d4c26297c4248239fe8838d827db6bd6065c6ed29a6ce"
  
  [[package]]
  name = "h2"
@@@ -1347,7 -1360,7 +1347,7 @@@ version = "0.2.7
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "fnv",
   "futures-core",
   "futures-sink",
   "http",
   "indexmap",
   "slab",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "tokio-util",
   "tracing",
   "tracing-futures",
@@@ -1396,19 -1409,13 +1396,19 @@@ dependencies = 
   "winapi 0.3.9",
  ]
  
 +[[package]]
 +name = "hound"
 +version = "3.4.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "8a164bb2ceaeff4f42542bdb847c41517c78a60f5649671b2a07312b6e117549"
 +
  [[package]]
  name = "http"
 -version = "0.2.1"
 +version = "0.2.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "28d569972648b2c512421b5f2a405ad6ac9666547189d0c5477a3f200f3e02f9"
 +checksum = "84129d298a6d57d246960ff8eb831ca4af3f96d29e2e28848dae275408658e26"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "fnv",
   "itoa",
  ]
@@@ -1419,7 -1426,7 +1419,7 @@@ version = "0.3.1
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "http",
  ]
  
@@@ -1442,7 -1449,7 +1442,7 @@@ dependencies = 
   "actix-web",
   "awc",
   "base64 0.12.3",
 - "bytes",
 + "bytes 0.5.6",
   "chrono",
   "futures",
   "http-signature-normalization",
@@@ -1458,7 -1465,7 +1458,7 @@@ source = "registry+https://github.com/r
  checksum = "7bc26a68f8963e26453c7fdea9e016e2e31a48ca018a9223f96afe2cca1a4bd1"
  dependencies = [
   "base64 0.12.3",
 - "bytes",
 + "bytes 0.5.6",
   "chrono",
   "futures",
   "http",
   "reqwest",
   "sha2",
   "thiserror",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
  ]
  
  [[package]]
@@@ -1489,11 -1496,11 +1489,11 @@@ checksum = "3c1ad908cc71012b7bea4d0c53b
  
  [[package]]
  name = "hyper"
 -version = "0.13.8"
 +version = "0.13.9"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "2f3afcfae8af5ad0576a31e768415edb627824129e8e5a29b8bfccb2f234e835"
 +checksum = "f6ad767baac13b44d4529fcf58ba2cd0995e36e7b435bc5b039de6f47e880dbf"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "futures-channel",
   "futures-core",
   "futures-util",
   "httparse",
   "httpdate",
   "itoa",
 - "pin-project 0.4.27",
 + "pin-project 1.0.2",
   "socket2",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "tower-service",
   "tracing",
   "want",
@@@ -1517,10 -1524,10 +1517,10 @@@ version = "0.4.3
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "hyper",
   "native-tls",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "tokio-tls",
  ]
  
@@@ -1531,7 -1538,7 +1531,7 @@@ source = "registry+https://github.com/r
  checksum = "2adce67e2c21cd95288ae3d9f2bbb2762cf17c03744628d49679f315ed1e2e58"
  dependencies = [
   "base64 0.13.0",
 - "bytes",
 + "bytes 0.5.6",
   "http",
   "httparse",
   "httpdate",
@@@ -1561,38 -1568,42 +1561,38 @@@ dependencies = 
  
  [[package]]
  name = "image"
 -version = "0.13.0"
 +version = "0.23.12"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1c3f4f5ea213ed9899eca760a8a14091d4b82d33e27cf8ced336ff730e9f6da8"
 +checksum = "7ce04077ead78e39ae8610ad26216aed811996b043d47beed5090db674f9e9b5"
  dependencies = [
 + "bytemuck",
   "byteorder",
 - "enum_primitive",
 + "color_quant",
   "gif",
   "jpeg-decoder",
   "num-iter",
   "num-rational",
 - "num-traits 0.1.43",
 + "num-traits 0.2.14",
   "png",
   "scoped_threadpool",
 + "tiff",
  ]
  
  [[package]]
  name = "indexmap"
 -version = "1.6.0"
 +version = "1.6.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "55e2e4c765aa53a0424761bf9f41aa7a6ac1efa87238f59560640e27fca028f2"
 +checksum = "4fb1fa934250de4de8aef298d81c729a7d33d8c239daa3a7575e6b92bfc7313b"
  dependencies = [
   "autocfg",
   "hashbrown",
  ]
  
 -[[package]]
 -name = "inflate"
 -version = "0.2.0"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "d1238524675af3938a7c74980899535854b88ba07907bb1c944abe5b8fc437e5"
 -
  [[package]]
  name = "instant"
 -version = "0.1.8"
 +version = "0.1.9"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "cb1fc4429a33e1f80d41dc9fea4d108a88bec1de8053878898ae448a0b52f613"
 +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
  dependencies = [
   "cfg-if 1.0.0",
  ]
@@@ -1651,9 -1662,9 +1651,9 @@@ dependencies = 
  
  [[package]]
  name = "js-sys"
 -version = "0.3.45"
 +version = "0.3.46"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ca059e81d9486668f12d455a4ea6daa600bd408134cd17e3d3fb5a32d1f016f8"
 +checksum = "cf3d7383929f7c9c7c2d0fa596f325832df98c3704f2c60553080f7127a58175"
  dependencies = [
   "wasm-bindgen",
  ]
@@@ -1667,7 -1678,7 +1667,7 @@@ dependencies = 
   "base64 0.12.3",
   "pem",
   "ring",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "simple_asn1",
  ]
@@@ -1724,15 -1735,15 +1724,15 @@@ dependencies = 
   "lemmy_websocket",
   "log",
   "openssl",
 - "rand 0.7.3",
 + "rand",
   "reqwest",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "sha2",
   "strum",
   "strum_macros",
   "thiserror",
 - "tokio 0.3.1",
 + "tokio 0.3.6",
   "url",
   "uuid",
  ]
@@@ -1768,15 -1779,15 +1768,15 @@@ dependencies = 
   "log",
   "openssl",
   "percent-encoding",
 - "rand 0.7.3",
 + "rand",
   "reqwest",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "sha2",
   "strum",
   "strum_macros",
   "thiserror",
 - "tokio 0.3.1",
 + "tokio 0.3.6",
   "url",
   "uuid",
  ]
@@@ -1788,11 -1799,12 +1788,12 @@@ dependencies = 
   "bcrypt",
   "chrono",
   "diesel",
+  "diesel_migrations",
   "lazy_static",
   "lemmy_utils",
   "log",
   "regex",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "sha2",
   "strum",
@@@ -1810,7 -1822,7 +1811,7 @@@ dependencies = 
   "log",
   "strum",
   "strum_macros",
 - "tokio 0.3.1",
 + "tokio 0.3.6",
  ]
  
  [[package]]
@@@ -1843,11 -1855,11 +1844,11 @@@ dependencies = 
   "openssl",
   "reqwest",
   "rss",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "sha2",
   "strum",
 - "tokio 0.3.1",
 + "tokio 0.3.6",
   "url",
  ]
  
@@@ -1861,7 -1873,7 +1862,7 @@@ dependencies = 
   "lemmy_db",
   "lemmy_utils",
   "log",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
  ]
  
@@@ -1881,10 -1893,10 +1882,10 @@@ dependencies = 
   "log",
   "openssl",
   "percent-encoding",
 - "rand 0.7.3",
 + "rand",
   "regex",
   "reqwest",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "thiserror",
   "url",
@@@ -1904,20 -1916,20 +1905,20 @@@ dependencies = 
   "lemmy_structs",
   "lemmy_utils",
   "log",
 - "rand 0.7.3",
 + "rand",
   "reqwest",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "strum",
   "strum_macros",
 - "tokio 0.3.1",
 + "tokio 0.3.6",
  ]
  
  [[package]]
  name = "lettre"
 -version = "0.10.0-alpha.3"
 +version = "0.10.0-alpha.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e422b6c03563bc47db09bb61a8ece4f1462de131455beb96c091e2998fa316a2"
 +checksum = "dc8c2fc7873920aca23647e5e86d44ff3f40bbc5a5efaab445c9eb0e001c9f71"
  dependencies = [
   "base64 0.13.0",
   "hostname",
   "idna",
   "mime",
   "native-tls",
 - "nom 5.1.2",
 + "nom 6.0.1",
   "once_cell",
   "quoted_printable",
   "r2d2",
 - "rand 0.7.3",
 + "rand",
   "regex",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_json",
   "uuid",
  ]
@@@ -1943,7 -1955,7 +1944,7 @@@ source = "registry+https://github.com/r
  checksum = "db65c6da02e61f55dae90a0ae427b2a5f6b3e8db09f58d10efab23af92592616"
  dependencies = [
   "arrayvec",
 - "bitflags 1.2.1",
 + "bitflags",
   "cfg-if 0.1.10",
   "ryu",
   "static_assertions",
  
  [[package]]
  name = "libc"
 -version = "0.2.80"
 +version = "0.2.81"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614"
 +checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb"
  
  [[package]]
  name = "linked-hash-map"
@@@ -1973,20 -1985,21 +1974,20 @@@ checksum = "8dd5a6d5999d9907cda8ed67bbd
  
  [[package]]
  name = "lock_api"
 -version = "0.4.1"
 +version = "0.4.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "28247cc5a5be2f05fbcd76dd0cf2c7d3b5400cb978a28042abcd4fa0b3f8261c"
 +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312"
  dependencies = [
   "scopeguard",
  ]
  
  [[package]]
  name = "lodepng"
 -version = "1.2.2"
 +version = "3.2.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "8ac1dfdf85b7d5dea61a620e12c051a72078189366a0b3c0ab331e30847def2f"
 +checksum = "b6eb909184223b89c76d66b80199b7ad4163aebb2519244e6ebac8ba74e67eab"
  dependencies = [
 - "c_vec",
 - "cc",
 + "flate2",
   "libc",
   "rgb",
  ]
@@@ -2009,6 -2022,12 +2010,6 @@@ dependencies = 
   "linked-hash-map 0.5.3",
  ]
  
 -[[package]]
 -name = "lzw"
 -version = "0.10.0"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084"
 -
  [[package]]
  name = "maplit"
  version = "1.0.2"
@@@ -2035,15 -2054,15 +2036,15 @@@ checksum = "60302e4db3a61da70c0cb799197
  
  [[package]]
  name = "memchr"
 -version = "2.3.3"
 +version = "2.3.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
 +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525"
  
  [[package]]
  name = "memoffset"
 -version = "0.5.6"
 +version = "0.6.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
 +checksum = "157b4208e3059a8f9e78d559edc658e13df41410cb3ae03979c83130067fdd87"
  dependencies = [
   "autocfg",
  ]
@@@ -2085,15 -2104,6 +2086,15 @@@ dependencies = 
   "unicase",
  ]
  
 +[[package]]
 +name = "miniz_oxide"
 +version = "0.3.7"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
 +dependencies = [
 + "adler32",
 +]
 +
  [[package]]
  name = "miniz_oxide"
  version = "0.4.3"
@@@ -2106,9 -2116,9 +2107,9 @@@ dependencies = 
  
  [[package]]
  name = "mio"
 -version = "0.6.22"
 +version = "0.6.23"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
 +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
  dependencies = [
   "cfg-if 0.1.10",
   "fuchsia-zircon",
@@@ -2136,9 -2146,9 +2137,9 @@@ dependencies = 
  
  [[package]]
  name = "miow"
 -version = "0.2.1"
 +version = "0.2.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
 +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
  dependencies = [
   "kernel32-sys",
   "net2",
  
  [[package]]
  name = "native-tls"
 -version = "0.2.4"
 +version = "0.2.6"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "2b0d88c06fe90d5ee94048ba40409ef1d9315d86f6f38c2efdaad4fb50c58b2d"
 +checksum = "6fcc7939b5edc4e4f86b1b4a04bb1498afaaf871b1a6691838ed06fcb48d3a3f"
  dependencies = [
   "lazy_static",
   "libc",
  
  [[package]]
  name = "net2"
 -version = "0.2.35"
 +version = "0.2.37"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "3ebc3ec692ed7c9a255596c67808dee269f64655d8baf7b4f0638e51ba1d6853"
 +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
  dependencies = [
   "cfg-if 0.1.10",
   "libc",
@@@ -2196,17 -2206,6 +2197,17 @@@ dependencies = 
   "version_check 0.9.2",
  ]
  
 +[[package]]
 +name = "nom"
 +version = "6.0.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "88034cfd6b4a0d54dd14f4a507eceee36c0b70e5a02236c4e4df571102be17f0"
 +dependencies = [
 + "bitvec",
 + "memchr",
 + "version_check 0.9.2",
 +]
 +
  [[package]]
  name = "num-bigint"
  version = "0.2.6"
@@@ -2215,39 -2214,38 +2216,39 @@@ checksum = "090c7f9998ee0ff65aa5b723e40
  dependencies = [
   "autocfg",
   "num-integer",
 - "num-traits 0.2.12",
 + "num-traits 0.2.14",
  ]
  
  [[package]]
  name = "num-integer"
 -version = "0.1.43"
 +version = "0.1.44"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
 +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
  dependencies = [
   "autocfg",
 - "num-traits 0.2.12",
 + "num-traits 0.2.14",
  ]
  
  [[package]]
  name = "num-iter"
 -version = "0.1.41"
 +version = "0.1.42"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
 +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
  dependencies = [
   "autocfg",
   "num-integer",
 - "num-traits 0.2.12",
 + "num-traits 0.2.14",
  ]
  
  [[package]]
  name = "num-rational"
 -version = "0.1.42"
 +version = "0.3.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e"
 +checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
  dependencies = [
 + "autocfg",
   "num-integer",
 - "num-traits 0.2.12",
 + "num-traits 0.2.14",
  ]
  
  [[package]]
@@@ -2256,14 -2254,14 +2257,14 @@@ version = "0.1.43
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31"
  dependencies = [
 - "num-traits 0.2.12",
 + "num-traits 0.2.14",
  ]
  
  [[package]]
  name = "num-traits"
 -version = "0.2.12"
 +version = "0.2.14"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
 +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
  dependencies = [
   "autocfg",
  ]
@@@ -2280,15 -2278,15 +2281,15 @@@ dependencies = 
  
  [[package]]
  name = "object"
 -version = "0.21.1"
 +version = "0.22.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "37fd5004feb2ce328a52b0b3d01dbf4ffff72583493900ed15f22d4111c51693"
 +checksum = "8d3b63360ec3cb337817c2dbd47ab4a0f170d285d8e5a2064600f3def1402397"
  
  [[package]]
  name = "once_cell"
 -version = "1.4.1"
 +version = "1.5.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad"
 +checksum = "13bd41f508810a131401606d54ac32a467c97172d74ba7662562ebba5ad07fa0"
  
  [[package]]
  name = "opaque-debug"
@@@ -2304,12 -2302,12 +2305,12 @@@ checksum = "624a8340c38c1b80fd549087862
  
  [[package]]
  name = "openssl"
 -version = "0.10.30"
 +version = "0.10.31"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4"
 +checksum = "8d008f51b1acffa0d3450a68606e6a51c123012edaacb0f4e1426bd978869187"
  dependencies = [
 - "bitflags 1.2.1",
 - "cfg-if 0.1.10",
 + "bitflags",
 + "cfg-if 1.0.0",
   "foreign-types",
   "lazy_static",
   "libc",
@@@ -2324,9 -2322,9 +2325,9 @@@ checksum = "77af24da69f9d9341038eba93a0
  
  [[package]]
  name = "openssl-sys"
 -version = "0.9.58"
 +version = "0.9.59"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de"
 +checksum = "de52d8eabd217311538a39bba130d7dea1f1e118010fee7a033d966845e7d5fe"
  dependencies = [
   "autocfg",
   "cc",
  
  [[package]]
  name = "parking_lot"
 -version = "0.11.0"
 +version = "0.11.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a4893845fa2ca272e647da5d0e46660a314ead9c2fdd9a883aabc32e481a8733"
 +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
  dependencies = [
   "instant",
   "lock_api",
  
  [[package]]
  name = "parking_lot_core"
 -version = "0.8.0"
 +version = "0.8.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b"
 +checksum = "d7c6d9b8427445284a09c55be860a15855ab580a417ccad9da88f5a06787ced0"
  dependencies = [
 - "cfg-if 0.1.10",
 - "cloudabi",
 + "cfg-if 1.0.0",
   "instant",
   "libc",
   "redox_syscall",
  
  [[package]]
  name = "pem"
 -version = "0.8.1"
 +version = "0.8.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "59698ea79df9bf77104aefd39cc3ec990cb9693fb59c3b0a70ddf2646fdffb4b"
 +checksum = "f4c220d01f863d13d96ca82359d1e81e64a7c6bf0637bcde7b2349630addf0c6"
  dependencies = [
 - "base64 0.12.3",
 + "base64 0.13.0",
   "once_cell",
   "regex",
  ]
@@@ -2431,11 -2430,11 +2432,11 @@@ dependencies = 
  
  [[package]]
  name = "pin-project"
 -version = "1.0.1"
 +version = "1.0.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ee41d838744f60d959d7074e3afb6b35c7456d0f61cad38a24e35e6553f73841"
 +checksum = "9ccc2237c2c489783abd8c4c80e5450fc0e98644555b1364da68cc29aa151ca7"
  dependencies = [
 - "pin-project-internal 1.0.1",
 + "pin-project-internal 1.0.2",
  ]
  
  [[package]]
@@@ -2451,9 -2450,9 +2452,9 @@@ dependencies = 
  
  [[package]]
  name = "pin-project-internal"
 -version = "1.0.1"
 +version = "1.0.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "81a4ffa594b66bff340084d4081df649a7dc049ac8d7fc458d8e628bfbbb2f86"
 +checksum = "f8e8d2bf0b23038a4424865103a4df472855692821aab4e4f5c3312d461d9e5f"
  dependencies = [
   "proc-macro2",
   "quote",
@@@ -2466,12 -2465,6 +2467,12 @@@ version = "0.1.11
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"
  
 +[[package]]
 +name = "pin-project-lite"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "6b063f57ec186e6140e2b8b6921e5f1bd89c7356dda5b33acc5401203ca6131c"
 +
  [[package]]
  name = "pin-utils"
  version = "0.1.0"
@@@ -2486,21 -2479,21 +2487,21 @@@ checksum = "3831453b3449ceb48b6d9c7ad7c
  
  [[package]]
  name = "png"
 -version = "0.7.0"
 +version = "0.16.8"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "48f397b84083c2753ba53c7b56ad023edb94512b2885ffe227c66ff7edb61868"
 +checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
  dependencies = [
 - "bitflags 0.7.0",
 + "bitflags",
 + "crc32fast",
   "deflate",
 - "inflate",
 - "num-iter",
 + "miniz_oxide 0.3.7",
  ]
  
  [[package]]
  name = "ppv-lite86"
 -version = "0.2.9"
 +version = "0.2.10"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
 +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
  
  [[package]]
  name = "pq-sys"
@@@ -2513,9 -2506,9 +2514,9 @@@ dependencies = 
  
  [[package]]
  name = "proc-macro-hack"
 -version = "0.5.18"
 +version = "0.5.19"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "99c605b9a0adc77b7211c6b1f722dcb613d68d66859a44f3d485a6da332b0598"
 +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
  
  [[package]]
  name = "proc-macro-nested"
@@@ -2575,10 -2568,27 +2576,10 @@@ dependencies = 
  ]
  
  [[package]]
 -name = "rand"
 -version = "0.3.23"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
 -dependencies = [
 - "libc",
 - "rand 0.4.6",
 -]
 -
 -[[package]]
 -name = "rand"
 -version = "0.4.6"
 +name = "radium"
 +version = "0.5.3"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
 -dependencies = [
 - "fuchsia-cprng",
 - "libc",
 - "rand_core 0.3.1",
 - "rdrand",
 - "winapi 0.3.9",
 -]
 +checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
  
  [[package]]
  name = "rand"
@@@ -2586,10 -2596,10 +2587,10 @@@ version = "0.7.3
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
  dependencies = [
 - "getrandom",
 + "getrandom 0.1.15",
   "libc",
   "rand_chacha",
 - "rand_core 0.5.1",
 + "rand_core",
   "rand_hc",
  ]
  
@@@ -2600,16 -2610,31 +2601,16 @@@ source = "registry+https://github.com/r
  checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
  dependencies = [
   "ppv-lite86",
 - "rand_core 0.5.1",
 + "rand_core",
  ]
  
 -[[package]]
 -name = "rand_core"
 -version = "0.3.1"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
 -dependencies = [
 - "rand_core 0.4.2",
 -]
 -
 -[[package]]
 -name = "rand_core"
 -version = "0.4.2"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
 -
  [[package]]
  name = "rand_core"
  version = "0.5.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
  dependencies = [
 - "getrandom",
 + "getrandom 0.1.15",
  ]
  
  [[package]]
@@@ -2618,7 -2643,7 +2619,7 @@@ version = "0.2.0
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
  dependencies = [
 - "rand_core 0.5.1",
 + "rand_core",
  ]
  
  [[package]]
@@@ -2641,11 -2666,20 +2642,11 @@@ checksum = "9ab346ac5921dc62ffa9f89b7a7
  dependencies = [
   "crossbeam-channel 0.5.0",
   "crossbeam-deque",
 - "crossbeam-utils 0.8.0",
 + "crossbeam-utils 0.8.1",
   "lazy_static",
   "num_cpus",
  ]
  
 -[[package]]
 -name = "rdrand"
 -version = "0.4.0"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
 -dependencies = [
 - "rand_core 0.3.1",
 -]
 -
  [[package]]
  name = "redox_syscall"
  version = "0.1.57"
@@@ -2654,9 -2688,9 +2655,9 @@@ checksum = "41cc0f7e4d5d4544e8861606a28
  
  [[package]]
  name = "regex"
 -version = "1.4.1"
 +version = "1.4.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "8963b85b8ce3074fecffde43b4b0dded83ce2f367dc8d363afc56679f3ee820b"
 +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c"
  dependencies = [
   "aho-corasick",
   "memchr",
  
  [[package]]
  name = "regex-syntax"
 -version = "0.6.20"
 +version = "0.6.21"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "8cab7a364d15cde1e505267766a2d3c4e22a843e1a601f0fa7564c0f82ced11c"
 +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189"
  
  [[package]]
  name = "remove_dir_all"
@@@ -2681,12 -2715,12 +2682,12 @@@ dependencies = 
  
  [[package]]
  name = "reqwest"
 -version = "0.10.8"
 +version = "0.10.10"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e9eaa17ac5d7b838b7503d118fa16ad88f440498bf9ffe5424e621f93190d61e"
 +checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c"
  dependencies = [
 - "base64 0.12.3",
 - "bytes",
 + "base64 0.13.0",
 + "bytes 0.5.6",
   "encoding_rs",
   "futures-core",
   "futures-util",
   "mime_guess",
   "native-tls",
   "percent-encoding",
 - "pin-project-lite",
 - "serde 1.0.117",
 + "pin-project-lite 0.2.0",
 + "serde 1.0.118",
   "serde_json",
   "serde_urlencoded",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "tokio-tls",
   "url",
   "wasm-bindgen",
  
  [[package]]
  name = "resolv-conf"
 -version = "0.6.3"
 +version = "0.7.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a"
 +checksum = "52e44394d2086d010551b14b53b1f24e31647570cd1deb0379e2c21b329aba00"
  dependencies = [
   "hostname",
   "quick-error",
@@@ -2736,9 -2770,9 +2737,9 @@@ dependencies = 
  
  [[package]]
  name = "ring"
 -version = "0.16.15"
 +version = "0.16.19"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "952cd6b98c85bbc30efa1ba5783b8abf12fec8b3287ffa52605b9432313e34e4"
 +checksum = "024a1e66fea74c66c66624ee5622a7ff0e4b73a13b4f5c326ddb50c708944226"
  dependencies = [
   "cc",
   "libc",
@@@ -2836,11 -2870,11 +2837,11 @@@ dependencies = 
  
  [[package]]
  name = "security-framework"
 -version = "0.4.4"
 +version = "2.0.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535"
 +checksum = "c1759c2e3c8580017a484a7ac56d3abc5a6c1feadf88db2f3633f12ae4268c69"
  dependencies = [
 - "bitflags 1.2.1",
 + "bitflags",
   "core-foundation",
   "core-foundation-sys",
   "libc",
  
  [[package]]
  name = "security-framework-sys"
 -version = "0.4.3"
 +version = "2.0.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405"
 +checksum = "f99b9d5e26d2a71633cc4f2ebae7cc9f874044e0c351a27e17892d76dce5678b"
  dependencies = [
   "core-foundation-sys",
   "libc",
@@@ -2880,9 -2914,9 +2881,9 @@@ checksum = "9dad3f759919b92c3068c696c15
  
  [[package]]
  name = "serde"
 -version = "1.0.117"
 +version = "1.0.118"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a"
 +checksum = "06c64263859d87aa2eb554587e2d23183398d617427327cf2b3d0ed8c69e4800"
  dependencies = [
   "serde_derive",
  ]
@@@ -2902,9 -2936,9 +2903,9 @@@ dependencies = 
  
  [[package]]
  name = "serde_derive"
 -version = "1.0.117"
 +version = "1.0.118"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e"
 +checksum = "c84d3526699cd55261af4b941e4e725444df67aa4f9e6a3564f18030d12672df"
  dependencies = [
   "proc-macro2",
   "quote",
  
  [[package]]
  name = "serde_json"
 -version = "1.0.59"
 +version = "1.0.60"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95"
 +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779"
  dependencies = [
   "indexmap",
   "itoa",
   "ryu",
 - "serde 1.0.117",
 + "serde 1.0.118",
  ]
  
  [[package]]
@@@ -2934,14 -2968,14 +2935,14 @@@ dependencies = 
  
  [[package]]
  name = "serde_urlencoded"
 -version = "0.6.1"
 +version = "0.7.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97"
 +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9"
  dependencies = [
 - "dtoa",
 + "form_urlencoded",
   "itoa",
 - "serde 1.0.117",
 - "url",
 + "ryu",
 + "serde 1.0.118",
  ]
  
  [[package]]
@@@ -2958,12 -2992,12 +2959,12 @@@ dependencies = 
  
  [[package]]
  name = "sha-1"
 -version = "0.9.1"
 +version = "0.9.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "170a36ea86c864a3f16dd2687712dd6646f7019f301e57537c7f4dc9f5916770"
 +checksum = "ce3cdf1b5e620a498ee6f2a171885ac7e22f0e12089ec4b3d22b84921792507c"
  dependencies = [
   "block-buffer 0.9.0",
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
   "cpuid-bool",
   "digest 0.9.0",
   "opaque-debug 0.3.0",
@@@ -2977,12 -3011,12 +2978,12 @@@ checksum = "2579985fda508104f7587689507
  
  [[package]]
  name = "sha2"
 -version = "0.9.1"
 +version = "0.9.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "2933378ddfeda7ea26f48c555bdad8bb446bf8a3d17832dc83e380d444cfb8c1"
 +checksum = "6e7aab86fe2149bad8c507606bdb3f4ef5e7b2380eb92350f56122cca72a42a8"
  dependencies = [
   "block-buffer 0.9.0",
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
   "cpuid-bool",
   "digest 0.9.0",
   "opaque-debug 0.3.0",
@@@ -2996,10 -3030,11 +2997,10 @@@ checksum = "b6fa3938c99da4914afedd13bf3
  
  [[package]]
  name = "signal-hook-registry"
 -version = "1.2.1"
 +version = "1.2.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
 +checksum = "ce32ea0c6c56d5eacaeb814fbed9960547021d3edd010ded1425f180536b20ab"
  dependencies = [
 - "arc-swap",
   "libc",
  ]
  
@@@ -3011,7 -3046,7 +3012,7 @@@ checksum = "692ca13de57ce0613a363c8c2f1
  dependencies = [
   "chrono",
   "num-bigint",
 - "num-traits 0.2.12",
 + "num-traits 0.2.14",
  ]
  
  [[package]]
@@@ -3022,18 -3057,19 +3023,18 @@@ checksum = "c111b5bd5695e56cffe5129854a
  
  [[package]]
  name = "smallvec"
 -version = "1.4.2"
 +version = "1.5.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252"
 +checksum = "ae524f056d7d770e174287294f562e95044c68e88dec909a00d2094805db9d75"
  
  [[package]]
  name = "socket2"
 -version = "0.3.15"
 +version = "0.3.18"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b1fa70dc5c8104ec096f4fe7ede7a221d35ae13dcd19ba1ad9a81d2cab9a1c44"
 +checksum = "97e0e9fd577458a4f61fb91fcb559ea2afecc54c934119421f9f5d3d5b1a1057"
  dependencies = [
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
   "libc",
 - "redox_syscall",
   "winapi 0.3.9",
  ]
  
@@@ -3045,9 -3081,9 +3046,9 @@@ checksum = "6e63cff320ae2c57904679ba7cb
  
  [[package]]
  name = "standback"
 -version = "0.2.11"
 +version = "0.2.13"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "f4e0831040d2cf2bdfd51b844be71885783d489898a192f254ae25d57cce725c"
 +checksum = "cf906c8b8fc3f6ecd1046e01da1d8ddec83e48c8b08b84dcc02b585a6bedf5a8"
  dependencies = [
   "version_check 0.9.2",
  ]
@@@ -3080,7 -3116,7 +3081,7 @@@ checksum = "c87a60a40fccc84bef0652345bb
  dependencies = [
   "proc-macro2",
   "quote",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_derive",
   "syn",
  ]
@@@ -3094,7 -3130,7 +3095,7 @@@ dependencies = 
   "base-x",
   "proc-macro2",
   "quote",
 - "serde 1.0.117",
 + "serde 1.0.118",
   "serde_derive",
   "serde_json",
   "sha1",
@@@ -3115,15 -3151,15 +3116,15 @@@ checksum = "6446ced80d6c486436db5c078dd
  
  [[package]]
  name = "strum"
 -version = "0.19.5"
 +version = "0.20.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b89a286a7e3b5720b9a477b23253bc50debac207c8d21505f8e70b36792f11b5"
 +checksum = "7318c509b5ba57f18533982607f24070a55d353e90d4cae30c467cdb2ad5ac5c"
  
  [[package]]
  name = "strum_macros"
 -version = "0.19.4"
 +version = "0.20.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e61bb0be289045cb80bfce000512e32d09f8337e54c186725da381377ad1f8d5"
 +checksum = "ee8bc6b87a5112aeeab1f4a9f7ab634fe6cbefc4850006df31267f4cfb9e3149"
  dependencies = [
   "heck",
   "proc-macro2",
  
  [[package]]
  name = "syn"
 -version = "1.0.48"
 +version = "1.0.54"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "cc371affeffc477f42a221a1e4297aedcea33d47d19b61455588bd9d8f6b19ac"
 +checksum = "9a2af957a63d6bd42255c359c93d9bfdb97076bd3b820897ce55ffbfbf107f44"
  dependencies = [
   "proc-macro2",
   "quote",
   "unicode-xid",
  ]
  
 +[[package]]
 +name = "tap"
 +version = "1.0.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "36474e732d1affd3a6ed582781b3683df3d0563714c59c39591e8ff707cf078e"
 +
  [[package]]
  name = "tempfile"
  version = "3.1.0"
@@@ -3156,7 -3186,7 +3157,7 @@@ checksum = "7a6e24d9338a0a5be79593e2fa1
  dependencies = [
   "cfg-if 0.1.10",
   "libc",
 - "rand 0.7.3",
 + "rand",
   "redox_syscall",
   "remove_dir_all",
   "winapi 0.3.9",
  
  [[package]]
  name = "termcolor"
 -version = "1.1.0"
 +version = "1.1.2"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
 +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
  dependencies = [
   "winapi-util",
  ]
  
  [[package]]
  name = "thiserror"
 -version = "1.0.21"
 +version = "1.0.22"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "318234ffa22e0920fe9a40d7b8369b5f649d490980cf7aadcf1eb91594869b42"
 +checksum = "0e9ae34b84616eedaaf1e9dd6026dbe00dcafa92aa0c8077cb69df1fcfe5e53e"
  dependencies = [
   "thiserror-impl",
  ]
  
  [[package]]
  name = "thiserror-impl"
 -version = "1.0.21"
 +version = "1.0.22"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "cae2447b6282786c3493999f40a9be2a6ad20cb8bd268b0a0dbf5a065535c0ab"
 +checksum = "9ba20f23e85b10754cd195504aebf6a27e2e6cbe28c17778a0c930724628dd56"
  dependencies = [
   "proc-macro2",
   "quote",
@@@ -3209,17 -3239,6 +3210,17 @@@ dependencies = 
   "num_cpus",
  ]
  
 +[[package]]
 +name = "tiff"
 +version = "0.6.1"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
 +dependencies = [
 + "jpeg-decoder",
 + "miniz_oxide 0.4.3",
 + "weezl",
 +]
 +
  [[package]]
  name = "time"
  version = "0.1.44"
@@@ -3233,9 -3252,9 +3234,9 @@@ dependencies = 
  
  [[package]]
  name = "time"
 -version = "0.2.22"
 +version = "0.2.23"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "55b7151c9065e80917fbf285d9a5d1432f60db41d170ccafc749a136b41a93af"
 +checksum = "bcdaeea317915d59b2b4cd3b5efcd156c309108664277793f5351700c02ce98b"
  dependencies = [
   "const_fn",
   "libc",
@@@ -3271,9 -3290,15 +3272,9 @@@ dependencies = 
  
  [[package]]
  name = "tinyvec"
 -version = "0.3.4"
 -source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117"
 -
 -[[package]]
 -name = "tinyvec"
 -version = "1.0.1"
 +version = "1.1.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b78a366903f506d2ad52ca8dc552102ffdd3e937ba8a227f024dc1d1eae28575"
 +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f"
  dependencies = [
   "tinyvec_macros",
  ]
@@@ -3286,11 -3311,11 +3287,11 @@@ checksum = "cda74da7e1a664f795bb1f8a87e
  
  [[package]]
  name = "tokio"
 -version = "0.2.22"
 +version = "0.2.24"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
 +checksum = "099837d3464c16a808060bb3f02263b412f6fafcb5d01c533d309985fbeebe48"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "fnv",
   "futures-core",
   "iovec",
   "memchr",
   "mio",
   "mio-uds",
 - "pin-project-lite",
 + "pin-project-lite 0.1.11",
   "signal-hook-registry",
   "slab",
   "winapi 0.3.9",
  
  [[package]]
  name = "tokio"
 -version = "0.3.1"
 +version = "0.3.6"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1911a203c5c240fd237e23a42e48846475f3d3b1e1dad3f17e6cc17a775b707c"
 +checksum = "720ba21c25078711bf456d607987d95bce90f7c3bea5abe1db587862e7a1e87c"
  dependencies = [
 - "fnv",
 - "pin-project-lite",
 + "autocfg",
 + "pin-project-lite 0.2.0",
  ]
  
  [[package]]
@@@ -3323,7 -3348,7 +3324,7 @@@ checksum = "e12831b255bcfa39dc0436b01e1
  dependencies = [
   "futures-core",
   "rustls",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "webpki",
  ]
  
@@@ -3334,7 -3359,7 +3335,7 @@@ source = "registry+https://github.com/r
  checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343"
  dependencies = [
   "native-tls",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
  ]
  
  [[package]]
@@@ -3343,13 -3368,13 +3344,13 @@@ version = "0.3.1
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499"
  dependencies = [
 - "bytes",
 + "bytes 0.5.6",
   "futures-core",
   "futures-io",
   "futures-sink",
   "log",
 - "pin-project-lite",
 - "tokio 0.2.22",
 + "pin-project-lite 0.1.11",
 + "tokio 0.2.24",
  ]
  
  [[package]]
@@@ -3360,13 -3385,13 +3361,13 @@@ checksum = "e987b6bf443f4b5b3b6f3870419
  
  [[package]]
  name = "tracing"
 -version = "0.1.21"
 +version = "0.1.22"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b0987850db3733619253fe60e17cb59b82d37c7e6c0236bb81e4d6b87c879f27"
 +checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
  dependencies = [
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
   "log",
 - "pin-project-lite",
 + "pin-project-lite 0.2.0",
   "tracing-core",
  ]
  
@@@ -3391,9 -3416,9 +3392,9 @@@ dependencies = 
  
  [[package]]
  name = "trust-dns-proto"
 -version = "0.19.5"
 +version = "0.19.6"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "cdd7061ba6f4d4d9721afedffbfd403f20f39a4301fee1b70d6fcd09cca69f28"
 +checksum = "53861fcb288a166aae4c508ae558ed18b53838db728d4d310aad08270a7d4c2b"
  dependencies = [
   "async-trait",
   "backtrace",
   "idna",
   "lazy_static",
   "log",
 - "rand 0.7.3",
 + "rand",
   "smallvec",
   "thiserror",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "url",
  ]
  
  [[package]]
  name = "trust-dns-resolver"
 -version = "0.19.5"
 +version = "0.19.6"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "0f23cdfdc3d8300b3c50c9e84302d3bd6d860fb9529af84ace6cf9665f181b77"
 +checksum = "6759e8efc40465547b0dfce9500d733c65f969a4cbbfbe3ccf68daaa46ef179e"
  dependencies = [
   "backtrace",
   "cfg-if 0.1.10",
   "resolv-conf",
   "smallvec",
   "thiserror",
 - "tokio 0.2.22",
 + "tokio 0.2.24",
   "trust-dns-proto",
  ]
  
@@@ -3489,18 -3514,18 +3490,18 @@@ dependencies = 
  
  [[package]]
  name = "unicode-normalization"
 -version = "0.1.13"
 +version = "0.1.16"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977"
 +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606"
  dependencies = [
 - "tinyvec 0.3.4",
 + "tinyvec",
  ]
  
  [[package]]
  name = "unicode-segmentation"
 -version = "1.6.0"
 +version = "1.7.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0"
 +checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796"
  
  [[package]]
  name = "unicode-xid"
@@@ -3522,15 -3547,14 +3523,15 @@@ checksum = "a156c684c91ea7d62626509bce3
  
  [[package]]
  name = "url"
 -version = "2.1.1"
 +version = "2.2.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
 +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
  dependencies = [
 + "form_urlencoded",
   "idna",
   "matches",
   "percent-encoding",
 - "serde 1.0.117",
 + "serde 1.0.118",
  ]
  
  [[package]]
@@@ -3539,15 -3563,15 +3540,15 @@@ version = "0.8.1
  source = "registry+https://github.com/rust-lang/crates.io-index"
  checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
  dependencies = [
 - "rand 0.7.3",
 - "serde 1.0.117",
 + "rand",
 + "serde 1.0.118",
  ]
  
  [[package]]
  name = "v_escape"
 -version = "0.13.2"
 +version = "0.14.1"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "039a44473286eb84e4e74f90165feff67c802dbeced7ee4c5b00d719b0d0475e"
 +checksum = "ccca9e73c678b882900cbaec16dae4d3662ace5a17774ac45af04e0f3988fafa"
  dependencies = [
   "buf-min",
   "v_escape_derive",
@@@ -3567,9 -3591,9 +3568,9 @@@ dependencies = 
  
  [[package]]
  name = "v_htmlescape"
 -version = "0.10.4"
 +version = "0.11.0"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "11d7c2a33ed7cf0dc1b42bcf39e01b6512f9df08f09e1cd8a49d9dc49a6a9482"
 +checksum = "db00c903248abee8499af60bf20d242e7882335bbbffd2614915184cbb207402"
  dependencies = [
   "cfg-if 1.0.0",
   "v_escape",
  
  [[package]]
  name = "vcpkg"
 -version = "0.2.10"
 +version = "0.2.11"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
 +checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb"
  
  [[package]]
  name = "version_check"
@@@ -3617,21 -3641,21 +3618,21 @@@ checksum = "1a143597ca7c7793eff794def35
  
  [[package]]
  name = "wasm-bindgen"
 -version = "0.2.68"
 +version = "0.2.69"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1ac64ead5ea5f05873d7c12b545865ca2b8d28adfc50a49b84770a3a97265d42"
 +checksum = "3cd364751395ca0f68cafb17666eee36b63077fb5ecd972bbcd74c90c4bf736e"
  dependencies = [
 - "cfg-if 0.1.10",
 - "serde 1.0.117",
 + "cfg-if 1.0.0",
 + "serde 1.0.118",
   "serde_json",
   "wasm-bindgen-macro",
  ]
  
  [[package]]
  name = "wasm-bindgen-backend"
 -version = "0.2.68"
 +version = "0.2.69"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "f22b422e2a757c35a73774860af8e112bff612ce6cb604224e8e47641a9e4f68"
 +checksum = "1114f89ab1f4106e5b55e688b828c0ab0ea593a1ea7c094b141b14cbaaec2d62"
  dependencies = [
   "bumpalo",
   "lazy_static",
  
  [[package]]
  name = "wasm-bindgen-futures"
 -version = "0.4.18"
 +version = "0.4.19"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "b7866cab0aa01de1edf8b5d7936938a7e397ee50ce24119aef3e1eaa3b6171da"
 +checksum = "1fe9756085a84584ee9457a002b7cdfe0bfff169f45d2591d8be1345a6780e35"
  dependencies = [
 - "cfg-if 0.1.10",
 + "cfg-if 1.0.0",
   "js-sys",
   "wasm-bindgen",
   "web-sys",
  
  [[package]]
  name = "wasm-bindgen-macro"
 -version = "0.2.68"
 +version = "0.2.69"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "6b13312a745c08c469f0b292dd2fcd6411dba5f7160f593da6ef69b64e407038"
 +checksum = "7a6ac8995ead1f084a8dea1e65f194d0973800c7f571f6edd70adf06ecf77084"
  dependencies = [
   "quote",
   "wasm-bindgen-macro-support",
  
  [[package]]
  name = "wasm-bindgen-macro-support"
 -version = "0.2.68"
 +version = "0.2.69"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "f249f06ef7ee334cc3b8ff031bfc11ec99d00f34d86da7498396dc1e3b1498fe"
 +checksum = "b5a48c72f299d80557c7c62e37e7225369ecc0c963964059509fbafe917c7549"
  dependencies = [
   "proc-macro2",
   "quote",
  
  [[package]]
  name = "wasm-bindgen-shared"
 -version = "0.2.68"
 +version = "0.2.69"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "1d649a3145108d7d3fbcde896a468d1bd636791823c9921135218ad89be08307"
 +checksum = "7e7811dd7f9398f14cc76efd356f98f03aa30419dea46aa810d71e819fc97158"
  
  [[package]]
  name = "web-sys"
 -version = "0.3.45"
 +version = "0.3.46"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "4bf6ef87ad7ae8008e15a355ce696bed26012b7caa21605188cfd8214ab51e2d"
 +checksum = "222b1ef9334f92a21d3fb53dc3fd80f30836959a90f9274a626d7e06315ba3c3"
  dependencies = [
   "js-sys",
   "wasm-bindgen",
  
  [[package]]
  name = "webpki"
 -version = "0.21.3"
 +version = "0.21.4"
  source = "registry+https://github.com/rust-lang/crates.io-index"
 -checksum = "ab146130f5f790d45f82aeeb09e55a256573373ec64409fc19a6fb82fb1032ae"
 +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
  dependencies = [
   "ring",
   "untrusted",
@@@ -3712,12 -3736,6 +3713,12 @@@ dependencies = 
   "webpki",
  ]
  
 +[[package]]
 +name = "weezl"
 +version = "0.1.3"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "3e2bb9fc8309084dd7cd651336673844c1d47f8ef6d2091ec160b27f5c4aa277"
 +
  [[package]]
  name = "widestring"
  version = "0.4.3"
@@@ -3795,12 -3813,6 +3796,12 @@@ dependencies = 
   "winapi-build",
  ]
  
 +[[package]]
 +name = "wyz"
 +version = "0.2.0"
 +source = "registry+https://github.com/rust-lang/crates.io-index"
 +checksum = "85e60b0d1b5f99db2556934e21937020776a5d31520bf169e851ac44e6420214"
 +
  [[package]]
  name = "xdg"
  version = "2.2.0"
diff --combined Cargo.toml
index f5b03adc58bc5fcbd9b3262295a35991db0fcfb5,f0b98709d3a041b686169148aa97160b3aa5bcdb..40b28677591cdb631bbd1a5d0c390b2200664280
@@@ -3,8 -3,8 +3,8 @@@ name = "lemmy_server
  version = "0.0.1"
  edition = "2018"
  
- [profile.release]
- lto = true
#[profile.release]
#lto = true
  
  [workspace]
  members = [
@@@ -25,32 -25,32 +25,32 @@@ lemmy_db = { path = "./lemmy_db" 
  lemmy_structs = { path = "./lemmy_structs" }
  lemmy_rate_limit = { path = "./lemmy_rate_limit" }
  lemmy_websocket = { path = "./lemmy_websocket" }
 -diesel = "1.4"
 -diesel_migrations = "1.4"
 -chrono = { version = "0.4", features = ["serde"] }
 -serde = { version = "1.0", features = ["derive"] }
 -actix = "0.10"
 -actix-web = { version = "3.1", default-features = false, features = ["rustls"] }
 -actix-files = { version = "0.4", default-features = false }
 -actix-web-actors = { version = "3.0", default-features = false }
 -awc = { version = "2.0", default-features = false }
 -log = "0.4"
 -env_logger = "0.8"
 -strum = "0.19"
 -lazy_static = "1.3"
 -rss = "1.9"
 -url = { version = "2.1", features = ["serde"] }
 -openssl = "0.10"
 -http-signature-normalization-actix = { version = "0.4", default-features = false, features = ["sha-2"] }
 -tokio = "0.3"
 -sha2 = "0.9"
 -anyhow = "1.0"
 -reqwest = { version = "0.10", features = ["json"] }
 -activitystreams = "0.7.0-alpha.4"
 -actix-rt = { version = "1.1", default-features = false }
 -serde_json = { version = "1.0", features = ["preserve_order"]}
 +diesel = "1.4.5"
 +diesel_migrations = "1.4.0"
 +chrono = { version = "0.4.19", features = ["serde"] }
 +serde = { version = "1.0.118", features = ["derive"] }
 +actix = "0.10.0"
 +actix-web = { version = "3.3.2", default-features = false, features = ["rustls"] }
 +actix-files = { version = "0.4.1", default-features = false }
 +actix-web-actors = { version = "3.0.0", default-features = false }
 +awc = { version = "2.0.3", default-features = false }
 +log = "0.4.11"
 +env_logger = "0.8.2"
 +strum = "0.20.0"
 +lazy_static = "1.4.0"
 +rss = "1.9.0"
 +url = { version = "2.2.0", features = ["serde"] }
 +openssl = "0.10.31"
 +http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }
 +tokio = "0.3.6"
 +sha2 = "0.9.2"
 +anyhow = "1.0.35"
 +reqwest = { version = "0.10.10", features = ["json"] }
 +activitystreams = "0.7.0-alpha.8"
 +actix-rt = { version = "1.1.1", default-features = false }
 +serde_json = { version = "1.0.60", features = ["preserve_order"] }
  
  [dev-dependencies.cargo-husky]
 -version = "1"
 +version = "1.5.0"
  default-features = false # Disable features which are enabled by default
  features = ["precommit-hook", "run-cargo-fmt", "run-cargo-clippy"]
index 8e86a2be7e68e2f0ac358a9d7ac6fd19281d3737,57756a39e3779749eb18c0ad919314453080d116..b0ca55d0d648fa8d0d8795248ff9fda6a31a816f
@@@ -11,7 -11,7 +11,7 @@@ import 
    followBeta,
    searchForBetaCommunity,
    createComment,
 -  updateComment,
 +  editComment,
    deleteComment,
    removeComment,
    getMentions,
    createCommunity,
    registerUser,
    API,
-   delay,
-   longDelay,
  } from './shared';
 -import {
 -  Comment,
 -} from 'lemmy-js-client';
 +import { CommentView } from 'lemmy-js-client';
  
  import { PostResponse } from 'lemmy-js-client';
  
@@@ -34,10 -34,9 +32,9 @@@ beforeAll(async () => 
    await followBeta(alpha);
    await followBeta(gamma);
    let search = await searchForBetaCommunity(alpha);
-   await longDelay();
    postRes = await createPost(
      alpha,
 -    search.communities.filter(c => c.local == false)[0].id
 +    search.communities.find(c => c.community.local == false).community.id
    );
  });
  
@@@ -47,35 -46,33 +44,34 @@@ afterAll(async () => 
  });
  
  function assertCommentFederation(
 -  commentOne: Comment,
 -  commentTwo: Comment) {
 -  expect(commentOne.ap_id).toBe(commentOne.ap_id);
 -  expect(commentOne.content).toBe(commentTwo.content);
 -  expect(commentOne.creator_name).toBe(commentTwo.creator_name);
 -  expect(commentOne.community_actor_id).toBe(commentTwo.community_actor_id);
 -  expect(commentOne.published).toBe(commentTwo.published);
 -  expect(commentOne.updated).toBe(commentOne.updated);
 -  expect(commentOne.deleted).toBe(commentOne.deleted);
 -  expect(commentOne.removed).toBe(commentOne.removed);
 +  commentOne: CommentView,
 +  commentTwo: CommentView
 +) {
 +  expect(commentOne.comment.ap_id).toBe(commentOne.comment.ap_id);
 +  expect(commentOne.comment.content).toBe(commentTwo.comment.content);
 +  expect(commentOne.creator.name).toBe(commentTwo.creator.name);
 +  expect(commentOne.community.actor_id).toBe(commentTwo.community.actor_id);
 +  expect(commentOne.comment.published).toBe(commentTwo.comment.published);
 +  expect(commentOne.comment.updated).toBe(commentOne.comment.updated);
 +  expect(commentOne.comment.deleted).toBe(commentOne.comment.deleted);
 +  expect(commentOne.comment.removed).toBe(commentOne.comment.removed);
  }
  
  test('Create a comment', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 -  expect(commentRes.comment.content).toBeDefined();
 -  expect(commentRes.comment.community_local).toBe(false);
 -  expect(commentRes.comment.creator_local).toBe(true);
 -  expect(commentRes.comment.score).toBe(1);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
 +  expect(commentRes.comment_view.comment.content).toBeDefined();
 +  expect(commentRes.comment_view.community.local).toBe(false);
 +  expect(commentRes.comment_view.creator.local).toBe(true);
 +  expect(commentRes.comment_view.counts.score).toBe(1);
-   await longDelay();
  
    // Make sure that comment is liked on beta
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment = searchBeta.comments[0];
    expect(betaComment).toBeDefined();
 -  expect(betaComment.community_local).toBe(true);
 -  expect(betaComment.creator_local).toBe(false);
 -  expect(betaComment.score).toBe(1);
 -  assertCommentFederation(betaComment, commentRes.comment);
 +  expect(betaComment.community.local).toBe(true);
 +  expect(betaComment.creator.local).toBe(false);
 +  expect(betaComment.counts.score).toBe(1);
 +  assertCommentFederation(betaComment, commentRes.comment_view);
  });
  
  test('Create a comment in a non-existent post', async () => {
  });
  
  test('Update a comment', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
    // Federate the comment first
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 -  assertCommentFederation(searchBeta.comments[0], commentRes.comment);
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
 +  assertCommentFederation(searchBeta.comments[0], commentRes.comment_view);
  
-   await delay();
 -  let updateCommentRes = await updateComment(alpha, commentRes.comment.id);
 -  expect(updateCommentRes.comment.content).toBe(
 +  let updateCommentRes = await editComment(
 +    alpha,
 +    commentRes.comment_view.comment.id
 +  );
 +  expect(updateCommentRes.comment_view.comment.content).toBe(
      'A jest test federated comment update'
    );
 -  expect(updateCommentRes.comment.community_local).toBe(false);
 -  expect(updateCommentRes.comment.creator_local).toBe(true);
 +  expect(updateCommentRes.comment_view.community.local).toBe(false);
 +  expect(updateCommentRes.comment_view.creator.local).toBe(true);
-   await delay();
  
    // Make sure that post is updated on beta
 -  let searchBetaUpdated = await searchComment(beta, commentRes.comment);
 -  assertCommentFederation(searchBetaUpdated.comments[0], updateCommentRes.comment);
 +  let searchBetaUpdated = await searchComment(
 +    beta,
 +    commentRes.comment_view.comment
 +  );
 +  assertCommentFederation(
 +    searchBetaUpdated.comments[0],
 +    updateCommentRes.comment_view
 +  );
  });
  
  test('Delete a comment', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await delay();
  
    let deleteCommentRes = await deleteComment(
      alpha,
      true,
 -    commentRes.comment.id
 +    commentRes.comment_view.comment.id
    );
 -  expect(deleteCommentRes.comment.deleted).toBe(true);
 +  expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
-   await delay();
  
    // Make sure that comment is undefined on beta
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment = searchBeta.comments[0];
    expect(betaComment).toBeUndefined();
-   await delay();
  
    let undeleteCommentRes = await deleteComment(
      alpha,
      false,
 -    commentRes.comment.id
 +    commentRes.comment_view.comment.id
    );
 -  expect(undeleteCommentRes.comment.deleted).toBe(false);
 +  expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false);
-   await delay();
  
    // Make sure that comment is undeleted on beta
 -  let searchBeta2 = await searchComment(beta, commentRes.comment);
 +  let searchBeta2 = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment2 = searchBeta2.comments[0];
 -  expect(betaComment2.deleted).toBe(false);
 -  assertCommentFederation(searchBeta2.comments[0], undeleteCommentRes.comment);
 +  expect(betaComment2.comment.deleted).toBe(false);
 +  assertCommentFederation(
 +    searchBeta2.comments[0],
 +    undeleteCommentRes.comment_view
 +  );
  });
  
  test('Remove a comment from admin and community on the same instance', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await delay();
  
    // Get the id for beta
 -  let betaCommentId = (await searchComment(beta, commentRes.comment))
 -    .comments[0].id;
 +  let betaCommentId = (
 +    await searchComment(beta, commentRes.comment_view.comment)
 +  ).comments[0].comment.id;
  
    // The beta admin removes it (the community lives on beta)
    let removeCommentRes = await removeComment(beta, true, betaCommentId);
 -  expect(removeCommentRes.comment.removed).toBe(true);
 +  expect(removeCommentRes.comment_view.comment.removed).toBe(true);
-   await longDelay();
  
    // Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
 -  let refetchedPost = await getPost(alpha, postRes.post.id);
 -  expect(refetchedPost.comments[0].removed).toBe(true);
 +  let refetchedPost = await getPost(alpha, postRes.post_view.post.id);
 +  expect(refetchedPost.comments[0].comment.removed).toBe(true);
  
    let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
 -  expect(unremoveCommentRes.comment.removed).toBe(false);
 +  expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
-   await longDelay();
  
    // Make sure that comment is unremoved on beta
 -  let refetchedPost2 = await getPost(alpha, postRes.post.id);
 -  expect(refetchedPost2.comments[0].removed).toBe(false);
 -  assertCommentFederation(refetchedPost2.comments[0], unremoveCommentRes.comment);
 +  let refetchedPost2 = await getPost(alpha, postRes.post_view.post.id);
 +  expect(refetchedPost2.comments[0].comment.removed).toBe(false);
 +  assertCommentFederation(
 +    refetchedPost2.comments[0],
 +    unremoveCommentRes.comment_view
 +  );
  });
  
  test('Remove a comment from admin and community on different instance', async () => {
  
    // New alpha user creates a community, post, and comment.
    let newCommunity = await createCommunity(newAlphaApi);
-   await delay();
 -  let newPost = await createPost(newAlphaApi, newCommunity.community.id);
 -  let commentRes = await createComment(newAlphaApi, newPost.post.id);
 -  expect(commentRes.comment.content).toBeDefined();
 +  let newPost = await createPost(
 +    newAlphaApi,
 +    newCommunity.community_view.community.id
 +  );
-   await delay();
 +  let commentRes = await createComment(newAlphaApi, newPost.post_view.post.id);
 +  expect(commentRes.comment_view.comment.content).toBeDefined();
-   await delay();
  
    // Beta searches that to cache it, then removes it
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment = searchBeta.comments[0];
 -  let removeCommentRes = await removeComment(beta, true, betaComment.id);
 -  expect(removeCommentRes.comment.removed).toBe(true);
 +  let removeCommentRes = await removeComment(
 +    beta,
 +    true,
 +    betaComment.comment.id
 +  );
 +  expect(removeCommentRes.comment_view.comment.removed).toBe(true);
-   await delay();
  
    // Make sure its not removed on alpha
 -  let refetchedPost = await getPost(newAlphaApi, newPost.post.id);
 -  expect(refetchedPost.comments[0].removed).toBe(false);
 -  assertCommentFederation(refetchedPost.comments[0], commentRes.comment);
 +  let refetchedPost = await getPost(newAlphaApi, newPost.post_view.post.id);
 +  expect(refetchedPost.comments[0].comment.removed).toBe(false);
 +  assertCommentFederation(refetchedPost.comments[0], commentRes.comment_view);
  });
  
  test('Unlike a comment', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 -  let unlike = await likeComment(alpha, 0, commentRes.comment);
 -  expect(unlike.comment.score).toBe(0);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await delay();
 +  let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
 +  expect(unlike.comment_view.counts.score).toBe(0);
-   await delay();
  
    // Make sure that post is unliked on beta
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment = searchBeta.comments[0];
    expect(betaComment).toBeDefined();
 -  expect(betaComment.community_local).toBe(true);
 -  expect(betaComment.creator_local).toBe(false);
 -  expect(betaComment.score).toBe(0);
 +  expect(betaComment.community.local).toBe(true);
 +  expect(betaComment.creator.local).toBe(false);
 +  expect(betaComment.counts.score).toBe(0);
  });
  
  test('Federated comment like', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await longDelay();
  
    // Find the comment on beta
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment = searchBeta.comments[0];
  
 -  let like = await likeComment(beta, 1, betaComment);
 -  expect(like.comment.score).toBe(2);
 +  let like = await likeComment(beta, 1, betaComment.comment);
 +  expect(like.comment_view.counts.score).toBe(2);
-   await longDelay();
  
    // Get the post from alpha, check the likes
 -  let post = await getPost(alpha, postRes.post.id);
 -  expect(post.comments[0].score).toBe(2);
 +  let post = await getPost(alpha, postRes.post_view.post.id);
 +  expect(post.comments[0].counts.score).toBe(2);
  });
  
  test('Reply to a comment', async () => {
    // Create a comment on alpha, find it on beta
 -  let commentRes = await createComment(alpha, postRes.post.id);
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await delay();
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
    let betaComment = searchBeta.comments[0];
  
    // find that comment id on beta
  
    // Reply from beta
 -  let replyRes = await createComment(beta, betaComment.post_id, betaComment.id);
 -  expect(replyRes.comment.content).toBeDefined();
 -  expect(replyRes.comment.community_local).toBe(true);
 -  expect(replyRes.comment.creator_local).toBe(true);
 -  expect(replyRes.comment.parent_id).toBe(betaComment.id);
 -  expect(replyRes.comment.score).toBe(1);
 +  let replyRes = await createComment(
 +    beta,
 +    betaComment.post.id,
 +    betaComment.comment.id
 +  );
 +  expect(replyRes.comment_view.comment.content).toBeDefined();
 +  expect(replyRes.comment_view.community.local).toBe(true);
 +  expect(replyRes.comment_view.creator.local).toBe(true);
 +  expect(replyRes.comment_view.comment.parent_id).toBe(betaComment.comment.id);
 +  expect(replyRes.comment_view.counts.score).toBe(1);
-   await longDelay();
  
    // Make sure that comment is seen on alpha
    // TODO not sure why, but a searchComment back to alpha, for the ap_id of betas
    // comment, isn't working.
    // let searchAlpha = await searchComment(alpha, replyRes.comment);
 -  let post = await getPost(alpha, postRes.post.id);
 +  let post = await getPost(alpha, postRes.post_view.post.id);
    let alphaComment = post.comments[0];
 -  expect(alphaComment.content).toBeDefined();
 -  expect(alphaComment.parent_id).toBe(post.comments[1].id);
 -  expect(alphaComment.community_local).toBe(false);
 -  expect(alphaComment.creator_local).toBe(false);
 -  expect(alphaComment.score).toBe(1);
 -  assertCommentFederation(alphaComment, replyRes.comment);
 +  expect(alphaComment.comment.content).toBeDefined();
 +  expect(alphaComment.comment.parent_id).toBe(post.comments[1].comment.id);
 +  expect(alphaComment.community.local).toBe(false);
 +  expect(alphaComment.creator.local).toBe(false);
 +  expect(alphaComment.counts.score).toBe(1);
 +  assertCommentFederation(alphaComment, replyRes.comment_view);
  });
  
  test('Mention beta', async () => {
    // Create a mention on alpha
    let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8551';
 -  let commentRes = await createComment(alpha, postRes.post.id);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await delay();
    let mentionRes = await createComment(
      alpha,
 -    postRes.post.id,
 -    commentRes.comment.id,
 +    postRes.post_view.post.id,
 +    commentRes.comment_view.comment.id,
      mentionContent
    );
 -  expect(mentionRes.comment.content).toBeDefined();
 -  expect(mentionRes.comment.community_local).toBe(false);
 -  expect(mentionRes.comment.creator_local).toBe(true);
 -  expect(mentionRes.comment.score).toBe(1);
 +  expect(mentionRes.comment_view.comment.content).toBeDefined();
 +  expect(mentionRes.comment_view.community.local).toBe(false);
 +  expect(mentionRes.comment_view.creator.local).toBe(true);
 +  expect(mentionRes.comment_view.counts.score).toBe(1);
-   await delay();
  
    let mentionsRes = await getMentions(beta);
 -  expect(mentionsRes.mentions[0].content).toBeDefined();
 -  expect(mentionsRes.mentions[0].community_local).toBe(true);
 -  expect(mentionsRes.mentions[0].creator_local).toBe(false);
 -  expect(mentionsRes.mentions[0].score).toBe(1);
 +  expect(mentionsRes.mentions[0].comment.content).toBeDefined();
 +  expect(mentionsRes.mentions[0].community.local).toBe(true);
 +  expect(mentionsRes.mentions[0].creator.local).toBe(false);
 +  expect(mentionsRes.mentions[0].counts.score).toBe(1);
  });
  
  test('Comment Search', async () => {
 -  let commentRes = await createComment(alpha, postRes.post.id);
 -  let searchBeta = await searchComment(beta, commentRes.comment);
 -  assertCommentFederation(searchBeta.comments[0], commentRes.comment);
 +  let commentRes = await createComment(alpha, postRes.post_view.post.id);
-   await delay();
 +  let searchBeta = await searchComment(beta, commentRes.comment_view.comment);
 +  assertCommentFederation(searchBeta.comments[0], commentRes.comment_view);
  });
  
  test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => {
    // Create a local post
    let alphaPost = await createPost(alpha, 2);
 -  expect(alphaPost.post.community_local).toBe(true);
 +  expect(alphaPost.post_view.community.local).toBe(true);
-   await delay();
  
    // Make sure gamma sees it
 -  let search = await searchPost(gamma, alphaPost.post);
 +  let search = await searchPost(gamma, alphaPost.post_view.post);
    let gammaPost = search.posts[0];
  
    let commentContent =
      'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551';
    let commentRes = await createComment(
      gamma,
 -    gammaPost.id,
 +    gammaPost.post.id,
      undefined,
      commentContent
    );
 -  expect(commentRes.comment.content).toBe(commentContent);
 -  expect(commentRes.comment.community_local).toBe(false);
 -  expect(commentRes.comment.creator_local).toBe(true);
 -  expect(commentRes.comment.score).toBe(1);
 +  expect(commentRes.comment_view.comment.content).toBe(commentContent);
 +  expect(commentRes.comment_view.community.local).toBe(false);
 +  expect(commentRes.comment_view.creator.local).toBe(true);
 +  expect(commentRes.comment_view.counts.score).toBe(1);
-   await longDelay();
  
    // Make sure alpha sees it
 -  let alphaPost2 = await getPost(alpha, alphaPost.post.id);
 -  expect(alphaPost2.comments[0].content).toBe(commentContent);
 -  expect(alphaPost2.comments[0].community_local).toBe(true);
 -  expect(alphaPost2.comments[0].creator_local).toBe(false);
 -  expect(alphaPost2.comments[0].score).toBe(1);
 -  assertCommentFederation(alphaPost2.comments[0], commentRes.comment);
 +  let alphaPost2 = await getPost(alpha, alphaPost.post_view.post.id);
 +  expect(alphaPost2.comments[0].comment.content).toBe(commentContent);
 +  expect(alphaPost2.comments[0].community.local).toBe(true);
 +  expect(alphaPost2.comments[0].creator.local).toBe(false);
 +  expect(alphaPost2.comments[0].counts.score).toBe(1);
 +  assertCommentFederation(alphaPost2.comments[0], commentRes.comment_view);
-   await delay();
  
    // Make sure beta has mentions
    let mentionsRes = await getMentions(beta);
 -  expect(mentionsRes.mentions[0].content).toBe(commentContent);
 -  expect(mentionsRes.mentions[0].community_local).toBe(false);
 -  expect(mentionsRes.mentions[0].creator_local).toBe(false);
 +  expect(mentionsRes.mentions[0].comment.content).toBe(commentContent);
 +  expect(mentionsRes.mentions[0].community.local).toBe(false);
 +  expect(mentionsRes.mentions[0].creator.local).toBe(false);
    // TODO this is failing because fetchInReplyTos aren't getting score
    // expect(mentionsRes.mentions[0].score).toBe(1);
  });
@@@ -361,64 -306,56 +332,60 @@@ test('Fetch in_reply_tos: A is unsubbe
    // Unfollow all remote communities
    let followed = await unfollowRemotes(alpha);
    expect(
 -    followed.communities.filter(c => c.community_local == false).length
 +    followed.communities.filter(c => c.community.local == false).length
    ).toBe(0);
  
    // B creates a post, and two comments, should be invisible to A
    let postRes = await createPost(beta, 2);
 -  expect(postRes.post.name).toBeDefined();
 +  expect(postRes.post_view.post.name).toBeDefined();
-   await delay();
  
    let parentCommentContent = 'An invisible top level comment from beta';
    let parentCommentRes = await createComment(
      beta,
 -    postRes.post.id,
 +    postRes.post_view.post.id,
      undefined,
      parentCommentContent
    );
 -  expect(parentCommentRes.comment.content).toBe(parentCommentContent);
 +  expect(parentCommentRes.comment_view.comment.content).toBe(
 +    parentCommentContent
 +  );
-   await delay();
  
    // B creates a comment, then a child one of that.
    let childCommentContent = 'An invisible child comment from beta';
    let childCommentRes = await createComment(
      beta,
 -    postRes.post.id,
 -    parentCommentRes.comment.id,
 +    postRes.post_view.post.id,
 +    parentCommentRes.comment_view.comment.id,
 +    childCommentContent
 +  );
 +  expect(childCommentRes.comment_view.comment.content).toBe(
      childCommentContent
    );
-   await delay();
 -  expect(childCommentRes.comment.content).toBe(childCommentContent);
  
    // Follow beta again
    let follow = await followBeta(alpha);
 -  expect(follow.community.local).toBe(false);
 -  expect(follow.community.name).toBe('main');
 +  expect(follow.community_view.community.local).toBe(false);
 +  expect(follow.community_view.community.name).toBe('main');
-   await delay();
  
    // An update to the child comment on beta, should push the post, parent, and child to alpha now
    let updatedCommentContent = 'An update child comment from beta';
 -  let updateRes = await updateComment(
 +  let updateRes = await editComment(
      beta,
 -    childCommentRes.comment.id,
 +    childCommentRes.comment_view.comment.id,
      updatedCommentContent
    );
 -  expect(updateRes.comment.content).toBe(updatedCommentContent);
 +  expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent);
-   await delay();
  
    // Get the post from alpha
 -  let search = await searchPost(alpha, postRes.post);
 +  let search = await searchPost(alpha, postRes.post_view.post);
    let alphaPostB = search.posts[0];
-   await longDelay();
  
 -  let alphaPost = await getPost(alpha, alphaPostB.id);
 -  expect(alphaPost.post.name).toBeDefined();
 -  assertCommentFederation(alphaPost.comments[1], parentCommentRes.comment);
 -  assertCommentFederation(alphaPost.comments[0], updateRes.comment);
 -  expect(alphaPost.post.community_local).toBe(false);
 -  expect(alphaPost.post.creator_local).toBe(false);
 +  let alphaPost = await getPost(alpha, alphaPostB.post.id);
 +  expect(alphaPost.post_view.post.name).toBeDefined();
 +  assertCommentFederation(alphaPost.comments[1], parentCommentRes.comment_view);
 +  assertCommentFederation(alphaPost.comments[0], updateRes.comment_view);
 +  expect(alphaPost.post_view.community.local).toBe(false);
 +  expect(alphaPost.post_view.creator.local).toBe(false);
+   await unfollowRemotes(alpha);
  });
index fcb9130b55b90436a4fd486f0a1f54d8d19ac51a,906900644203215f11dd734f639a4d51f25e1cae..25d8109c3694e13a66b0a1e16fe24ae28914b1bf
@@@ -9,176 -9,132 +9,161 @@@ import 
    removeCommunity,
    getCommunity,
    followCommunity,
-   delay,
  } from './shared';
 -import {
 -  Community,
 -} from 'lemmy-js-client';
 +import { CommunityView } from 'lemmy-js-client';
  
  beforeAll(async () => {
    await setupLogins();
  });
  
  function assertCommunityFederation(
 -  communityOne: Community,
 -  communityTwo: Community) {
 -  expect(communityOne.actor_id).toBe(communityTwo.actor_id);
 -  expect(communityOne.name).toBe(communityTwo.name);
 -  expect(communityOne.title).toBe(communityTwo.title);
 -  expect(communityOne.description).toBe(communityTwo.description);
 -  expect(communityOne.icon).toBe(communityTwo.icon);
 -  expect(communityOne.banner).toBe(communityTwo.banner);
 -  expect(communityOne.published).toBe(communityTwo.published);
 -  expect(communityOne.creator_actor_id).toBe(communityTwo.creator_actor_id);
 -  expect(communityOne.nsfw).toBe(communityTwo.nsfw);
 -  expect(communityOne.category_id).toBe(communityTwo.category_id);
 -  expect(communityOne.removed).toBe(communityTwo.removed);
 -  expect(communityOne.deleted).toBe(communityTwo.deleted);
 +  communityOne: CommunityView,
 +  communityTwo: CommunityView
 +) {
 +  expect(communityOne.community.actor_id).toBe(communityTwo.community.actor_id);
 +  expect(communityOne.community.name).toBe(communityTwo.community.name);
 +  expect(communityOne.community.title).toBe(communityTwo.community.title);
 +  expect(communityOne.community.description).toBe(
 +    communityTwo.community.description
 +  );
 +  expect(communityOne.community.icon).toBe(communityTwo.community.icon);
 +  expect(communityOne.community.banner).toBe(communityTwo.community.banner);
 +  expect(communityOne.community.published).toBe(
 +    communityTwo.community.published
 +  );
 +  expect(communityOne.creator.actor_id).toBe(communityTwo.creator.actor_id);
 +  expect(communityOne.community.nsfw).toBe(communityTwo.community.nsfw);
 +  expect(communityOne.community.category_id).toBe(
 +    communityTwo.community.category_id
 +  );
 +  expect(communityOne.community.removed).toBe(communityTwo.community.removed);
 +  expect(communityOne.community.deleted).toBe(communityTwo.community.deleted);
  }
  
  test('Create community', async () => {
    let communityRes = await createCommunity(alpha);
 -  expect(communityRes.community.name).toBeDefined();
 +  expect(communityRes.community_view.community.name).toBeDefined();
  
    // A dupe check
 -  let prevName = communityRes.community.name;
 -  let communityRes2 = await createCommunity(alpha, prevName);
 +  let prevName = communityRes.community_view.community.name;
 +  let communityRes2: any = await createCommunity(alpha, prevName);
    expect(communityRes2['error']).toBe('community_already_exists');
-   await delay();
  
    // Cache the community on beta, make sure it has the other fields
    let searchShort = `!${prevName}@lemmy-alpha:8541`;
    let search = await searchForCommunity(beta, searchShort);
    let communityOnBeta = search.communities[0];
 -  assertCommunityFederation(communityOnBeta, communityRes.community);
 +  assertCommunityFederation(communityOnBeta, communityRes.community_view);
  });
  
  test('Delete community', async () => {
    let communityRes = await createCommunity(beta);
-   await delay();
  
    // Cache the community on Alpha
 -  let searchShort = `!${communityRes.community.name}@lemmy-beta:8551`;
 +  let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
    let search = await searchForCommunity(alpha, searchShort);
    let communityOnAlpha = search.communities[0];
 -  assertCommunityFederation(communityOnAlpha, communityRes.community);
 +  assertCommunityFederation(communityOnAlpha, communityRes.community_view);
-   await delay();
  
    // Follow the community from alpha
 -  let follow = await followCommunity(alpha, true, communityOnAlpha.id);
 +  let follow = await followCommunity(
 +    alpha,
 +    true,
 +    communityOnAlpha.community.id
 +  );
  
    // Make sure the follow response went through
 -  expect(follow.community.local).toBe(false);
 +  expect(follow.community_view.community.local).toBe(false);
-   await delay();
  
    let deleteCommunityRes = await deleteCommunity(
      beta,
      true,
 -    communityRes.community.id
 +    communityRes.community_view.community.id
    );
 -  expect(deleteCommunityRes.community.deleted).toBe(true);
 +  expect(deleteCommunityRes.community_view.community.deleted).toBe(true);
-   await delay();
  
    // Make sure it got deleted on A
 -  let communityOnAlphaDeleted = await getCommunity(alpha, communityOnAlpha.id);
 -  expect(communityOnAlphaDeleted.community.deleted).toBe(true);
 +  let communityOnAlphaDeleted = await getCommunity(
 +    alpha,
 +    communityOnAlpha.community.id
 +  );
 +  expect(communityOnAlphaDeleted.community_view.community.deleted).toBe(true);
-   await delay();
  
    // Undelete
    let undeleteCommunityRes = await deleteCommunity(
      beta,
      false,
 -    communityRes.community.id
 +    communityRes.community_view.community.id
    );
 -  expect(undeleteCommunityRes.community.deleted).toBe(false);
 +  expect(undeleteCommunityRes.community_view.community.deleted).toBe(false);
-   await delay();
  
    // Make sure it got undeleted on A
 -  let communityOnAlphaUnDeleted = await getCommunity(alpha, communityOnAlpha.id);
 -  expect(communityOnAlphaUnDeleted.community.deleted).toBe(false);
 +  let communityOnAlphaUnDeleted = await getCommunity(
 +    alpha,
 +    communityOnAlpha.community.id
 +  );
 +  expect(communityOnAlphaUnDeleted.community_view.community.deleted).toBe(
 +    false
 +  );
  });
  
  test('Remove community', async () => {
    let communityRes = await createCommunity(beta);
-   await delay();
  
    // Cache the community on Alpha
 -  let searchShort = `!${communityRes.community.name}@lemmy-beta:8551`;
 +  let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
    let search = await searchForCommunity(alpha, searchShort);
    let communityOnAlpha = search.communities[0];
 -  assertCommunityFederation(communityOnAlpha, communityRes.community);
 +  assertCommunityFederation(communityOnAlpha, communityRes.community_view);
-   await delay();
  
    // Follow the community from alpha
 -  let follow = await followCommunity(alpha, true, communityOnAlpha.id);
 +  let follow = await followCommunity(
 +    alpha,
 +    true,
 +    communityOnAlpha.community.id
 +  );
  
    // Make sure the follow response went through
 -  expect(follow.community.local).toBe(false);
 +  expect(follow.community_view.community.local).toBe(false);
-   await delay();
  
    let removeCommunityRes = await removeCommunity(
      beta,
      true,
 -    communityRes.community.id
 +    communityRes.community_view.community.id
    );
 -  expect(removeCommunityRes.community.removed).toBe(true);
 +  expect(removeCommunityRes.community_view.community.removed).toBe(true);
-   await delay();
  
    // Make sure it got Removed on A
 -  let communityOnAlphaRemoved = await getCommunity(alpha, communityOnAlpha.id);
 -  expect(communityOnAlphaRemoved.community.removed).toBe(true);
 +  let communityOnAlphaRemoved = await getCommunity(
 +    alpha,
 +    communityOnAlpha.community.id
 +  );
 +  expect(communityOnAlphaRemoved.community_view.community.removed).toBe(true);
-   await delay();
  
    // unremove
    let unremoveCommunityRes = await removeCommunity(
      beta,
      false,
 -    communityRes.community.id
 +    communityRes.community_view.community.id
    );
 -  expect(unremoveCommunityRes.community.removed).toBe(false);
 +  expect(unremoveCommunityRes.community_view.community.removed).toBe(false);
-   await delay();
  
    // Make sure it got undeleted on A
 -  let communityOnAlphaUnRemoved = await getCommunity(alpha, communityOnAlpha.id);
 -  expect(communityOnAlphaUnRemoved.community.removed).toBe(false);
 +  let communityOnAlphaUnRemoved = await getCommunity(
 +    alpha,
 +    communityOnAlpha.community.id
 +  );
 +  expect(communityOnAlphaUnRemoved.community_view.community.removed).toBe(
 +    false
 +  );
  });
  
  test('Search for beta community', async () => {
    let communityRes = await createCommunity(beta);
 -  expect(communityRes.community.name).toBeDefined();
 +  expect(communityRes.community_view.community.name).toBeDefined();
-   await delay();
  
 -  let searchShort = `!${communityRes.community.name}@lemmy-beta:8551`;
 +  let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
    let search = await searchForCommunity(alpha, searchShort);
    let communityOnAlpha = search.communities[0];
 -  assertCommunityFederation(communityOnAlpha, communityRes.community);
 +  assertCommunityFederation(communityOnAlpha, communityRes.community_view);
  });
index 8917efc02414b2835374412bf691365a044a6e8f,651c526a22f6ce94358958a5226c97a57f5c199a..0749439c08e99b89cd755a5570957c6de66d738f
@@@ -6,8 -6,6 +6,6 @@@ import 
    followCommunity,
    checkFollowedCommunities,
    unfollowRemotes,
-   delay,
-   longDelay,
  } from './shared';
  
  beforeAll(async () => {
@@@ -20,29 -18,22 +18,26 @@@ afterAll(async () => 
  
  test('Follow federated community', async () => {
    let search = await searchForBetaCommunity(alpha); // TODO sometimes this is returning null?
 -  let follow = await followCommunity(alpha, true, search.communities[0].id);
 +  let follow = await followCommunity(
 +    alpha,
 +    true,
 +    search.communities[0].community.id
 +  );
  
    // Make sure the follow response went through
 -  expect(follow.community.local).toBe(false);
 -  expect(follow.community.name).toBe('main');
 +  expect(follow.community_view.community.local).toBe(false);
 +  expect(follow.community_view.community.name).toBe('main');
-   await longDelay();
  
    // Check it from local
    let followCheck = await checkFollowedCommunities(alpha);
-   await delay();
 -  let remoteCommunityId = followCheck.communities.filter(
 -    c => c.community_local == false
 -  )[0].community_id;
 +  let remoteCommunityId = followCheck.communities.find(
 +    c => c.community.local == false
 +  ).community.id;
    expect(remoteCommunityId).toBeDefined();
  
    // Test an unfollow
    let unfollow = await followCommunity(alpha, false, remoteCommunityId);
 -  expect(unfollow.community.local).toBe(false);
 +  expect(unfollow.community_view.community.local).toBe(false);
-   await delay();
  
    // Make sure you are unsubbed locally
    let unfollowCheck = await checkFollowedCommunities(alpha);
index 4a34e1ed6bf863db3f9d4d5d82d7356c182fd415,44edcb24cd76eb5351c6fd24631649d820427551..01befa60cff84a15408986655112183c2c103791
@@@ -7,7 -7,7 +7,7 @@@ import 
    epsilon,
    setupLogins,
    createPost,
 -  updatePost,
 +  editPost,
    stickyPost,
    lockPost,
    searchPost,
    removePost,
    getPost,
    unfollowRemotes,
-   delay,
-   longDelay,
    searchForUser,
    banUserFromSite,
    searchPostLocal,
    banUserFromCommunity,
  } from './shared';
- import { PostView } from 'lemmy-js-client';
 -import {
 -  Post,
 -  Community,
 -} from 'lemmy-js-client';
++import { PostView, CommunityView } from 'lemmy-js-client';
 -let betaCommunity: Community;
++let betaCommunity: CommunityView;
  
  beforeAll(async () => {
    await setupLogins();
-   await followBeta(alpha);
-   await followBeta(gamma);
-   await followBeta(delta);
-   await followBeta(epsilon);
-   await longDelay();
+   let search = await searchForBetaCommunity(alpha);
+   betaCommunity = search.communities[0];
+   await unfollows();
  });
  
  afterAll(async () => {
+   await unfollows();
+ });
+ async function unfollows() {
    await unfollowRemotes(alpha);
    await unfollowRemotes(gamma);
    await unfollowRemotes(delta);
    await unfollowRemotes(epsilon);
- });
+ }
  
 -function assertPostFederation(
 -  postOne: Post,
 -  postTwo: Post) {
 -  expect(postOne.ap_id).toBe(postTwo.ap_id);
 -  expect(postOne.name).toBe(postTwo.name);
 -  expect(postOne.body).toBe(postTwo.body);
 -  expect(postOne.url).toBe(postTwo.url);
 -  expect(postOne.nsfw).toBe(postTwo.nsfw);
 -  expect(postOne.embed_title).toBe(postTwo.embed_title);
 -  expect(postOne.embed_description).toBe(postTwo.embed_description);
 -  expect(postOne.embed_html).toBe(postTwo.embed_html);
 -  expect(postOne.published).toBe(postTwo.published);
 -  expect(postOne.community_actor_id).toBe(postTwo.community_actor_id);
 -  expect(postOne.locked).toBe(postTwo.locked);
 -  expect(postOne.removed).toBe(postTwo.removed);
 -  expect(postOne.deleted).toBe(postTwo.deleted);
 +function assertPostFederation(postOne: PostView, postTwo: PostView) {
 +  expect(postOne.post.ap_id).toBe(postTwo.post.ap_id);
 +  expect(postOne.post.name).toBe(postTwo.post.name);
 +  expect(postOne.post.body).toBe(postTwo.post.body);
 +  expect(postOne.post.url).toBe(postTwo.post.url);
 +  expect(postOne.post.nsfw).toBe(postTwo.post.nsfw);
 +  expect(postOne.post.embed_title).toBe(postTwo.post.embed_title);
 +  expect(postOne.post.embed_description).toBe(postTwo.post.embed_description);
 +  expect(postOne.post.embed_html).toBe(postTwo.post.embed_html);
 +  expect(postOne.post.published).toBe(postTwo.post.published);
 +  expect(postOne.community.actor_id).toBe(postTwo.community.actor_id);
 +  expect(postOne.post.locked).toBe(postTwo.post.locked);
 +  expect(postOne.post.removed).toBe(postTwo.post.removed);
 +  expect(postOne.post.deleted).toBe(postTwo.post.deleted);
  }
  
  test('Create a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   await delay();
-   let postRes = await createPost(alpha, search.communities[0].community.id);
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
 -  expect(postRes.post.community_local).toBe(false);
 -  expect(postRes.post.creator_local).toBe(true);
 -  expect(postRes.post.score).toBe(1);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
 +  expect(postRes.post_view.post).toBeDefined();
 +  expect(postRes.post_view.community.local).toBe(false);
 +  expect(postRes.post_view.creator.local).toBe(true);
 +  expect(postRes.post_view.counts.score).toBe(1);
-   await longDelay();
  
    // Make sure that post is liked on beta
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
  
    expect(betaPost).toBeDefined();
 -  expect(betaPost.community_local).toBe(true);
 -  expect(betaPost.creator_local).toBe(false);
 -  expect(betaPost.score).toBe(1);
 -  assertPostFederation(betaPost, postRes.post);
 +  expect(betaPost.community.local).toBe(true);
 +  expect(betaPost.creator.local).toBe(false);
 +  expect(betaPost.counts.score).toBe(1);
 +  assertPostFederation(betaPost, postRes.post_view);
  
    // Delta only follows beta, so it should not see an alpha ap_id
 -  let searchDelta = await searchPost(delta, postRes.post);
 +  let searchDelta = await searchPost(delta, postRes.post_view.post);
    expect(searchDelta.posts[0]).toBeUndefined();
  
    // Epsilon has alpha blocked, it should not see the alpha post
 -  let searchEpsilon = await searchPost(epsilon, postRes.post);
 +  let searchEpsilon = await searchPost(epsilon, postRes.post_view.post);
    expect(searchEpsilon.posts[0]).toBeUndefined();
  });
  
@@@ -95,274 -99,234 +94,234 @@@ test('Create a post in a non-existent c
  });
  
  test('Unlike a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  let unlike = await likePost(alpha, 0, postRes.post);
 -  expect(unlike.post.score).toBe(0);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
 +  let unlike = await likePost(alpha, 0, postRes.post_view.post);
 +  expect(unlike.post_view.counts.score).toBe(0);
-   await delay();
  
    // Try to unlike it again, make sure it stays at 0
 -  let unlike2 = await likePost(alpha, 0, postRes.post);
 -  expect(unlike2.post.score).toBe(0);
 +  let unlike2 = await likePost(alpha, 0, postRes.post_view.post);
 +  expect(unlike2.post_view.counts.score).toBe(0);
-   await longDelay();
  
    // Make sure that post is unliked on beta
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
    expect(betaPost).toBeDefined();
 -  expect(betaPost.community_local).toBe(true);
 -  expect(betaPost.creator_local).toBe(false);
 -  expect(betaPost.score).toBe(0);
 -  assertPostFederation(betaPost, postRes.post);
 +  expect(betaPost.community.local).toBe(true);
 +  expect(betaPost.creator.local).toBe(false);
 +  expect(betaPost.counts.score).toBe(0);
 +  assertPostFederation(betaPost, postRes.post_view);
  });
  
  test('Update a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
  
    let updatedName = 'A jest test federated post, updated';
 -  let updatedPost = await updatePost(alpha, postRes.post);
 -  expect(updatedPost.post.name).toBe(updatedName);
 -  expect(updatedPost.post.community_local).toBe(false);
 -  expect(updatedPost.post.creator_local).toBe(true);
 +  let updatedPost = await editPost(alpha, postRes.post_view.post);
 +  expect(updatedPost.post_view.post.name).toBe(updatedName);
 +  expect(updatedPost.post_view.community.local).toBe(false);
 +  expect(updatedPost.post_view.creator.local).toBe(true);
-   await delay();
  
    // Make sure that post is updated on beta
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
 -  expect(betaPost.community_local).toBe(true);
 -  expect(betaPost.creator_local).toBe(false);
 -  expect(betaPost.name).toBe(updatedName);
 -  assertPostFederation(betaPost, updatedPost.post);
 +  expect(betaPost.community.local).toBe(true);
 +  expect(betaPost.creator.local).toBe(false);
 +  expect(betaPost.post.name).toBe(updatedName);
 +  assertPostFederation(betaPost, updatedPost.post_view);
-   await delay();
  
    // Make sure lemmy beta cannot update the post
 -  let updatedPostBeta = await updatePost(beta, betaPost);
 +  let updatedPostBeta = await editPost(beta, betaPost.post);
    expect(updatedPostBeta).toStrictEqual({ error: 'no_post_edit_allowed' });
  });
  
  test('Sticky a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
 +
 +  let stickiedPostRes = await stickyPost(alpha, true, postRes.post_view.post);
 +  expect(stickiedPostRes.post_view.post.stickied).toBe(true);
-   await delay();
  
 -  let stickiedPostRes = await stickyPost(alpha, true, postRes.post);
 -  expect(stickiedPostRes.post.stickied).toBe(true);
    // Make sure that post is stickied on beta
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
 -  expect(betaPost.community_local).toBe(true);
 -  expect(betaPost.creator_local).toBe(false);
 -  expect(betaPost.stickied).toBe(true);
 +  expect(betaPost.community.local).toBe(true);
 +  expect(betaPost.creator.local).toBe(false);
 +  expect(betaPost.post.stickied).toBe(true);
  
    // Unsticky a post
 -  let unstickiedPost = await stickyPost(alpha, false, postRes.post);
 -  expect(unstickiedPost.post.stickied).toBe(false);
 +  let unstickiedPost = await stickyPost(alpha, false, postRes.post_view.post);
 +  expect(unstickiedPost.post_view.post.stickied).toBe(false);
-   await delay();
  
    // Make sure that post is unstickied on beta
 -  let searchBeta2 = await searchPost(beta, postRes.post);
 +  let searchBeta2 = await searchPost(beta, postRes.post_view.post);
    let betaPost2 = searchBeta2.posts[0];
 -  expect(betaPost2.community_local).toBe(true);
 -  expect(betaPost2.creator_local).toBe(false);
 -  expect(betaPost2.stickied).toBe(false);
 +  expect(betaPost2.community.local).toBe(true);
 +  expect(betaPost2.creator.local).toBe(false);
 +  expect(betaPost2.post.stickied).toBe(false);
  
    // Make sure that gamma cannot sticky the post on beta
 -  let searchGamma = await searchPost(gamma, postRes.post);
 +  let searchGamma = await searchPost(gamma, postRes.post_view.post);
    let gammaPost = searchGamma.posts[0];
 -  let gammaTrySticky = await stickyPost(gamma, true, gammaPost);
 -  let searchBeta3 = await searchPost(beta, postRes.post);
 +  let gammaTrySticky = await stickyPost(gamma, true, gammaPost.post);
-   await delay();
 +  let searchBeta3 = await searchPost(beta, postRes.post_view.post);
    let betaPost3 = searchBeta3.posts[0];
 -  expect(gammaTrySticky.post.stickied).toBe(true);
 -  expect(betaPost3.stickied).toBe(false);
 +  expect(gammaTrySticky.post_view.post.stickied).toBe(true);
 +  expect(betaPost3.post.stickied).toBe(false);
  });
  
  test('Lock a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   await delay();
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
  
    // Lock the post
 -  let lockedPostRes = await lockPost(alpha, true, postRes.post);
 -  expect(lockedPostRes.post.locked).toBe(true);
 +  let lockedPostRes = await lockPost(alpha, true, postRes.post_view.post);
 +  expect(lockedPostRes.post_view.post.locked).toBe(true);
-   await longDelay();
  
    // Make sure that post is locked on beta
 -  let searchBeta = await searchPostLocal(beta, postRes.post);
 +  let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
    let betaPost1 = searchBeta.posts[0];
 -  expect(betaPost1.locked).toBe(true);
 +  expect(betaPost1.post.locked).toBe(true);
-   await delay();
  
    // Try to make a new comment there, on alpha
 -  let comment = await createComment(alpha, postRes.post.id);
 +  let comment: any = await createComment(alpha, postRes.post_view.post.id);
    expect(comment['error']).toBe('locked');
-   await delay();
  
    // Unlock a post
 -  let unlockedPost = await lockPost(alpha, false, postRes.post);
 -  expect(unlockedPost.post.locked).toBe(false);
 +  let unlockedPost = await lockPost(alpha, false, postRes.post_view.post);
 +  expect(unlockedPost.post_view.post.locked).toBe(false);
-   await delay();
  
    // Make sure that post is unlocked on beta
 -  let searchBeta2 = await searchPost(beta, postRes.post);
 +  let searchBeta2 = await searchPost(beta, postRes.post_view.post);
    let betaPost2 = searchBeta2.posts[0];
 -  expect(betaPost2.community_local).toBe(true);
 -  expect(betaPost2.creator_local).toBe(false);
 -  expect(betaPost2.locked).toBe(false);
 +  expect(betaPost2.community.local).toBe(true);
 +  expect(betaPost2.creator.local).toBe(false);
 +  expect(betaPost2.post.locked).toBe(false);
  
    // Try to create a new comment, on beta
 -  let commentBeta = await createComment(beta, betaPost2.id);
 +  let commentBeta = await createComment(beta, betaPost2.post.id);
    expect(commentBeta).toBeDefined();
  });
  
  test('Delete a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
++  let postRes = await createPost(alpha, betaCommunity.community.id);
++  expect(postRes.post_view.post).toBeDefined();
  
 -  let deletedPost = await deletePost(alpha, true, postRes.post);
 -  expect(deletedPost.post.deleted).toBe(true);
 +  let deletedPost = await deletePost(alpha, true, postRes.post_view.post);
 +  expect(deletedPost.post_view.post.deleted).toBe(true);
-   await delay();
  
    // Make sure lemmy beta sees post is deleted
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
    // This will be undefined because of the tombstone
    expect(betaPost).toBeUndefined();
-   await delay();
  
    // Undelete
 -  let undeletedPost = await deletePost(alpha, false, postRes.post);
 -  expect(undeletedPost.post.deleted).toBe(false);
 +  let undeletedPost = await deletePost(alpha, false, postRes.post_view.post);
 +  expect(undeletedPost.post_view.post.deleted).toBe(false);
-   await delay();
  
    // Make sure lemmy beta sees post is undeleted
 -  let searchBeta2 = await searchPost(beta, postRes.post);
 +  let searchBeta2 = await searchPost(beta, postRes.post_view.post);
    let betaPost2 = searchBeta2.posts[0];
 -  expect(betaPost2.deleted).toBe(false);
 -  assertPostFederation(betaPost2, undeletedPost.post);
 +  expect(betaPost2.post.deleted).toBe(false);
 +  assertPostFederation(betaPost2, undeletedPost.post_view);
  
    // Make sure lemmy beta cannot delete the post
 -  let deletedPostBeta = await deletePost(beta, true, betaPost2);
 +  let deletedPostBeta = await deletePost(beta, true, betaPost2.post);
    expect(deletedPostBeta).toStrictEqual({ error: 'no_post_edit_allowed' });
  });
  
  test('Remove a post from admin and community on different instance', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
  
 -  let removedPost = await removePost(alpha, true, postRes.post);
 -  expect(removedPost.post.removed).toBe(true);
 +  let removedPost = await removePost(alpha, true, postRes.post_view.post);
 +  expect(removedPost.post_view.post.removed).toBe(true);
-   await delay();
  
    // Make sure lemmy beta sees post is NOT removed
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
 -  expect(betaPost.removed).toBe(false);
 +  expect(betaPost.post.removed).toBe(false);
-   await delay();
  
    // Undelete
 -  let undeletedPost = await removePost(alpha, false, postRes.post);
 -  expect(undeletedPost.post.removed).toBe(false);
 +  let undeletedPost = await removePost(alpha, false, postRes.post_view.post);
 +  expect(undeletedPost.post_view.post.removed).toBe(false);
-   await delay();
  
    // Make sure lemmy beta sees post is undeleted
 -  let searchBeta2 = await searchPost(beta, postRes.post);
 +  let searchBeta2 = await searchPost(beta, postRes.post_view.post);
    let betaPost2 = searchBeta2.posts[0];
 -  expect(betaPost2.removed).toBe(false);
 -  assertPostFederation(betaPost2, undeletedPost.post);
 +  expect(betaPost2.post.removed).toBe(false);
 +  assertPostFederation(betaPost2, undeletedPost.post_view);
  });
  
  test('Remove a post from admin and community on same instance', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await longDelay();
+   await followBeta(alpha);
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
++  let postRes = await createPost(alpha, betaCommunity.community.id);
++  expect(postRes.post_view.post).toBeDefined();
  
    // Get the id for beta
-   let searchBeta = await searchPost(beta, postRes.post_view.post);
 -  let searchBeta = await searchPostLocal(beta, postRes.post);
++  let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
-   await longDelay();
+   expect(betaPost).toBeDefined();
  
    // The beta admin removes it (the community lives on beta)
 -  let removePostRes = await removePost(beta, true, betaPost);
 -  expect(removePostRes.post.removed).toBe(true);
 +  let removePostRes = await removePost(beta, true, betaPost.post);
 +  expect(removePostRes.post_view.post.removed).toBe(true);
-   await longDelay();
  
    // Make sure lemmy alpha sees post is removed
 -  let alphaPost = await getPost(alpha, postRes.post.id);
 -  // expect(alphaPost.post.removed).toBe(true); // TODO this shouldn't be commented
 -  // assertPostFederation(alphaPost.post, removePostRes.post);
 +  let alphaPost = await getPost(alpha, postRes.post_view.post.id);
-   expect(alphaPost.post_view.post.removed).toBe(true);
-   assertPostFederation(alphaPost.post_view, removePostRes.post_view);
-   await longDelay();
++  // expect(alphaPost.post_view.post.removed).toBe(true); // TODO this shouldn't be commented
++  // assertPostFederation(alphaPost.post_view, removePostRes.post_view);
  
    // Undelete
 -  let undeletedPost = await removePost(beta, false, betaPost);
 -  expect(undeletedPost.post.removed).toBe(false);
 +  let undeletedPost = await removePost(beta, false, betaPost.post);
 +  expect(undeletedPost.post_view.post.removed).toBe(false);
-   await longDelay();
  
    // Make sure lemmy alpha sees post is undeleted
 -  let alphaPost2 = await getPost(alpha, postRes.post.id);
 -  expect(alphaPost2.post.removed).toBe(false);
 -  assertPostFederation(alphaPost2.post, undeletedPost.post);
 +  let alphaPost2 = await getPost(alpha, postRes.post_view.post.id);
-   await delay();
 +  expect(alphaPost2.post_view.post.removed).toBe(false);
 +  assertPostFederation(alphaPost2.post_view, undeletedPost.post_view);
+   await unfollowRemotes(alpha);
  });
  
  test('Search for a post', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   await delay();
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
+   await unfollowRemotes(alpha);
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
++  let postRes = await createPost(alpha, betaCommunity.community.id);
++  expect(postRes.post_view.post).toBeDefined();
 -  let searchBeta = await searchPost(beta, postRes.post);
 +  let searchBeta = await searchPost(beta, postRes.post_view.post);
  
 -  expect(searchBeta.posts[0].name).toBeDefined();
 +  expect(searchBeta.posts[0].post.name).toBeDefined();
  });
  
  test('A and G subscribe to B (center) A posts, it gets announced to G', async () => {
-   let search = await searchForBetaCommunity(alpha);
-   let postRes = await createPost(alpha, search.communities[0].community.id);
-   await delay();
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
++  let postRes = await createPost(alpha, betaCommunity.community.id);
++  expect(postRes.post_view.post).toBeDefined();
  
 -  let search2 = await searchPost(gamma, postRes.post);
 -  expect(search2.posts[0].name).toBeDefined();
 +  let search2 = await searchPost(gamma, postRes.post_view.post);
 +  expect(search2.posts[0].post.name).toBeDefined();
  });
  
  test('Enforce site ban for federated user', async () => {
 -
    let alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`;
    let userSearch = await searchForUser(beta, alphaShortname);
    let alphaUser = userSearch.users[0];
    expect(alphaUser).toBeDefined();
-   await delay();
  
    // ban alpha from beta site
 -  let banAlpha = await banUserFromSite(beta, alphaUser.id, true);
 +  let banAlpha = await banUserFromSite(beta, alphaUser.user.id, true);
    expect(banAlpha.banned).toBe(true);
-   await longDelay();
  
    // Alpha makes post on beta
-   let search = await searchForBetaCommunity(alpha);
-   await delay();
-   let postRes = await createPost(alpha, search.communities[0].community.id);
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
 -  expect(postRes.post.community_local).toBe(false);
 -  expect(postRes.post.creator_local).toBe(true);
 -  expect(postRes.post.score).toBe(1);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
 +  expect(postRes.post_view.post).toBeDefined();
 +  expect(postRes.post_view.community.local).toBe(false);
 +  expect(postRes.post_view.creator.local).toBe(true);
 +  expect(postRes.post_view.counts.score).toBe(1);
-   await longDelay();
  
    // Make sure that post doesn't make it to beta
 -  let searchBeta = await searchPostLocal(beta, postRes.post);
 +  let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
    expect(betaPost).toBeUndefined();
-   await delay();
  
    // Unban alpha
 -  let unBanAlpha = await banUserFromSite(beta, alphaUser.id, false);
 +  let unBanAlpha = await banUserFromSite(beta, alphaUser.user.id, false);
    expect(unBanAlpha.banned).toBe(false);
  });
  
@@@ -371,35 -335,25 +330,30 @@@ test('Enforce community ban for federat
    let userSearch = await searchForUser(beta, alphaShortname);
    let alphaUser = userSearch.users[0];
    expect(alphaUser).toBeDefined();
-   await delay();
  
    // ban alpha from beta site
 -  await banUserFromCommunity(beta, alphaUser.id, 2, false);
 -  let banAlpha = await banUserFromCommunity(beta, alphaUser.id, 2, true);
 +  await banUserFromCommunity(beta, alphaUser.user.id, 2, false);
 +  let banAlpha = await banUserFromCommunity(beta, alphaUser.user.id, 2, true);
    expect(banAlpha.banned).toBe(true);
-   await longDelay();
  
    // Alpha makes post on beta
-   let search = await searchForBetaCommunity(alpha);
-   await delay();
-   let postRes = await createPost(alpha, search.communities[0].community.id);
 -  let postRes = await createPost(alpha, betaCommunity.id);
 -  expect(postRes.post).toBeDefined();
 -  expect(postRes.post.community_local).toBe(false);
 -  expect(postRes.post.creator_local).toBe(true);
 -  expect(postRes.post.score).toBe(1);
++  let postRes = await createPost(alpha, betaCommunity.community.id);
 +  expect(postRes.post_view.post).toBeDefined();
 +  expect(postRes.post_view.community.local).toBe(false);
 +  expect(postRes.post_view.creator.local).toBe(true);
 +  expect(postRes.post_view.counts.score).toBe(1);
-   await longDelay();
  
    // Make sure that post doesn't make it to beta community
 -  let searchBeta = await searchPostLocal(beta, postRes.post);
 +  let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
    let betaPost = searchBeta.posts[0];
    expect(betaPost).toBeUndefined();
  
    // Unban alpha
 -  let unBanAlpha = await banUserFromCommunity(beta, alphaUser.id, 2, false);
 +  let unBanAlpha = await banUserFromCommunity(
 +    beta,
 +    alphaUser.user.id,
 +    2,
 +    false
 +  );
    expect(unBanAlpha.banned).toBe(false);
  });
index 8bb00e127dc422dff5119ea4e374b909f9194ffe,4dc0e705951352632ee4e3435cf96c559752570d..5539b19c7d47918a2f82b221c042e81a77be840a
@@@ -5,12 -5,10 +5,10 @@@ import 
    setupLogins,
    followBeta,
    createPrivateMessage,
 -  updatePrivateMessage,
 +  editPrivateMessage,
    listPrivateMessages,
    deletePrivateMessage,
    unfollowRemotes,
-   delay,
-   longDelay,
  } from './shared';
  
  let recipient_id: number;
@@@ -18,8 -16,7 +16,7 @@@
  beforeAll(async () => {
    await setupLogins();
    let follow = await followBeta(alpha);
-   await longDelay();
 -  recipient_id = follow.community.creator_id;
 +  recipient_id = follow.community_view.creator.id;
  });
  
  afterAll(async () => {
  
  test('Create a private message', async () => {
    let pmRes = await createPrivateMessage(alpha, recipient_id);
 -  expect(pmRes.message.content).toBeDefined();
 -  expect(pmRes.message.local).toBe(true);
 -  expect(pmRes.message.creator_local).toBe(true);
 -  expect(pmRes.message.recipient_local).toBe(false);
 +  expect(pmRes.private_message_view.private_message.content).toBeDefined();
 +  expect(pmRes.private_message_view.private_message.local).toBe(true);
 +  expect(pmRes.private_message_view.creator.local).toBe(true);
 +  expect(pmRes.private_message_view.recipient.local).toBe(false);
-   await delay();
  
    let betaPms = await listPrivateMessages(beta);
 -  expect(betaPms.messages[0].content).toBeDefined();
 -  expect(betaPms.messages[0].local).toBe(false);
 -  expect(betaPms.messages[0].creator_local).toBe(false);
 -  expect(betaPms.messages[0].recipient_local).toBe(true);
 +  expect(betaPms.private_messages[0].private_message.content).toBeDefined();
 +  expect(betaPms.private_messages[0].private_message.local).toBe(false);
 +  expect(betaPms.private_messages[0].creator.local).toBe(false);
 +  expect(betaPms.private_messages[0].recipient.local).toBe(true);
  });
  
  test('Update a private message', async () => {
    let updatedContent = 'A jest test federated private message edited';
  
    let pmRes = await createPrivateMessage(alpha, recipient_id);
 -  let pmUpdated = await updatePrivateMessage(alpha, pmRes.message.id);
 -  expect(pmUpdated.message.content).toBe(updatedContent);
 +  let pmUpdated = await editPrivateMessage(
 +    alpha,
 +    pmRes.private_message_view.private_message.id
 +  );
 +  expect(pmUpdated.private_message_view.private_message.content).toBe(
 +    updatedContent
 +  );
-   await longDelay();
  
    let betaPms = await listPrivateMessages(beta);
 -  expect(betaPms.messages[0].content).toBe(updatedContent);
 +  expect(betaPms.private_messages[0].private_message.content).toBe(
 +    updatedContent
 +  );
  });
  
  test('Delete a private message', async () => {
    let pmRes = await createPrivateMessage(alpha, recipient_id);
-   await delay();
    let betaPms1 = await listPrivateMessages(beta);
 -  let deletedPmRes = await deletePrivateMessage(alpha, true, pmRes.message.id);
 -  expect(deletedPmRes.message.deleted).toBe(true);
 +  let deletedPmRes = await deletePrivateMessage(
 +    alpha,
 +    true,
 +    pmRes.private_message_view.private_message.id
 +  );
 +  expect(deletedPmRes.private_message_view.private_message.deleted).toBe(true);
-   await delay();
  
    // The GetPrivateMessages filters out deleted,
    // even though they are in the actual database.
    // no reason to show them
    let betaPms2 = await listPrivateMessages(beta);
 -  expect(betaPms2.messages.length).toBe(betaPms1.messages.length - 1);
 +  expect(betaPms2.private_messages.length).toBe(
 +    betaPms1.private_messages.length - 1
 +  );
-   await delay();
  
    // Undelete
    let undeletedPmRes = await deletePrivateMessage(
      alpha,
      false,
 -    pmRes.message.id
 +    pmRes.private_message_view.private_message.id
 +  );
 +  expect(undeletedPmRes.private_message_view.private_message.deleted).toBe(
 +    false
    );
-   await longDelay();
 -  expect(undeletedPmRes.message.deleted).toBe(false);
  
    let betaPms3 = await listPrivateMessages(beta);
 -  expect(betaPms3.messages.length).toBe(betaPms1.messages.length);
 +  expect(betaPms3.private_messages.length).toBe(
 +    betaPms1.private_messages.length
 +  );
  });
diff --combined api_tests/src/shared.ts
index 21ab1942dc1888dd88487a43eb10aed797232f76,cf1fb7c6d75a702bd48be24ff907d6be48afb2d1..8e6d5334847c17382f129630b52850c3891c7271
@@@ -1,54 -1,52 +1,54 @@@
  import {
 -  LoginForm,
 +  Login,
    LoginResponse,
 -  Post,
 -  PostForm,
 -  Comment,
 -  DeletePostForm,
 -  RemovePostForm,
 -  StickyPostForm,
 -  LockPostForm,
 +  CreatePost,
 +  EditPost,
 +  CreateComment,
 +  DeletePost,
 +  RemovePost,
 +  StickyPost,
 +  LockPost,
    PostResponse,
    SearchResponse,
 -  FollowCommunityForm,
 +  FollowCommunity,
    CommunityResponse,
    GetFollowedCommunitiesResponse,
    GetPostResponse,
 -  RegisterForm,
 -  CommentForm,
 -  DeleteCommentForm,
 -  RemoveCommentForm,
 -  SearchForm,
 +  Register,
 +  Comment,
 +  EditComment,
 +  DeleteComment,
 +  RemoveComment,
 +  Search,
    CommentResponse,
 -  GetCommunityForm,
 -  CommunityForm,
 -  DeleteCommunityForm,
 -  RemoveCommunityForm,
 -  GetUserMentionsForm,
 -  CommentLikeForm,
 -  CreatePostLikeForm,
 -  PrivateMessageForm,
 -  EditPrivateMessageForm,
 -  DeletePrivateMessageForm,
 -  GetFollowedCommunitiesForm,
 -  GetPrivateMessagesForm,
 -  GetSiteForm,
 -  GetPostForm,
 +  GetCommunity,
 +  CreateCommunity,
 +  DeleteCommunity,
 +  RemoveCommunity,
 +  GetUserMentions,
 +  CreateCommentLike,
 +  CreatePostLike,
 +  EditPrivateMessage,
 +  DeletePrivateMessage,
 +  GetFollowedCommunities,
 +  GetPrivateMessages,
 +  GetSite,
 +  GetPost,
    PrivateMessageResponse,
    PrivateMessagesResponse,
    GetUserMentionsResponse,
 -  UserSettingsForm,
 +  SaveUserSettings,
    SortType,
    ListingType,
    GetSiteResponse,
    SearchType,
    LemmyHttp,
    BanUserResponse,
 -  BanUserForm,
 -  BanFromCommunityForm,
 +  BanUser,
 +  BanFromCommunity,
    BanFromCommunityResponse,
 +  Post,
 +  CreatePrivateMessage,
  } from 'lemmy-js-client';
  
  export interface API {
  }
  
  export let alpha: API = {
 -  client: new LemmyHttp('http://localhost:8541/api/v1'),
 +  client: new LemmyHttp('http://localhost:8541/api/v2'),
  };
  
  export let beta: API = {
 -  client: new LemmyHttp('http://localhost:8551/api/v1'),
 +  client: new LemmyHttp('http://localhost:8551/api/v2'),
  };
  
  export let gamma: API = {
 -  client: new LemmyHttp('http://localhost:8561/api/v1'),
 +  client: new LemmyHttp('http://localhost:8561/api/v2'),
  };
  
  export let delta: API = {
 -  client: new LemmyHttp('http://localhost:8571/api/v1'),
 +  client: new LemmyHttp('http://localhost:8571/api/v2'),
  };
  
  export let epsilon: API = {
 -  client: new LemmyHttp('http://localhost:8581/api/v1'),
 +  client: new LemmyHttp('http://localhost:8581/api/v2'),
  };
  
  export async function setupLogins() {
 -  let formAlpha: LoginForm = {
 +  let formAlpha: Login = {
      username_or_email: 'lemmy_alpha',
      password: 'lemmy',
    };
@@@ -129,7 -127,7 +129,7 @@@ export async function createPost
    let name = randomString(5);
    let body = randomString(10);
    let url = 'https://google.com/';
 -  let form: PostForm = {
 +  let form: CreatePost = {
      name,
      url,
      body,
    return api.client.createPost(form);
  }
  
 -export async function updatePost(api: API, post: Post): Promise<PostResponse> {
 +export async function editPost(api: API, post: Post): Promise<PostResponse> {
    let name = 'A jest test federated post, updated';
 -  let form: PostForm = {
 +  let form: EditPost = {
      name,
      edit_id: post.id,
      auth: api.auth,
@@@ -156,7 -154,7 +156,7 @@@ export async function deletePost
    deleted: boolean,
    post: Post
  ): Promise<PostResponse> {
 -  let form: DeletePostForm = {
 +  let form: DeletePost = {
      edit_id: post.id,
      deleted: deleted,
      auth: api.auth,
@@@ -169,7 -167,7 +169,7 @@@ export async function removePost
    removed: boolean,
    post: Post
  ): Promise<PostResponse> {
 -  let form: RemovePostForm = {
 +  let form: RemovePost = {
      edit_id: post.id,
      removed,
      auth: api.auth,
@@@ -182,7 -180,7 +182,7 @@@ export async function stickyPost
    stickied: boolean,
    post: Post
  ): Promise<PostResponse> {
 -  let form: StickyPostForm = {
 +  let form: StickyPost = {
      edit_id: post.id,
      stickied,
      auth: api.auth,
@@@ -195,7 -193,7 +195,7 @@@ export async function lockPost
    locked: boolean,
    post: Post
  ): Promise<PostResponse> {
 -  let form: LockPostForm = {
 +  let form: LockPost = {
      edit_id: post.id,
      locked,
      auth: api.auth,
@@@ -207,7 -205,7 +207,7 @@@ export async function searchPost
    api: API,
    post: Post
  ): Promise<SearchResponse> {
 -  let form: SearchForm = {
 +  let form: Search = {
      q: post.ap_id,
      type_: SearchType.Posts,
      sort: SortType.TopAll,
@@@ -219,7 -217,7 +219,7 @@@ export async function searchPostLocal
    api: API,
    post: Post
  ): Promise<SearchResponse> {
 -  let form: SearchForm = {
 +  let form: Search = {
      q: post.name,
      type_: SearchType.Posts,
      sort: SortType.TopAll,
@@@ -231,7 -229,7 +231,7 @@@ export async function getPost
    api: API,
    post_id: number
  ): Promise<GetPostResponse> {
 -  let form: GetPostForm = {
 +  let form: GetPost = {
      id: post_id,
    };
    return api.client.getPost(form);
@@@ -241,7 -239,7 +241,7 @@@ export async function searchComment
    api: API,
    comment: Comment
  ): Promise<SearchResponse> {
 -  let form: SearchForm = {
 +  let form: Search = {
      q: comment.ap_id,
      type_: SearchType.Comments,
      sort: SortType.TopAll,
@@@ -254,7 -252,7 +254,7 @@@ export async function searchForBetaComm
  ): Promise<SearchResponse> {
    // Make sure lemmy-beta/c/main is cached on lemmy_alpha
    // Use short-hand search url
 -  let form: SearchForm = {
 +  let form: Search = {
      q: '!main@lemmy-beta:8551',
      type_: SearchType.Communities,
      sort: SortType.TopAll,
  
  export async function searchForCommunity(
    api: API,
 -  q: string,
 +  q: string
  ): Promise<SearchResponse> {
    // Use short-hand search url
 -  let form: SearchForm = {
 +  let form: Search = {
      q,
      type_: SearchType.Communities,
      sort: SortType.TopAll,
@@@ -281,7 -279,7 +281,7 @@@ export async function searchForUser
  ): Promise<SearchResponse> {
    // Make sure lemmy-beta/c/main is cached on lemmy_alpha
    // Use short-hand search url
 -  let form: SearchForm = {
 +  let form: Search = {
      q: apShortname,
      type_: SearchType.Users,
      sort: SortType.TopAll,
  export async function banUserFromSite(
    api: API,
    user_id: number,
 -  ban: boolean,
 +  ban: boolean
  ): Promise<BanUserResponse> {
    // Make sure lemmy-beta/c/main is cached on lemmy_alpha
    // Use short-hand search url
 -  let form: BanUserForm = {
 +  let form: BanUser = {
      user_id,
      ban,
 +    remove_data: false,
      auth: api.auth,
    };
    return api.client.banUser(form);
@@@ -309,14 -306,13 +309,14 @@@ export async function banUserFromCommun
    api: API,
    user_id: number,
    community_id: number,
 -  ban: boolean,
 +  ban: boolean
  ): Promise<BanFromCommunityResponse> {
    // Make sure lemmy-beta/c/main is cached on lemmy_alpha
    // Use short-hand search url
 -  let form: BanFromCommunityForm = {
 +  let form: BanFromCommunity = {
      user_id,
      community_id,
 +    remove_data: false,
      ban,
      auth: api.auth,
    };
@@@ -328,7 -324,7 +328,7 @@@ export async function followCommunity
    follow: boolean,
    community_id: number
  ): Promise<CommunityResponse> {
 -  let form: FollowCommunityForm = {
 +  let form: FollowCommunity = {
      community_id,
      follow,
      auth: api.auth,
  export async function checkFollowedCommunities(
    api: API
  ): Promise<GetFollowedCommunitiesResponse> {
 -  let form: GetFollowedCommunitiesForm = {
 +  let form: GetFollowedCommunities = {
      auth: api.auth,
    };
    return api.client.getFollowedCommunities(form);
@@@ -350,7 -346,7 +350,7 @@@ export async function likePost
    score: number,
    post: Post
  ): Promise<PostResponse> {
 -  let form: CreatePostLikeForm = {
 +  let form: CreatePostLike = {
      post_id: post.id,
      score: score,
      auth: api.auth,
@@@ -365,7 -361,7 +365,7 @@@ export async function createComment
    parent_id?: number,
    content = 'a jest test comment'
  ): Promise<CommentResponse> {
 -  let form: CommentForm = {
 +  let form: CreateComment = {
      content,
      post_id,
      parent_id,
    return api.client.createComment(form);
  }
  
 -export async function updateComment(
 +export async function editComment(
    api: API,
    edit_id: number,
    content = 'A jest test federated comment update'
  ): Promise<CommentResponse> {
 -  let form: CommentForm = {
 +  let form: EditComment = {
      content,
      edit_id,
      auth: api.auth,
@@@ -392,7 -388,7 +392,7 @@@ export async function deleteComment
    deleted: boolean,
    edit_id: number
  ): Promise<CommentResponse> {
 -  let form: DeleteCommentForm = {
 +  let form: DeleteComment = {
      edit_id,
      deleted,
      auth: api.auth,
@@@ -405,7 -401,7 +405,7 @@@ export async function removeComment
    removed: boolean,
    edit_id: number
  ): Promise<CommentResponse> {
 -  let form: RemoveCommentForm = {
 +  let form: RemoveComment = {
      edit_id,
      removed,
      auth: api.auth,
  }
  
  export async function getMentions(api: API): Promise<GetUserMentionsResponse> {
 -  let form: GetUserMentionsForm = {
 +  let form: GetUserMentions = {
      sort: SortType.New,
      unread_only: false,
      auth: api.auth,
@@@ -427,7 -423,7 +427,7 @@@ export async function likeComment
    score: number,
    comment: Comment
  ): Promise<CommentResponse> {
 -  let form: CommentLikeForm = {
 +  let form: CreateCommentLike = {
      comment_id: comment.id,
      score,
      auth: api.auth,
@@@ -442,7 -438,7 +442,7 @@@ export async function createCommunity
    let description = 'a sample description';
    let icon = 'https://image.flaticon.com/icons/png/512/35/35896.png';
    let banner = 'https://image.flaticon.com/icons/png/512/35/35896.png';
 -  let form: CommunityForm = {
 +  let form: CreateCommunity = {
      name: name_,
      title: name_,
      description,
  
  export async function getCommunity(
    api: API,
 -  id: number,
 +  id: number
  ): Promise<CommunityResponse> {
 -  let form: GetCommunityForm = {
 +  let form: GetCommunity = {
      id,
    };
    return api.client.getCommunity(form);
@@@ -470,7 -466,7 +470,7 @@@ export async function deleteCommunity
    deleted: boolean,
    edit_id: number
  ): Promise<CommunityResponse> {
 -  let form: DeleteCommunityForm = {
 +  let form: DeleteCommunity = {
      edit_id,
      deleted,
      auth: api.auth,
@@@ -483,7 -479,7 +483,7 @@@ export async function removeCommunity
    removed: boolean,
    edit_id: number
  ): Promise<CommunityResponse> {
 -  let form: RemoveCommunityForm = {
 +  let form: RemoveCommunity = {
      edit_id,
      removed,
      auth: api.auth,
@@@ -496,7 -492,7 +496,7 @@@ export async function createPrivateMess
    recipient_id: number
  ): Promise<PrivateMessageResponse> {
    let content = 'A jest test federated private message';
 -  let form: PrivateMessageForm = {
 +  let form: CreatePrivateMessage = {
      content,
      recipient_id,
      auth: api.auth,
    return api.client.createPrivateMessage(form);
  }
  
 -export async function updatePrivateMessage(
 +export async function editPrivateMessage(
    api: API,
    edit_id: number
  ): Promise<PrivateMessageResponse> {
    let updatedContent = 'A jest test federated private message edited';
 -  let form: EditPrivateMessageForm = {
 +  let form: EditPrivateMessage = {
      content: updatedContent,
      edit_id,
      auth: api.auth,
@@@ -522,7 -518,7 +522,7 @@@ export async function deletePrivateMess
    deleted: boolean,
    edit_id: number
  ): Promise<PrivateMessageResponse> {
 -  let form: DeletePrivateMessageForm = {
 +  let form: DeletePrivateMessage = {
      deleted,
      edit_id,
      auth: api.auth,
@@@ -534,7 -530,7 +534,7 @@@ export async function registerUser
    api: API,
    username: string = randomString(5)
  ): Promise<LoginResponse> {
 -  let form: RegisterForm = {
 +  let form: Register = {
      username,
      password: 'test',
      password_verify: 'test',
@@@ -548,11 -544,11 +548,11 @@@ export async function saveUserSettingsB
    api: API,
    auth: string
  ): Promise<LoginResponse> {
 -  let form: UserSettingsForm = {
 +  let form: SaveUserSettings = {
      show_nsfw: true,
      theme: 'darkly',
 -    default_sort_type: Object.keys(SortType).indexOf(SortType.Active),
 -    default_listing_type: Object.keys(ListingType).indexOf(ListingType.All),
 +    default_sort_type: SortType.Active,
 +    default_listing_type: ListingType.All,
      lang: 'en',
      show_avatars: true,
      send_notifications_to_email: false,
  
  export async function saveUserSettings(
    api: API,
 -  form: UserSettingsForm
 +  form: SaveUserSettings
  ): Promise<LoginResponse> {
    return api.client.saveUserSettings(form);
  }
@@@ -573,7 -569,7 +573,7 @@@ export async function getSite
    api: API,
    auth: string
  ): Promise<GetSiteResponse> {
 -  let form: GetSiteForm = {
 +  let form: GetSite = {
      auth,
    };
    return api.client.getSite(form);
  export async function listPrivateMessages(
    api: API
  ): Promise<PrivateMessagesResponse> {
 -  let form: GetPrivateMessagesForm = {
 +  let form: GetPrivateMessages = {
      auth: api.auth,
      unread_only: false,
      limit: 999,
@@@ -596,29 -592,29 +596,27 @@@ export async function unfollowRemotes
    // Unfollow all remote communities
    let followed = await checkFollowedCommunities(api);
    let remoteFollowed = followed.communities.filter(
 -    c => c.community_local == false
 +    c => c.community.local == false
    );
    for (let cu of remoteFollowed) {
 -    await followCommunity(api, false, cu.community_id);
 +    await followCommunity(api, false, cu.community.id);
    }
    let followed2 = await checkFollowedCommunities(api);
    return followed2;
  }
  
  export async function followBeta(api: API): Promise<CommunityResponse> {
-   await unfollowRemotes(api);
    // Cache it
    let search = await searchForBetaCommunity(api);
 -  let com = search.communities.filter(c => c.local == false);
 -  if (com[0]) {
 -    let follow = await followCommunity(api, true, com[0].id);
 +  let com = search.communities.find(c => c.community.local == false);
 +  if (com) {
 +    let follow = await followCommunity(api, true, com.community.id);
      return follow;
    }
  }
  
  export function delay(millis: number = 500) {
 -  return new Promise((resolve, _reject) => {
 -    setTimeout(_ => resolve(), millis);
 -  });
 +  return new Promise(resolve => setTimeout(resolve, millis));
  }
  
  export function longDelay() {
index 532749390c8b026615ff80624333006d6d1fec52,3a327c2a1d3a6c33ac7e1d4d7a04b744a5fb3020..7886f8eb42003275cd1fce6e679508824c48d930
@@@ -4,28 -4,27 +4,27 @@@ import 
    beta,
    registerUser,
    searchForUser,
-   saveUserSettingsBio,
    saveUserSettings,
    getSite,
  } from './shared';
  import {
 -  UserView,
 -  UserSettingsForm,
 +  UserViewSafe,
 +  SaveUserSettings,
 +  SortType,
 +  ListingType,
  } from 'lemmy-js-client';
  
  let auth: string;
  let apShortname: string;
  
 -function assertUserFederation(
 -  userOne: UserView,
 -  userTwo: UserView) {
 -  expect(userOne.name).toBe(userTwo.name);
 -  expect(userOne.preferred_username).toBe(userTwo.preferred_username);
 -  expect(userOne.bio).toBe(userTwo.bio);
 -  expect(userOne.actor_id).toBe(userTwo.actor_id);
 -  expect(userOne.avatar).toBe(userTwo.avatar);
 -  expect(userOne.banner).toBe(userTwo.banner);
 -  expect(userOne.published).toBe(userTwo.published);
 +function assertUserFederation(userOne: UserViewSafe, userTwo: UserViewSafe) {
 +  expect(userOne.user.name).toBe(userTwo.user.name);
 +  expect(userOne.user.preferred_username).toBe(userTwo.user.preferred_username);
 +  expect(userOne.user.bio).toBe(userTwo.user.bio);
 +  expect(userOne.user.actor_id).toBe(userTwo.user.actor_id);
 +  expect(userOne.user.avatar).toBe(userTwo.user.avatar);
 +  expect(userOne.user.banner).toBe(userTwo.user.banner);
 +  expect(userOne.user.published).toBe(userTwo.user.published);
  }
  
  test('Create user', async () => {
    apShortname = `@${site.my_user.name}@lemmy-alpha:8541`;
  });
  
- test('Save user settings, check changed bio from beta', async () => {
-   let bio = 'a changed bio';
-   let userRes = await saveUserSettingsBio(alpha, auth);
-   expect(userRes.jwt).toBeDefined();
-   let site = await getSite(alpha, auth);
-   expect(site.my_user.bio).toBe(bio);
-   let searchAlpha = await searchForUser(alpha, site.my_user.actor_id);
-   // Make sure beta sees this bio is changed
-   let searchBeta = await searchForUser(beta, apShortname);
-   assertUserFederation(searchAlpha.users[0], searchBeta.users[0]);
- });
- test('Set avatar and banner, check that they are federated', async () => {
+ test('Set some user settings, check that they are federated', async () => {
    let avatar = 'https://image.flaticon.com/icons/png/512/35/35896.png';
    let banner = 'https://image.flaticon.com/icons/png/512/36/35896.png';
 -  let form: UserSettingsForm = {
+   let bio = 'a changed bio';
 +  let form: SaveUserSettings = {
      show_nsfw: false,
 -    theme: "",
 -    default_sort_type: 0,
 -    default_listing_type: 0,
 -    lang: "",
 +    theme: '',
 +    default_sort_type: SortType.Hot,
 +    default_listing_type: ListingType.All,
 +    lang: '',
      avatar,
      banner,
 -    preferred_username: "user321",
 +    preferred_username: 'user321',
      show_avatars: false,
      send_notifications_to_email: false,
+     bio,
      auth,
 -  }
 +  };
-   let _settingsRes = await saveUserSettings(alpha, form);
+   await saveUserSettings(alpha, form);
  
-   let searchAlpha = await searchForUser(beta, apShortname);
+   let searchAlpha = await searchForUser(alpha, apShortname);
    let userOnAlpha = searchAlpha.users[0];
    let searchBeta = await searchForUser(beta, apShortname);
    let userOnBeta = searchBeta.users[0];
index 07990457c0df92c8f19feadd6d3fe09f3d3d34f2,6c6851a4cf3214333752e34f3cc797235a43fcc4..d94c7cbf75bed9ba207bc0e8d8d63c043b5b3dc9
@@@ -19,16 -19,13 +19,16 @@@ use background_jobs::
    WorkerConfig,
  };
  use itertools::Itertools;
 -use lemmy_db::{community::Community, user::User_, DbPool};
 +use lemmy_db::{
 +  source::{community::Community, user::User_},
 +  DbPool,
 +};
  use lemmy_utils::{location_info, settings::Settings, LemmyError};
  use lemmy_websocket::LemmyContext;
  use log::{debug, warn};
  use reqwest::Client;
  use serde::{export::fmt::Debug, Deserialize, Serialize};
- use std::{collections::BTreeMap, future::Future, pin::Pin};
+ use std::{collections::BTreeMap, env, future::Future, pin::Pin};
  use url::Url;
  
  /// Sends a local activity to a single, remote actor.
@@@ -36,7 -33,7 +36,7 @@@
  /// * `activity` the apub activity to be sent
  /// * `creator` the local actor which created the activity
  /// * `inbox` the inbox url where the activity should be delivered to
 -pub async fn send_activity_single_dest<T, Kind>(
 +pub(crate) async fn send_activity_single_dest<T, Kind>(
    activity: T,
    creator: &dyn ActorType,
    inbox: Url,
@@@ -74,7 -71,7 +74,7 @@@ wher
  /// * `community` the sending community
  /// * `sender_shared_inbox` in case of an announce, this should be the shared inbox of the inner
  ///                         activities creator, as receiving a known activity will cause an error
 -pub async fn send_to_community_followers<T, Kind>(
 +pub(crate) async fn send_to_community_followers<T, Kind>(
    activity: T,
    community: &Community,
    context: &LemmyContext,
@@@ -119,7 -116,7 +119,7 @@@ wher
  /// * `creator` the creator of the activity
  /// * `community` the destination community
  ///
 -pub async fn send_to_community<T, Kind>(
 +pub(crate) async fn send_to_community<T, Kind>(
    activity: T,
    creator: &User_,
    community: &Community,
@@@ -163,7 -160,7 +163,7 @@@ wher
  /// * `creator` user who created the comment
  /// * `mentions` list of inboxes of users which are mentioned in the comment
  /// * `activity` either a `Create/Note` or `Update/Note`
 -pub async fn send_comment_mentions<T, Kind>(
 +pub(crate) async fn send_comment_mentions<T, Kind>(
    creator: &User_,
    mentions: Vec<Url>,
    activity: T,
@@@ -237,7 -234,11 +237,11 @@@ wher
        actor_id: actor.actor_id()?,
        private_key: actor.private_key().context(location_info!())?,
      };
-     activity_sender.queue::<SendActivityTask>(message)?;
+     if env::var("LEMMY_TEST_SEND_SYNC").is_ok() {
+       do_send(message, &Client::default()).await?;
+     } else {
+       activity_sender.queue::<SendActivityTask>(message)?;
+     }
    }
  
    Ok(())
@@@ -262,30 -263,32 +266,32 @@@ impl ActixJob for SendActivityTask 
    const BACKOFF: Backoff = Backoff::Exponential(2);
  
    fn run(self, state: Self::State) -> Self::Future {
-     Box::pin(async move {
-       let mut headers = BTreeMap::<String, String>::new();
-       headers.insert("Content-Type".into(), "application/json".into());
-       let result = sign_and_send(
-         &state.client,
-         headers,
-         &self.inbox,
-         self.activity.clone(),
-         &self.actor_id,
-         self.private_key.to_owned(),
-       )
-       .await;
+     Box::pin(async move { do_send(self, &state.client).await })
+   }
+ }
  
-       if let Err(e) = result {
-         warn!("{}", e);
-         return Err(anyhow!(
-           "Failed to send activity {} to {}",
-           &self.activity,
-           self.inbox
-         ));
-       }
-       Ok(())
-     })
+ async fn do_send(task: SendActivityTask, client: &Client) -> Result<(), Error> {
+   let mut headers = BTreeMap::<String, String>::new();
+   headers.insert("Content-Type".into(), "application/json".into());
+   let result = sign_and_send(
+     client,
+     headers,
+     &task.inbox,
+     task.activity.clone(),
+     &task.actor_id,
+     task.private_key.to_owned(),
+   )
+   .await;
+   if let Err(e) = result {
+     warn!("{}", e);
+     return Err(anyhow!(
+       "Failed to send activity {} to {}",
+       &task.activity,
+       task.inbox
+     ));
    }
+   Ok(())
  }
  
  pub fn create_activity_queue() -> QueueHandle {
diff --combined lemmy_db/Cargo.toml
index d7c0fe3b0ee7c018df756bfaaf0515b69186b701,f85225e09d6832f08571ccae333186d41c772cc9..5963f32cc331e8c81f77691417db884704627337
@@@ -9,15 -9,16 +9,16 @@@ path = "src/lib.rs
  
  [dependencies]
  lemmy_utils = { path = "../lemmy_utils" }
 -diesel = { version = "1.4", features = ["postgres","chrono","r2d2","64-column-tables","serde_json"] }
 -diesel_migrations = "1.4"
 -chrono = { version = "0.4", features = ["serde"] }
 -serde = { version = "1.0", features = ["derive"] }
 -serde_json = { version = "1.0", features = ["preserve_order"]}
 -strum = "0.19"
 -strum_macros = "0.19"
 -log = "0.4"
 -sha2 = "0.9"
 -bcrypt = "0.8"
 -url = { version = "2.1", features = ["serde"] }
 -lazy_static = "1.3"
 -regex = "1.3"
 +diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] }
++diesel_migrations = "1.4.0"
 +chrono = { version = "0.4.19", features = ["serde"] }
 +serde = { version = "1.0.118", features = ["derive"] }
 +serde_json = { version = "1.0.60", features = ["preserve_order"] }
 +strum = "0.20.0"
 +strum_macros = "0.20.1"
 +log = "0.4.11"
 +sha2 = "0.9.2"
 +bcrypt = "0.9.0"
 +url = { version = "2.2.0", features = ["serde"] }
 +lazy_static = "1.4.0"
 +regex = "1.4.2"
diff --combined lemmy_db/src/lib.rs
index 387e38a2829471e219e5bee198cbc38703be0f90,57ca24eb3c36d07d3866f5b376456bedbc148810..4df69ca432fb9da21e4d20a34a2c7afe4a2ebae4
@@@ -4,6 -4,10 +4,10 @@@ extern crate diesel
  extern crate strum_macros;
  #[macro_use]
  extern crate lazy_static;
+ // this is used in tests
+ #[allow(unused_imports)]
+ #[macro_use]
+ extern crate diesel_migrations;
  
  use chrono::NaiveDateTime;
  use diesel::{result::Error, *};
@@@ -11,10 -15,28 +15,10 @@@ use regex::Regex
  use serde::{Deserialize, Serialize};
  use std::{env, env::VarError};
  
 -pub mod activity;
 -pub mod category;
 -pub mod comment;
 -pub mod comment_report;
 -pub mod comment_view;
 -pub mod community;
 -pub mod community_view;
 -pub mod moderator;
 -pub mod moderator_views;
 -pub mod password_reset_request;
 -pub mod post;
 -pub mod post_report;
 -pub mod post_view;
 -pub mod private_message;
 -pub mod private_message_view;
 +pub mod aggregates;
  pub mod schema;
 -pub mod site;
 -pub mod site_view;
 -pub mod user;
 -pub mod user_mention;
 -pub mod user_mention_view;
 -pub mod user_view;
 +pub mod source;
 +pub mod views;
  
  pub type DbPool = diesel::r2d2::Pool<diesel::r2d2::ConnectionManager<diesel::PgConnection>>;
  
@@@ -106,15 -128,6 +110,15 @@@ pub trait Reportable<T> 
      Self: Sized;
  }
  
 +pub trait ApubObject<T> {
 +  fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Self, Error>
 +  where
 +    Self: Sized;
 +  fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
 +  where
 +    Self: Sized;
 +}
 +
  pub trait MaybeOptional<T> {
    fn get_optional(self) -> Option<T>;
  }
@@@ -131,11 -144,6 +135,11 @@@ impl<T> MaybeOptional<T> for Option<T> 
    }
  }
  
 +pub(crate) trait ToSafe {
 +  type SafeColumns;
 +  fn safe_columns_tuple() -> Self::SafeColumns;
 +}
 +
  pub fn get_database_url_from_env() -> Result<String, VarError> {
    env::var("LEMMY_DATABASE_URL")
  }
@@@ -209,20 -217,14 +213,22 @@@ lazy_static! 
      Regex::new(r"^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$").unwrap();
  }
  
 +pub(crate) mod functions {
 +  use diesel::sql_types::*;
 +
 +  sql_function! {
 +    fn hot_rank(score: BigInt, time: Timestamp) -> Integer;
 +  }
 +}
 +
  #[cfg(test)]
  mod tests {
    use super::fuzzy_search;
    use crate::{get_database_url_from_env, is_email_regex};
    use diesel::{Connection, PgConnection};
  
+   embed_migrations!();
    pub fn establish_unpooled_connection() -> PgConnection {
      let db_url = match get_database_url_from_env() {
        Ok(url) => url,
          e
        ),
      };
-     PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url))
+     let conn =
+       PgConnection::establish(&db_url).unwrap_or_else(|_| panic!("Error connecting to {}", db_url));
+     embedded_migrations::run(&conn).unwrap();
+     conn
    }
  
    #[test]
diff --combined test.sh
index 3ea3f8305db4c13163199301e8270b12c100f062,ef2e608f4574c66377ec2e8fd400dba8500c99ef..21093d0cf03b3b1cb0abed64eeb0b6423b633093
+++ b/test.sh
@@@ -1,9 -1,8 +1,10 @@@
  #!/bin/sh
- export DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
+ set -e
  export LEMMY_DATABASE_URL=postgres://lemmy:password@localhost:5432/lemmy
 +# Commenting since this will overwrite schema.rs, which will break things now
 +# diesel migration run
  # Integration tests only work on stable due to a bug in config-rs
  # https://github.com/mehcode/config-rs/issues/158
  RUST_BACKTRACE=1 RUST_TEST_THREADS=1 \
 -  cargo +stable test --workspace --no-fail-fast
 +  cargo +1.47.0 test --workspace --no-fail-fast
index a61c8ff6e02f5e23a3ee93ebf5cbe8b3afe3edf9,69f2d5f5baedcb3246413e6fe1186abcd00ddc97..75753d28089bb0821c3768b1cefe54f854bcc608
@@@ -29,10 -29,8 +29,10 @@@ use lemmy_apub::
    },
  };
  use lemmy_db::{
 -  community::{Community, CommunityForm},
 -  user::{User_, *},
 +  source::{
 +    community::{Community, CommunityForm},
 +    user::{User_, *},
 +  },
    Crud,
    ListingType,
    SortType,
@@@ -156,6 -154,7 +156,7 @@@ fn create_http_request() -> HttpReques
  }
  
  #[actix_rt::test]
+ #[ignore]
  async fn test_shared_inbox_expired_signature() {
    let request = create_http_request();
    let context = create_context();
  }
  
  #[actix_rt::test]
+ #[ignore]
  async fn test_user_inbox_expired_signature() {
    let request = create_http_request();
    let context = create_context();
  }
  
  #[actix_rt::test]
+ #[ignore]
  async fn test_community_inbox_expired_signature() {
    let context = create_context();
    let connection = &context.pool().get().unwrap();