@@ -28,6 +28,61 @@ mod tracing_span_tests {
2828
2929 #[ test]
3030 fn test_span_propagates_across_operations ( ) {
31+ let env = make_env ( ) ;
32+ env. ledger ( ) . set ( LedgerInfo {
33+ timestamp : 1000 ,
34+ protocol_version : 21 ,
35+ sequence_number : 0 ,
36+ network_id : Default :: default ( ) ,
37+ base_reserve : 0 ,
38+ min_persistent_entry_ttl : 4096 ,
39+ min_temp_entry_ttl : 16 ,
40+ max_entry_ttl : 6312000 ,
41+ } ) ;
42+ let contract_id = env. register_contract ( None , AnchorKitContract ) ;
43+ let client = AnchorKitContractClient :: new ( & env, & contract_id) ;
44+
45+ let admin = Address :: generate ( & env) ;
46+ let attestor = Address :: generate ( & env) ;
47+ let subject = Address :: generate ( & env) ;
48+
49+ client. initialize ( & admin) ;
50+ let sk = SigningKey :: generate ( & mut OsRng ) ;
51+ register_attestor_with_sep10 ( & env, & client, & attestor, & attestor, & sk) ;
52+
53+ // Root span
54+ let root_id = client. generate_request_id ( ) ;
55+ client. submit_with_request_id (
56+ & root_id,
57+ & attestor,
58+ & subject,
59+ & 1000u64 ,
60+ & payload ( & env, 0x01 ) ,
61+ & Bytes :: new ( & env) ,
62+ ) ;
63+
64+ // Child span
65+ let child_id = client. generate_request_id ( ) ;
66+ client. propagate_span (
67+ & root_id,
68+ & child_id,
69+ & String :: from_str ( & env, "fetch_transaction_status" ) ,
70+ & attestor,
71+ ) ;
72+
73+ // Verify child references parent
74+ let child_span = client. get_tracing_span ( & child_id. id ) . unwrap ( ) ;
75+ assert_eq ! ( child_span. parent_request_id_bytes, root_id. id) ;
76+ assert_eq ! ( child_span. span_index, 1 ) ;
77+
78+ // Root span has no parent (empty bytes)
79+ let root_span = client. get_tracing_span ( & root_id. id ) . unwrap ( ) ;
80+ assert ! ( root_span. parent_request_id_bytes. is_empty( ) ) ;
81+ assert_eq ! ( root_span. span_index, 0 ) ;
82+ }
83+
84+ #[ test]
85+ fn test_root_span_has_no_parent ( ) {
3186 let env = make_env ( ) ;
3287 env. ledger ( ) . set ( LedgerInfo {
3388 timestamp : 0 ,
@@ -44,14 +99,186 @@ mod tracing_span_tests {
4499
45100 let admin = Address :: generate ( & env) ;
46101 let attestor = Address :: generate ( & env) ;
102+ let subject = Address :: generate ( & env) ;
47103
48104 client. initialize ( & admin) ;
105+ let sk = SigningKey :: generate ( & mut OsRng ) ;
106+ register_attestor_with_sep10 ( & env, & client, & attestor, & attestor, & sk) ;
107+
49108 let req_id = client. generate_request_id ( ) ;
109+ client. submit_with_request_id (
110+ & req_id,
111+ & attestor,
112+ & subject,
113+ & 1000u64 ,
114+ & payload ( & env, 0x02 ) ,
115+ & Bytes :: new ( & env) ,
116+ ) ;
117+
118+ let span = client. get_tracing_span ( & req_id. id ) . unwrap ( ) ;
119+ assert ! ( span. parent_request_id_bytes. is_empty( ) , "root span must have no parent" ) ;
120+ assert_eq ! ( span. span_index, 0 ) ;
121+ }
122+
123+ #[ test]
124+ fn test_sibling_spans_share_same_parent ( ) {
125+ let env = make_env ( ) ;
126+ env. ledger ( ) . set ( LedgerInfo {
127+ timestamp : 500 ,
128+ protocol_version : 21 ,
129+ sequence_number : 0 ,
130+ network_id : Default :: default ( ) ,
131+ base_reserve : 0 ,
132+ min_persistent_entry_ttl : 4096 ,
133+ min_temp_entry_ttl : 16 ,
134+ max_entry_ttl : 6312000 ,
135+ } ) ;
136+ let contract_id = env. register_contract ( None , AnchorKitContract ) ;
137+ let client = AnchorKitContractClient :: new ( & env, & contract_id) ;
138+
139+ let admin = Address :: generate ( & env) ;
140+ let attestor = Address :: generate ( & env) ;
141+ let subject = Address :: generate ( & env) ;
142+
143+ client. initialize ( & admin) ;
50144 let sk = SigningKey :: generate ( & mut OsRng ) ;
51145 register_attestor_with_sep10 ( & env, & client, & attestor, & attestor, & sk) ;
52146
53- let span = client. get_tracing_span ( & req_id. id ) ;
54- assert ! ( span. is_none( ) ) ;
147+ let root_id = client. generate_request_id ( ) ;
148+ client. submit_with_request_id (
149+ & root_id,
150+ & attestor,
151+ & subject,
152+ & 1000u64 ,
153+ & payload ( & env, 0x03 ) ,
154+ & Bytes :: new ( & env) ,
155+ ) ;
156+
157+ let child_a = client. generate_request_id ( ) ;
158+ let child_b = client. generate_request_id ( ) ;
159+
160+ client. propagate_span (
161+ & root_id,
162+ & child_a,
163+ & String :: from_str ( & env, "step_a" ) ,
164+ & attestor,
165+ ) ;
166+ client. propagate_span (
167+ & root_id,
168+ & child_b,
169+ & String :: from_str ( & env, "step_b" ) ,
170+ & attestor,
171+ ) ;
172+
173+ let span_a = client. get_tracing_span ( & child_a. id ) . unwrap ( ) ;
174+ let span_b = client. get_tracing_span ( & child_b. id ) . unwrap ( ) ;
175+
176+ // Both siblings reference the same parent
177+ assert_eq ! ( span_a. parent_request_id_bytes, root_id. id) ;
178+ assert_eq ! ( span_b. parent_request_id_bytes, root_id. id) ;
179+ // Siblings have different span indices
180+ assert_ne ! ( span_a. span_index, span_b. span_index) ;
181+ }
182+
183+ #[ test]
184+ fn test_get_trace_returns_all_spans_in_order ( ) {
185+ let env = make_env ( ) ;
186+ env. ledger ( ) . set ( LedgerInfo {
187+ timestamp : 100 ,
188+ protocol_version : 21 ,
189+ sequence_number : 0 ,
190+ network_id : Default :: default ( ) ,
191+ base_reserve : 0 ,
192+ min_persistent_entry_ttl : 4096 ,
193+ min_temp_entry_ttl : 16 ,
194+ max_entry_ttl : 6312000 ,
195+ } ) ;
196+ let contract_id = env. register_contract ( None , AnchorKitContract ) ;
197+ let client = AnchorKitContractClient :: new ( & env, & contract_id) ;
198+
199+ let admin = Address :: generate ( & env) ;
200+ let attestor = Address :: generate ( & env) ;
201+ let subject = Address :: generate ( & env) ;
202+
203+ client. initialize ( & admin) ;
204+ let sk = SigningKey :: generate ( & mut OsRng ) ;
205+ register_attestor_with_sep10 ( & env, & client, & attestor, & attestor, & sk) ;
206+
207+ let root_id = client. generate_request_id ( ) ;
208+ client. submit_with_request_id (
209+ & root_id,
210+ & attestor,
211+ & subject,
212+ & 1000u64 ,
213+ & payload ( & env, 0x04 ) ,
214+ & Bytes :: new ( & env) ,
215+ ) ;
216+
217+ let child1 = client. generate_request_id ( ) ;
218+ let child2 = client. generate_request_id ( ) ;
219+
220+ client. propagate_span ( & root_id, & child1, & String :: from_str ( & env, "op1" ) , & attestor) ;
221+ client. propagate_span ( & root_id, & child2, & String :: from_str ( & env, "op2" ) , & attestor) ;
222+
223+ let trace = client. get_trace ( & root_id. id ) ;
224+ assert_eq ! ( trace. len( ) , 3 ) ;
225+ // First span is root (span_index 0)
226+ assert_eq ! ( trace. get( 0 ) . unwrap( ) . span_index, 0 ) ;
227+ assert_eq ! ( trace. get( 1 ) . unwrap( ) . span_index, 1 ) ;
228+ assert_eq ! ( trace. get( 2 ) . unwrap( ) . span_index, 2 ) ;
229+ }
230+
231+ #[ test]
232+ fn test_structured_log_format_includes_parent_request_id ( ) {
233+ let env = make_env ( ) ;
234+ env. ledger ( ) . set ( LedgerInfo {
235+ timestamp : 200 ,
236+ protocol_version : 21 ,
237+ sequence_number : 0 ,
238+ network_id : Default :: default ( ) ,
239+ base_reserve : 0 ,
240+ min_persistent_entry_ttl : 4096 ,
241+ min_temp_entry_ttl : 16 ,
242+ max_entry_ttl : 6312000 ,
243+ } ) ;
244+ let contract_id = env. register_contract ( None , AnchorKitContract ) ;
245+ let client = AnchorKitContractClient :: new ( & env, & contract_id) ;
246+
247+ let admin = Address :: generate ( & env) ;
248+ let attestor = Address :: generate ( & env) ;
249+ let subject = Address :: generate ( & env) ;
250+
251+ client. initialize ( & admin) ;
252+ let sk = SigningKey :: generate ( & mut OsRng ) ;
253+ register_attestor_with_sep10 ( & env, & client, & attestor, & attestor, & sk) ;
254+
255+ let root_id = client. generate_request_id ( ) ;
256+ client. submit_with_request_id (
257+ & root_id,
258+ & attestor,
259+ & subject,
260+ & 1000u64 ,
261+ & payload ( & env, 0x05 ) ,
262+ & Bytes :: new ( & env) ,
263+ ) ;
264+
265+ let child_id = client. generate_request_id ( ) ;
266+ client. propagate_span (
267+ & root_id,
268+ & child_id,
269+ & String :: from_str ( & env, "sep6_deposit" ) ,
270+ & attestor,
271+ ) ;
272+
273+ let child_span = client. get_tracing_span ( & child_id. id ) . unwrap ( ) ;
274+ // Structured log: parent_request_id_bytes is non-empty when span is a child
275+ assert ! ( !child_span. parent_request_id_bytes. is_empty( ) ) ;
276+ assert_eq ! (
277+ child_span. parent_request_id_bytes,
278+ root_id. id,
279+ "structured log must include parent_request_id"
280+ ) ;
281+ assert_eq ! ( child_span. operation, String :: from_str( & env, "sep6_deposit" ) ) ;
55282 }
56283
57284 #[ test]
0 commit comments