Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
test_thread_context.cpp File Reference

Unit tests for thread context and metadata. More...

#include <gtest/gtest.h>
#include <kcenon/monitoring/context/thread_context.h>
#include <thread>
#include <vector>
#include <set>
#include <future>
Include dependency graph for test_thread_context.cpp:

Go to the source code of this file.

Classes

class  ThreadContextTest
 

Functions

 TEST_F (ThreadContextTest, ContextMetadataBasicOperations)
 
 TEST_F (ThreadContextTest, DISABLED_ContextMetadataMerge)
 
 TEST_F (ThreadContextTest, ThreadContextBasicOperations)
 
 TEST_F (ThreadContextTest, RequestIdGeneration)
 
 TEST_F (ThreadContextTest, CorrelationIdGeneration)
 
 TEST_F (ThreadContextTest, ContextScope)
 
 TEST_F (ThreadContextTest, ContextScopeWithPreservation)
 
 TEST_F (ThreadContextTest, ContextPropagator)
 
 TEST_F (ThreadContextTest, CrossThreadPropagation)
 
 TEST_F (ThreadContextTest, ContextAwareEnrichment)
 
 TEST_F (ThreadContextTest, ContextMetricsCollector)
 
 TEST_F (ThreadContextTest, ThreadIsolation)
 
 TEST_F (ThreadContextTest, CopyFromContext)
 

Detailed Description

Unit tests for thread context and metadata.

Definition in file test_thread_context.cpp.

Function Documentation

◆ TEST_F() [1/13]

TEST_F ( ThreadContextTest ,
ContextAwareEnrichment  )

Test context_aware_monitoring enrichment

Definition at line 291 of file test_thread_context.cpp.

291 {
292 // Create test implementation
293 class test_context_aware : public context_aware_monitoring {
294 public:
295 // Use default implementation
296 };
297
298 test_context_aware aware;
299
300 // Create context
301 thread_context::create("enrich-request");
302 thread_context::current()->correlation_id = "enrich-corr";
303 thread_context::current()->user_id = "enrich-user";
304 thread_context::current()->add_tag("custom1", "value1");
305 thread_context::current()->add_tag("custom2", "value2");
306
307 // Create monitoring data
308 monitoring_data data("test-component");
309 data.add_metric("metric1", 100.0);
310
311 // Enrich with context
312 auto result = aware.enrich_with_context(data);
313 ASSERT_TRUE(result);
314
315 // Verify context was added as tags
316 auto req_id = data.get_tag("request_id");
317 ASSERT_TRUE(req_id.has_value());
318 EXPECT_EQ(req_id.value(), "enrich-request");
319
320 auto corr_id = data.get_tag("correlation_id");
321 ASSERT_TRUE(corr_id.has_value());
322 EXPECT_EQ(corr_id.value(), "enrich-corr");
323
324 auto user_id = data.get_tag("user_id");
325 ASSERT_TRUE(user_id.has_value());
326 EXPECT_EQ(user_id.value(), "enrich-user");
327
328 // Custom tags should have "ctx." prefix
329 auto custom1 = data.get_tag("ctx.custom1");
330 ASSERT_TRUE(custom1.has_value());
331 EXPECT_EQ(custom1.value(), "value1");
332
333 auto custom2 = data.get_tag("ctx.custom2");
334 ASSERT_TRUE(custom2.has_value());
335 EXPECT_EQ(custom2.value(), "value2");
336
337 // Original metric should remain
338 auto metric = data.get_metric("metric1");
339 ASSERT_TRUE(metric.has_value());
340 EXPECT_DOUBLE_EQ(metric.value(), 100.0);
341}
static thread_context_data * current()
Get the current thread-local context.
static thread_context_data & create(const std::string &request_id="")
Create a new thread-local context, replacing any existing one.
Basic metric structure for interface compatibility.
std::variant< double, int64_t, std::string > value
Container for monitoring metrics from a component.
void add_tag(const std::string &key, const std::string &value)
Add or update a custom tag.
std::string correlation_id
Correlation ID for cross-service tracing.
std::string user_id
User identifier associated with the request.

References kcenon::monitoring::monitoring_data::add_metric(), kcenon::monitoring::thread_context_data::add_tag(), kcenon::monitoring::thread_context_data::correlation_id, kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), kcenon::monitoring::monitoring_data::get_metric(), kcenon::monitoring::monitoring_data::get_tag(), kcenon::monitoring::thread_context_data::user_id, and kcenon::monitoring::metric::value.

