From a03573ce0ef1ce93db9be81273d17cf9561068fe Mon Sep 17 00:00:00 2001 From: Race Williams Date: Mon, 9 Dec 2024 18:30:18 -0500 Subject: [PATCH] impl assertion test --- CMakeLists.txt | 12 ++- examples/errors/json_missing_key.raq | 14 +++ examples/errors/json_value_mismatch.raq | 13 +++ src/CMakeLists.txt | 3 +- src/assertions.cpp | 12 +-- src/request.cpp | 1 - tests/CMakeLists.txt | 4 +- tests/assertions_test.cpp | 111 ++++++++++++++++++++++++ tests/parser_test.cpp | 4 - tests/tautology_test.cpp | 6 -- 10 files changed, 157 insertions(+), 23 deletions(-) create mode 100644 examples/errors/json_missing_key.raq create mode 100644 examples/errors/json_value_mismatch.raq create mode 100644 tests/assertions_test.cpp delete mode 100644 tests/parser_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8e58c82..da44ee0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,8 @@ set(CMAKE_EXPORT_COMPILE_COMMAND ON) include_directories(include) add_definitions(-DSOME_DEFINITION) add_subdirectory(src) + +enable_testing() add_subdirectory(tests) include(FetchContent) @@ -27,11 +29,15 @@ FetchContent_Declare( googletest URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip) +FetchContent_Declare( + fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt + GIT_TAG e69e5f977d458f2650bb346dadf2ad30c5320281) # 10.2.1 +FetchContent_MakeAvailable(fmt) + # For Windows: Prevent overriding the parent project's compiler/linker settings set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest CLI11 json curl) - -enable_testing() +FetchContent_MakeAvailable(googletest CLI11 json curl fmt) diff --git a/examples/errors/json_missing_key.raq b/examples/errors/json_missing_key.raq new file mode 100644 index 0000000..7b75573 --- /dev/null +++ b/examples/errors/json_missing_key.raq @@ -0,0 +1,14 @@ +[request] +POST https://jsonplaceholder.typicode.com/posts + +[headers] +Content-Type: application/json + +[body type="json"] +{ + "title": "foo" +} + +[assertions] +json_field: title ^foo$ +json_field: other ^bar$ diff --git a/examples/errors/json_value_mismatch.raq b/examples/errors/json_value_mismatch.raq new file mode 100644 index 0000000..93d0ee6 --- /dev/null +++ b/examples/errors/json_value_mismatch.raq @@ -0,0 +1,13 @@ +[request] +POST https://jsonplaceholder.typicode.com/posts + +[headers] +Content-Type: application/json + +[body type="json"] +{ + "title": "bar" +} + +[assertions] +json_field: title ^foo$ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 43d4016..d0a3347 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,7 +2,8 @@ set(SOURCE_FILES parser.cpp request.cpp assertions.cpp response.cpp) add_library(raquestlib ${SOURCE_FILES}) -target_link_libraries(raquestlib CURL::libcurl nlohmann_json::nlohmann_json) +target_link_libraries(raquestlib CURL::libcurl nlohmann_json::nlohmann_json + fmt::fmt) add_executable(raquest main.cpp) target_link_libraries(raquest raquestlib) diff --git a/src/assertions.cpp b/src/assertions.cpp index e1ed368..1560d74 100644 --- a/src/assertions.cpp +++ b/src/assertions.cpp @@ -40,7 +40,8 @@ Assertions::validate(const Response &response) const { std::optional Assertions::validate_status_code(const Response &response) const { int status_code = response.get_status_code(); - if (std::find(expected_status_codes.begin(), expected_status_codes.end(), + if (!expected_status_codes.empty() && + std::find(expected_status_codes.begin(), expected_status_codes.end(), status_code) == expected_status_codes.end()) { std::stringstream ss; ss << "Expected one of these status codes: "; @@ -90,11 +91,10 @@ Assertions::validate_json_fields(const Response &response) const { } if (!std::regex_match(val, pattern.get_regex())) { - return FailedAssertion(FailedAssertionType::UnexpectedJsonField, - "Expected json field '" + key + - "' to match pattern '" + - pattern.get_pattern() + - "' but it didn't, it was '" + val + "'"); + return FailedAssertion( + FailedAssertionType::UnexpectedJsonField, + "Expected json field '" + key + "' to match pattern '" + + pattern.get_pattern() + "' but it didn't, it was '" + val + "'"); } } return std::nullopt; diff --git a/src/request.cpp b/src/request.cpp index a93a700..80cdece 100644 --- a/src/request.cpp +++ b/src/request.cpp @@ -17,7 +17,6 @@ void Request::set_method(const std::string &method) { this->method = method; } void Request::set_url(const std::string &url) { this->url = url; } void Request::add_header(const std::string &key, const std::string &value) { - // TODO: smart validation of key and value mapping against stds headers.push_back(key + ": " + value); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3092eec..cda2552 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,8 +1,8 @@ -set(TEST_SOURCE_FILES tautology_test.cpp parser_test.cpp) +set(TEST_SOURCE_FILES tautology_test.cpp assertions_test.cpp) add_executable(raquest_test ${TEST_SOURCE_FILES}) -target_link_libraries(raquest_test gtest gtest_main raquestlib) +target_link_libraries(raquest_test gtest gtest_main gmock raquestlib pthread) add_test(NAME test COMMAND raquest_test) diff --git a/tests/assertions_test.cpp b/tests/assertions_test.cpp new file mode 100644 index 0000000..3a4cf01 --- /dev/null +++ b/tests/assertions_test.cpp @@ -0,0 +1,111 @@ +#include "assertions.hpp" +#include "response.hpp" +#include + +class AssertionsTestFixture : public ::testing::Test { +protected: + Assertions assertions; + + AssertionsTestFixture() : assertions(Assertions::create()) {} +}; + +TEST_F(AssertionsTestFixture, SuccessfulValidation) { + assertions.status_codes({200, 201}) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer exampletoken12345") + .json_field("title", "^foo$") + .json_field("age", "^30$") + .json_field("isActive", "^true$") + .json_field("description", "^here is the description$"); + + Response response; + response.parse_status_code(201); + response.parse_header("Content-Type: application/json"); + response.parse_header("Authorization: Bearer exampletoken12345"); + response.set_body(R"({ + "title": "foo", + "age": "30", + "isActive": "true", + "description": "here is the description" + })"); + + std::optional result = assertions.validate(response); + + EXPECT_FALSE(result.has_value()) + << "Validation should pass but failed with: " << result->message; +} + +TEST_F(AssertionsTestFixture, UnexpectedStatusCode) { + assertions.status_codes({200, 201}); + + Response response; + response.parse_status_code(404); + + std::optional result = assertions.validate(response); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->type, FailedAssertionType::UnexpectedStatusCode); + EXPECT_EQ(result->message, + "Expected one of these status codes: 200 201 but got 404"); +} + +TEST_F(AssertionsTestFixture, MissingHeader) { + assertions.header("Authorization", "Bearer exampletoken12345"); + + Response response; + response.parse_status_code(200); + + std::optional result = assertions.validate(response); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->type, FailedAssertionType::MissingHeader); + EXPECT_EQ(result->message, + "Expected header 'Authorization' to be present but it wasn't"); +} + +TEST_F(AssertionsTestFixture, UnexpectedHeaderValue) { + assertions.header("Content-Type", "application/json"); + + Response response; + response.parse_status_code(200); + response.parse_header("Content-Type: text/html"); + + std::optional result = assertions.validate(response); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->type, FailedAssertionType::UnexpectedHeader); + EXPECT_EQ(result->message, "Expected header 'Content-Type' to have value " + "'application/json' but got 'text/html'"); +} + +TEST_F(AssertionsTestFixture, MissingJsonField) { + assertions.json_field("age", "^30$"); + + Response response; + response.parse_status_code(200); + response.set_body(R"({ + "title": "foo", + })"); // "age" field is missing + + std::optional result = assertions.validate(response); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->type, FailedAssertionType::MissingJsonField); + EXPECT_EQ(result->message, + "Expected json field 'age' to be present but it wasn't"); +} + +TEST_F(AssertionsTestFixture, JsonFieldPatternMismatch) { + assertions.json_field("title", "^foo$"); + + Response response; + response.set_body(R"({ + "title": "bar" + })"); + + std::optional result = assertions.validate(response); + + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(result->type, FailedAssertionType::UnexpectedJsonField); + EXPECT_EQ(result->message, "Expected json field 'title' to match pattern '^foo$' but it didn't, it was 'bar'"); +} diff --git a/tests/parser_test.cpp b/tests/parser_test.cpp deleted file mode 100644 index 40aced2..0000000 --- a/tests/parser_test.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include -#include -#include "parser.hpp" - diff --git a/tests/tautology_test.cpp b/tests/tautology_test.cpp index 8c35c65..3ed5b3f 100644 --- a/tests/tautology_test.cpp +++ b/tests/tautology_test.cpp @@ -1,11 +1,5 @@ #include -#include TEST(Tautology, Addition) { EXPECT_EQ(1 + 1, 2); } - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -}