@@ -24,15 +24,15 @@ def my_method_with_exception
2424 end
2525
2626 def my_span
27- Traces . trace ( ' my_span' ) { |span | return span }
27+ Traces . trace ( " my_span" ) { |span | return span }
2828 end
2929
3030 def my_context
31- Traces . trace ( ' my_context' ) { |span | return Traces . trace_context }
31+ Traces . trace ( " my_context" ) { |span | return Traces . trace_context }
3232 end
3333
3434 def my_span_and_context
35- Traces . trace ( ' my_span_and_context' ) { |span | return span , Traces . trace_context }
35+ Traces . trace ( " my_span_and_context" ) { |span | return span , Traces . trace_context }
3636 end
3737end
3838
@@ -66,14 +66,26 @@ def my_span_and_context
6666 end
6767
6868 describe OpenTelemetry ::Trace ::Span do
69- let ( :span ) { instance . my_span }
70-
71- it "can assign name" do
72- span . name = "new_name"
69+ it "can assign name while span is active" do
70+ span_name = nil
71+
72+ Traces . trace ( "test" ) do |span |
73+ span . name = "new_name"
74+ span_name = span . name
75+ end
76+
77+ expect ( span_name ) . to be == "new_name"
7378 end
7479
75- it "can assign attributes" do
76- span [ "my_key" ] = "tag_value"
80+ it "can assign attributes while span is active" do
81+ Traces . trace ( "test" ) do |span |
82+ # Both syntaxes should work without error:
83+ span [ "my_key" ] = "tag_value"
84+ span . set_attribute ( "another_key" , "another_value" )
85+
86+ # Test passes if no exception is raised:
87+ expect ( span ) . to be_a ( ::OpenTelemetry ::Trace ::Span )
88+ end
7789 end
7890 end
7991
@@ -82,15 +94,15 @@ def my_span_and_context
8294 let ( :span ) { span_and_context . first }
8395 let ( :context ) { span_and_context . last }
8496
85- with ' #trace_context' do
97+ with " #trace_context" do
8698 it "has a valid trace id" do
8799 expect ( context ) . to have_attributes (
88100 trace_id : be != nil
89101 )
90102 end
91103 end
92104
93- with ' #trace_context=' do
105+ with " #trace_context=" do
94106 it "can update trace context" do
95107 Traces . trace_context = context
96108
@@ -106,4 +118,172 @@ def my_span_and_context
106118 end
107119 end
108120 end
121+
122+ describe "Context Propagation Methods" do
123+ with "#current_context" do
124+ it "returns current OpenTelemetry context" do
125+ current = Traces . current_context
126+ expect ( current ) . to be_a ( ::OpenTelemetry ::Context )
127+ end
128+
129+ it "returns different contexts in different spans" do
130+ context1 = nil
131+ context2 = nil
132+
133+ Traces . trace ( "span1" ) do
134+ context1 = Traces . current_context
135+ end
136+
137+ Traces . trace ( "span2" ) do
138+ context2 = Traces . current_context
139+ end
140+
141+ # Contexts should be different objects (different spans):
142+ expect ( context1 ) . not . to be_equal ( context2 )
143+ end
144+ end
145+
146+ with "#with_context" do
147+ it "executes block within specified context" do
148+ original_context = Traces . current_context
149+ test_context = ::OpenTelemetry ::Context . empty
150+ executed = false
151+
152+ Traces . with_context ( test_context ) do
153+ executed = true
154+ expect ( ::OpenTelemetry ::Context . current ) . to be_equal ( test_context )
155+ end
156+
157+ expect ( executed ) . to be == true
158+ # Context should be restored after block:
159+ expect ( ::OpenTelemetry ::Context . current ) . to be_equal ( original_context )
160+ end
161+
162+ it "permanently sets context when called without block" do
163+ original_context = Traces . current_context
164+ test_context = ::OpenTelemetry ::Context . empty
165+
166+ token = Traces . with_context ( test_context )
167+ expect ( ::OpenTelemetry ::Context . current ) . to be_equal ( test_context )
168+
169+ # Clean up by detaching:
170+ ::OpenTelemetry ::Context . detach ( token )
171+ end
172+ end
173+
174+ with "#inject" do
175+ it "injects trace context into headers" do
176+ headers = { }
177+
178+ Traces . trace ( "test" ) do
179+ Traces . inject ( headers )
180+ end
181+
182+ expect ( headers ) . to have_keys (
183+ "traceparent" => be_a ( String )
184+ )
185+
186+ traceparent = headers [ "traceparent" ]
187+ expect ( traceparent ) . to be =~ /^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/
188+ end
189+
190+ it "creates new headers hash when none provided" do
191+ Traces . trace ( "test" ) do
192+ headers = Traces . inject ( )
193+ expect ( headers ) . to be_a ( Hash )
194+ expect ( headers [ "traceparent" ] ) . to be =~ /^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/
195+ end
196+ end
197+
198+ it "uses specific context when provided" do
199+ headers = { }
200+
201+ Traces . trace ( "test" ) do
202+ specific_context = Traces . current_context
203+ Traces . inject ( headers , specific_context )
204+ expect ( headers [ "traceparent" ] ) . to be =~ /^00-[0-9a-f]{32}-[0-9a-f]{16}-[0-9a-f]{2}$/
205+ end
206+ end
207+
208+ it "returns nil when no headers provided and no active trace" do
209+ # Clear any active trace:
210+ ::OpenTelemetry ::Context . clear
211+
212+ result = Traces . inject ( )
213+ expect ( result ) . to be == nil
214+ end
215+
216+ it "returns nil when headers provided but no active trace" do
217+ # Clear any active trace:
218+ ::OpenTelemetry ::Context . clear
219+ headers = { "existing" => "value" }
220+
221+ result = Traces . inject ( headers )
222+ expect ( result ) . to be == nil
223+ # Original headers should remain unchanged:
224+ expect ( headers [ "existing" ] ) . to be == "value"
225+ expect ( headers . key? ( "traceparent" ) ) . to be == false
226+ end
227+ end
228+
229+ with "#extract" do
230+ it "extracts context from headers" do
231+ headers = {
232+ "traceparent" => "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
233+ }
234+
235+ context = Traces . extract ( headers )
236+ expect ( context ) . to be_a ( ::OpenTelemetry ::Context )
237+
238+ # Verify we can use the extracted context:
239+ Traces . with_context ( context ) do
240+ span = ::OpenTelemetry ::Trace . current_span
241+ # Convert binary trace_id to hex for comparison:
242+ trace_id_hex = span . context . trace_id . unpack1 ( "H*" )
243+ expect ( trace_id_hex ) . to be == "4bf92f3577b34da6a3ce929d0e0e4736"
244+ end
245+ end
246+
247+ it "returns original context for invalid headers" do
248+ headers = { "traceparent" => "invalid" }
249+ original_context = ::OpenTelemetry ::Context . current
250+
251+ result = Traces . extract ( headers )
252+ expect ( result ) . to be_equal ( original_context )
253+ end
254+
255+ it "handles missing headers gracefully" do
256+ headers = { }
257+ original_context = ::OpenTelemetry ::Context . current
258+
259+ result = Traces . extract ( headers )
260+ expect ( result ) . to be_equal ( original_context )
261+ end
262+ end
263+
264+ with "round-trip inject/extract" do
265+ it "preserves context through inject and extract cycle" do
266+ original_headers = { }
267+
268+ # Create a trace and inject it:
269+ Traces . trace ( "test" ) do
270+ Traces . inject ( original_headers )
271+ end
272+
273+ expect ( original_headers ) . to have_keys (
274+ "traceparent" => be_a ( String )
275+ )
276+
277+ # Extract the context:
278+ extracted_context = Traces . extract ( original_headers )
279+ expect ( extracted_context ) . to be_a ( ::OpenTelemetry ::Context )
280+
281+ # Use extracted context to create another trace:
282+ Traces . with_context ( extracted_context ) do
283+ span = ::OpenTelemetry ::Trace . current_span
284+ expect ( span . context . remote? ) . to be == true
285+ end
286+ end
287+ end
288+ end
109289end
0 commit comments