Here is the call graph for this function:

◆ TEST_F() [2/13]

TEST_F ( ThreadContextTest ,
ContextMetadataBasicOperations  )

Test context_metadata basic operations

Definition at line 39 of file test_thread_context.cpp.

39 {
40 context_metadata metadata("req-123");
41
42 // Check initial state
43 EXPECT_EQ(metadata.request_id, "req-123");
44 EXPECT_TRUE(metadata.correlation_id.empty());
45 EXPECT_TRUE(metadata.user_id.empty());
46 EXPECT_FALSE(metadata.empty());
47
48 // Set fields
49 metadata.correlation_id = "corr-456";
50 metadata.user_id = "user-789";
51
52 // Add custom tags using set_tag method
53 metadata.set_tag("environment", "production");
54 metadata.set_tag("version", "1.2.3");
55
56 // Verify tags using get_tag method (returns string, not optional)
57 auto env = metadata.get_tag("environment");
58 EXPECT_EQ(env, "production");
59
60 auto ver = metadata.get_tag("version");
61 EXPECT_EQ(ver, "1.2.3");
62
63 auto missing = metadata.get_tag("nonexistent");
64 EXPECT_EQ(missing, ""); // Returns empty string for missing tags
65
66 // Verify tags map directly
67 EXPECT_TRUE(metadata.tags.find("environment") != metadata.tags.end());
68 EXPECT_TRUE(metadata.tags.find("version") != metadata.tags.end());
69
70 // Verify empty check
71 EXPECT_FALSE(metadata.empty()); // Should not be empty with data
72}
Context metadata for thread-specific information.

References kcenon::monitoring::context_metadata::correlation_id, kcenon::monitoring::context_metadata::empty(), kcenon::monitoring::context_metadata::get_tag(), kcenon::monitoring::context_metadata::request_id, kcenon::monitoring::context_metadata::set_tag(), kcenon::monitoring::context_metadata::tags, and kcenon::monitoring::context_metadata::user_id.

Here is the call graph for this function:

◆ TEST_F() [3/13]

TEST_F ( ThreadContextTest ,
ContextMetricsCollector  )

Test context_metrics_collector

Definition at line 346 of file test_thread_context.cpp.

346 {
347 // Create test collector
348 class test_collector : public context_metrics_collector {
349 public:
350 explicit test_collector(const std::string& name)
351 : context_metrics_collector(name) {}
352
353 kcenon::common::Result<metrics_snapshot> collect() override {
354 auto snapshot = create_snapshot_with_context();
355 snapshot.add_metric("test_metric", 42.0);
356 return kcenon::common::ok(std::move(snapshot));
357 }
358 };
359
360 test_collector collector("test-collector");
361
362 // Set thread context
363 thread_context::create("collector-request");
364 thread_context::current()->user_id = "collector-user";
365
366 // Collect with context awareness enabled
367 collector.set_context_aware(true);
368 auto result1 = collector.collect();
369 ASSERT_TRUE(result1);
370
371 auto snapshot1 = result1.value();
372 EXPECT_EQ(snapshot1.source_id, "test-collector");
373
374 // Verify metric
375 auto metric = snapshot1.get_metric("test_metric");
376 ASSERT_TRUE(metric.has_value());
377 EXPECT_DOUBLE_EQ(metric.value(), 42.0);
378
379 // Collect with context awareness disabled
380 collector.set_context_aware(false);
381 auto result2 = collector.collect();
382 ASSERT_TRUE(result2);
383
384 // Should still work but without context
385 auto snapshot2 = result2.value();
386 EXPECT_EQ(snapshot2.source_id, "test-collector");
387}

References kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context_data::user_id, and kcenon::monitoring::metric::value.

Here is the call graph for this function:

◆ TEST_F() [4/13]

TEST_F ( ThreadContextTest ,
ContextPropagator  )

Test context_propagator

Definition at line 217 of file test_thread_context.cpp.

