Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

switched from manual to semi-automated snapshot tests #1327

Merged
merged 13 commits into from
Jul 20, 2024
Merged
4 changes: 3 additions & 1 deletion .github/workflows/server-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ jobs:
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- run: cargo test --workspace
- name: Setup | install insta
run: curl -LsSf https://insta.rs/install.sh | sh
- run: cargo insta test --workspace
working-directory: server
linting:
runs-on: ubuntu-latest
Expand Down
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ data/sources/15_patches-rooms_tumonline.yaml
data/sources/45_custom-maps.yaml
data/sources/46_overlay-maps.yaml
data/sources/img/img-sources.yaml
server/main-api/test/test-queries.yaml
deployment/k3s
91 changes: 91 additions & 0 deletions server/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ opt-level = 3
# https://github.com/launchbadge/sqlx?tab=readme-ov-file#compile-time-verification
[profile.dev.package.sqlx-macros]
opt-level = 3

# https://insta.rs/docs/quickstart/
[profile.dev.package]
insta.opt-level = 3
similar.opt-level = 3
1 change: 1 addition & 0 deletions server/main-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ base64 = "0.22.1"
time = "0.3.36"

[dev-dependencies]
insta = { version = "1.39.0", features = ["yaml", "json", "redactions"] }
pretty_assertions = "1.4.0"
testcontainers = { version = "0.20.0", features = ["watchdog"] }
testcontainers-modules = { version = "0.8.0", features = ["postgres", "meilisearch"] }
Expand Down
9 changes: 9 additions & 0 deletions server/main-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,15 @@ some fuzzing-goals are automatically tested in our CI.
You can exchange `--base-url=http://localhost:3003` to `--base-url=https://nav.tum.de` for the full public API, or
restrict your scope using an option like `--endpoint=/api/search`.

### Approval tests

