Skip to content

Commit 6347a7e

Browse files
committed
Add tests and fix found bugs
1 parent 60e6481 commit 6347a7e

File tree

6 files changed

+295
-2
lines changed

6 files changed

+295
-2
lines changed

samples/client/MutateClient.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ const peg::ast& GetRequestObject() noexcept
165165

166166
response::Value serializeVariables(Variables&& variables)
167167
{
168-
response::Value result;
168+
response::Value result { response::Type::Map };
169169

170170
result.emplace_back(R"js(input)js"s, ModifiedVariable<Variables::CompleteTaskInput>::serialize(std::move(variables.input)));
171171

samples/today/TodayMock.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,12 @@ service::FieldResult<std::vector<std::shared_ptr<object::Expensive>>> Query::get
424424
return result;
425425
}
426426

427+
service::FieldResult<std::optional<TaskState>> Query::getTestTaskState(
428+
service::FieldParams&& /*params*/) const
429+
{
430+
return TaskState::Unassigned;
431+
}
432+
427433
Mutation::Mutation(completeTaskMutation&& mutateCompleteTask)
428434
: _mutateCompleteTask(std::move(mutateCompleteTask))
429435
{

samples/today/TodayMock.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ class Query : public object::Query
7474
service::FieldParams&& params) const final;
7575
service::FieldResult<std::vector<std::shared_ptr<object::Expensive>>> getExpensive(
7676
service::FieldParams&& params) const final;
77+
service::FieldResult<std::optional<TaskState>> getTestTaskState(
78+
service::FieldParams&& params) const final;
7779

7880
private:
7981
std::shared_ptr<Appointment> findAppointment(

src/ClientGenerator.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ response::Value ModifiedVariable<Variables::)cpp" << cppType
662662
sourceFile << R"cpp(
663663
response::Value serializeVariables(Variables&& variables)
664664
{
665-
response::Value result;
665+
response::Value result { response::Type::Map };
666666
667667
)cpp";
668668