217 {
218 // Create context in main thread
219 thread_context::create("main-request");
220 thread_context::current()->user_id = "main-user";
221 thread_context::current()->add_tag("source", "main");
222
223 // Capture context
224 context_propagator propagator;
225 auto capture_result = propagator.capture();
226 ASSERT_TRUE(capture_result);
227 EXPECT_TRUE(propagator.has_captured());
228
229 // Clear main thread context
231 EXPECT_FALSE(thread_context::has_context());
232
233 // Apply in same thread
234 auto apply_result = propagator.apply();
235 ASSERT_TRUE(apply_result);
236
237 // Should have restored context
238 EXPECT_TRUE(thread_context::has_context());
239 EXPECT_EQ(thread_context::current()->request_id, "main-request");
240 EXPECT_EQ(thread_context::current()->user_id, "main-user");
241 auto tag = thread_context::current()->get_tag("source");
242 ASSERT_TRUE(tag.has_value());
243 EXPECT_EQ(tag.value(), "main");
244}
static void clear()
Clear and destroy the current thread-local context.
static bool has_context()
Check whether a context exists on the current thread.
std::string get_tag(const std::string &key) const
Retrieve a tag value by key.

References kcenon::monitoring::thread_context_data::add_tag(), kcenon::monitoring::thread_context::clear(), kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context_data::get_tag(), kcenon::monitoring::thread_context::has_context(), and kcenon::monitoring::thread_context_data::user_id.

Here is the call graph for this function:

◆ TEST_F() [5/13]

TEST_F ( ThreadContextTest ,
ContextScope  )

Test context_scope RAII wrapper

Definition at line 170 of file test_thread_context.cpp.

170 {
171 // No initial context
172 EXPECT_FALSE(thread_context::has_context());
173
174 {
175 // Create scope with new context
176 context_scope scope("scoped-request");
177
178 EXPECT_TRUE(thread_context::has_context());
179 EXPECT_EQ(thread_context::current()->request_id, "scoped-request");
180
181 // Modify context within scope
182 thread_context::current()->user_id = "scoped-user";
183 }
184
185 // Context should be cleared after scope
186 EXPECT_FALSE(thread_context::has_context());
187}

References kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context::has_context(), and kcenon::monitoring::thread_context_data::user_id.

Here is the call graph for this function:

◆ TEST_F() [6/13]

TEST_F ( ThreadContextTest ,
ContextScopeWithPreservation  )

Test context_scope with preservation

Definition at line 192 of file test_thread_context.cpp.

192 {
193 // Set initial context
194 thread_context::create("original-request");
195 thread_context::current()->user_id = "original-user";
196
197 {
198 // Create scope that preserves previous context
199 auto new_metadata = std::make_unique<context_metadata>("scoped-request");
200 new_metadata->user_id = "scoped-user";
201 context_scope scope(std::move(new_metadata), true);
202
203 // Should have new context
204 EXPECT_EQ(thread_context::current()->request_id, "scoped-request");
205 EXPECT_EQ(thread_context::current()->user_id, "scoped-user");
206 }
207
208 // Should restore original context
209 EXPECT_TRUE(thread_context::has_context());
210 EXPECT_EQ(thread_context::current()->request_id, "original-request");
211 EXPECT_EQ(thread_context::current()->user_id, "original-user");
212}

References kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context::has_context(), and kcenon::monitoring::thread_context_data::user_id.

Here is the call graph for this function:

◆ TEST_F() [7/13]

TEST_F ( ThreadContextTest ,
CopyFromContext  )

Test copy_from functionality

Definition at line 444 of file test_thread_context.cpp.

444 {
445 // Create source context
446 context_metadata source("source-request");
447 source.correlation_id = "source-corr";
448 source.user_id = "source-user";
449 source.add_tag("tag1", "value1");
450
451 // Copy to current thread
452 auto result = thread_context::copy_from(source);
453 ASSERT_TRUE(result);
454
455 // Verify copy
456 EXPECT_TRUE(thread_context::has_context());
457 auto* current = thread_context::current();
458 ASSERT_NE(current, nullptr);
459
460 EXPECT_EQ(current->request_id, "source-request");
461 EXPECT_EQ(current->correlation_id, "source-corr");
462 EXPECT_EQ(current->user_id, "source-user");
463
464 auto tag = current->get_tag("tag1");
465 ASSERT_TRUE(tag.has_value());
466 EXPECT_EQ(tag.value(), "value1");
467
468 // Modifications shouldn't affect source
469 current->user_id = "modified-user";
470 EXPECT_EQ(source.user_id, "source-user");
471}
static bool copy_from(const thread_context_data &source)
Copy context data from another source into the current thread.

