File
Blob: src/workerd/io/trace-test.c++
| 1 | // Copyright (c) 2017-2022 Cloudflare, Inc. |
| 2 | // Licensed under the Apache 2.0 license found in the LICENSE file or at: |
| 3 | // https://opensource.org/licenses/Apache-2.0 |
| 4 | |
| 5 | #include <workerd/io/trace.h> |
| 6 | #include <workerd/util/thread-scopes.h> |
| 7 | |
| 8 | #include <capnp/message.h> |
| 9 | #include <kj/compat/http.h> |
| 10 | #include <kj/test.h> |
| 11 | |
| 12 | namespace workerd::tracing { |
| 13 | namespace { |
| 14 | |
| 15 | class FakeEntropySource final: public kj::EntropySource { |
| 16 | public: |
| 17 | void generate(kj::ArrayPtr<byte> buffer) override { |
| 18 | // Write the uint64_t value to the buffer |
| 19 | buffer[0] = counter & 0xff; |
| 20 | buffer[1] = (counter >> 8) & 0xff; |
| 21 | buffer[2] = (counter >> 16) & 0xff; |
| 22 | buffer[3] = (counter >> 24) & 0xff; |
| 23 | buffer[4] = (counter >> 32) & 0xff; |
| 24 | buffer[5] = (counter >> 40) & 0xff; |
| 25 | buffer[6] = (counter >> 48) & 0xff; |
| 26 | buffer[7] = (counter >> 56) & 0xff; |
| 27 | counter++; |
| 28 | } |
| 29 | |
| 30 | private: |
| 31 | uint64_t counter = 0; |
| 32 | }; |
| 33 | |
| 34 | KJ_TEST("can read trace ID string format") { |
| 35 | KJ_EXPECT(TraceId::fromGoString("z"_kj) == kj::none); |
| 36 | |
| 37 | KJ_EXPECT(TraceId::fromGoString("fedcba9876543210z"_kj) == kj::none); |
| 38 | |
| 39 | // Go parser supports non-(64 or 128) bit lengths -- unclear if anything cares. |
| 40 | KJ_EXPECT(TraceId(0, 0) == KJ_ASSERT_NONNULL(TraceId::fromGoString(""_kj))); |
| 41 | KJ_EXPECT(TraceId(0x1, 0) == KJ_ASSERT_NONNULL(TraceId::fromGoString("1"_kj))); |
| 42 | |
| 43 | KJ_EXPECT(TraceId(0xfedcba9876543210, 0) == |
| 44 | KJ_ASSERT_NONNULL(TraceId::fromGoString("fedcba9876543210"_kj))); |
| 45 | KJ_EXPECT(TraceId(0xfedcba9876543210, 0) == |
| 46 | KJ_ASSERT_NONNULL(TraceId::fromGoString("FEDCBA9876543210"_kj))); |
| 47 | |
| 48 | KJ_EXPECT(TraceId(0xfedcba9876543210, 0x1) == |
| 49 | KJ_ASSERT_NONNULL(TraceId::fromGoString("01fedcba9876543210"_kj))); |
| 50 | |
| 51 | KJ_EXPECT(TraceId(0xfedcba9876543211, 0xfedcba9876543212) == |
| 52 | KJ_ASSERT_NONNULL(TraceId::fromGoString("fedcba9876543212fedcba9876543211"_kj))); |
| 53 | |
| 54 | KJ_EXPECT(TraceId::fromGoString("01fedcba9876543212fedcba9876543211"_kj) == kj::none); |
| 55 | } |
| 56 | |
| 57 | KJ_TEST("can write trace ID string format") { |
| 58 | KJ_EXPECT(TraceId(0x1, 0).toGoString() == "0000000000000001"_kj); |
| 59 | KJ_EXPECT(TraceId(0xfedcba9876543210, 0).toGoString() == "fedcba9876543210"_kj); |
| 60 | KJ_EXPECT(TraceId(0xfedcba9876543210, 0x1).toGoString() == "0000000000000001fedcba9876543210"_kj); |
| 61 | |
| 62 | KJ_EXPECT(TraceId(0xfedcba9876543211, 0xfedcba9876543212).toGoString() == |
| 63 | "fedcba9876543212fedcba9876543211"_kj); |
| 64 | } |
| 65 | |
| 66 | KJ_TEST("can read trace ID protobuf format") { |
| 67 | KJ_EXPECT(TraceId::fromProtobuf(""_kjb) == kj::none); |
| 68 | KJ_EXPECT(TraceId::fromProtobuf("z"_kjb) == kj::none); |
| 69 | KJ_EXPECT(TraceId::fromProtobuf("\xfe\xdc\xba\x98\x76\x54\x32\x12\xfe"_kjb) == kj::none); |
| 70 | KJ_EXPECT( |
| 71 | TraceId::fromProtobuf( |
| 72 | "\xfe\xdc\xba\x98\x76\x54\x32\x12\xfe\xdc\xba\x98\x76\x54\x32\x11\x01"_kjb) == kj::none); |
| 73 | |
| 74 | KJ_EXPECT(KJ_ASSERT_NONNULL(TraceId::fromProtobuf( |
| 75 | "\xfe\xdc\xba\x98\x76\x54\x32\x12\xfe\xdc\xba\x98\x76\x54\x32\x11"_kjb)) == |
| 76 | TraceId(0xfedcba9876543211, 0xfedcba9876543212)); |
| 77 | } |
| 78 | |
| 79 | KJ_TEST("can write trace ID protobuf format") { |
| 80 | KJ_EXPECT(TraceId(0, 0).toProtobuf() == |
| 81 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"_kjb); |
| 82 | |
| 83 | KJ_EXPECT(TraceId(0xfedcba9876543211, 0xfedcba9876543212).toProtobuf() == |
| 84 | "\xfe\xdc\xba\x98\x76\x54\x32\x12\xfe\xdc\xba\x98\x76\x54\x32\x11"_kjb); |
| 85 | } |
| 86 | |
| 87 | KJ_TEST("InvocationSpanContext") { |
| 88 | setPredictableModeForTest(); |
| 89 | FakeEntropySource fakeEntropySource; |
| 90 | auto sc = InvocationSpanContext::newForInvocation(kj::none, fakeEntropySource); |
| 91 | |
| 92 | // In predictable mode, TraceId::fromEntropy returns deterministic but |
| 93 | // call-distinct values (process-wide counter), so the traceId and invocationId |
| 94 | // of independent invocations differ from each other and across tests. Capture |
| 95 | // the IDs and assert the propagation chain rather than specific constants. |
| 96 | auto initialTraceId = sc.getTraceId(); |
| 97 | auto initialInvocationId = sc.getInvocationId(); |
| 98 | KJ_EXPECT(sc.getSpanId() == SpanId(1)); |
| 99 | |
| 100 | // And serialize that to a capnp struct... |
| 101 | capnp::MallocMessageBuilder builder; |
| 102 | auto root = builder.initRoot<rpc::InvocationSpanContext>(); |
| 103 | sc.toCapnp(root); |
| 104 | |
| 105 | // Then back again... |
| 106 | auto sc2 = KJ_ASSERT_NONNULL(InvocationSpanContext::fromCapnp(root.asReader())); |
| 107 | KJ_EXPECT(sc2.getTraceId() == initialTraceId); |
| 108 | KJ_EXPECT(sc2.getInvocationId() == initialInvocationId); |
| 109 | KJ_EXPECT(sc2.getSpanId() == SpanId(1)); |
| 110 | KJ_EXPECT(sc2.isTrigger()); |
| 111 | |
| 112 | // The one that has been deserialized from capnp cannot create children... |
| 113 | try { |
| 114 | sc2.newChild(); |
| 115 | KJ_FAIL_ASSERT("should not be able to create child span with SpanContext from capnp"); |
| 116 | } catch (kj::Exception& ex) { |
| 117 | KJ_EXPECT(ex.getDescription() == |
| 118 | "expected !isTrigger(); unable to create child spans on this context"_kj); |
| 119 | } |
| 120 | |
| 121 | // Children inherit both the traceId and invocationId of their parent. |
| 122 | auto sc3 = sc.newChild(); |
| 123 | KJ_EXPECT(sc3.getTraceId() == initialTraceId); |
| 124 | KJ_EXPECT(sc3.getInvocationId() == initialInvocationId); |
| 125 | KJ_EXPECT(sc3.getSpanId() == SpanId(2)); |
| 126 | |
| 127 | // Trigger-context propagation: traceId is inherited from sc2, but the |
| 128 | // invocationId is freshly generated for the new invocation. |
| 129 | auto sc4 = InvocationSpanContext::newForInvocation(sc2, fakeEntropySource); |
| 130 | KJ_EXPECT(sc4.getTraceId() == initialTraceId); |
| 131 | KJ_EXPECT(sc4.getInvocationId() != initialInvocationId); |
| 132 | KJ_EXPECT(sc4.getSpanId() == SpanId(3)); |
| 133 | |
| 134 | auto& sc5 = KJ_ASSERT_NONNULL(sc4.getParent()); |
| 135 | KJ_EXPECT(sc5.getTraceId() == initialTraceId); |
| 136 | KJ_EXPECT(sc5.getInvocationId() == initialInvocationId); |
| 137 | KJ_EXPECT(sc5.getSpanId() == SpanId(1)); |
| 138 | KJ_EXPECT(sc5.isTrigger()); |
| 139 | } |
| 140 | |
| 141 | KJ_TEST("InvocationSpanContext propagates traceFlags from trigger") { |
| 142 | setPredictableModeForTest(); |
| 143 | FakeEntropySource fakeEntropySource; |
| 144 | |
| 145 | // Trigger with traceFlags set — propagates to new invocation |
| 146 | auto trigger = InvocationSpanContext(TraceId(1, 2), TraceId(3, 4), SpanId(5), TraceFlags(0x01)); |
| 147 | KJ_EXPECT(KJ_ASSERT_NONNULL(trigger.getTraceFlags()) == TraceFlags(0x01)); |
| 148 | |
| 149 | auto sc = InvocationSpanContext::newForInvocation(trigger, fakeEntropySource); |
| 150 | KJ_EXPECT(KJ_ASSERT_NONNULL(sc.getTraceFlags()) == TraceFlags(0x01)); |
| 151 | |
| 152 | // No trigger — traceFlags is absent |
| 153 | auto sc2 = InvocationSpanContext::newForInvocation(kj::none, fakeEntropySource); |
| 154 | KJ_EXPECT(sc2.getTraceFlags() == kj::none); |
| 155 | |
| 156 | // newChild() propagates traceFlags |
| 157 | auto child = sc.newChild(); |
| 158 | KJ_EXPECT(KJ_ASSERT_NONNULL(child.getTraceFlags()) == TraceFlags(0x01)); |
| 159 | |
| 160 | auto child2 = sc2.newChild(); |
| 161 | KJ_EXPECT(child2.getTraceFlags() == kj::none); |
| 162 | } |
| 163 | |
| 164 | KJ_TEST("InvocationSpanContext traceFlags capnp round-trip and newChild propagation") { |
| 165 | setPredictableModeForTest(); |
| 166 | FakeEntropySource fakeEntropySource; |
| 167 | |
| 168 | // traceFlags=0x01 (sampled) survives round-trip and propagates through newChild |
| 169 | auto sampled = InvocationSpanContext(TraceId(1, 2), TraceId(3, 4), SpanId(5), TraceFlags(0x01)); |
| 170 | capnp::MallocMessageBuilder b1; |
| 171 | sampled.toCapnp(b1.initRoot<rpc::InvocationSpanContext>()); |
| 172 | auto rt1 = |
| 173 | KJ_ASSERT_NONNULL(InvocationSpanContext::fromCapnp(b1.getRoot<rpc::InvocationSpanContext>())); |
| 174 | KJ_EXPECT(KJ_ASSERT_NONNULL(rt1.getTraceFlags()) == TraceFlags(0x01)); |
| 175 | auto child1 = InvocationSpanContext::newForInvocation(rt1, fakeEntropySource); |
| 176 | KJ_EXPECT(KJ_ASSERT_NONNULL(child1.newChild().getTraceFlags()) == TraceFlags(0x01)); |
| 177 | |
| 178 | // traceFlags=0x00 (unsampled) is distinct from absent |
| 179 | auto unsampled = InvocationSpanContext(TraceId(1, 2), TraceId(3, 4), SpanId(5), TraceFlags(0x00)); |
| 180 | capnp::MallocMessageBuilder b2; |
| 181 | unsampled.toCapnp(b2.initRoot<rpc::InvocationSpanContext>()); |
| 182 | auto rt2 = |
| 183 | KJ_ASSERT_NONNULL(InvocationSpanContext::fromCapnp(b2.getRoot<rpc::InvocationSpanContext>())); |
| 184 | KJ_EXPECT(KJ_ASSERT_NONNULL(rt2.getTraceFlags()) == TraceFlags(0x00)); |
| 185 | auto child2 = InvocationSpanContext::newForInvocation(rt2, fakeEntropySource); |
| 186 | KJ_EXPECT(KJ_ASSERT_NONNULL(child2.newChild().getTraceFlags()) == TraceFlags(0x00)); |
| 187 | |
| 188 | // traceFlags absent stays absent |
| 189 | auto absent = InvocationSpanContext(TraceId(1, 2), TraceId(3, 4), SpanId(5), kj::none); |
| 190 | capnp::MallocMessageBuilder b3; |
| 191 | absent.toCapnp(b3.initRoot<rpc::InvocationSpanContext>()); |
| 192 | auto rt3 = |
| 193 | KJ_ASSERT_NONNULL(InvocationSpanContext::fromCapnp(b3.getRoot<rpc::InvocationSpanContext>())); |
| 194 | KJ_EXPECT(rt3.getTraceFlags() == kj::none); |
| 195 | auto child3 = InvocationSpanContext::newForInvocation(rt3, fakeEntropySource); |
| 196 | KJ_EXPECT(child3.newChild().getTraceFlags() == kj::none); |
| 197 | } |
| 198 | |
| 199 | KJ_TEST("SpanContext") { |
| 200 | setPredictableModeForTest(); |
| 201 | FakeEntropySource fakeEntropySource; |
| 202 | auto sc = |
| 203 | SpanContext(TraceId::fromEntropy(fakeEntropySource), SpanId::fromEntropy(fakeEntropySource)); |
| 204 | |
| 205 | // In predictable mode, TraceId::fromEntropy returns deterministic but |
| 206 | // call-distinct values; capture the IDs and verify capnp round-trip. |
| 207 | auto initialTraceId = sc.getTraceId(); |
| 208 | KJ_EXPECT(sc.getSpanId() == SpanId(1)); |
| 209 | KJ_EXPECT(sc.getTraceFlags() == kj::none); |
| 210 | |
| 211 | capnp::MallocMessageBuilder builder; |
| 212 | auto root = builder.initRoot<rpc::SpanContext>(); |
| 213 | sc.toCapnp(root); |
| 214 | |
| 215 | auto sc2 = SpanContext::fromCapnp(root.asReader()); |
| 216 | KJ_EXPECT(sc2.getTraceId() == initialTraceId); |
| 217 | KJ_EXPECT(sc2.getSpanId() == SpanId(1)); |
| 218 | KJ_EXPECT(sc2.getTraceFlags() == kj::none); |
| 219 | } |
| 220 | |
| 221 | KJ_TEST("SpanContext traceFlags preserved through capnp when set, absent when unset") { |
| 222 | auto sc = SpanContext(TraceId(1, 2), SpanId(3), TraceFlags(0x01)); |
| 223 | KJ_EXPECT(KJ_ASSERT_NONNULL(sc.getTraceFlags()) == TraceFlags(0x01)); |
| 224 | |
| 225 | capnp::MallocMessageBuilder builder; |
| 226 | auto root = builder.initRoot<rpc::SpanContext>(); |
| 227 | sc.toCapnp(root); |
| 228 | |
| 229 | auto sc2 = SpanContext::fromCapnp(root.asReader()); |
| 230 | KJ_EXPECT(sc2.getTraceId() == TraceId(1, 2)); |
| 231 | KJ_EXPECT(KJ_ASSERT_NONNULL(sc2.getSpanId()) == SpanId(3)); |
| 232 | KJ_EXPECT(KJ_ASSERT_NONNULL(sc2.getTraceFlags()) == TraceFlags(0x01)); |
| 233 | |
| 234 | auto sc3 = SpanContext(TraceId(4, 5), SpanId(6)); |
| 235 | capnp::MallocMessageBuilder builder2; |
| 236 | auto root2 = builder2.initRoot<rpc::SpanContext>(); |
| 237 | sc3.toCapnp(root2); |
| 238 | |
| 239 | auto sc4 = SpanContext::fromCapnp(root2.asReader()); |
| 240 | KJ_EXPECT(sc4.getTraceFlags() == kj::none); |
| 241 | } |
| 242 | |
| 243 | KJ_TEST("Read/Write FetchEventInfo works") { |
| 244 | capnp::MallocMessageBuilder builder; |
| 245 | auto fetchInfoBuilder = builder.initRoot<rpc::Trace::FetchEventInfo>(); |
| 246 | |
| 247 | kj::Vector<FetchEventInfo::Header> headers; |
| 248 | headers.add(FetchEventInfo::Header(kj::str("foo"), kj::str("bar"))); |
| 249 | |
| 250 | FetchEventInfo info( |
| 251 | kj::HttpMethod::GET, kj::str("https://example.com"), kj::str("{}"), headers.releaseAsArray()); |
| 252 | |
| 253 | info.copyTo(fetchInfoBuilder); |
| 254 | |
| 255 | auto reader = fetchInfoBuilder.asReader(); |
| 256 | |
| 257 | FetchEventInfo info2(reader); |
| 258 | KJ_ASSERT(info2.method == kj::HttpMethod::GET); |
| 259 | KJ_ASSERT(info2.url == "https://example.com"_kj); |
| 260 | KJ_ASSERT(info2.cfJson == "{}"_kj); |
| 261 | KJ_ASSERT(info2.headers.size() == 1); |
| 262 | KJ_ASSERT(info2.headers[0].name == "foo"_kj); |
| 263 | KJ_ASSERT(info2.headers[0].value == "bar"_kj); |
| 264 | |
| 265 | FetchEventInfo info3 = info.clone(); |
| 266 | KJ_ASSERT(info3.method == kj::HttpMethod::GET); |
| 267 | KJ_ASSERT(info3.url == "https://example.com"_kj); |
| 268 | KJ_ASSERT(info3.cfJson == "{}"_kj); |
| 269 | KJ_ASSERT(info3.headers.size() == 1); |
| 270 | KJ_ASSERT(info3.headers[0].name == "foo"_kj); |
| 271 | KJ_ASSERT(info3.headers[0].value == "bar"_kj); |
| 272 | } |
| 273 | |
| 274 | KJ_TEST("Read/Write JsRpcEventInfo works") { |
| 275 | capnp::MallocMessageBuilder builder; |
| 276 | auto jsRpcInfoBuilder = builder.initRoot<rpc::Trace::JsRpcEventInfo>(); |
| 277 | |
| 278 | JsRpcEventInfo info(kj::str("foo")); |
| 279 | |
| 280 | info.copyTo(jsRpcInfoBuilder); |
| 281 | |
| 282 | auto reader = jsRpcInfoBuilder.asReader(); |
| 283 | |
| 284 | JsRpcEventInfo info2(reader); |
| 285 | KJ_ASSERT(info2.methodName == "foo"_kj); |
| 286 | |
| 287 | JsRpcEventInfo info3 = info.clone(); |
| 288 | KJ_ASSERT(info3.methodName == "foo"_kj); |
| 289 | } |
| 290 | |
| 291 | KJ_TEST("Read/Write ScheduledEventInfo workers") { |
| 292 | capnp::MallocMessageBuilder builder; |
| 293 | auto infoBuilder = builder.initRoot<rpc::Trace::ScheduledEventInfo>(); |
| 294 | |
| 295 | ScheduledEventInfo info(1.2, kj::str("foo")); |
| 296 | |
| 297 | info.copyTo(infoBuilder); |
| 298 | |
| 299 | auto reader = infoBuilder.asReader(); |
| 300 | |
| 301 | ScheduledEventInfo info2(reader); |
| 302 | KJ_ASSERT(info2.scheduledTime == 1.2); |
| 303 | KJ_ASSERT(info2.cron == "foo"_kj); |
| 304 | |
| 305 | ScheduledEventInfo info3 = info.clone(); |
| 306 | KJ_ASSERT(info3.scheduledTime == 1.2); |
| 307 | KJ_ASSERT(info3.cron == "foo"_kj); |
| 308 | } |
| 309 | |
| 310 | KJ_TEST("Read/Write AlarmEventInfo works") { |
| 311 | capnp::MallocMessageBuilder builder; |
| 312 | auto infoBuilder = builder.initRoot<rpc::Trace::AlarmEventInfo>(); |
| 313 | |
| 314 | AlarmEventInfo info(kj::UNIX_EPOCH); |
| 315 | |
| 316 | info.copyTo(infoBuilder); |
| 317 | |
| 318 | auto reader = infoBuilder.asReader(); |
| 319 | |
| 320 | AlarmEventInfo info2(reader); |
| 321 | KJ_ASSERT(info.scheduledTime == info2.scheduledTime); |
| 322 | |
| 323 | AlarmEventInfo info3 = info.clone(); |
| 324 | KJ_ASSERT(info.scheduledTime == info3.scheduledTime); |
| 325 | } |
| 326 | |
| 327 | KJ_TEST("Read/Write QueueEventInfo works") { |
| 328 | capnp::MallocMessageBuilder builder; |
| 329 | auto infoBuilder = builder.initRoot<rpc::Trace::QueueEventInfo>(); |
| 330 | |
| 331 | QueueEventInfo info(kj::str("foo"), 1); |
| 332 | |
| 333 | info.copyTo(infoBuilder); |
| 334 | |
| 335 | auto reader = infoBuilder.asReader(); |
| 336 | |
| 337 | QueueEventInfo info2(reader); |
| 338 | KJ_ASSERT(info2.queueName == "foo"_kj); |
| 339 | KJ_ASSERT(info2.batchSize == 1); |
| 340 | |
| 341 | QueueEventInfo info3 = info.clone(); |
| 342 | KJ_ASSERT(info2.queueName == "foo"_kj); |
| 343 | KJ_ASSERT(info2.batchSize == 1); |
| 344 | } |
| 345 | |
| 346 | KJ_TEST("Read/Write EmailEventInfo works") { |
| 347 | capnp::MallocMessageBuilder builder; |
| 348 | auto infoBuilder = builder.initRoot<rpc::Trace::EmailEventInfo>(); |
| 349 | |
| 350 | EmailEventInfo info(kj::str("foo"), kj::str("bar"), 1); |
| 351 | info.copyTo(infoBuilder); |
| 352 | |
| 353 | auto reader = infoBuilder.asReader(); |
| 354 | |
| 355 | EmailEventInfo info2(reader); |
| 356 | KJ_ASSERT(info2.mailFrom == "foo"_kj); |
| 357 | KJ_ASSERT(info2.rcptTo == "bar"_kj); |
| 358 | KJ_ASSERT(info2.rawSize == 1); |
| 359 | |
| 360 | EmailEventInfo info3 = info.clone(); |
| 361 | KJ_ASSERT(info3.mailFrom == "foo"_kj); |
| 362 | KJ_ASSERT(info3.rcptTo == "bar"_kj); |
| 363 | KJ_ASSERT(info3.rawSize == 1); |
| 364 | } |
| 365 | |
| 366 | KJ_TEST("Read/Write TraceEventInfo works") { |
| 367 | capnp::MallocMessageBuilder builder; |
| 368 | auto infoBuilder = builder.initRoot<rpc::Trace::TraceEventInfo>(); |
| 369 | |
| 370 | kj::Vector<kj::Own<Trace>> items(1); |
| 371 | items.add(kj::heap<Trace>(kj::none, kj::str("foo"), kj::none, kj::none, kj::none, |
| 372 | kj::Array<kj::String>(), kj::none, ExecutionModel::STATELESS)); |
| 373 | |
| 374 | TraceEventInfo info(items.asPtr()); |
| 375 | info.copyTo(infoBuilder); |
| 376 | |
| 377 | auto reader = infoBuilder.asReader(); |
| 378 | |
| 379 | TraceEventInfo info2(reader); |
| 380 | KJ_ASSERT(info2.traces.size() == 1); |
| 381 | KJ_ASSERT(KJ_ASSERT_NONNULL(info2.traces[0].scriptName) == "foo"_kj); |
| 382 | |
| 383 | TraceEventInfo info3 = info.clone(); |
| 384 | KJ_ASSERT(info2.traces.size() == 1); |
| 385 | KJ_ASSERT(KJ_ASSERT_NONNULL(info2.traces[0].scriptName) == "foo"_kj); |
| 386 | } |
| 387 | |
| 388 | KJ_TEST("Read/Write HibernatableWebSocketEventInfo works") { |
| 389 | capnp::MallocMessageBuilder builder; |
| 390 | auto infoBuilder = builder.initRoot<rpc::Trace::HibernatableWebSocketEventInfo>(); |
| 391 | |
| 392 | HibernatableWebSocketEventInfo info(HibernatableWebSocketEventInfo::Message{}); |
| 393 | info.copyTo(infoBuilder); |
| 394 | |
| 395 | auto reader = infoBuilder.asReader(); |
| 396 | |
| 397 | HibernatableWebSocketEventInfo info2(reader); |
| 398 | KJ_ASSERT(info2.type.is<HibernatableWebSocketEventInfo::Message>()); |
| 399 | |
| 400 | HibernatableWebSocketEventInfo info3 = info.clone(); |
| 401 | KJ_ASSERT(info3.type.is<HibernatableWebSocketEventInfo::Message>()); |
| 402 | } |
| 403 | |
| 404 | KJ_TEST("Read/Write FetchResponseInfo works") { |
| 405 | capnp::MallocMessageBuilder builder; |
| 406 | auto infoBuilder = builder.initRoot<rpc::Trace::FetchResponseInfo>(); |
| 407 | |
| 408 | FetchResponseInfo info(123); |
| 409 | info.copyTo(infoBuilder); |
| 410 | |
| 411 | auto reader = infoBuilder.asReader(); |
| 412 | FetchResponseInfo info2(reader); |
| 413 | KJ_ASSERT(info2.statusCode == 123); |
| 414 | |
| 415 | FetchResponseInfo info3 = info.clone(); |
| 416 | KJ_ASSERT(info3.statusCode == 123); |
| 417 | } |
| 418 | |
| 419 | KJ_TEST("Read/Write DiagnosticChannelEvent works") { |
| 420 | capnp::MallocMessageBuilder builder; |
| 421 | auto infoBuilder = builder.initRoot<rpc::Trace::DiagnosticChannelEvent>(); |
| 422 | |
| 423 | DiagnosticChannelEvent info(kj::UNIX_EPOCH, kj::str("foo"), kj::Array<kj::byte>()); |
| 424 | info.copyTo(infoBuilder); |
| 425 | |
| 426 | auto reader = infoBuilder.asReader(); |
| 427 | |
| 428 | DiagnosticChannelEvent info2(reader); |
| 429 | KJ_ASSERT(info2.timestamp == info.timestamp); |
| 430 | KJ_ASSERT(info2.channel == "foo"_kj); |
| 431 | KJ_ASSERT(info2.message.size() == 0); |
| 432 | |
| 433 | DiagnosticChannelEvent info3 = info.clone(); |
| 434 | KJ_ASSERT(info3.timestamp == info.timestamp); |
| 435 | KJ_ASSERT(info3.channel == "foo"_kj); |
| 436 | KJ_ASSERT(info3.message.size() == 0); |
| 437 | } |
| 438 | |
| 439 | KJ_TEST("Read/Write Log works") { |
| 440 | capnp::MallocMessageBuilder builder; |
| 441 | auto infoBuilder = builder.initRoot<rpc::Trace::Log>(); |
| 442 | |
| 443 | Log info(kj::UNIX_EPOCH, LogLevel::INFO, kj::str("foo")); |
| 444 | info.copyTo(infoBuilder); |
| 445 | |
| 446 | auto reader = infoBuilder.asReader(); |
| 447 | Log info2(reader); |
| 448 | KJ_ASSERT(info.timestamp == info2.timestamp); |
| 449 | KJ_ASSERT(info2.logLevel == LogLevel::INFO); |
| 450 | KJ_ASSERT(info2.message == "foo"_kj); |
| 451 | |
| 452 | Log info3 = info.clone(); |
| 453 | KJ_ASSERT(info.timestamp == info3.timestamp); |
| 454 | KJ_ASSERT(info3.logLevel == LogLevel::INFO); |
| 455 | KJ_ASSERT(info3.message == "foo"_kj); |
| 456 | } |
| 457 | |
| 458 | KJ_TEST("Read/Write Exception works") { |
| 459 | capnp::MallocMessageBuilder builder; |
| 460 | auto infoBuilder = builder.initRoot<rpc::Trace::Exception>(); |
| 461 | |
| 462 | Exception info(kj::UNIX_EPOCH, kj::str("foo"), kj::str("bar"), kj::none); |
| 463 | info.copyTo(infoBuilder); |
| 464 | |
| 465 | auto reader = infoBuilder.asReader(); |
| 466 | Exception info2(reader); |
| 467 | KJ_ASSERT(info.timestamp == info2.timestamp); |
| 468 | KJ_ASSERT(info2.name == "foo"_kj); |
| 469 | KJ_ASSERT(info2.message == "bar"_kj); |
| 470 | KJ_ASSERT(info2.stack == kj::none); |
| 471 | |
| 472 | Exception info3 = info.clone(); |
| 473 | KJ_ASSERT(info.timestamp == info3.timestamp); |
| 474 | KJ_ASSERT(info3.name == "foo"_kj); |
| 475 | KJ_ASSERT(info3.message == "bar"_kj); |
| 476 | KJ_ASSERT(info3.stack == kj::none); |
| 477 | } |
| 478 | |
| 479 | KJ_TEST("Read/Write StreamDiagnosticsEvent works") { |
| 480 | capnp::MallocMessageBuilder builder; |
| 481 | auto infoBuilder = builder.initRoot<rpc::Trace::StreamDiagnosticsEvent>(); |
| 482 | |
| 483 | StreamDiagnosticsEvent info(42); |
| 484 | info.copyTo(infoBuilder); |
| 485 | |
| 486 | auto reader = infoBuilder.asReader(); |
| 487 | StreamDiagnosticsEvent info2(reader); |
| 488 | KJ_ASSERT(info2.droppedEventsCount == 42); |
| 489 | |
| 490 | StreamDiagnosticsEvent info3 = info.clone(); |
| 491 | KJ_ASSERT(info3.droppedEventsCount == 42); |
| 492 | } |
| 493 | |
| 494 | KJ_TEST("Read/Write Attribute works") { |
| 495 | capnp::MallocMessageBuilder builder; |
| 496 | auto infoBuilder = builder.initRoot<rpc::Trace::Attribute>(); |
| 497 | |
| 498 | Attribute attr("foo"_kjc, {123.0, 321.2}); |
| 499 | attr.copyTo(infoBuilder); |
| 500 | |
| 501 | auto reader = infoBuilder.asReader(); |
| 502 | Attribute info2(reader); |
| 503 | KJ_ASSERT(info2.name == "foo"_kj); |
| 504 | KJ_ASSERT(KJ_ASSERT_NONNULL(info2.value[0].tryGet<double>()) == 123.0); |
| 505 | KJ_ASSERT(KJ_ASSERT_NONNULL(info2.value[1].tryGet<double>()) == 321.2); |
| 506 | } |
| 507 | |
| 508 | KJ_TEST("Read/Write Return works") { |
| 509 | capnp::MallocMessageBuilder builder; |
| 510 | auto infoBuilder = builder.initRoot<rpc::Trace::Return>(); |
| 511 | |
| 512 | FetchResponseInfo fetchInfo(123); |
| 513 | Return info(kj::mv(fetchInfo)); |
| 514 | info.copyTo(infoBuilder); |
| 515 | |
| 516 | auto reader = infoBuilder.asReader(); |
| 517 | Return info2(reader); |
| 518 | auto& fetchInfo2 = KJ_ASSERT_NONNULL(info2.info); |
| 519 | KJ_ASSERT(fetchInfo2.statusCode == 123); |
| 520 | |
| 521 | Return info3 = info.clone(); |
| 522 | auto& fetchInfo3 = KJ_ASSERT_NONNULL(info3.info); |
| 523 | KJ_ASSERT(fetchInfo3.statusCode == 123); |
| 524 | } |
| 525 | |
| 526 | KJ_TEST("Read/Write SpanOpen works") { |
| 527 | capnp::MallocMessageBuilder builder; |
| 528 | auto infoBuilder = builder.initRoot<rpc::Trace::SpanOpen>(); |
| 529 | |
| 530 | SpanOpen info(0x2a2a2a2a2a2a2a2a, "foo"_kjc, kj::none); |
| 531 | info.copyTo(infoBuilder); |
| 532 | |
| 533 | auto reader = infoBuilder.asReader(); |
| 534 | SpanOpen info2(reader); |
| 535 | KJ_ASSERT(info2.operationName == "foo"_kj); |
| 536 | KJ_ASSERT(info2.info == kj::none); |
| 537 | |
| 538 | SpanOpen info3 = info.clone(); |
| 539 | KJ_ASSERT(info3.operationName == "foo"_kj); |
| 540 | KJ_ASSERT(info3.info == kj::none); |
| 541 | } |
| 542 | |
| 543 | KJ_TEST("Read/Write SpanClose works") { |
| 544 | capnp::MallocMessageBuilder builder; |
| 545 | auto infoBuilder = builder.initRoot<rpc::Trace::SpanClose>(); |
| 546 | |
| 547 | SpanClose info(EventOutcome::EXCEPTION); |
| 548 | info.copyTo(infoBuilder); |
| 549 | |
| 550 | auto reader = infoBuilder.asReader(); |
| 551 | |
| 552 | SpanClose info2(reader); |
| 553 | KJ_ASSERT(info2.outcome == EventOutcome::EXCEPTION); |
| 554 | |
| 555 | SpanClose info3 = info.clone(); |
| 556 | KJ_ASSERT(info3.outcome == EventOutcome::EXCEPTION); |
| 557 | } |
| 558 | |
| 559 | KJ_TEST("Read/Write Onset works") { |
| 560 | capnp::MallocMessageBuilder builder; |
| 561 | auto infoBuilder = builder.initRoot<rpc::Trace::Onset>(); |
| 562 | |
| 563 | FetchEventInfo fetchInfo( |
| 564 | kj::HttpMethod::GET, kj::str("https://example.com"), kj::str("{}"), nullptr); |
| 565 | |
| 566 | Onset info(staticSpanId, Onset::Info(kj::mv(fetchInfo)), |
| 567 | { |
| 568 | .scriptName = kj::str("foo"), |
| 569 | .preview = TracePreview(kj::str("63bafce9179948688866bb22268eb1c6"), |
| 570 | kj::str("feature-my-branch"), kj::str("feature/my-branch")), |
| 571 | }, |
| 572 | nullptr); |
| 573 | info.copyTo(infoBuilder); |
| 574 | |
| 575 | auto reader = infoBuilder.asReader(); |
| 576 | Onset info2(reader); |
| 577 | FetchEventInfo& fetchInfo2 = KJ_ASSERT_NONNULL(info2.info.tryGet<FetchEventInfo>()); |
| 578 | KJ_ASSERT(fetchInfo2.method == kj::HttpMethod::GET); |
| 579 | KJ_ASSERT(fetchInfo2.url == "https://example.com"_kj); |
| 580 | KJ_ASSERT(info2.workerInfo.executionModel == ExecutionModel::STATELESS); |
| 581 | auto& preview2 = KJ_ASSERT_NONNULL(info2.workerInfo.preview); |
| 582 | KJ_ASSERT(preview2.id == "63bafce9179948688866bb22268eb1c6"_kj); |
| 583 | KJ_ASSERT(preview2.slug == "feature-my-branch"_kj); |
| 584 | KJ_ASSERT(preview2.name == "feature/my-branch"_kj); |
| 585 | |
| 586 | Onset info3 = info.clone(); |
| 587 | FetchEventInfo& fetchInfo3 = KJ_ASSERT_NONNULL(info3.info.tryGet<FetchEventInfo>()); |
| 588 | KJ_ASSERT(fetchInfo3.method == kj::HttpMethod::GET); |
| 589 | KJ_ASSERT(fetchInfo3.url == "https://example.com"_kj); |
| 590 | KJ_ASSERT(info3.workerInfo.executionModel == ExecutionModel::STATELESS); |
| 591 | auto& preview3 = KJ_ASSERT_NONNULL(info3.workerInfo.preview); |
| 592 | KJ_ASSERT(preview3.id == "63bafce9179948688866bb22268eb1c6"_kj); |
| 593 | KJ_ASSERT(preview3.slug == "feature-my-branch"_kj); |
| 594 | KJ_ASSERT(preview3.name == "feature/my-branch"_kj); |
| 595 | } |
| 596 | |
| 597 | KJ_TEST("Read/Write Outcome works") { |
| 598 | capnp::MallocMessageBuilder builder; |
| 599 | auto infoBuilder = builder.initRoot<rpc::Trace::Outcome>(); |
| 600 | |
| 601 | Outcome info(EventOutcome::EXCEPTION, 1 * kj::MILLISECONDS, 2 * kj::MILLISECONDS); |
| 602 | info.copyTo(infoBuilder); |
| 603 | |
| 604 | auto reader = infoBuilder.asReader(); |
| 605 | Outcome info2(reader); |
| 606 | KJ_ASSERT(info2.outcome == EventOutcome::EXCEPTION); |
| 607 | KJ_ASSERT(info2.wallTime == 2 * kj::MILLISECONDS); |
| 608 | KJ_ASSERT(info2.cpuTime == 1 * kj::MILLISECONDS); |
| 609 | |
| 610 | Outcome info3 = info.clone(); |
| 611 | KJ_ASSERT(info3.outcome == EventOutcome::EXCEPTION); |
| 612 | KJ_ASSERT(info3.wallTime == 2 * kj::MILLISECONDS); |
| 613 | KJ_ASSERT(info3.cpuTime == 1 * kj::MILLISECONDS); |
| 614 | } |
| 615 | |
| 616 | KJ_TEST("Read/Write TailEvent works") { |
| 617 | capnp::MallocMessageBuilder builder; |
| 618 | auto infoBuilder = builder.initRoot<rpc::Trace::TailEvent>(); |
| 619 | |
| 620 | auto context = SpanContext(TraceId(0, 0), {staticSpanId}); |
| 621 | Log log(kj::UNIX_EPOCH, LogLevel::INFO, kj::str("foo")); |
| 622 | auto invocationId = TraceId(0, 0); |
| 623 | TailEvent info( |
| 624 | context.getTraceId(), invocationId, context.getSpanId(), kj::UNIX_EPOCH, 0, kj::mv(log)); |
| 625 | info.copyTo(infoBuilder); |
| 626 | |
| 627 | auto reader = infoBuilder.asReader(); |
| 628 | |
| 629 | TailEvent info2(reader); |
| 630 | KJ_ASSERT(info2.timestamp == kj::UNIX_EPOCH); |
| 631 | KJ_ASSERT(info2.sequence == 0); |
| 632 | KJ_ASSERT(info2.invocationId == invocationId); |
| 633 | KJ_ASSERT(info2.spanContext == context); |
| 634 | |
| 635 | auto& log2 = KJ_ASSERT_NONNULL(info2.event.tryGet<Log>()); |
| 636 | KJ_ASSERT(log2.timestamp == kj::UNIX_EPOCH); |
| 637 | KJ_ASSERT(log2.logLevel == LogLevel::INFO); |
| 638 | KJ_ASSERT(log2.message == "foo"_kj); |
| 639 | |
| 640 | TailEvent info3 = info.clone(); |
| 641 | KJ_ASSERT(info3.timestamp == kj::UNIX_EPOCH); |
| 642 | KJ_ASSERT(info3.sequence == 0); |
| 643 | KJ_ASSERT(info3.invocationId == invocationId); |
| 644 | KJ_ASSERT(info3.spanContext == context); |
| 645 | |
| 646 | auto& log3 = KJ_ASSERT_NONNULL(info3.event.tryGet<Log>()); |
| 647 | KJ_ASSERT(log3.timestamp == kj::UNIX_EPOCH); |
| 648 | KJ_ASSERT(log3.logLevel == LogLevel::INFO); |
| 649 | KJ_ASSERT(log3.message == "foo"_kj); |
| 650 | } |
| 651 | |
| 652 | KJ_TEST("Read/Write TailEvent with Multiple Attributes") { |
| 653 | capnp::MallocMessageBuilder builder; |
| 654 | auto infoBuilder = builder.initRoot<rpc::Trace::TailEvent>(); |
| 655 | |
| 656 | TraceId traceId(0, 0); |
| 657 | auto context = SpanContext(traceId, {staticSpanId}); |
| 658 | |
| 659 | // An attribute event can have one or more Attributes specified. |
| 660 | kj::Vector<Attribute> attrs(2); |
| 661 | attrs.add(Attribute("foo"_kjc, true)); |
| 662 | attrs.add(Attribute("bar"_kjc, static_cast<int64_t>(123))); |
| 663 | |
| 664 | TailEvent info(kj::mv(context), traceId, kj::UNIX_EPOCH, 0, attrs.releaseAsArray()); |
| 665 | info.copyTo(infoBuilder); |
| 666 | |
| 667 | TailEvent info2(infoBuilder.asReader()); |
| 668 | auto& attrs2 = KJ_ASSERT_NONNULL(info2.event.tryGet<kj::Array<Attribute>>()); |
| 669 | KJ_ASSERT(attrs2.size() == 2); |
| 670 | |
| 671 | KJ_ASSERT(attrs2[0].name == "foo"_kj); |
| 672 | KJ_ASSERT(attrs2[1].name == "bar"_kj); |
| 673 | } |
| 674 | |
| 675 | KJ_TEST("Trace with Preview") { |
| 676 | auto trace = kj::refcounted<Trace>(kj::str("test-stable-id"), kj::str("test-script"), |
| 677 | kj::none, // scriptVersion |
| 678 | kj::str("test-namespace"), kj::str("test-script-id"), |
| 679 | kj::Array<kj::String>(), // scriptTags |
| 680 | kj::str("test-entrypoint"), ExecutionModel::STATELESS, |
| 681 | kj::none, // durableObjectId |
| 682 | TracePreview(kj::str("63bafce9179948688866bb22268eb1c6"), kj::str("feature-my-branch"), |
| 683 | kj::str("feature/my-branch"))); |
| 684 | |
| 685 | capnp::MallocMessageBuilder builder; |
| 686 | auto traceBuilder = builder.initRoot<rpc::Trace>(); |
| 687 | trace->copyTo(traceBuilder); |
| 688 | |
| 689 | auto trace2 = kj::refcounted<Trace>(traceBuilder.asReader()); |
| 690 | auto& preview = KJ_ASSERT_NONNULL(trace2->preview); |
| 691 | KJ_ASSERT(preview.id == "63bafce9179948688866bb22268eb1c6"_kj); |
| 692 | KJ_ASSERT(preview.slug == "feature-my-branch"_kj); |
| 693 | KJ_ASSERT(preview.name == "feature/my-branch"_kj); |
| 694 | } |
| 695 | |
| 696 | KJ_TEST("Trace with Durable Object ID") { |
| 697 | auto trace = kj::refcounted<Trace>(kj::str("test-stable-id"), kj::str("test-script"), |
| 698 | kj::none, // scriptVersion |
| 699 | kj::str("test-namespace"), kj::str("test-script-id"), |
| 700 | kj::Array<kj::String>(), // scriptTags |
| 701 | kj::str("test-entrypoint"), ExecutionModel::DURABLE_OBJECT, |
| 702 | kj::str("abc123def456") // durableObjectId |
| 703 | ); |
| 704 | |
| 705 | capnp::MallocMessageBuilder builder; |
| 706 | auto traceBuilder = builder.initRoot<rpc::Trace>(); |
| 707 | trace->copyTo(traceBuilder); |
| 708 | |
| 709 | auto trace2 = kj::refcounted<Trace>(traceBuilder.asReader()); |
| 710 | KJ_ASSERT(KJ_REQUIRE_NONNULL(trace2->durableObjectId) == "abc123def456"_kj); |
| 711 | } |
| 712 | KJ_TEST("SpanContext::tryFromTraceparent valid") { |
| 713 | auto result = KJ_ASSERT_NONNULL(SpanContext::tryFromTraceparent( |
| 714 | "00-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-01"_kj)); |
| 715 | KJ_EXPECT(result.getTraceId() == TraceId(0x9900aabbccddeeff, 0x1122334455667788)); |
| 716 | KJ_EXPECT(KJ_ASSERT_NONNULL(result.getSpanId()) == SpanId(0xa1b2c3d4e5f60718)); |
| 717 | KJ_EXPECT(KJ_ASSERT_NONNULL(result.getTraceFlags()) == 0x01); |
| 718 | } |
| 719 | |
| 720 | KJ_TEST("SpanContext::tryFromTraceparent sampled with extra flags") { |
| 721 | auto result = KJ_ASSERT_NONNULL(SpanContext::tryFromTraceparent( |
| 722 | "00-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-03"_kj)); |
| 723 | KJ_EXPECT(result.getTraceId() == TraceId(0x9900aabbccddeeff, 0x1122334455667788)); |
| 724 | KJ_EXPECT(KJ_ASSERT_NONNULL(result.getSpanId()) == SpanId(0xa1b2c3d4e5f60718)); |
| 725 | KJ_EXPECT(KJ_ASSERT_NONNULL(result.getTraceFlags()) == 0x03); |
| 726 | } |
| 727 | |
| 728 | KJ_TEST("SpanContext::tryFromTraceparent rejects invalid inputs") { |
| 729 | // Empty |
| 730 | KJ_EXPECT(SpanContext::tryFromTraceparent(""_kj) == kj::none); |
| 731 | // Degenerate |
| 732 | KJ_EXPECT(SpanContext::tryFromTraceparent("---"_kj) == kj::none); |
| 733 | KJ_EXPECT(SpanContext::tryFromTraceparent("00-1-1-00"_kj) == kj::none); |
| 734 | |
| 735 | // Wrong total length (too short, too long) |
| 736 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 737 | "00-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-0"_kj) == kj::none); |
| 738 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 739 | "00-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-012"_kj) == kj::none); |
| 740 | |
| 741 | // Wrong field sizes: version too short |
| 742 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 743 | "0-11223344556677889900aabbccddeeff0-a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 744 | // Wrong field sizes: trace-id too long |
| 745 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 746 | "00-11223344556677889900aabbccddeeff0-1b2c3d4e5f60718-01"_kj) == kj::none); |
| 747 | // Wrong field sizes: trace-id too short |
| 748 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 749 | "00-11223344556677889900aabbccddee-a1b2c3d4e5f6071800-01"_kj) == kj::none); |
| 750 | // Wrong field sizes: parent-id too long |
| 751 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 752 | "00-1223344556677889900aabbccddeeff-a1b2c3d4e5f607180-01"_kj) == kj::none); |
| 753 | // Wrong field sizes: parent-id too short |
| 754 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 755 | "00-112233445566778899900aabbccddeeff-1b2c3d4e5f6071-01"_kj) == kj::none); |
| 756 | // Empty fields |
| 757 | KJ_EXPECT(SpanContext::tryFromTraceparent("00--a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 758 | KJ_EXPECT( |
| 759 | SpanContext::tryFromTraceparent("00-11223344556677889900aabbccddeeff--01"_kj) == kj::none); |
| 760 | |
| 761 | // Bad hex |
| 762 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 763 | "0g-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 764 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 765 | "00-x1223344556677889900aabbccddeeff-a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 766 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 767 | "00-11223344556677889900aabbccddeeff-a1b2c3d4e5f6071x-01"_kj) == kj::none); |
| 768 | |
| 769 | // Unsupported version |
| 770 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 771 | "01-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 772 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 773 | "ff-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 774 | |
| 775 | // All-zero trace-id |
| 776 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 777 | "00-00000000000000000000000000000000-a1b2c3d4e5f60718-01"_kj) == kj::none); |
| 778 | |
| 779 | // All-zero parent-id |
| 780 | KJ_EXPECT(SpanContext::tryFromTraceparent( |
| 781 | "00-11223344556677889900aabbccddeeff-0000000000000000-01"_kj) == kj::none); |
| 782 | |
| 783 | // Unsampled |
| 784 | auto unsampled = KJ_ASSERT_NONNULL(SpanContext::tryFromTraceparent( |
| 785 | "00-11223344556677889900aabbccddeeff-a1b2c3d4e5f60718-00"_kj)); |
| 786 | KJ_EXPECT(unsampled.getTraceId() == TraceId(0x9900aabbccddeeff, 0x1122334455667788)); |
| 787 | KJ_EXPECT(KJ_ASSERT_NONNULL(unsampled.getSpanId()) == SpanId(0xa1b2c3d4e5f60718)); |
| 788 | KJ_EXPECT(KJ_ASSERT_NONNULL(unsampled.getTraceFlags()) == 0x00); |
| 789 | } |
| 790 | |
| 791 | } // namespace |
| 792 | } // namespace workerd::tracing |