test/CMakeLists.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ target_link_libraries(today_tests PRIVATE
2323
add_bigobj_flag(today_tests)
2424
gtest_add_tests(TARGET today_tests)
2525

26+
add_executable(client_tests ClientTests.cpp)
27+
target_link_libraries(client_tests PRIVATE
28+
unifiedgraphql
29+
todayclient
30+
GTest::GTest
31+
GTest::Main)
32+
add_bigobj_flag(client_tests)
33+
gtest_add_tests(TARGET client_tests)
34+
2635
add_executable(nointrospection_tests NoIntrospectionTests.cpp)
2736
target_link_libraries(nointrospection_tests PRIVATE
2837
unifiedgraphql_nointrospection
@@ -82,6 +91,7 @@ if(WIN32 AND BUILD_SHARED_LIBS)
8291

8392
add_dependencies(validation_tests copy_test_dlls)
8493
add_dependencies(today_tests copy_test_dlls)
94+
add_dependencies(client_tests copy_test_dlls)
8595
add_dependencies(nointrospection_tests copy_test_dlls)
8696
add_dependencies(argument_tests copy_test_dlls)
8797
add_dependencies(pegtl_tests copy_test_dlls)

test/ClientTests.cpp

Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
#include <gtest/gtest.h>
5+
6+
#include "MutateClient.h"
7+
#include "QueryClient.h"
8+
#include "SubscribeClient.h"
9+
#include "TodayMock.h"
10+
11+
#include <chrono>
12+
13+
using namespace graphql;
14+
15+
using namespace std::literals;
16+
17+
class ClientCase : public ::testing::Test
18+
{
19+
public:
20+
static void SetUpTestCase()
21+
{
22+
std::string fakeAppointmentId("fakeAppointmentId");
23+
_fakeAppointmentId.resize(fakeAppointmentId.size());
24+
std::copy(fakeAppointmentId.cbegin(), fakeAppointmentId.cend(), _fakeAppointmentId.begin());
25+
26+
std::string fakeTaskId("fakeTaskId");
27+
_fakeTaskId.resize(fakeTaskId.size());
28+
std::copy(fakeTaskId.cbegin(), fakeTaskId.cend(), _fakeTaskId.begin());
29+
30+
std::string fakeFolderId("fakeFolderId");
31+
_fakeFolderId.resize(fakeFolderId.size());
32+
std::copy(fakeFolderId.cbegin(), fakeFolderId.cend(), _fakeFolderId.begin());
33+
34+
auto query = std::make_shared<today::Query>(
35+
[]() -> std::vector<std::shared_ptr<today::Appointment>> {
36+
++_getAppointmentsCount;
37+
return { std::make_shared<today::Appointment>(response::IdType(_fakeAppointmentId),
38+
"tomorrow",
39+
"Lunch?",
40+
false) };
41+
},
42+
[]() -> std::vector<std::shared_ptr<today::Task>> {
43+
++_getTasksCount;
44+
return { std::make_shared<today::Task>(response::IdType(_fakeTaskId),
45+
"Don't forget",
46+
true) };
47+
},
48+
[]() -> std::vector<std::shared_ptr<today::Folder>> {
49+
++_getUnreadCountsCount;
50+
return { std::make_shared<today::Folder>(response::IdType(_fakeFolderId),
51+
"\"Fake\" Inbox",
52+
3) };
53+
});
54+
auto mutation = std::make_shared<today::Mutation>(
55+
[](today::CompleteTaskInput&& input) -> std::shared_ptr<today::CompleteTaskPayload> {
56+
return std::make_shared<today::CompleteTaskPayload>(
57+
std::make_shared<today::Task>(std::move(input.id),
58+
"Mutated Task!",
59+
*(input.isComplete)),
60+
std::move(input.clientMutationId));
61+
});
62+
auto subscription = std::make_shared<today::NextAppointmentChange>(
63+
[](const std::shared_ptr<service::RequestState>&)
64+
-> std::shared_ptr<today::Appointment> {
65+
return { std::make_shared<today::Appointment>(response::IdType(_fakeAppointmentId),
66+
"tomorrow",
67+
"Lunch?",
68+
true) };
69+
});
70+
71+
_service = std::make_shared<today::Operations>(query, mutation, subscription);
72+
}
73+
74+
static void TearDownTestCase()
75+
{
76+
_fakeAppointmentId.clear();
77+
_fakeTaskId.clear();
78+
_fakeFolderId.clear();
79+
_service.reset();
80+
}
81+
82+
protected:
83+
static response::IdType _fakeAppointmentId;
84+
static response::IdType _fakeTaskId;
85+
static response::IdType _fakeFolderId;
86+
87+
static std::shared_ptr<today::Operations> _service;
88+
static size_t _getAppointmentsCount;
89+
static size_t _getTasksCount;
90+
static size_t _getUnreadCountsCount;
91+
};
92+
93+
response::IdType ClientCase::_fakeAppointmentId;
94+
response::IdType ClientCase::_fakeTaskId;
95+
response::IdType ClientCase::_fakeFolderId;
96+
97+
std::shared_ptr<today::Operations> ClientCase::_service;
98+
size_t ClientCase::_getAppointmentsCount = 0;
99+
size_t ClientCase::_getTasksCount = 0;
100+
size_t ClientCase::_getUnreadCountsCount = 0;
101+
102+
TEST_F(ClientCase, QueryEverything)
103+
{
104+
using namespace client::query::Query;
105+
106+
auto query = GetRequestObject();
107+
108+
response::Value variables(response::Type::Map);
109+
auto state = std::make_shared<today::RequestState>(1);
110+
auto result =
111+
_service->resolve(std::launch::async, state, query, "", std::move(variables)).get();
112+
EXPECT_EQ(size_t(1), _getAppointmentsCount)
113+
<< "today service lazy loads the appointments and caches the result";
114+
EXPECT_EQ(size_t(1), _getTasksCount)
115+
<< "today service lazy loads the tasks and caches the result";
116+
EXPECT_EQ(size_t(1), _getUnreadCountsCount)
117+
<< "today service lazy loads the unreadCounts and caches the result";
118+
EXPECT_EQ(size_t(1), state->appointmentsRequestId)
119+
<< "today service passed the same RequestState";
120+
EXPECT_EQ(size_t(1), state->tasksRequestId) << "today service passed the same RequestState";
121+
EXPECT_EQ(size_t(1), state->unreadCountsRequestId)
122+
<< "today service passed the same RequestState";
123+
EXPECT_EQ(size_t(1), state->loadAppointmentsCount) << "today service called the loader once";
124+
EXPECT_EQ(size_t(1), state->loadTasksCount) << "today service called the loader once";
125+
EXPECT_EQ(size_t(1), state->loadUnreadCountsCount) << "today service called the loader once";
126+
127+
try
128+
{
129+
ASSERT_TRUE(result.type() == response::Type::Map);
130+
auto errorsItr = result.find("errors");
131+
if (errorsItr != result.get<response::MapType>().cend())
132+
{
133+
FAIL() << "service returned errors";
134+
}
135+
136+
const auto response = parseResponse(service::ScalarArgument::require("data", result));
137+
138+
ASSERT_TRUE(response.appointments.edges.has_value()) << "appointments should be set";
139+
ASSERT_EQ(1, response.appointments.edges->size()) << "appointments should have 1 entry";
140+
ASSERT_TRUE((*response.appointments.edges)[0].has_value()) << "edge should be set";
141+
const auto& appointmentNode = (*response.appointments.edges)[0]->node;
142+
ASSERT_TRUE(appointmentNode.has_value()) << "node should be set";
143+
EXPECT_EQ(_fakeAppointmentId, appointmentNode->id) << "id should match in base64 encoding";
144+
ASSERT_TRUE(appointmentNode->subject.has_value()) << "subject should be set";
145+
EXPECT_EQ("Lunch?", *(appointmentNode->subject)) << "subject should match";
146+
ASSERT_TRUE(appointmentNode->when.has_value()) << "when should be set";
147+
EXPECT_EQ("tomorrow", appointmentNode->when->get<response::StringType>())
148+
<< "when should match";
149+
EXPECT_FALSE(appointmentNode->isNow) << "isNow should match";
150+
EXPECT_EQ("Appointment", appointmentNode->_typename) << "__typename should match";
151+
152+
ASSERT_TRUE(response.tasks.edges.has_value()) << "tasks should be set";
153+
ASSERT_EQ(1, response.tasks.edges->size()) << "tasks should have 1 entry";
154+
ASSERT_TRUE((*response.tasks.edges)[0].has_value()) << "edge should be set";
155+
const auto& taskNode = (*response.tasks.edges)[0]->node;
156+
ASSERT_TRUE(taskNode.has_value()) << "node should be set";
157+
EXPECT_EQ(_fakeTaskId, taskNode->id) << "id should match in base64 encoding";
158+
ASSERT_TRUE(taskNode->title.has_value()) << "subject should be set";
159+
EXPECT_EQ("Don't forget", *(taskNode->title)) << "title should match";
160+
EXPECT_TRUE(taskNode->isComplete) << "isComplete should match";
161+
EXPECT_EQ("Task", taskNode->_typename) << "__typename should match";
162+
163+
ASSERT_TRUE(response.unreadCounts.edges.has_value()) << "unreadCounts should be set";
164+
ASSERT_EQ(1, response.unreadCounts.edges->size()) << "unreadCounts should have 1 entry";
165+
ASSERT_TRUE((*response.unreadCounts.edges)[0].has_value()) << "edge should be set";
166+
const auto& unreadCountNode = (*response.unreadCounts.edges)[0]->node;
167+
ASSERT_TRUE(unreadCountNode.has_value()) << "node should be set";
168+
EXPECT_EQ(_fakeFolderId, unreadCountNode->id) << "id should match in base64 encoding";
169+
ASSERT_TRUE(unreadCountNode->name.has_value()) << "name should be set";
170+
EXPECT_EQ("\"Fake\" Inbox", *(unreadCountNode->name)) << "name should match";
171+
EXPECT_EQ(3, unreadCountNode->unreadCount) << "unreadCount should match";
172+
EXPECT_EQ("Folder", unreadCountNode->_typename) << "__typename should match";
173+
174+
ASSERT_TRUE(response.testTaskState.has_value()) << "testTaskState should be set";
175+
EXPECT_EQ(client::query::Query::TaskState::Unassigned, *response.testTaskState)
176+
<< "testTaskState should match";
177+
}
178+
catch (const std::logic_error& ex)
179+
{
180+
FAIL() << ex.what();
181+
}
182+
}
183+
184+
TEST_F(ClientCase, MutateCompleteTask)
185+
{
186+
using namespace client::mutation::CompleteTaskMutation;
187+
188+
auto query = GetRequestObject();
189+
auto variables = serializeVariables({ { _fakeTaskId,
190+
std::nullopt,
191+
std::make_optional(true),
192+
std::make_optional("Hi There!"s) } });
193+
194+
auto state = std::make_shared<today::RequestState>(5);
195+
auto result = _service->resolve(state, query, "", std::move(variables)).get();
196+
197+
try
198+
{
199+
ASSERT_TRUE(result.type() == response::Type::Map);
200+
auto errorsItr = result.find("errors");
201+
if (errorsItr != result.get<response::MapType>().cend())
202+
{
203+
FAIL() << "service returned errors";
204+
}
205+
206+
const auto response = parseResponse(service::ScalarArgument::require("data", result));
207+
208+
const auto& completedTask = response.completedTask;
209+
const auto& task = completedTask.completedTask;
210+
ASSERT_TRUE(task.has_value()) << "should get back a task";
211+
EXPECT_EQ(_fakeTaskId, task->completedTaskId) << "id should match in base64 encoding";
212+
ASSERT_TRUE(task->title.has_value()) << "title should be set";
213+
EXPECT_EQ("Mutated Task!", *(task->title)) << "title should match";
214+
EXPECT_TRUE(task->isComplete) << "isComplete should match";
215+
216+
const auto& clientMutationId = completedTask.clientMutationId;
217+
ASSERT_TRUE(clientMutationId.has_value()) << "clientMutationId should be set";
218+
EXPECT_EQ("Hi There!", *clientMutationId) << "clientMutationId should match";
219+
}
220+
catch (const std::logic_error& ex)
221+
{
222+
FAIL() << ex.what();
223+
}
224+
}
225+
226+
TEST_F(ClientCase, SubscribeNextAppointmentChangeDefault)
227+
{
228+
using namespace client::subscription::TestSubscription;
229+
230+
auto query = GetRequestObject();
231+
232+
response::Value variables(response::Type::Map);
233+
auto state = std::make_shared<today::RequestState>(6);
234+
response::Value result;
235+
auto key = _service->subscribe(service::SubscriptionParams { state,
236+
std::move(query),
237+
"TestSubscription",
238+
std::move(std::move(variables)) },
239+
[&result](std::future<response::Value> response) {
240+
result = response.get();
241+
});
242+
_service->deliver("nextAppointmentChange", nullptr);
243+
_service->unsubscribe(key);
244+
245+
try
246+
{
247+
ASSERT_TRUE(result.type() == response::Type::Map);
248+
auto errorsItr = result.find("errors");
249+
if (errorsItr != result.get<response::MapType>().cend())
250+
{
251+
FAIL() << "service returned errors";
252+
}
253+
254+
const auto response = parseResponse(service::ScalarArgument::require("data", result));
255+
256+
const auto& appointmentNode = response.nextAppointment;
257+
ASSERT_TRUE(appointmentNode.has_value()) << "should get back a task";
258+
EXPECT_EQ(_fakeAppointmentId, appointmentNode->nextAppointmentId)
259+
<< "id should match in base64 encoding";
260+
ASSERT_TRUE(appointmentNode->subject.has_value()) << "subject should be set";
261+
EXPECT_EQ("Lunch?", *(appointmentNode->subject)) << "subject should match";
262+
ASSERT_TRUE(appointmentNode->when.has_value()) << "when should be set";
263+
EXPECT_EQ("tomorrow", appointmentNode->when->get<response::StringType>())
264+
<< "when should match";
265+
EXPECT_TRUE(appointmentNode->isNow) << "isNow should match";
266+
}
267+
catch (const std::logic_error& ex)
268+
{
269+
FAIL() << ex.what();
270+
}
271+
}
272+
273+
size_t today::NextAppointmentChange::_notifySubscribeCount = 0;
274+
size_t today::NextAppointmentChange::_subscriptionCount = 0;
275+
size_t today::NextAppointmentChange::_notifyUnsubscribeCount = 0;

0 commit comments

Comments
 (0)