From 3aadfafd6194e4e04f4e522aa55f367558c039b2 Mon Sep 17 00:00:00 2001 From: tapiocaboy Date: Wed, 9 Oct 2024 12:03:21 +0530 Subject: [PATCH 1/6] Ported code --- .gitignore | 2 +- .idea/isotopes.iml | 1 + Cargo.toml | 16 ++++++++- src/lib.rs | 33 +++++++++++++++++ src/main.rs | 14 ++++++-- test/integration_test.rs | 10 ------ tests/integration_test.rs | 76 +++++++++++++++++++++++++++++++++++++++ 7 files changed, 138 insertions(+), 14 deletions(-) create mode 100644 src/lib.rs delete mode 100644 test/integration_test.rs create mode 100644 tests/integration_test.rs diff --git a/.gitignore b/.gitignore index d01bd1a..a8b5cd3 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,4 @@ Cargo.lock # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore # and can be added to the global gitignore or merged into this file. For a more nuclear # option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ \ No newline at end of file +.idea/ \ No newline at end of file diff --git a/.idea/isotopes.iml b/.idea/isotopes.iml index cf84ae4..bbe0a70 100644 --- a/.idea/isotopes.iml +++ b/.idea/isotopes.iml @@ -3,6 +3,7 @@ + diff --git a/Cargo.toml b/Cargo.toml index e9aae15..079d6f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,5 +3,19 @@ name = "isotopes" version = "0.1.0" edition = "2021" +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +path = "src/lib.rs" + +[[bin]] +path = "src/main.rs" +name = "isotopes" + [dependencies] -serde = "1.0" \ No newline at end of file +actix-web = "4.9.0" +tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] } +serde = {version = "1.0.130", features = ["derive"]} +[dev-dependencies] +reqwest = "0.12.7" + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..16dd010 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,33 @@ +use actix_web::{App, HttpRequest, HttpResponse, HttpServer, Responder, web}; +use actix_web::dev::Server; +use std::net::TcpListener; +async fn greet(req: HttpRequest) -> impl Responder { + let name = req.match_info().get("name").unwrap_or("World"); + format!("Hello {}!", &name) +} +async fn health_check() -> impl Responder { + HttpResponse::Ok().finish() +} + +async fn subscribe(_form: web::Form) -> impl Responder { + HttpResponse::Ok().finish() +} + +#[derive(serde::Deserialize)] +pub struct FromData { + email: String, + name: String, +} + + +pub async fn run(listener: TcpListener) -> Result { + let server = HttpServer::new(|| { + App::new() + .route("/", web::get().to(greet)) + .route("/{name}", web::get().to(greet)) + .route("/health_check", web::get().to(health_check)) + .route("/subscriptions", web::post().to(subscribe)) + }).listen(listener)? + .run(); + Ok(server) +} diff --git a/src/main.rs b/src/main.rs index e7a11a9..b433b73 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,13 @@ -fn main() { - println!("Hello, world!"); +use std::net::TcpListener; +use isotopes::run; + +#[tokio::main] +async fn main() -> Result<(), std::io::Error> { + // let address = "127.0.0.1:4000"; + + let listener = TcpListener::bind("127.0.0.1:4000").expect("failed to bind address"); + // let port = listener.local_addr().unwrap().port(); + // println!("{}", port); + + run(listener).await?.await } diff --git a/test/integration_test.rs b/test/integration_test.rs deleted file mode 100644 index abb1d8b..0000000 --- a/test/integration_test.rs +++ /dev/null @@ -1,10 +0,0 @@ -use assert_cmd::Command; - -#[test] -fn test_main_output() { - Command::cargo_bin("isotopes") - .unwrap() - .assert() - .success() - .stdout("Hello, world!\n"); -} \ No newline at end of file diff --git a/tests/integration_test.rs b/tests/integration_test.rs new file mode 100644 index 0000000..a1b63f1 --- /dev/null +++ b/tests/integration_test.rs @@ -0,0 +1,76 @@ +use std::net::TcpListener; + +const ADDRESS: &str = "127.0.0.1:4100"; + +async fn spawn_app() -> String { + let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind address"); + let port = listener.local_addr().unwrap().port(); + + let server = isotopes::run(listener).await.expect("failed to bind address"); + let _ = tokio::spawn(server); + format!("http://127.0.0.1:{}", port) +} +#[tokio::test] +async fn health_check_works() { + + // Arrange + let address = spawn_app().await; + let client = reqwest::Client::new(); + + // Act + let resp = client.get(&format!("{}/health_check", &address)) + .send() + .await + .expect("failed to execute request"); + // Assert + assert!(resp.status().is_success()); + assert_eq!(Some(19), resp.content_length()); +} + +#[tokio::test] +async fn subscribe_return_a_200_for_valid_form_data() { + // Arrange + let app_address = spawn_app().await; + let client = reqwest::Client::new(); + + let body = "name=le%20guin&email=ursula_le_guin%40gmail.com"; + let response = client + .post(&format!("{}/subscriptions", &app_address)) + .header("Content-Type", "application/x-www-form-urlencoded") + .body(body) + .send() + .await + .expect("failed to execute request"); + + // Assert + assert_eq!(200, response.status().as_u16()); +} + +#[tokio::test] +async fn sibscribe_returns_a_400_when_date_is_missing() { + let app_address = spawn_app().await; + let client = reqwest::Client::new(); + + let test_cases = vec![ + ("name=le%20guin", "missing email"), + ("email=ursula_le_guin%40gmail.com", "missing name"), + ("", "missing name and email"), + ]; + + for (invalid_body, error_messages) in test_cases { + let response = client + .post(&format!("{}/subscriptions", &app_address)) + .header("Content-Type", "application/x-www-form-urlencoded") + .body(invalid_body) + .send() + .await. + expect("failed to execute request"); + + assert_eq!( + 400, + response.status().as_u16()); + } + + + +} From 7ba905e2083cada1abc6c818879f3df76a255f82 Mon Sep 17 00:00:00 2001 From: tapiocaboy Date: Wed, 9 Oct 2024 12:06:17 +0530 Subject: [PATCH 2/6] Ported code --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 16dd010..4b64631 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,8 @@ async fn subscribe(_form: web::Form) -> impl Responder { #[derive(serde::Deserialize)] pub struct FromData { - email: String, - name: String, + _email: String, + _name: String, } From 2304da24c67eb47e00c273638237025cab153658 Mon Sep 17 00:00:00 2001 From: tapiocaboy Date: Wed, 9 Oct 2024 12:08:39 +0530 Subject: [PATCH 3/6] Ported code --- tests/integration_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration_test.rs b/tests/integration_test.rs index a1b63f1..b87d290 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,6 +1,6 @@ use std::net::TcpListener; -const ADDRESS: &str = "127.0.0.1:4100"; +// const ADDRESS: &str = "127.0.0.1:4100"; async fn spawn_app() -> String { let listener = TcpListener::bind("127.0.0.1:0").expect("failed to bind address"); @@ -57,7 +57,7 @@ async fn sibscribe_returns_a_400_when_date_is_missing() { ("", "missing name and email"), ]; - for (invalid_body, error_messages) in test_cases { + for (invalid_body, _error_messages) in test_cases { let response = client .post(&format!("{}/subscriptions", &app_address)) .header("Content-Type", "application/x-www-form-urlencoded") From 28e7cbfdc550d169bd253e1cf750c15d217ac78c Mon Sep 17 00:00:00 2001 From: tapiocaboy Date: Wed, 9 Oct 2024 12:12:52 +0530 Subject: [PATCH 4/6] Ported code --- src/lib.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4b64631..3857814 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,14 +9,16 @@ async fn health_check() -> impl Responder { HttpResponse::Ok().finish() } -async fn subscribe(_form: web::Form) -> impl Responder { +async fn subscribe(form: web::Form) -> impl Responder { + let _x = form.email.clone(); + let _y = form.name.clone(); HttpResponse::Ok().finish() } #[derive(serde::Deserialize)] pub struct FromData { - _email: String, - _name: String, + email: String, + name: String, } From 8ccfef8fe9ae63a7888f75f3d83e4a13da138bd3 Mon Sep 17 00:00:00 2001 From: tapiocaboy Date: Wed, 9 Oct 2024 12:57:58 +0530 Subject: [PATCH 5/6] Ported code --- .github/workflows/general_build.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/.github/workflows/general_build.yml b/.github/workflows/general_build.yml index 40d84f9..b2e9cf6 100644 --- a/.github/workflows/general_build.yml +++ b/.github/workflows/general_build.yml @@ -51,3 +51,24 @@ jobs: - name: Install dependencies run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc + # Install tarpaulin, a Rust coverage tool + - name: Install cargo-tarpaulin + run: cargo install cargo-tarpaulin + + # Run tarpaulin to generate coverage report + - name: Run coverage + run: cargo tarpaulin --out Xml + + # Upload coverage report to Codecov + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./cobertura.xml # Specify the report generated by tarpaulin + fail_ci_if_error: true + verbose: true + + # Display coverage in GitHub PR + - name: Coverage badge + run: | + curl -s https://codecov.io/gh/${{ github.repository }}/branch/${{ github.ref_name }}/graph/badge.svg \ + -o coverage-badge.svg \ No newline at end of file From 3e5f224aa83ad1f5518f01417b42a8f25e7dff9f Mon Sep 17 00:00:00 2001 From: tapiocaboy Date: Wed, 9 Oct 2024 13:07:04 +0530 Subject: [PATCH 6/6] Ported code --- .github/workflows/general_build.yml | 60 ++++++++++++++--------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/.github/workflows/general_build.yml b/.github/workflows/general_build.yml index b2e9cf6..a440205 100644 --- a/.github/workflows/general_build.yml +++ b/.github/workflows/general_build.yml @@ -42,33 +42,33 @@ jobs: - name: Generate documentation run: cargo doc --no-deps --document-private-items - code_coverage: - needs: build - runs-on: ubuntu-latest - if: github.event_name == 'push' || github.event_name == 'pull_request' - steps: - - uses: actions/checkout@v3 - - - name: Install dependencies - run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc - # Install tarpaulin, a Rust coverage tool - - name: Install cargo-tarpaulin - run: cargo install cargo-tarpaulin - - # Run tarpaulin to generate coverage report - - name: Run coverage - run: cargo tarpaulin --out Xml - - # Upload coverage report to Codecov - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v3 - with: - files: ./cobertura.xml # Specify the report generated by tarpaulin - fail_ci_if_error: true - verbose: true - - # Display coverage in GitHub PR - - name: Coverage badge - run: | - curl -s https://codecov.io/gh/${{ github.repository }}/branch/${{ github.ref_name }}/graph/badge.svg \ - -o coverage-badge.svg \ No newline at end of file +# code_coverage: +# needs: build +# runs-on: ubuntu-latest +# if: github.event_name == 'push' || github.event_name == 'pull_request' +# steps: +# - uses: actions/checkout@v3 +# +# - name: Install dependencies +# run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc +# # Install tarpaulin, a Rust coverage tool +# - name: Install cargo-tarpaulin +# run: cargo install cargo-tarpaulin +# +# # Run tarpaulin to generate coverage report +# - name: Run coverage +# run: cargo tarpaulin --out Xml +# +# # Upload coverage report to Codecov +# - name: Upload coverage to Codecov +# uses: codecov/codecov-action@v3 +# with: +# files: ./cobertura.xml # Specify the report generated by tarpaulin +# fail_ci_if_error: true +# verbose: true +# +# # Display coverage in GitHub PR +# - name: Coverage badge +# run: | +# curl -s https://codecov.io/gh/${{ github.repository }}/branch/${{ github.ref_name }}/graph/badge.svg \ +# -o coverage-badge.svg \ No newline at end of file