Some of our tests are approval tests.
Please install [insta](https://insta.rs/docs/quickstart/) to have a working environment.

You can then run `cargo insta test` instead of `cargo test` to review the needed changes.
If you don't want to do this, using the version we provide via CI is fine, but the DX is way better with the correct
tooling.

## License

This program is free software: you can redistribute it and/or modify
Expand Down
108 changes: 30 additions & 78 deletions server/main-api/src/calendar/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -294,12 +294,10 @@ mod tests {
.insert_header(ContentType::json())
.to_request();
let (_, resp) = test::call_service(&app, req).await.into_parts();
run_testcase(
resp,
400,
"Json deserialize error: EOF while parsing a value at line 1 column 0",
)
.await;

let (status, actual) = run_testcase(resp).await;
assert_eq!(status, 400);
insta::assert_snapshot!(actual, @r###""Json deserialize error: EOF while parsing a value at line 1 column 0""###);
}
{
// missing required query parameters
Expand All @@ -314,7 +312,10 @@ mod tests {
.insert_header(ContentType::json())
.to_request();
let (_, resp) = test::call_service(&app, req).await.into_parts();
run_testcase(resp, 400, "No id requested").await;

let (status, actual) = run_testcase(resp).await;
assert_eq!(status, 400);
insta::assert_snapshot!(actual, @r###""No id requested""###);
}
{
// way too many parameters
Expand All @@ -329,7 +330,10 @@ mod tests {
.insert_header(ContentType::json())
.to_request();
let (_, resp) = test::call_service(&app, req).await.into_parts();
run_testcase(resp, 400, "Too many ids to query. We suspect that users don't need this. If you need this limit increased, please send us a message").await;

let (status, actual) = run_testcase(resp).await;
assert_eq!(status, 400);
insta::assert_snapshot!(actual, @r###""Too many ids to query. We suspect that users don't need this. If you need this limit increased, please send us a message""###);
}
{
// room without a calendar
Expand All @@ -344,7 +348,10 @@ mod tests {
.insert_header(ContentType::json())
.to_request();
let (_, resp) = test::call_service(&app, req).await.into_parts();
run_testcase(resp, 404, "Room 5121.EG.002/None does not have a calendar").await;

let (status, actual) = run_testcase(resp).await;
assert_eq!(status, 404);
insta::assert_snapshot!(actual, @r###""Room 5121.EG.002/None does not have a calendar""###);
}
{
// show all entries of 5121.EG.003
Expand All @@ -359,43 +366,10 @@ mod tests {
.insert_header(ContentType::json())
.to_request();
let (_, resp) = test::call_service(&app, req).await.into_parts();
let expected = serde_json::json!({
"5121.EG.003": {
"events": [
{
"id": 1,
"room_code": "5121.EG.003",
"start_at": "2012-01-01T01:00:00Z",
"end_at": "2014-01-01T01:00:00Z",
"stp_title_de": "Quantenteleportation",
"stp_title_en": "Quantum teleportation",
"stp_type": "Vorlesung mit Zentralübung",
"entry_type": "lecture",
"detailed_entry_type": "Abhaltung"
},
{
"id": 2,
"room_code": "5121.EG.003",
"start_at": "2014-01-01T01:00:00Z",
"end_at": "2016-01-01T01:00:00Z",
"stp_title_de": "Quantenteleportation 2",
"stp_title_en": "Quantum teleportation 2",
"stp_type": "Vorlesung mit Zentralübung",
"entry_type": "lecture",
"detailed_entry_type": "Abhaltung"
}
],
"location": {
"key": "5121.EG.003",
"name": "5121.EG.003 (Computerraum)",
"last_calendar_scrape_at": now,
"calendar_url": "https://campus.tum.de/3",
"type_common_name": "Serverraum",
"type": "room"
}
}
});
run_testcase(resp, 200, &expected.to_string()).await;

let (status, actual) = run_testcase(resp).await;
assert_eq!(status, 200);
insta::assert_yaml_snapshot!(actual, {".**.last_calendar_scrape_at" => "[last_calendar_scrape_at]"});
}
{
// show both rooms, but a limited timeframe
Expand All @@ -410,46 +384,24 @@ mod tests {
.insert_header(ContentType::json())
.to_request();
let (_, resp) = test::call_service(&app, req).await.into_parts();
let expected = serde_json::json!({
"5121.EG.001": {
"events": [],
"location": {
"calendar_url": "https://campus.tum.de/1",
"key": "5121.EG.001",
"last_calendar_scrape_at": now,
"name": "5121.EG.001 (Montage- und Versuchshalle)",
"type": "room",
"type_common_name": "Versuchshalle",
},
},
"5121.EG.003": {
"events": [],
"location": {
"calendar_url": "https://campus.tum.de/3",
"key": "5121.EG.003",
"last_calendar_scrape_at": now,
"name": "5121.EG.003 (Computerraum)",
"type": "room",
"type_common_name": "Serverraum",
},
},
});
run_testcase(resp, 200, &expected.to_string()).await;

let (status, actual) = run_testcase(resp).await;
assert_eq!(status, 200);
insta::assert_yaml_snapshot!(actual, {".**.last_calendar_scrape_at" => "[last_calendar_scrape_at]"});
}
}

async fn run_testcase(resp: HttpResponse, expected_status: u16, expected_body: &str) {
async fn run_testcase(resp: HttpResponse) -> (u16, Value) {
let actual_status = resp.status().as_u16();
let body_box = resp.into_body();
let body_bytes = actix_web::body::to_bytes(body_box).await.unwrap();
let body_text = String::from_utf8(body_bytes.into_iter().collect()).unwrap();
// if the expected value cleanly deserializes into json, we should compare using this
if let Ok(expected_value) = serde_json::from_str::<Value>(expected_body) {
let actual_value = serde_json::from_str::<Value>(&body_text).unwrap();
assert_eq!(actual_value, expected_value);
let body = if let Ok(actual) = serde_json::from_str::<Value>(&body_text) {
actual
} else {
assert_eq!(body_text, expected_body);
}
assert_eq!(actual_status, expected_status);
Value::String(body_text)
};
(actual_status, body)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
source: main-api/src/calendar/mod.rs
expression: actual
---
5121.EG.003:
events:
- detailed_entry_type: Abhaltung
end_at: "2014-01-01T01:00:00Z"
entry_type: lecture
id: 1
room_code: 5121.EG.003
start_at: "2012-01-01T01:00:00Z"
stp_title_de: Quantenteleportation
stp_title_en: Quantum teleportation
stp_type: Vorlesung mit Zentralübung
- detailed_entry_type: Abhaltung
end_at: "2016-01-01T01:00:00Z"
entry_type: lecture
id: 2
room_code: 5121.EG.003
start_at: "2014-01-01T01:00:00Z"
stp_title_de: Quantenteleportation 2
stp_title_en: Quantum teleportation 2
stp_type: Vorlesung mit Zentralübung
location:
calendar_url: "https://campus.tum.de/3"
key: 5121.EG.003
last_calendar_scrape_at: "[last_calendar_scrape_at]"
name: 5121.EG.003 (Computerraum)
type: room
type_common_name: Serverraum
Loading