Skip to content

Commit 1b3a739

Browse files
committed
Add MeterReadingControllerTest
1 parent 6a51fcc commit 1b3a739

File tree

5 files changed

+158
-1
lines changed

5 files changed

+158
-1
lines changed

rest/configuration.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
#include "generator.h"
88

9+
#include <unordered_map>
10+
911
constexpr auto DR_EVILS_DARK_ENERGY_ENERGY_SUPPLIER = "Dr Evil's Dark Energy";
1012
constexpr auto THE_GREEN_ECO_ENERGY_SUPPLIER = "The Green Eco";
1113
constexpr auto POWER_FOR_EVERYONE_ENERGY_SUPPLIER = "Power for Everyone";

rest/controller/MeterReadingController.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class MeterReadingController {
6464
auto body = nlohmann::json::parse(req.body());
6565
auto smartMeterId = body["smartMeterId"];
6666
std::vector<ElectricityReading> electricityReadings;
67+
if (!IsMeterReadingsValid(smartMeterId, body["electricityReadings"])) {
68+
return {http::status::internal_server_error, 11};
69+
}
6770
for (auto &electricityReading : body["electricityReadings"]) {
6871
electricityReadings.emplace_back(detail::fromRfc3339(electricityReading["time"]), electricityReading["reading"]);
6972
}
@@ -74,6 +77,13 @@ class MeterReadingController {
7477
private:
7578
ElectricityReadingService &electricityReadingService;
7679
MeterReadingService &meterReadingService;
80+
bool IsMeterReadingsValid(const nlohmann::basic_json<> &smartMeterId,
81+
const nlohmann::basic_json<> &electricityReadings) {
82+
if (smartMeterId.type() == nlohmann::json::value_t::null || electricityReadings.empty()) {
83+
return false;
84+
}
85+
return true;
86+
}
7787
};
7888

7989
#endif // DEVELOPER_JOYOFENERGY_CPP_BEAST_METERREADINGCONTROLLER_H

rest/domain/ElectricityReading.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ class ElectricityReading {
1414

1515
size_t getReading() const { return reading; }
1616

17+
bool operator==(const ElectricityReading& rhs) const { return time == rhs.time && reading == rhs.reading; }
18+
bool operator!=(const ElectricityReading& rhs) const { return !(rhs == *this); }
19+
1720
private:
1821
time_point_type time;
1922
size_t reading;

test/CMakeLists.txt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@ find_package(GTest REQUIRED)
44
find_package(nlohmann_json REQUIRED)
55
target_link_libraries(endpoint_test PRIVATE GTest::gmock_main nlohmann_json::nlohmann_json rest)
66

7-
add_test(endpoint_test endpoint_test)
7+
add_executable(controller_test)
8+
target_sources(controller_test PRIVATE controller/MeterReadingControllerTest.cpp)
9+
target_link_libraries(controller_test PRIVATE GTest::gmock_main nlohmann_json::nlohmann_json rest)
10+
11+
add_test(endpoint_test endpoint_test)
12+
add_test(controller_test controller_test)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#include <gmock/gmock.h>
2+
#include <rest/configuration.h>
3+
#include <rest/controller/MeterReadingController.h>
4+
5+
#include <boost/beast/http.hpp>
6+
#include <nlohmann/json.hpp>
7+
8+
using nlohmann::json;
9+
using ::testing::Eq;
10+
11+
namespace http = boost::beast::http;
12+
13+
class MeterReadingControllerTest : public ::testing::Test {
14+
protected:
15+
std::unordered_map<std::string, std::vector<ElectricityReading>> meterAssociatedReadings;
16+
ElectricityReadingService electricityReadingService{meterAssociatedReadings};
17+
MeterReadingService meterReadingService{meterAssociatedReadings};
18+
MeterReadingController controller{electricityReadingService, meterReadingService};
19+
20+
http::request<http::string_body> BuildRequest(http::verb verb, boost::string_view target, const json &request_body) {
21+
http::request<http::string_body> req{http::verb::post, "/readings/store", 11};
22+
req.set(http::field::content_type, "application/json");
23+
req.body() = request_body.dump();
24+
req.prepare_payload();
25+
return req;
26+
}
27+
};
28+
29+
TEST_F(MeterReadingControllerTest, StoreShouldResponseWithErrorGivenNoMeterIdIsSupplied) {
30+
auto req = BuildRequest(http::verb::post, "/readings/store", R"({})"_json);
31+
std::vector<std::string> queries;
32+
33+
auto response = controller.Store(req, queries);
34+
35+
EXPECT_THAT(response.result(), Eq(http::status::internal_server_error));
36+
}
37+
38+
TEST_F(MeterReadingControllerTest, StoreShouldResponseWithErrorGivenEmptyMeterReading) {
39+
json body = R"({
40+
"smartMeterId": "smart-meter-0",
41+
"electricityReadings": []
42+
})"_json;
43+
auto req = BuildRequest(http::verb::post, "/readings/store", body);
44+
std::vector<std::string> queries;
45+
46+
auto response = controller.Store(req, queries);
47+
48+
EXPECT_THAT(response.result(), Eq(http::status::internal_server_error));
49+
}
50+
51+
TEST_F(MeterReadingControllerTest, StoreShouldResponseWithErrorGivenNoMeterReadingIsSupplied) {
52+
json body = R"({
53+
"smartMeterId": "smart-meter-0"
54+
})"_json;
55+
auto req = BuildRequest(http::verb::post, "/readings/store", body);
56+
std::vector<std::string> queries;
57+
58+
auto response = controller.Store(req, queries);
59+
60+
EXPECT_THAT(response.result(), Eq(http::status::internal_server_error));
61+
}
62+
63+
TEST_F(MeterReadingControllerTest, StoreShouldStoreGivenMultipleBatchesOfMeterReadings) {
64+
json body1 = R"({
65+
"smartMeterId": "smart-meter-0",
66+
"electricityReadings": [
67+
{
68+
"time": "2021-08-18T06:42:15.725202Z",
69+
"reading": 1
70+
}
71+
]
72+
})"_json;
73+
json body2 = R"({
74+
"smartMeterId": "smart-meter-0",
75+
"electricityReadings": [
76+
{
77+
"time": "2021-08-18T06:44:15.725202Z",
78+
"reading": 2
79+
}
80+
]
81+
})"_json;
82+
auto req1 = BuildRequest(http::verb::post, "/readings/store", body1);
83+
auto req2 = BuildRequest(http::verb::post, "/readings/store", body2);
84+
std::vector<std::string> queries;
85+
86+
controller.Store(req1, queries);
87+
controller.Store(req2, queries);
88+
89+
std::vector<ElectricityReading> expectedElectricityReadings = {
90+
{detail::fromRfc3339("2021-08-18T06:42:15.725202Z"), 1},
91+
{detail::fromRfc3339("2021-08-18T06:44:15.725202Z"), 2}
92+
};
93+
94+
EXPECT_THAT(meterReadingService.getReadings("smart-meter-0"), Eq(expectedElectricityReadings));
95+
}
96+
97+
TEST_F(MeterReadingControllerTest, StoreShouldStoreAssociatedWithUserGivenMeterReadingsAssociatedWithTheUser) {
98+
json body1 = R"({
99+
"smartMeterId": "smart-meter-0",
100+
"electricityReadings": [
101+
{
102+
"time": "2021-08-18T06:42:15.725202Z",
103+
"reading": 1
104+
}
105+
]
106+
})"_json;
107+
json body2 = R"({
108+
"smartMeterId": "smart-meter-1",
109+
"electricityReadings": [
110+
{
111+
"time": "2021-08-18T06:44:15.725202Z",
112+
"reading": 2
113+
}
114+
]
115+
})"_json;
116+
auto req1 = BuildRequest(http::verb::post, "/readings/store", body1);
117+
auto req2 = BuildRequest(http::verb::post, "/readings/store", body2);
118+
std::vector<std::string> queries;
119+
120+
controller.Store(req1, queries);
121+
controller.Store(req2, queries);
122+
123+
std::vector<ElectricityReading> expectedElectricityReadings = {
124+
{detail::fromRfc3339("2021-08-18T06:42:15.725202Z"), 1},
125+
};
126+
127+
EXPECT_THAT(meterReadingService.getReadings("smart-meter-0"), Eq(expectedElectricityReadings));
128+
}
129+
130+
TEST_F(MeterReadingControllerTest, ReadShouldReturnNotFoundGivenMeterIdThatIsNotRecognised) {
131+
auto req = BuildRequest(http::verb::post, "/readings/store", R"({})"_json);
132+
std::vector<std::string> queries = {"smart-meter-0"};
133+
134+
auto response = controller.Read(req, queries);
135+
136+
EXPECT_THAT(response.result(), Eq(http::status::not_found));
137+
}

0 commit comments

Comments
 (0)