Add test scaffolding

Signed-off-by: Olivier 'reivilibre <olivier@librepush.net>
This commit is contained in:
Olivier 'reivilibre' 2024-07-05 22:55:32 +01:00
parent ed9a36a662
commit eb0a417c35
8 changed files with 222 additions and 1 deletions

44
Cargo.lock generated
View File

@ -613,6 +613,18 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "console"
version = "0.15.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb"
dependencies = [
"encode_unicode",
"lazy_static",
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "const-oid"
version = "0.9.5"
@ -872,6 +884,12 @@ dependencies = [
"serde",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
[[package]]
name = "encoding_rs"
version = "0.8.34"
@ -1629,6 +1647,7 @@ dependencies = [
"futures",
"governor",
"hornbeam",
"insta",
"josekit",
"metrics",
"metrics-exporter-prometheus",
@ -1723,6 +1742,19 @@ dependencies = [
"libc",
]
[[package]]
name = "insta"
version = "1.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "810ae6042d48e2c9e9215043563a58a80b877bc863228a74cf10c49d4620a6f5"
dependencies = [
"console",
"lazy_static",
"linked-hash-map",
"serde",
"similar",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -1901,6 +1933,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
@ -3017,6 +3055,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "similar"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
[[package]]
name = "sketches-ddsketch"
version = "0.2.1"

View File

@ -42,5 +42,6 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
[dev-dependencies]
axum-test-helper = "0.3.0"
insta = { version = "1.39.0", features = ["serde", "yaml"] }
pgtemp = "0.3.0"
rstest = "0.21.0"

View File

@ -10,4 +10,4 @@
# Development
- [Testing](dev/testing.md)

45
docs/dev/testing.md Normal file
View File

@ -0,0 +1,45 @@
# Testing
## Testing approach
### Unit tests
Unit tests should live inside a module in the code unit they are testing,
gated behind `#[cfg(test)]`. \
This is fairly common Rust practice.
There is no hard and fast rule for the granularity of the unit tests,
but they should test the smallest amount of logic that is simple to test,
but no smaller. \
In practice this means that unit tests should be at the function-level
where this makes sense, or at the struct-level if this makes more sense.
For now, avoid the use of test mocks, but use them if it makes
strong sense to do so.
### Integration tests
Integration tests should live in the `tests/` directory.
In general, each test will get its own throwaway Postgres database.
#### Snapshot tests
Some of the integration tests will compare snapshots (of e.g. HTML) against
a gold standard.
When a new snapshot is created, the output should be manually verified,
including in a browser if necessary.
It goes without saying that all snapshot changes should be expected;
if they are not then treated as failures.
### End-to-end tests
idCoop doesn't currently have end-to-end tests but this is on the wishlist
for the future.
Will eventually look into Playwright etc.

View File

@ -9,3 +9,6 @@ pub mod config;
pub mod passwords;
pub mod store;
pub mod web;
#[cfg(test)]
mod tests;

91
src/tests.rs Normal file
View File

@ -0,0 +1,91 @@
use std::sync::Arc;
use axum::Router;
use confique::{Config, Partial};
use josekit::jwk::alg::rsa::RsaKeyPair;
use pgtemp::PgTempDB;
use serde_json::json;
use crate::{
config::{Configuration, SecretConfig},
store::IdCoopStore,
web::make_router,
};
struct TestSystem {
database: PgTempDB,
web: Router,
}
const RSA_KEY_PAIR_PEM: &[u8] = include_bytes!("tests/keypair.pem");
const RSA_PUBLIC_KEY_PEM: &[u8] = include_bytes!("tests/publickey.crt");
// #[rstest::fixture]
async fn basic_system() -> TestSystem {
let temp_db = pgtemp::PgTempDBBuilder::new()
.with_dbname("test_idcoop")
.start_async()
.await;
let store = IdCoopStore::connect(&temp_db.connection_uri())
.await
.expect("failed to connect to pgtemp db");
let config_partial: <Configuration as confique::Config>::Partial =
serde_json::from_value(json!({
"listen": {
// Not useful, not actually used in the tests
"bind": "127.0.0.1:1",
"public_base_uri": "http://idcoop.example.com",
},
"postgres": {
"connect": "postgres://not-used-in-tests"
},
"oidc": {
"issuer": "http://issuer.example.com",
"rsa_keypair": "not-used-in-tests",
"clients": {
"aclient": {
"redirect_uris": [
"http://aclient.example.com/redirect"
],
"name": "AClient",
"allow_user_classes": ["active"],
"secret": "secretA",
}
}
},
"password_hashing": {
// Use weak password hash settings; we're not testing Argon2 here,
// we just want it to be fast.
"memory": 512,
"iterations": 1,
},
"ratelimits": {
"login": "3 per hour, 2 burst",
},
}))
.expect("bad test config");
let config =
Configuration::from_partial(config_partial.with_fallback(Partial::default_values()))
.expect("failed to load builtin config");
let secrets = SecretConfig {
rsa_key_pair: RsaKeyPair::from_pem(RSA_KEY_PAIR_PEM)
.expect("failed to decode builtin RSA keypair"),
};
let router = make_router(Arc::new(store), Arc::new(config), Arc::new(secrets))
.await
.expect("failed to make router");
TestSystem {
database: temp_db,
web: router,
}
}
#[tokio::test]
async fn test_demo() {
let basic = basic_system().await;
}

28
src/tests/keypair.pem Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDu6acOa+3ae2S
0llp5oMsXjBMd5QJeQJCcY5Q9NAITF2U9VBwAiMf2wmaTZ1aWWFGSb/zWef7Hx1e
qNhsK9MYL+QdJih2I+KpMtDWm7hhy9FtCHVc9i1z9PruXb0om2jDWLuBkPdCqJZT
C58ObZKmgL4OH5F1Qv5JR/ZX21OjLolXPJo1sonLv9mlgufvhUmnC17onSSqFLBA
nhUedjbfdnLShkp0xa8G0nW7Ls7Idaxyo8M5S2M+azJyLI87eqjjfz0yIW0Am890
mRa81hO2D+YNcVA2wIE9MEI/ie480YxLQ0VHCjX4DcVir4ceExysYkdL8VK+U14g
NO67k4NVAgMBAAECggEABnseJyoZ0V7miOgCIemKClwMCVwkQLQLCRwtdCzG/p9Y
sef1g9/uPc3I4Z0USruO5v7mJi6h6cS7+jhpAhvpX3GmgfiTemXxyVxvYcvCLSrM
gmm3SR61npNMA7yC2OdcbqtvefjM1x4x7AoEeDvUkULOCDWvYUyYkuCZHYubl1mS
Rtcp9rxzky2tjdp8CHySBa9Kz9LEjWdFGky7g3vSyqZtw6tkK5CTwMPb9aHwiEq1
yDWCbqAPPnb300dXSqx7z3AcsxBi/lpCs79fQS1vSJ1/L9POpYxX0SMtffkR5+Bl
Mkg9dZUVenfbP4n40FdMypTTX2KJiMkc8+f0+Tz9QQKBgQDrdT7xs0lqMeZWp2rD
y2KLzRl0iO/+yKse+BgkeBggZ/Vh2TeF9ylTRYdmimxkJ8eZRipTr0F64BS/LPEk
RgWviuf9dUl0gyuhYTOJgUw1wcgtB6e+04UKEUsQW6JoNZekkM+xTa7FskLmlJ4J
zzowjF1lgJeEyX1tvWXKIVe+/QKBgQDUzy5nZoUqBrVTYxL2uadIUh8oiBKPU93U
Gz3DUq90yfDa7lFhwMRQRXfNqGUy6tshsaF4fT1b62hZDSz1OH3h/y1LKQOdF5kc
JJyk/4b7NJna16kwBLzWje5SjQKr51aQWU8JftZ5/8uck2j7vMi+mgwzpG45J7kv
Q1I5decBOQKBgFq1sKotB/uBfdukY91KXYy+VzAuEUd2x3YG3kYufhz97+riZCGY
NrN99cvrSBbNvHewMF5NBkzwRw3foob28vnN6dIbfVEFt6lUaSZwSYvsO9IdQOKj
Wn2ma+TBaK/89Y7QuzLzWoGPS3bJipj83M4XRWP1RmpBtbCxZqWYctWBAoGAMZPi
16wGsffGHpsiO+CcnDilkafByypar6N5DBwjTC4PsrF6vC9QjPLiKkNk8CvOyVa8
q3lh5hw9vyFWq/pxOUldn/j6Iorw3KGa7MWrCLMEdPtxKwKvi7ydHRZE3Q+UFyT3
SNsH1HxHTz74Yk1k5yK0XQOduisK9XvVmBVjr+ECgYEAyoSbo/1cyLKWgrIr0K/f
stiKL9SmBmYbaGaxtQToB5Hnqso7Hz5YEDlrcr8s1ukEFghgeNYuDYw3ZKKGGfZm
yVQKAt8ouoO8rfkLrtt0H+/0uJgouhewDEqf/O+MfzwDnFcT89J5ZTEf+9n6pjry
fuiQnuwEsPYGCCFuWWlrdHQ=
-----END PRIVATE KEY-----

9
src/tests/publickey.crt Normal file
View File

@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw7umnDmvt2ntktJZaeaD
LF4wTHeUCXkCQnGOUPTQCExdlPVQcAIjH9sJmk2dWllhRkm/81nn+x8dXqjYbCvT
GC/kHSYodiPiqTLQ1pu4YcvRbQh1XPYtc/T67l29KJtow1i7gZD3QqiWUwufDm2S
poC+Dh+RdUL+SUf2V9tToy6JVzyaNbKJy7/ZpYLn74VJpwte6J0kqhSwQJ4VHnY2
33Zy0oZKdMWvBtJ1uy7OyHWscqPDOUtjPmsyciyPO3qo4389MiFtAJvPdJkWvNYT
tg/mDXFQNsCBPTBCP4nuPNGMS0NFRwo1+A3FYq+HHhMcrGJHS/FSvlNeIDTuu5OD
VQIDAQAB
-----END PUBLIC KEY-----