1
+ /* *
2
+ * @file test_mcp_c_filter_chain_exception_safety.cc
3
+ * @brief Tests for exception safety in MCP Filter Chain C API
4
+ *
5
+ * These tests verify that:
6
+ * 1. No exceptions cross the C API boundary
7
+ * 2. Functions return appropriate error codes on failure
8
+ * 3. Resource cleanup happens correctly
9
+ */
10
+
11
+ #include < gtest/gtest.h>
12
+ #include < gmock/gmock.h>
13
+ #include < memory>
14
+ #include < stdexcept>
15
+
16
+ #include " mcp/c_api/mcp_c_filter_chain.h"
17
+ #include " mcp/c_api/mcp_c_types.h"
18
+ #include " mcp/c_api/mcp_c_api.h"
19
+ #include " mcp/c_api/mcp_c_api_json.h"
20
+
21
+ using namespace testing ;
22
+
23
+ namespace {
24
+
25
+ // Test fixture for exception safety tests
26
+ class FilterChainExceptionSafetyTest : public ::testing::Test {
27
+ protected:
28
+ void SetUp () override {
29
+ // Initialize MCP API
30
+ mcp_result_t result = mcp_initialize (nullptr );
31
+ ASSERT_EQ (result, MCP_OK);
32
+
33
+ // Create a dispatcher for testing
34
+ dispatcher_ = mcp_dispatcher_create ();
35
+ ASSERT_NE (dispatcher_, nullptr );
36
+ }
37
+
38
+ void TearDown () override {
39
+ if (dispatcher_) {
40
+ mcp_dispatcher_destroy (dispatcher_);
41
+ dispatcher_ = nullptr ;
42
+ }
43
+
44
+ mcp_cleanup ();
45
+ }
46
+
47
+ mcp_dispatcher_t dispatcher_ = nullptr ;
48
+ };
49
+
50
+ // Test that handle-returning functions return nullptr/0 on exception
51
+ TEST_F (FilterChainExceptionSafetyTest, HandleReturningFunctionsReturnNullOnException) {
52
+ // Test mcp_chain_builder_create_ex with null config
53
+ mcp_filter_chain_builder_t builder = mcp_chain_builder_create_ex (dispatcher_, nullptr );
54
+ EXPECT_EQ (builder, nullptr );
55
+
56
+ // Test mcp_chain_create_from_json with null json
57
+ mcp_filter_chain_t chain = mcp_chain_create_from_json (dispatcher_, nullptr );
58
+ EXPECT_EQ (chain, 0 );
59
+
60
+ // Test mcp_chain_export_to_json with invalid chain
61
+ mcp_json_value_t json = mcp_chain_export_to_json (0 );
62
+ EXPECT_NE (json, nullptr ); // Returns null JSON, not nullptr
63
+ mcp_json_free (json);
64
+
65
+ // Test mcp_chain_clone with invalid chain
66
+ mcp_filter_chain_t cloned = mcp_chain_clone (0 );
67
+ EXPECT_EQ (cloned, 0 );
68
+
69
+ // Test mcp_chain_merge with invalid chains
70
+ mcp_filter_chain_t merged = mcp_chain_merge (0 , 0 , MCP_CHAIN_MODE_SEQUENTIAL);
71
+ EXPECT_EQ (merged, 0 );
72
+
73
+ // Test mcp_chain_dump with invalid chain
74
+ char * dump = mcp_chain_dump (0 , " text" );
75
+ EXPECT_EQ (dump, nullptr );
76
+ }
77
+
78
+ // Test that status-returning functions return error codes on exception
79
+ TEST_F (FilterChainExceptionSafetyTest, StatusReturningFunctionsReturnErrorOnException) {
80
+ // Test functions with invalid parameters
81
+ mcp_result_t result;
82
+
83
+ // Test mcp_chain_builder_add_node with null builder
84
+ result = mcp_chain_builder_add_node (nullptr , nullptr );
85
+ EXPECT_EQ (result, MCP_ERROR_INVALID_ARGUMENT);
86
+
87
+ // Test mcp_chain_pause with invalid chain
88
+ result = mcp_chain_pause (0 );
89
+ EXPECT_EQ (result, MCP_ERROR_NOT_FOUND);
90
+
91
+ // Test mcp_chain_resume with invalid chain
92
+ result = mcp_chain_resume (0 );
93
+ EXPECT_EQ (result, MCP_ERROR_NOT_FOUND);
94
+
95
+ // Test mcp_chain_reset with invalid chain
96
+ result = mcp_chain_reset (0 );
97
+ EXPECT_EQ (result, MCP_ERROR_NOT_FOUND);
98
+
99
+ // Test mcp_chain_set_filter_enabled with null filter name
100
+ result = mcp_chain_set_filter_enabled (0 , nullptr , true );
101
+ EXPECT_EQ (result, MCP_ERROR_INVALID_ARGUMENT);
102
+
103
+ // Test mcp_chain_get_stats with null stats
104
+ result = mcp_chain_get_stats (0 , nullptr );
105
+ EXPECT_EQ (result, MCP_ERROR_INVALID_ARGUMENT);
106
+
107
+ // Test mcp_chain_set_event_callback with invalid chain
108
+ result = mcp_chain_set_event_callback (0 , nullptr , nullptr );
109
+ EXPECT_EQ (result, MCP_ERROR_NOT_FOUND);
110
+ }
111
+
112
+ // Test that state-returning functions return error state on exception
113
+ TEST_F (FilterChainExceptionSafetyTest, StateReturningFunctionsReturnErrorStateOnException) {
114
+ // Test mcp_chain_get_state with invalid chain
115
+ mcp_chain_state_t state = mcp_chain_get_state (0 );
116
+ EXPECT_EQ (state, MCP_CHAIN_STATE_ERROR);
117
+ }
118
+
119
+ // Test that void functions don't crash on exception
120
+ TEST_F (FilterChainExceptionSafetyTest, VoidFunctionsNoCrashOnException) {
121
+ // These should not crash even with invalid parameters
122
+
123
+ // Test mcp_chain_router_destroy with null
124
+ mcp_chain_router_destroy (nullptr );
125
+
126
+ // Test mcp_chain_pool_return with null pool
127
+ mcp_chain_pool_return (nullptr , 0 );
128
+
129
+ // Test mcp_chain_pool_destroy with null
130
+ mcp_chain_pool_destroy (nullptr );
131
+ }
132
+
133
+ // Test router functions for exception safety
134
+ TEST_F (FilterChainExceptionSafetyTest, RouterFunctionsExceptionSafety) {
135
+ // Create router with null config
136
+ mcp_chain_router_t router = mcp_chain_router_create (nullptr );
137
+ EXPECT_EQ (router, nullptr );
138
+
139
+ // Test add_route with null router
140
+ mcp_result_t result = mcp_chain_router_add_route (nullptr , nullptr , 0 );
141
+ EXPECT_EQ (result, MCP_ERROR_INVALID_ARGUMENT);
142
+
143
+ // Test route with null router
144
+ mcp_filter_chain_t chain = mcp_chain_router_route (nullptr , nullptr , nullptr );
145
+ EXPECT_EQ (chain, 0 );
146
+ }
147
+
148
+ // Test pool functions for exception safety
149
+ TEST_F (FilterChainExceptionSafetyTest, PoolFunctionsExceptionSafety) {
150
+ // Create pool with invalid base chain
151
+ mcp_chain_pool_t pool = mcp_chain_pool_create (0 , 10 , MCP_ROUTING_ROUND_ROBIN);
152
+ // This may or may not return nullptr depending on implementation
153
+
154
+ // Test get_next with null pool
155
+ mcp_filter_chain_t chain = mcp_chain_pool_get_next (nullptr );
156
+ EXPECT_EQ (chain, 0 );
157
+
158
+ // Test get_stats with null pool
159
+ size_t active, idle;
160
+ uint64_t processed;
161
+ mcp_result_t result = mcp_chain_pool_get_stats (nullptr , &active, &idle, &processed);
162
+ EXPECT_EQ (result, MCP_ERROR_INVALID_ARGUMENT);
163
+ }
164
+
165
+ // Test optimization functions for exception safety
166
+ TEST_F (FilterChainExceptionSafetyTest, OptimizationFunctionsExceptionSafety) {
167
+ mcp_result_t result;
168
+
169
+ // Test with invalid chain (should be safe)
170
+ result = mcp_chain_optimize (0 );
171
+ EXPECT_EQ (result, MCP_OK); // TODO functions return OK
172
+
173
+ result = mcp_chain_reorder_filters (0 );
174
+ EXPECT_EQ (result, MCP_OK); // TODO functions return OK
175
+
176
+ result = mcp_chain_profile (0 , nullptr , 100 , nullptr );
177
+ EXPECT_EQ (result, MCP_OK); // TODO functions return OK
178
+
179
+ result = mcp_chain_set_trace_level (0 , 1 );
180
+ EXPECT_EQ (result, MCP_OK); // TODO functions return OK
181
+
182
+ result = mcp_chain_validate (0 , nullptr );
183
+ EXPECT_EQ (result, MCP_OK); // TODO functions return OK
184
+ }
185
+
186
+ // Test that complex operations with valid inputs don't throw
187
+ TEST_F (FilterChainExceptionSafetyTest, ValidOperationsNoThrow) {
188
+ // Create a simple chain configuration
189
+ mcp_json_value_t config = mcp_json_create_object ();
190
+ ASSERT_NE (config, nullptr );
191
+
192
+ mcp_json_value_t name = mcp_json_create_string (" test_chain" );
193
+ mcp_json_object_set (config, " name" , name);
194
+
195
+ mcp_json_value_t filters = mcp_json_create_array ();
196
+ mcp_json_object_set (config, " filters" , filters);
197
+
198
+ // This should handle any internal exceptions gracefully
199
+ mcp_filter_chain_t chain = mcp_chain_create_from_json (dispatcher_, config);
200
+ // Chain creation might fail but shouldn't crash
201
+
202
+ if (chain != 0 ) {
203
+ // Test various operations that should be exception-safe
204
+ mcp_chain_state_t state = mcp_chain_get_state (chain);
205
+ EXPECT_NE (state, MCP_CHAIN_STATE_ERROR);
206
+
207
+ mcp_result_t result = mcp_chain_pause (chain);
208
+ EXPECT_TRUE (result == MCP_OK || result == MCP_ERROR_NOT_FOUND);
209
+
210
+ result = mcp_chain_resume (chain);
211
+ EXPECT_TRUE (result == MCP_OK || result == MCP_ERROR_NOT_FOUND);
212
+
213
+ // Clean up
214
+ // Note: No direct destroy function for chains in the API
215
+ }
216
+
217
+ mcp_json_free (config);
218
+ }
219
+
220
+ } // namespace
0 commit comments