Skip to content

Commit 029ce47

Browse files
committed
add HTTP interface tests and README documentation
1 parent b932426 commit 029ce47

File tree

2 files changed

+222
-0
lines changed

2 files changed

+222
-0
lines changed

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,58 @@ void myLogHandler(const char* message, GALoggerMessageType type)
124124
gameAnalytics_configureCustomLogHandler(myLogHandler);
125125
```
126126

127+
### Custom HTTP client
128+
129+
By default, the SDK uses cURL for HTTP requests. If you need to use a different HTTP library (e.g. on consoles or custom platforms), you can provide your own implementation by subclassing `GAHttpWrapper`:
130+
131+
``` c++
132+
#include "GameAnalytics/GAHttpWrapper.h"
133+
134+
class MyHttpClient : public gameanalytics::GAHttpWrapper
135+
{
136+
public:
137+
void initialize() override
138+
{
139+
// Set up your HTTP library
140+
}
141+
142+
void cleanup() override
143+
{
144+
// Tear down your HTTP library
145+
}
146+
147+
Response sendRequest(
148+
std::string const& url,
149+
std::string const& auth,
150+
std::vector<uint8_t> const& payloadData,
151+
bool useGzip,
152+
void* userData) override
153+
{
154+
Response response;
155+
156+
// Use your HTTP library to POST payloadData to url.
157+
// Set the following headers:
158+
// - auth (e.g. "Authorization: ...")
159+
// - "Content-Type: application/json"
160+
// - "Content-Encoding: gzip" (if useGzip is true)
161+
//
162+
// Fill in response.code with the HTTP status code.
163+
// Fill in response.packet with the response body bytes.
164+
165+
return response;
166+
}
167+
};
168+
```
169+
170+
Register it **before** calling `initialize()`:
171+
172+
``` c++
173+
gameanalytics::GameAnalytics::configureHttpClient(std::make_unique<MyHttpClient>());
174+
gameanalytics::GameAnalytics::initialize("<your game key>", "<your secret key>");
175+
```
176+
177+
If `configureHttpClient` is not called, the built-in cURL implementation is used.
178+
127179
### Configuration
128180
129181
Example:

test/GAHttpInterfaceTests.cpp

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
//
2+
// GA-SDK-CPP
3+
// Tests for the HTTP interface abstraction and custom implementation registration
4+
//
5+
6+
#include <gmock/gmock.h>
7+
#include <gtest/gtest.h>
8+
9+
#include "GameAnalytics/GameAnalytics.h"
10+
#include "GameAnalytics/GAHttpWrapper.h"
11+
#include "GAHTTPApi.h"
12+
13+
namespace
14+
{
15+
16+
// A mock HTTP implementation for testing
17+
class MockHttpClient : public gameanalytics::GAHttpWrapper
18+
{
19+
public:
20+
void initialize() override
21+
{
22+
initialized = true;
23+
}
24+
25+
void cleanup() override
26+
{
27+
cleanedUp = true;
28+
}
29+
30+
Response sendRequest(
31+
std::string const& url,
32+
std::string const& auth,
33+
std::vector<uint8_t> const& payloadData,
34+
bool useGzip,
35+
void* userData) override
36+
{
37+
lastUrl = url;
38+
lastAuth = auth;
39+
lastPayload = payloadData;
40+
lastUseGzip = useGzip;
41+
requestCount++;
42+
43+
return configuredResponse;
44+
}
45+
46+
// Test inspection
47+
bool initialized = false;
48+
bool cleanedUp = false;
49+
int requestCount = 0;
50+
std::string lastUrl;
51+
std::string lastAuth;
52+
std::vector<uint8_t> lastPayload;
53+
bool lastUseGzip = false;
54+
55+
// Configurable response
56+
Response configuredResponse = {};
57+
};
58+
59+
// -------- Response struct tests --------
60+
61+
TEST(GAHttpWrapperResponse, DefaultResponseHasNegativeCode)
62+
{
63+
gameanalytics::GAHttpWrapper::Response response;
64+
EXPECT_EQ(response.code, -1);
65+
EXPECT_TRUE(response.packet.empty());
66+
}
67+
68+
TEST(GAHttpWrapperResponse, ToStringReturnsEmptyForEmptyPacket)
69+
{
70+
gameanalytics::GAHttpWrapper::Response response;
71+
EXPECT_TRUE(response.toString().empty());
72+
}
73+
74+
TEST(GAHttpWrapperResponse, ToStringReturnsPacketContent)
75+
{
76+
gameanalytics::GAHttpWrapper::Response response;
77+
std::string body = R"({"status":"ok"})";
78+
response.packet.assign(body.begin(), body.end());
79+
response.code = 200;
80+
81+
std::string_view result = response.toString();
82+
EXPECT_EQ(result, body);
83+
EXPECT_EQ(result.size(), body.size());
84+
}
85+
86+
TEST(GAHttpWrapperResponse, ToStringHandlesBinaryData)
87+
{
88+
gameanalytics::GAHttpWrapper::Response response;
89+
response.packet = {0x00, 0x01, 0x02, 0xFF};
90+
response.code = 200;
91+
92+
std::string_view result = response.toString();
93+
EXPECT_EQ(result.size(), 4u);
94+
}
95+
96+
// -------- Mock implementation tests --------
97+
98+
TEST(GAHttpInterface, MockImplementsInterface)
99+
{
100+
auto mock = std::make_unique<MockHttpClient>();
101+
EXPECT_FALSE(mock->initialized);
102+
EXPECT_FALSE(mock->cleanedUp);
103+
104+
mock->initialize();
105+
EXPECT_TRUE(mock->initialized);
106+
107+
mock->cleanup();
108+
EXPECT_TRUE(mock->cleanedUp);
109+
}
110+
111+
TEST(GAHttpInterface, MockSendRequestRecordsParameters)
112+
{
113+
MockHttpClient mock;
114+
mock.configuredResponse.code = 200;
115+
std::string responseBody = R"({"ok":true})";
116+
mock.configuredResponse.packet.assign(responseBody.begin(), responseBody.end());
117+
118+
std::string url = "https://api.gameanalytics.com/v2/test/events";
119+
std::string auth = "Authorization: abc123";
120+
std::vector<uint8_t> payload = {'[', '{', '}', ']'};
121+
122+
auto response = mock.sendRequest(url, auth, payload, true, nullptr);
123+
124+
EXPECT_EQ(mock.requestCount, 1);
125+
EXPECT_EQ(mock.lastUrl, url);
126+
EXPECT_EQ(mock.lastAuth, auth);
127+
EXPECT_EQ(mock.lastPayload, payload);
128+
EXPECT_TRUE(mock.lastUseGzip);
129+
EXPECT_EQ(response.code, 200);
130+
EXPECT_EQ(response.toString(), responseBody);
131+
}
132+
133+
TEST(GAHttpInterface, MockCanReturnErrorResponse)
134+
{
135+
MockHttpClient mock;
136+
mock.configuredResponse.code = 500;
137+
std::string body = "Internal Server Error";
138+
mock.configuredResponse.packet.assign(body.begin(), body.end());
139+
140+
auto response = mock.sendRequest("http://test.com", "auth", {}, false, nullptr);
141+
142+
EXPECT_EQ(response.code, 500);
143+
EXPECT_EQ(response.toString(), body);
144+
}
145+
146+
TEST(GAHttpInterface, MockCanReturnNoContentResponse)
147+
{
148+
MockHttpClient mock;
149+
mock.configuredResponse.code = 204;
150+
// 204 has no body
151+
152+
auto response = mock.sendRequest("http://test.com", "auth", {}, false, nullptr);
153+
154+
EXPECT_EQ(response.code, 204);
155+
EXPECT_TRUE(response.packet.empty());
156+
}
157+
158+
// -------- Registration tests --------
159+
160+
TEST(GAHttpInterface, SetCustomHttpImplAcceptsUniquePtr)
161+
{
162+
// Verify the static method compiles and runs without crashing
163+
auto mock = std::make_unique<MockHttpClient>();
164+
gameanalytics::http::GAHTTPApi::setCustomHttpImpl(std::move(mock));
165+
166+
// Clean up: reset to nullptr so it doesn't affect other tests
167+
gameanalytics::http::GAHTTPApi::setCustomHttpImpl(nullptr);
168+
}
169+
170+
} // namespace

0 commit comments

Comments
 (0)