chore: first working version with integration test

This commit is contained in:
Sandro Eiler 2023-11-12 14:11:13 +01:00
parent f869b0f067
commit 122c38a0da
19 changed files with 266 additions and 1214 deletions

16
.github/workflows/audit.yml vendored Normal file
View file

@ -0,0 +1,16 @@
name: Security audit
on:
schedule:
- cron: "0 0 * * *"
push:
paths:
- "**/Cargo.toml"
- "**/Cargo.lock"
jobs:
security_audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: taiki-e/install-action@cargo-deny
- name: Scan for vulnerabilities
run: cargo deny check advisories

48
.github/workflows/changelog.yml vendored Normal file
View file

@ -0,0 +1,48 @@
name: Generate changelog
# on:
# push:
# tags:
# - "v[0-9]+.[0-9]+.[0-9]+"
jobs:
changelog:
name: Generate changelog
runs-on: ubuntu-latest
outputs:
release_body: ${{ steps.git-cliff.outputs.content }}
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Generate a changelog
uses: orhun/git-cliff-action@v2
id: git-cliff
with:
config: cliff.toml
args: -vv --latest --strip header
env:
OUTPUT: CHANGES.md
# use release body in the same job
- name: Upload the binary releases
uses: svenstaro/upload-release-action@v2
with:
file: binary_release.zip
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
body: ${{ steps.git-cliff.outputs.content }}
# use release body in another job
upload:
name: Upload the release
needs: changelog
runs-on: ubuntu-latest
steps:
- name: Upload the binary releases
uses: svenstaro/upload-release-action@v2
with:
file: binary_release.zip
repo_token: ${{ secrets.GITHUB_TOKEN }}
tag: ${{ github.ref }}
body: ${{ needs.changelog.outputs.release_body }}

53
.github/workflows/general.yml vendored Normal file
View file

@ -0,0 +1,53 @@
name: Rust
on: [push, pull_request]
env:
CARGO_TERM_COLOR: always
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- name: Run tests
run: cargo test
fmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- name: Enforce formatting
run: cargo fmt --check
clippy:
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- name: Linting
run: cargo clippy -- -D warnings
coverage:
name: Code coverage
runs-on: ubuntu-latest
container:
image: xd009642/tarpaulin
options: --security-opt seccomp=unconfined
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Generate code coverage
run: |
cargo tarpaulin --verbose --workspace

630
Cargo.lock generated
View file