References kcenon::monitoring::thread_context::copy_from(), kcenon::monitoring::context_metadata::correlation_id, kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context::has_context(), and kcenon::monitoring::context_metadata::user_id.

Here is the call graph for this function:

◆ TEST_F() [8/13]

TEST_F ( ThreadContextTest ,
CorrelationIdGeneration  )

Test correlation ID generation

Definition at line 154 of file test_thread_context.cpp.

154 {
157
158 EXPECT_FALSE(corr_id1.empty());
159 EXPECT_FALSE(corr_id2.empty());
160 EXPECT_NE(corr_id1, corr_id2);
161
162 // Should have "corr-" prefix
163 EXPECT_EQ(corr_id1.substr(0, 5), "corr-");
164 EXPECT_EQ(corr_id2.substr(0, 5), "corr-");
165}
static std::string generate_correlation_id()
Generate a unique correlation ID.

References kcenon::monitoring::thread_context::generate_correlation_id().

Here is the call graph for this function:

◆ TEST_F() [9/13]

TEST_F ( ThreadContextTest ,
CrossThreadPropagation  )

Test context propagation across threads

Definition at line 249 of file test_thread_context.cpp.

249 {
250 // Create context in main thread
251 thread_context::create("main-thread-request");
252 thread_context::current()->correlation_id = "main-correlation";
253 thread_context::current()->add_tag("thread", "main");
254
255 // Capture for propagation
256 auto propagator = context_propagator::from_current();
257
258 // Verify in another thread
259 std::thread worker([propagator]() {
260 // Initially no context in new thread
261 EXPECT_FALSE(thread_context::has_context());
262
263 // Apply captured context
264 auto result = propagator.apply();
265 EXPECT_TRUE(result);
266
267 // Should have context from main thread
268 EXPECT_TRUE(thread_context::has_context());
269 EXPECT_EQ(thread_context::current()->request_id, "main-thread-request");
270 EXPECT_EQ(thread_context::current()->correlation_id, "main-correlation");
271
272 auto tag = thread_context::current()->get_tag("thread");
273 ASSERT_TRUE(tag.has_value());
274 EXPECT_EQ(tag.value(), "main");
275
276 // Modify in worker thread
277 thread_context::current()->add_tag("thread", "worker");
278 });
279
280 worker.join();
281
282 // Main thread context should be unchanged
283 auto main_tag = thread_context::current()->get_tag("thread");
284 ASSERT_TRUE(main_tag.has_value());
285 EXPECT_EQ(main_tag.value(), "main");
286}

References kcenon::monitoring::thread_context_data::add_tag(), kcenon::monitoring::thread_context_data::correlation_id, kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context_data::get_tag(), and kcenon::monitoring::thread_context::has_context().

Here is the call graph for this function:

◆ TEST_F() [10/13]

TEST_F ( ThreadContextTest ,
DISABLED_ContextMetadataMerge  )

Test context_metadata copy and comparison

Definition at line 77 of file test_thread_context.cpp.

77 {
78 // Disabled until merge functionality is implemented
79 context_metadata metadata1("req-1");
80 metadata1.user_id = "user-1";
81 metadata1.set_tag("tag1", "value1");
82
83 context_metadata metadata2("req-2");
84 metadata2.correlation_id = "corr-2";
85 metadata2.user_id = "user-2";
86 metadata2.set_tag("tag2", "value2");
87
88 // Basic copy test instead of merge
89 context_metadata copy = metadata1;
90 EXPECT_EQ(copy.request_id, metadata1.request_id);
91 EXPECT_EQ(copy.user_id, metadata1.user_id);
92
93 // Verify tags are copied
94 EXPECT_EQ(copy.get_tag("tag1"), "value1");
95}
std::string request_id
Unique identifier for the current request.
std::string user_id
User identifier associated with the request.
std::string get_tag(const std::string &key) const
Retrieve a tag value by key.

References kcenon::monitoring::context_metadata::correlation_id, kcenon::monitoring::context_metadata::get_tag(), kcenon::monitoring::context_metadata::request_id, kcenon::monitoring::context_metadata::set_tag(), and kcenon::monitoring::context_metadata::user_id.

