Skip to content

Commit 7aab6d9

Browse files
authored
Merge branch 'main' into rlamb/pin-actions-2
2 parents 02b6774 + 245dd97 commit 7aab6d9

18 files changed

Lines changed: 771 additions & 15 deletions

File tree

.github/workflows/server.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ jobs:
3434
with:
3535
# Inform the test harness of test service's port.
3636
test_service_port: ${{ env.TEST_SERVICE_PORT }}
37-
extra_params: '-skip-from ./contract-tests/server-contract-tests/test-suppressions.txt'
3837
token: ${{ secrets.GITHUB_TOKEN }}
3938

4039
contract-tests-curl:
@@ -58,7 +57,6 @@ jobs:
5857
with:
5958
# Inform the test harness of test service's port.
6059
test_service_port: ${{ env.TEST_SERVICE_PORT }}
61-
extra_params: '-skip-from ./contract-tests/server-contract-tests/test-suppressions.txt'
6260
token: ${{ secrets.GITHUB_TOKEN }}
6361

6462
build-test-server:

contract-tests/client-contract-tests/src/entity_manager.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,15 @@ std::optional<std::string> EntityManager::create(ConfigParams const& in) {
146146
config_builder.HttpProperties().Tls(std::move(builder));
147147
}
148148

149+
if (in.wrapper) {
150+
if (!in.wrapper->name.empty()) {
151+
config_builder.HttpProperties().WrapperName(in.wrapper->name);
152+
}
153+
if (!in.wrapper->version.empty()) {
154+
config_builder.HttpProperties().WrapperVersion(in.wrapper->version);
155+
}
156+
}
157+
149158
auto config = config_builder.Build();
150159
if (!config) {
151160
LD_LOG(logger_, LogLevel::kWarn)

contract-tests/client-contract-tests/src/main.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ int main(int argc, char* argv[]) {
4747
srv.add_capability("tls:skip-verify-peer");
4848
srv.add_capability("tls:custom-ca");
4949
srv.add_capability("client-prereq-events");
50+
srv.add_capability("wrapper");
5051
// Proxies are supported only with CURL networking.
5152
#ifdef LD_CURL_NETWORKING
5253
srv.add_capability("http-proxy");

contract-tests/data-model/include/data_model/data_model.hpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,43 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigTags,
113113
applicationId,
114114
applicationVersion);
115115

116+
enum class HookStage {
117+
BeforeEvaluation,
118+
AfterEvaluation,
119+
AfterTrack
120+
};
121+
122+
NLOHMANN_JSON_SERIALIZE_ENUM(HookStage,
123+
{{HookStage::BeforeEvaluation, "beforeEvaluation"},
124+
{HookStage::AfterEvaluation, "afterEvaluation"},
125+
{HookStage::AfterTrack, "afterTrack"}})
126+
127+
struct ConfigHookInstance {
128+
std::string name;
129+
std::string callbackUri;
130+
std::optional<std::unordered_map<std::string, std::unordered_map<std::string, nlohmann::json>>> data;
131+
std::optional<std::unordered_map<std::string, std::string>> errors;
132+
};
133+
134+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigHookInstance,
135+
name,
136+
callbackUri,
137+
data,
138+
errors);
139+
140+
struct ConfigHooksParams {
141+
std::vector<ConfigHookInstance> hooks;
142+
};
143+
144+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigHooksParams, hooks);
145+
146+
struct ConfigWrapper {
147+
std::string name;
148+
std::string version;
149+
};
150+
151+
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigWrapper, name, version);
152+
116153
struct ConfigParams {
117154
std::string credential;
118155
std::optional<uint32_t> startWaitTimeMs;
@@ -125,6 +162,8 @@ struct ConfigParams {
125162
std::optional<ConfigTags> tags;
126163
std::optional<ConfigTLSParams> tls;
127164
std::optional<ConfigProxyParams> proxy;
165+
std::optional<ConfigHooksParams> hooks;
166+
std::optional<ConfigWrapper> wrapper;
128167
};
129168

130169
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams,
@@ -138,7 +177,9 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(ConfigParams,
138177
clientSide,
139178
tags,
140179
tls,
141-
proxy);
180+
proxy,
181+
hooks,
182+
wrapper);
142183

143184
struct ContextSingleParams {
144185
std::optional<std::string> kind;

contract-tests/server-contract-tests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_executable(server-tests
1616
src/session.cpp
1717
src/entity_manager.cpp
1818
src/client_entity.cpp
19+
src/contract_test_hook.cpp
1920
)
2021

2122
target_link_libraries(server-tests PRIVATE
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#pragma once
2+
3+
#include <data_model/data_model.hpp>
4+
#include <launchdarkly/server_side/hooks/hook.hpp>
5+
6+
#include <boost/asio/any_io_executor.hpp>
7+
#include <memory>
8+
#include <string>
9+
10+
/**
11+
* ContractTestHook implements the Hook interface for contract testing.
12+
*
13+
* It posts hook execution payloads to a callback URI specified in the test
14+
* configuration, allowing the test harness to verify hook behavior.
15+
*/
16+
class ContractTestHook : public launchdarkly::server_side::hooks::Hook {
17+
public:
18+
/**
19+
* Constructs a contract test hook.
20+
* @param executor IO executor for async HTTP operations.
21+
* @param config Hook configuration from the test harness.
22+
*/
23+
ContractTestHook(boost::asio::any_io_executor executor,
24+
ConfigHookInstance config);
25+
26+
~ContractTestHook() override = default;
27+
28+
[[nodiscard]] launchdarkly::server_side::hooks::HookMetadata const&
29+
Metadata() const override;
30+
31+
launchdarkly::server_side::hooks::EvaluationSeriesData BeforeEvaluation(
32+
launchdarkly::server_side::hooks::EvaluationSeriesContext const&
33+
series_context,
34+
launchdarkly::server_side::hooks::EvaluationSeriesData data) override;
35+
36+
launchdarkly::server_side::hooks::EvaluationSeriesData AfterEvaluation(
37+
launchdarkly::server_side::hooks::EvaluationSeriesContext const&
38+
series_context,
39+
launchdarkly::server_side::hooks::EvaluationSeriesData data,
40+
launchdarkly::EvaluationDetail<launchdarkly::Value> const& detail)
41+
override;
42+
43+
void AfterTrack(launchdarkly::server_side::hooks::TrackSeriesContext const&
44+
series_context) override;
45+
46+
private:
47+
/**
48+
* Posts a hook execution payload to the callback URI.
49+
* @param stage The stage being executed.
50+
* @param payload The JSON payload to send.
51+
*/
52+
void PostCallback(std::string const& stage, nlohmann::json const& payload);
53+
54+
/**
55+
* Gets configured data for a specific stage.
56+
* @param stage The stage name.
57+
* @return Optional map of key-value pairs for this stage.
58+
*/
59+
std::optional<std::unordered_map<std::string, nlohmann::json>> GetDataForStage(
60+
std::string const& stage) const;
61+
62+
/**
63+
* Gets configured error for a specific stage.
64+
* @param stage The stage name.
65+
* @return Optional error message for this stage.
66+
*/
67+
std::optional<std::string> GetErrorForStage(std::string const& stage) const;
68+
69+
boost::asio::any_io_executor executor_;
70+
ConfigHookInstance config_;
71+
launchdarkly::server_side::hooks::HookMetadata metadata_;
72+
};

0 commit comments

Comments
 (0)