@ -17,36 +17,6 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
dependencies = [
"memchr",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "async-trait"
version = "0.1.68"
@ -72,7 +42,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
dependencies = [
"async-trait",
"axum-core",
"bitflags 1.3.2",
"bitflags",
"bytes",
"futures-util",
"http",
@ -140,12 +110,6 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bumpalo"
version = "3.13.0"
@ -170,75 +134,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38"
dependencies = [
"android-tzdata",
"iana-time-zone",
"num-traits",
"serde",
"windows-targets",
]
[[package]]
name = "cookie"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "cookie_store"
version = "0.16.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa"
dependencies = [
"cookie 0.16.2",
"idna 0.2.3",
"log",
"publicsuffix",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "cookie_store"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5a18f35792056f8c7c2de9c002e7e4fe44c7b5f66e7d99f46468dbb730a7ea7"
dependencies = [
"cookie 0.16.2",
"idna 0.3.0",
"log",
"publicsuffix",
"serde",
"serde_derive",
"serde_json",
"time",
"url",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -255,41 +150,6 @@ version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
[[package]]
name = "darling"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "encoding_rs"
version = "0.8.32"
@ -299,12 +159,6 @@ dependencies = [
"cfg-if",
]
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.1"
@ -380,17 +234,6 @@ version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-macro"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.28"
@ -410,22 +253,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533"
dependencies = [
"futures-core",
"futures-macro",
"futures-task",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "getrandom"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
@ -446,7 +276,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http",
"indexmap 1.9.3",
"indexmap",
"slab",
"tokio",
"tokio-util",
@ -459,30 +289,12 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12"
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
[[package]]
name = "hex"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
name = "http"
version = "0.2.9"
@ -505,34 +317,12 @@ dependencies = [
"pin-project-lite",
]
[[package]]
name = "http-range-header"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29"
[[package]]
name = "httparse"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904"
[[package]]
name = "httpc-test"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81a425cb8352fb5080b3622e8a4265c63e75bedd68a4c19a83f7d4c88f9c9667"
dependencies = [
"cookie 0.16.2",
"http",
"reqwest",
"reqwest_cookie_store",
"serde",
"serde_json",
"thiserror",
"tokio",
]
[[package]]
name = "httpdate"
version = "1.0.2"
@ -576,56 +366,6 @@ dependencies = [
"tokio-native-tls",
]
[[package]]
name = "iana-time-zone"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "idna"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "idna"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "idna"
version = "0.4.0"
@ -643,19 +383,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"serde",
]
[[package]]
name = "indexmap"
version = "2.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897"
dependencies = [
"equivalent",
"hashbrown 0.14.1",
"serde",
"hashbrown",
]
[[package]]
@ -699,29 +427,6 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy-regex"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e723bd417b2df60a0f6a2b6825f297ea04b245d4ba52b5a22cb679bdf58b05fa"
dependencies = [
"lazy-regex-proc_macros",
"once_cell",
"regex",
]
[[package]]
name = "lazy-regex-proc_macros"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f0a1d9139f0ee2e862e08a9c5d0ba0470f2aa21cd1e1aa1b1562f83116c725f"
dependencies = [
"proc-macro2",
"quote",
"regex",
"syn",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -732,19 +437,10 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
name = "learn_axum"
version = "0.1.0"
dependencies = [
"anyhow",
"async-trait",
"axum",
"httpc-test",
"lazy-regex",
"serde",
"serde_json",
"serde_with",
"strum_macros",
"hyper",
"reqwest",
"tokio",
"tower-cookies",
"tower-http",
"uuid",
]
[[package]]
@ -775,12 +471,6 @@ version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
name = "matches"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "matchit"
version = "0.7.0"
@ -799,16 +489,6 @@ version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef"
dependencies = [
"mime",
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
@ -847,15 +527,6 @@ dependencies = [
"tempfile",
]
[[package]]
name = "num-traits"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.16.0"
@ -887,7 +558,7 @@ version = "0.10.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"cfg-if",
"foreign-types",
"libc",
@ -992,12 +663,6 @@ version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "proc-macro2"
version = "1.0.63"
@ -1007,22 +672,6 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "psl-types"
version = "2.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
[[package]]
name = "publicsuffix"
version = "2.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96a8c1bda5ae1af7f99a2962e49df150414a43d62404644d98dd5c3a93d07457"
dependencies = [
"idna 0.3.0",
"psl-types",
]
[[package]]
name = "quote"
version = "1.0.29"
@ -1032,74 +681,15 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
dependencies = [
"bitflags 1.3.2",
"bitflags",
]
[[package]]
name = "regex"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "reqwest"
version = "0.11.18"
@ -1108,8 +698,6 @@ checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55"
dependencies = [
"base64",
"bytes",
"cookie 0.16.2",
"cookie_store 0.16.2",
"encoding_rs",
"futures-core",
"futures-util",
@ -1139,19 +727,6 @@ dependencies = [
"winreg",
]
[[package]]
name = "reqwest_cookie_store"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06b407c05de7a0f7e4cc2a56af5e9bd6468e509124e81078ce1f8bc2ed3536bf"
dependencies = [
"bytes",
"cookie 0.16.2",
"cookie_store 0.19.1",
"reqwest",
"url",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
@ -1164,7 +739,7 @@ version = "0.37.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62f25693a73057a1b4cb56179dd3c7ea21a7c6c5ee7d85781f5749b46f34b79c"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"errno",
"io-lifetimes",
"libc",
@ -1205,7 +780,7 @@ version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
dependencies = [
"bitflags 1.3.2",
"bitflags",
"core-foundation",
"core-foundation-sys",
"libc",
@ -1227,20 +802,6 @@ name = "serde"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.164"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
@ -1274,35 +835,6 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_with"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237"
dependencies = [
"base64",
"chrono",
"hex",
"indexmap 1.9.3",
"indexmap 2.0.2",
"serde",
"serde_json",
"serde_with_macros",
"time",
]
[[package]]
name = "serde_with_macros"
version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
@ -1347,25 +879,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck",
"proc-macro2",
"quote",
"rustversion",
"syn",
]
[[package]]
name = "syn"
version = "2.0.22"
@ -1397,53 +910,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "thiserror"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd"
dependencies = [
"itoa",
"serde",
"time-core",
"time-macros",
]
[[package]]
name = "time-core"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb"
[[package]]
name = "time-macros"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b"
dependencies = [
"time-core",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -1529,48 +995,6 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-cookies"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40f38d941a2ffd8402b36e02ae407637a9caceb693aaf2edc910437db0f36984"
dependencies = [
"async-trait",
"axum-core",
"cookie 0.17.0",
"futures-util",
"http",
"parking_lot",
"pin-project-lite",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-http"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140"
dependencies = [
"bitflags 2.3.3",
"bytes",
"futures-core",
"futures-util",
"http",
"http-body",
"http-range-header",
"httpdate",
"mime",
"mime_guess",
"percent-encoding",
"pin-project-lite",
"tokio",
"tokio-util",
"tower-layer",
"tower-service",
"tracing",
]
[[package]]
name = "tower-layer"
version = "0.3.2"
@ -1610,15 +1034,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]]
name = "unicode-bidi"
version = "0.3.13"
@ -1647,32 +1062,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
dependencies = [
"form_urlencoded",
"idna 0.4.0",
"idna",
"percent-encoding",
]
[[package]]
name = "uuid"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
dependencies = [
"getrandom",
"rand",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "want"
version = "0.3.1"
@ -1786,15 +1185,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.42.0"

View file

@ -3,24 +3,31 @@ name = "learn_axum"
version = "0.1.0"
edition = "2021"
[lib]
path = "src/lib.rs"
[[bin]]
path = "src/main.rs"
name = "learn_axum"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "1.32.0", features = ["full"] }
# Serde / json
serde = { version = "1.0", features = ["derive"] }
serde_json = "1"
serde_with = "3"
# Axum
hyper = { version = "0.14.27", features = ["full"] }
# # Serde / json
# serde = { version = "1.0", features = ["derive"] }
# serde_json = "1"
# serde_with = "3"
# # Axum
axum = { version = "0.6.20" }
tower-http = { version = "0.4.4", features = ["fs"] }
tower-cookies = "0.9"
# Others
lazy-regex = "3"
async-trait = "0.1"
strum_macros = "0.25"
uuid = { version = "1", features = ["v4", "fast-rng"] }
# tower-http = { version = "0.4.4", features = ["fs"] }
# tower-cookies = "0.9"
# # Others
# lazy-regex = "3"
# async-trait = "0.1"
# strum_macros = "0.25"
# uuid = { version = "1", features = ["v4", "fast-rng"] }
[dev-dependencies]
anyhow = "1"
httpc-test = "0.1.5"
reqwest = "0.11"

View file

@ -0,0 +1,8 @@
-- Create Subscriptions Table
CREATE TABLE subscriptions (
id UUID NOT NULL,
PRIMARY KEY (id),
email TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
subscribed_at TIMESTAMPTZ NOT NULL
);

46
scripts/init_db.sh Executable file
View file

@ -0,0 +1,46 @@
#!/usr/bin/env bash
set -x
set -eo pipefail
if ! [ -x "$(command -v psql)" ]; then
echo >&2 "Error: psql is not installed."
exit 1
fi
if ! [ -x "$(command -v sqlx)" ]; then
echo >&2 "Error: sqlx is not installed."
echo >&2 "Use:"
echo >&2 " cargo install --version=0.5.7 sqlx-cli --no-default-features --features postgres"
echo >&2 "to install it."
exit 1
fi
DB_USER=${POSTGRES_USER:=postgres}
DB_PASSWORD="${POSTGRES_PASSWORD:=password}"
DB_NAME="${POSTGRES_DB:=newsletter}"
DB_PORT="${POSTGRES_PORT:=5432}"
# Allow to skip container run if a containerized Postgres database is already running
if [[ -z "${SKIP_DB_RUN}" ]]
then
podman run \
-e POSTGRES_USER=${DB_USER} \
-e POSTGRES_PASSWORD=${DB_PASSWORD} \
-e POSTGRES_DB=${DB_NAME} \
-p "${DB_PORT}":5432 \
-d postgres \
postgres -N 1000
fi
until PGPASSWORD="${DB_PASSWORD}" psql -h "localhost" -U "${DB_USER}" -p "${DB_PORT}" -d "postgres" -c '\q'; do
>&2 echo "Postgres is still unavailable - sleeping"
sleep 1
done
>&2 echo "Postgres is up and running on port ${DB_PORT} - running migrations now!"
export DATABASE_URL=postgres://${DB_USER}:${DB_PASSWORD}@localhost:${DB_PORT}/${DB_NAME}
sqlx database create
sqlx migrate run
>&2 echo "Postgres has been migrated, ready to go!"

View file

@ -1,18 +0,0 @@
#[derive(Debug, Clone)]
pub struct Ctx {
user_id: u64,
}
// Constructor
impl Ctx {
pub fn new(user_id: u64) -> Self {
Self { user_id }
}
}
// Property Accessors.
impl Ctx {
pub fn user_id(&self) -> u64 {
self.user_id
}
}

View file

@ -1,67 +0,0 @@
use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::Serialize;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Clone, Debug, Serialize, strum_macros::AsRefStr)]
#[serde(tag = "type", content = "data")]
pub enum Error {
LoginFail,
// -- Auth errors.
AuthFailNoAuthTokenCookie,
AuthFailTokenWrongFormat,
AuthFailCtxNotInRequestExt,
// -- Model errors.
PropertyDeleteFailIdNotFound { id: u64 },
}
impl Error {
pub fn client_status_and_error(&self) -> (StatusCode, ClientError) {
match self {
// -- Login.
Self::LoginFail => (StatusCode::UNAUTHORIZED, ClientError::LOGIN_FAIL),
// -- Auth.
Self::AuthFailNoAuthTokenCookie
| Self::AuthFailTokenWrongFormat
| Self::AuthFailCtxNotInRequestExt => (StatusCode::FORBIDDEN, ClientError::NO_AUTH),
// -- Model.
Self::PropertyDeleteFailIdNotFound { .. } => {
(StatusCode::BAD_REQUEST, ClientError::INVALID_PARAMS)
}
// -- Fallback.
_ => (
StatusCode::INTERNAL_SERVER_ERROR,
ClientError::SERVICE_ERROR,
),
}
}
}
impl IntoResponse for Error {
fn into_response(self) -> Response {
println!("->> {:<12} - {self:?}", "INTO_RESPONSE");
// Create a placeholder Axum response.
let mut response = StatusCode::INTERNAL_SERVER_ERROR.into_response();
// Insert the Error into the response.
response.extensions_mut().insert(self);
response
}
}
#[derive(Debug, strum_macros::AsRefStr)]
#[allow(non_camel_case_types)]
pub enum ClientError {
LOGIN_FAIL,
NO_AUTH,
INVALID_PARAMS,
SERVICE_ERROR,
}

24
src/lib.rs Normal file
View file

@ -0,0 +1,24 @@
#![allow(unused)]
use axum::extract::{Path, Query};
use axum::http::{Method, Uri};
use axum::response::{Html, IntoResponse, Response};
use axum::routing::{get, get_service, IntoMakeService};
use axum::Server;
use axum::{middleware, Json, Router};
use hyper::server::conn::AddrIncoming;
use std::net::SocketAddr;
use std::net::TcpListener;
pub type App = Server<AddrIncoming, IntoMakeService<Router>>;
/// API routes
fn app() -> Router {
Router::new().route("/health_check", get(|| async {}))
}
/// Start the server
pub fn run(listener: TcpListener) -> hyper::Result<App> {
let app = app();
let server = Server::from_tcp(listener)?.serve(app.into_make_service());
Ok(server)
}

View file

@ -1,70 +0,0 @@
use std::time::SystemTime;
use crate::ctx::Ctx;
use crate::error::ClientError;
use crate::{Error, Result};
use axum::http::{Method, Uri};
use serde::Serialize;
use serde_json::{json, Value};
use serde_with::skip_serializing_none;
use uuid::Uuid;
pub async fn log_request(
uuid: Uuid,
req_method: Method,
uri: Uri,
ctx: Option<Ctx>,
service_error: Option<&Error>,
client_error: Option<ClientError>,
) -> Result<()> {
let timestamp = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_millis();
let error_type = service_error.map(|se| se.as_ref().to_string());
let error_data = serde_json::to_value(service_error)
.ok()
.and_then(|mut v| v.get_mut("data").map(|v| v.take()));
// Create the RequestLogLine.
let log_line = RequestLogLine {
uuid: uuid.to_string(),
timestamp: timestamp.to_string(),
req_path: uri.path().to_string(),
req_method: req_method.to_string(),
user_id: ctx.map(|ctx| ctx.user_id()),
client_error_type: client_error.map(|e| e.as_ref().to_string()),
error_type,
error_data,
};
println!(" ->> log request: \n{}", json!(log_line));
// TODO: Send to cloud-watch or something.
Ok(())
}
#[skip_serializing_none]
#[derive(Serialize)]
struct RequestLogLine {
uuid: String, // uuid string formatted
timestamp: String, // (should be iso8601)
// -- User and context attributes.
user_id: Option<u64>,
// -- http request attributes.
req_path: String,
req_method: String,
// -- Errors attributes.
client_error_type: Option<String>,
error_type: Option<String>,
error_data: Option<Value>,
}

View file

@ -1,108 +1,9 @@
#![allow(unused)]
use crate::model::ModelController;
use self::ctx::Ctx;
pub use self::error::{Error, Result};
use self::log::log_request;
use std::net::SocketAddr;
use axum::extract::{Path, Query};
use axum::http::{Method, Uri};
use axum::response::{Html, IntoResponse, Response};
use axum::routing::{get, get_service};
use axum::Server;
use axum::{middleware, Json, Router};
use serde::Deserialize;
use serde_json::json;
use tower_cookies::CookieManagerLayer;
use tower_http::services::ServeDir;
use uuid::Uuid;
mod ctx;
mod error;
mod log;
mod model;
mod web;
#[derive(Debug, Deserialize)]
struct HelloParams {
name: Option<String>,
}
use learn_axum::run;
use std::net::TcpListener;
#[tokio::main]
async fn main() -> Result<()> {
let mc = ModelController::new().await?;
let routes_apis = web::routes_properties::routes(mc.clone())
.route_layer(middleware::from_fn(web::mw_auth::mw_require_auth));
let routes_all = Router::new()
.merge(Router::new().route("/health_check", get(|| async {})))
.merge(web::routes_login::routes())
.nest("/api", routes_apis)
.layer(middleware::map_response(main_response_mapper))
.layer(middleware::from_fn_with_state(
mc.clone(),
web::mw_auth::mw_ctx_resolver,
))
.layer(CookieManagerLayer::new()) // must be above? the auth routes
.fallback_service(routes_static());
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
println!("->> Listening on {addr}\n");
Server::bind(&addr)
.serve(routes_all.into_make_service())
.await
.unwrap();
Ok(())
}
/// Map the response to add headers, etc.
///
/// * `res`: the response to map
async fn main_response_mapper(
ctx: Option<Ctx>,
uri: Uri,
req_method: Method,
res: Response,
) -> Response {
println!("->> {:<12} - main_response_mapper", "RES_MAPPER");
let uuid = Uuid::new_v4();
// -- Get the eventual response error.
let service_error = res.extensions().get::<Error>();
let client_status_error = service_error.map(|se| se.client_status_and_error());
// -- If client error, build the new response
let error_response = client_status_error
.as_ref()
.map(|(status_code, client_error)| {
let client_error_body = json!({
"error": {
"type": client_error.as_ref(),
"req_uuid": uuid.to_string(),
}
});
println!(" ->> client_error_body: {client_error_body}");
// Build the new response from the client error body.
(*status_code, Json(client_error_body)).into_response()
});
// -- Build and log the server log line.
let client_error = client_status_error.unzip().1;
log_request(uuid, req_method, uri, ctx, service_error, client_error).await;
println!();
error_response.unwrap_or(res)
}
/// Serve static files
// FIXME: remove
fn routes_static() -> Router {
Router::new().nest_service("/", get_service(ServeDir::new("./")))
async fn main() -> hyper::Result<()> {
let addr = format!("127.0.0.1:{}", "3000");
let listener = TcpListener::bind(addr).expect("Unable to bind to port");
run(listener)?.await
}

View file

@ -1,69 +0,0 @@
//! Simplistic model layer
//! (with mock-store layer)
use crate::{ctx::Ctx, Error, Result};
use serde::{Deserialize, Serialize};
use std::sync::{Arc, Mutex};
// region: --- Property Types
#[derive(Clone, Debug, Serialize)]
pub struct Property {
pub id: u64,
pub creator_id: u64,
pub address: String,
pub contact: String,
}
#[derive(Deserialize)]
pub struct PropertyForCreate {
pub address: String,
pub contact: String,
}
// endregion: --- Property Types
// region: --- Model Controller
#[derive(Clone)]
pub struct ModelController {
property_store: Arc<Mutex<Vec<Option<Property>>>>,
}
// Constructor
impl ModelController {
pub async fn new() -> Result<Self> {
Ok(Self {
property_store: Arc::default(),
})
}
}
// CRUD implementation
impl ModelController {
pub async fn create_property(&self, ctx: Ctx, property: PropertyForCreate) -> Result<Property> {
let mut store = self.property_store.lock().unwrap();
let id = store.len() as u64;
let property = Property {
id,
creator_id: ctx.user_id(),
address: property.address,
contact: property.contact,
};
store.push(Some(property.clone()));
Ok(property)
}
pub async fn list_properties(&self, ctx: Ctx) -> Result<Vec<Property>> {
let store = self.property_store.lock().unwrap();
let properties = store.iter().filter_map(|p| p.clone()).collect();
Ok(properties)
}
pub async fn delete_property(&self, ctx: Ctx, id: u64) -> Result<Property> {
let mut store = self.property_store.lock().unwrap();
let property = store.get_mut(id as usize).and_then(|p| p.take());
property.ok_or(Error::PropertyDeleteFailIdNotFound { id })
}
}
// endregion: --- Model Controller

View file

@ -1,6 +0,0 @@
pub mod mw_auth;
pub mod routes_login;
pub mod routes_properties;
/// The cookie name for the auth token
pub const AUTH_TOKEN: &str = "auth-token";

View file

@ -1,93 +0,0 @@
use async_trait::async_trait;
use axum::extract::FromRequestParts;
use axum::http::request::Parts;
use axum::http::Request;
use axum::middleware::Next;
use axum::response::Response;
use axum::RequestPartsExt;
use lazy_regex::regex_captures;
use tower_cookies::{Cookie, Cookies};
use crate::ctx::Ctx;
use crate::web::AUTH_TOKEN;
use crate::{Error, Result};
pub async fn mw_ctx_resolver<B>(
cookies: Cookies,
mut req: Request<B>,
next: Next<B>,
) -> Result<Response> {
println!("->> {:<12} - mw_ctx_resolver", "MIDDLEWARE");
let auth_token = cookies.get(AUTH_TOKEN).map(|c| c.value().to_string());
// Compute Result<Ctx>.
let result_ctx = match auth_token
.ok_or(Error::AuthFailNoAuthTokenCookie)
.and_then(parse_token)
{
Ok((user_id, _exp, _sign)) => {
// TODO: Token components validations.
Ok(Ctx::new(user_id))
}
Err(e) => Err(e),
};
// Remove the cookie if something went wrong other than NoAuthTokenCookie.
if result_ctx.is_err() && !matches!(result_ctx, Err(Error::AuthFailNoAuthTokenCookie)) {
cookies.remove(Cookie::named(AUTH_TOKEN))
}
// Store the ctx_result in the request extension.
req.extensions_mut().insert(result_ctx);
Ok(next.run(req).await)
}
/// Middleware to require authentication.
pub async fn mw_require_auth<B>(
ctx: Result<Ctx>,
req: Request<B>,
next: Next<B>,
) -> Result<Response> {
println!("->> {:<12} - mw_require_auth - {ctx:?}", "MIDDLEWARE");
ctx?;
Ok(next.run(req).await)
}
// region: --- Ctx Extractor
#[async_trait]
impl<S: Send + Sync> FromRequestParts<S> for Ctx {
type Rejection = Error;
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self> {
println!("->> {:<12} - Ctx", "EXTRACTOR");
parts
.extensions
.get::<Result<Ctx>>()
.ok_or(Error::AuthFailCtxNotInRequestExt)?
.clone()
}
}
// endregion: --- Ctx Extractor
/// Parse a token of format `user-[user-id].[expiration].[signature]`
/// Returns (user-id, expiration, signature)
fn parse_token(token: String) -> Result<(u64, String, String)> {
let (_whole, user_id, exp, sign) = regex_captures!(
r#"^user-(\d+)\.(.+)\.(.+)"#, // a literal regex
&token
)
.ok_or(Error::AuthFailTokenWrongFormat)?;
let user_id: u64 = user_id
.parse()
.map_err(|_| Error::AuthFailTokenWrongFormat)?;
Ok((user_id, exp.to_string(), sign.to_string()))
}

View file

@ -1,35 +0,0 @@
use crate::{web, Error, Result};
use axum::routing::post;
use axum::{Json, Router};
use serde::Deserialize;
use serde_json::{json, Value};
use tower_cookies::{Cookie, Cookies};
pub fn routes() -> Router {
Router::new().route("/api/login", post(api_login))
}
async fn api_login(cookies: Cookies, payload: Json<LoginPayload>) -> Result<Json<Value>> {
println!("->> {:<12} - api_login", "HANDLER");
if payload.username != "demo1" || payload.password != "demo1" {
return Err(Error::LoginFail);
}
// FIXME: Implement real auth-token generation/signature.
cookies.add(Cookie::new(web::AUTH_TOKEN, "user-1.exp.sign"));
let body = Json(json!({
"result": {
"success": true
}
}));
Ok(body)
}
#[derive(Debug, Deserialize)]
struct LoginPayload {
username: String,
password: String,
}

View file

@ -1,46 +0,0 @@
use crate::ctx::Ctx;
use crate::model::{ModelController, Property, PropertyForCreate};
use crate::Result;
use axum::extract::{Path, State};
use axum::routing::{delete, post};
use axum::{Json, Router};
pub fn routes(mc: ModelController) -> Router {
Router::new()
.route("/properties", post(create_property).get(list_properties))
.route("/properties/:id", delete(delete_property))
.with_state(mc)
}
// region: --- REST Handlers
async fn create_property(
State(mc): State<ModelController>,
ctx: Ctx,
Json(property_fc): Json<PropertyForCreate>,
) -> Result<Json<Property>> {
println!("->> {:<12} - create_property", "HANDLER");
let property = mc.create_property(ctx, property_fc).await?;
Ok(Json(property))
}
async fn list_properties(
State(mc): State<ModelController>,
ctx: Ctx,
) -> Result<Json<Vec<Property>>> {
println!("->> {:<12} - list_properties", "HANDLER");
let properties = mc.list_properties(ctx).await?;
Ok(Json(properties))
}
async fn delete_property(
State(mc): State<ModelController>,
ctx: Ctx,
Path(id): Path<u64>,
) -> Result<Json<Property>> {
println!("->> {:<12} - delete_property", "HANDLER");
let property = mc.delete_property(ctx, id).await?;
Ok(Json(property))
}
// endregion: --- REST Handlers

34
tests/health_check.rs Normal file
View file

@ -0,0 +1,34 @@
use std::net::{SocketAddr, TcpListener};
struct TestApp {
addr: SocketAddr,
}
#[tokio::test]
async fn health_check_works() {
let TestApp { addr, .. } = spawn_app().await;
let client = reqwest::Client::new();
let response = client
.get(format!("http://{addr}/health_check"))
.send()
.await
.expect("Failed to execute request.");
assert!(response.status().is_success());
assert_eq!(Some(0), response.content_length());
}
// fn spawn_app() {
// let server = learn_axum::run().expect("Failed to bind address.");
// tokio::spawn(server);
// }
async fn spawn_app() -> TestApp {
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
let addr = listener.local_addr().unwrap();
let server = learn_axum::run(listener).expect("Failed to bind to address");
tokio::spawn(server);
TestApp { addr }
}

View file

@ -1,71 +0,0 @@
#![allow(unused_imports)]
use anyhow::Result;
use serde_json::json;
#[tokio::test]
async fn test_quick_dev() -> Result<()> {
let hc = httpc_test::new_client("http://localhost:3000")?;
hc.do_get("/hello?name=jen").await?.print().await?;
let hc = httpc_test::new_client("http://localhost:3000")?;
hc.do_get("/hello2/mike").await?.print().await?;
hc.do_get("/src/main.rs").await?.print().await?;
hc.do_get("/src/blub.rs").await?.print().await?;
let req_login = hc.do_post(
"/api/login",
json!(
{
"username": "demo1",
"password": "demowrong"
}
),
);
req_login.await?.print().await?;
let req_login = hc.do_post(
"/api/login",
json!(
{
"username": "demo1",
"password": "demo1"
}
),
);
req_login.await?.print().await?;
hc.do_get("/hello2/mike").await?.print().await?;
let req_create_property = hc.do_post(
"/api/properties",
json!(
{
"address": "Lolilat Street 1",
"contact": "01234 567890"
}
),
);
req_create_property.await?.print().await?;
let req_create_property = hc.do_post(
"/api/properties",
json!(
{
"address": "Lolilat Street 2",
"contact": "01243 217890"
}
),
);
req_create_property.await?.print().await?;
let req_get_properties = hc.do_get("/api/properties").await?;
req_get_properties.print().await?;
let req_delete_property = hc.do_delete("/api/properties/1").await?;
req_delete_property.print().await?;
let req_get_properties = hc.do_get("/api/properties").await?;
req_get_properties.print().await?;
let req_delete_property = hc.do_delete("/api/properties/0").await?;
req_delete_property.print().await?;
Ok(())
}