Here is the call graph for this function:

◆ TEST_F() [11/13]

TEST_F ( ThreadContextTest ,
RequestIdGeneration  )

Test automatic request ID generation

Definition at line 132 of file test_thread_context.cpp.

132 {
133 // Generate multiple IDs
134 std::set<std::string> ids;
135 for (int i = 0; i < 100; ++i) {
137 }
138
139 // All should be unique
140 EXPECT_EQ(ids.size(), 100);
141
142 // Create context without request ID
143 auto& ctx = thread_context::create();
144 EXPECT_FALSE(ctx.request_id.empty());
145
146 // Generated ID should be unique
147 auto generated_id = ctx.request_id;
148 EXPECT_EQ(ids.find(generated_id), ids.end());
149}
static std::string generate_request_id()
Generate a unique request ID.

References kcenon::monitoring::thread_context::create(), and kcenon::monitoring::thread_context::generate_request_id().

Here is the call graph for this function:

◆ TEST_F() [12/13]

TEST_F ( ThreadContextTest ,
ThreadContextBasicOperations  )

Test thread_context basic operations

Definition at line 100 of file test_thread_context.cpp.

100 {
101 // Initially no context
102 EXPECT_FALSE(thread_context::has_context());
103 EXPECT_EQ(thread_context::current(), nullptr);
104
105 // Create context
106 auto& ctx = thread_context::create("test-request");
107 EXPECT_TRUE(thread_context::has_context());
108 EXPECT_NE(thread_context::current(), nullptr);
109 EXPECT_EQ(ctx.request_id, "test-request");
110
111 // Modify current context
112 auto* current = thread_context::current();
113 ASSERT_NE(current, nullptr);
114 current->user_id = "test-user";
115 current->add_tag("test", "value");
116
117 // Verify modifications
118 EXPECT_EQ(thread_context::current()->user_id, "test-user");
119 auto tag = thread_context::current()->get_tag("test");
120 ASSERT_TRUE(tag.has_value());
121 EXPECT_EQ(tag.value(), "value");
122
123 // Clear context
125 EXPECT_FALSE(thread_context::has_context());
126 EXPECT_EQ(thread_context::current(), nullptr);
127}

References kcenon::monitoring::thread_context::clear(), kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), kcenon::monitoring::thread_context_data::get_tag(), and kcenon::monitoring::thread_context::has_context().

Here is the call graph for this function:

◆ TEST_F() [13/13]

TEST_F ( ThreadContextTest ,
ThreadIsolation  )

Test thread isolation

Definition at line 392 of file test_thread_context.cpp.

392 {
393 const int thread_count = 10;
394 std::vector<std::thread> threads;
395 std::vector<std::future<std::string>> futures;
396
397 // Create threads with their own contexts
398 for (int i = 0; i < thread_count; ++i) {
399 std::promise<std::string> promise;
400 futures.push_back(promise.get_future());
401
402 threads.emplace_back([i, p = std::move(promise)]() mutable {
403 // Each thread creates its own context
404 auto req_id = "thread-" + std::to_string(i);
406 thread_context::current()->user_id = "user-" + std::to_string(i);
407
408 // Do some work
409 std::this_thread::sleep_for(std::chrono::milliseconds(10));
410
411 // Verify context is still correct
412 auto* ctx = thread_context::current();
413 if (ctx && ctx->request_id == req_id &&
414 ctx->user_id == "user-" + std::to_string(i)) {
415 p.set_value(req_id);
416 } else {
417 p.set_value("error");
418 }
419 });
420 }
421
422 // Collect results
423 std::set<std::string> results;
424 for (auto& future : futures) {
425 results.insert(future.get());
426 }
427
428 // Wait for threads
429 for (auto& t : threads) {
430 t.join();
431 }
432
433 // Each thread should have maintained its own context
434 EXPECT_EQ(results.size(), thread_count);
435 for (int i = 0; i < thread_count; ++i) {
436 auto expected = "thread-" + std::to_string(i);
437 EXPECT_NE(results.find(expected), results.end());
438 }
439}

References kcenon::monitoring::thread_context::create(), kcenon::monitoring::thread_context::current(), and kcenon::monitoring::thread_context_data::user_id.

Here is the call graph for this function: