diff --git a/Gemfile b/Gemfile index d9eda6be..f8ad03d9 100644 --- a/Gemfile +++ b/Gemfile @@ -10,14 +10,16 @@ gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" + gem "standard", "~> 1.31.0" gem "yard" diff --git a/LICENSE-3rdparty.csv b/LICENSE-3rdparty.csv index 049eb1b2..0abe6b1b 100644 --- a/LICENSE-3rdparty.csv +++ b/LICENSE-3rdparty.csv @@ -1,2 +1,3 @@ Component,Origin,License,Copyright dd-trace-rb,https://github.com/DataDog/dd-trace-rb,Apache 2.0,"Copyright 2016-Present Datadog, Inc." +msgpack,https://rubygems.org/gems/msgpack,Apache-2.0,"Copyright (c) 2008-2015 Sadayuki Furuhashi" diff --git a/README.md b/README.md index 813430af..0a28c209 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,70 @@ end | `service_name` | Service name used for `cucumber` instrumentation. | `'cucumber'` | | `operation_name` | Operation name used for `cucumber` instrumentation. Useful if you want rename automatic trace metrics e.g. `trace.#{operation_name}.errors`. | `'cucumber.test'` | +## Agentless mode + +If you are using a cloud CI provider without access to the underlying worker nodes, such as GitHub Actions or CircleCI, configure the library to use the Agentless mode. For this, set the following environment variables: +`DD_CIVISIBILITY_AGENTLESS_ENABLED=true (Required)` and `DD_API_KEY=your_secret_api_key (Required)`. + +Additionally, configure which [Datadog site](https://docs.datadoghq.com/getting_started/site/) you want to send data to: +`DD_SITE=your.datadoghq.com` (datadoghq.com by default). + +Agentless mode can also be enabled via `Datadog.configure` (but don't forget to set DD_API_KEY environment variable): + +```ruby +Datadog.configure { |c| c.ci.agentless_mode_enabled = true } +``` + +## Additional configuration + +### Add tracing instrumentations + +It can be useful to have rich tracing information about your tests that includes time spent performing database operations +or other external calls like here: + +![Test trace with redis instrumented](./docs/screenshots/test-trace-with-redis.png) + +In order to achieve this you can configure ddtrace instrumentations in your configure block: + +```ruby +Datadog.configure do |c| + # ... ci configs and instrumentation here ... + c.instrument :redis + c.instrument :pg +end +``` + +...or enable auto instrumentation in your test_helper/spec_helper: + +```ruby +require "ddtrace/auto_instrument" +``` + +Note: in CI mode these traces are going to be submitted to CI Visibility, +they will **not** show up in Datadog APM. + +For the full list of available instrumentations see [ddtrace documentation](https://github.com/DataDog/dd-trace-rb/blob/master/docs/GettingStarted.md) + +### Disabling startup logs + +Startup logs produce a report of tracing state when the application is initially configured. +These logs are activated by default in test mode, if you don't want them you can disable this +via `diagnostics.startup_logs.enabled = false` or `DD_TRACE_STARTUP_LOGS=0`. + +```ruby +Datadog.configure { |c| c.diagnostics.startup_logs.enabled = false } +``` + +### Enabling debug mode + +Switching the library into debug mode will produce verbose, detailed logs about tracing activity, including any suppressed errors. This output can be helpful in identifying errors, confirming trace output, or catching HTTP transport issues. + +You can enable this via `diagnostics.debug = true` or `DD_TRACE_DEBUG=1`. + +```ruby +Datadog.configure { |c| c.diagnostics.debug = true } +``` + ## Contributing See [development guide](/docs/DevelopmentGuide.md), [static typing guide](docs/StaticTypingGuide.md) and [contributing guidelines](/CONTRIBUTING.md). diff --git a/Steepfile b/Steepfile index 831f6f4a..ad68d9c9 100644 --- a/Steepfile +++ b/Steepfile @@ -4,6 +4,7 @@ target :lib do check "lib" ignore "lib/datadog/ci/configuration/settings.rb" + ignore "lib/datadog/ci/transport/gzip.rb" library "pathname" library "json" @@ -18,4 +19,5 @@ target :lib do library "open3" library "rspec" library "cucumber" + library "msgpack" end diff --git a/datadog-ci.gemspec b/datadog-ci.gemspec index c7432083..33bbcafe 100644 --- a/datadog-ci.gemspec +++ b/datadog-ci.gemspec @@ -39,4 +39,6 @@ Gem::Specification.new do |spec| ]].select { |fn| File.file?(fn) } # We don't want directories, only files spec.require_paths = ["lib"] + + spec.add_dependency "msgpack" end diff --git a/docs/screenshots/test-trace-with-redis.png b/docs/screenshots/test-trace-with-redis.png new file mode 100644 index 00000000..a9f1be95 Binary files /dev/null and b/docs/screenshots/test-trace-with-redis.png differ diff --git a/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile b/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile index e333024b..be1f65fc 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile +++ b/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile.lock b/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile.lock index e081c6b9..2fdacb92 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_cucumber_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -126,6 +127,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -149,6 +151,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile b/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile index 118961f1..5e26524b 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile +++ b/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile.lock b/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile.lock index 237113d3..8f63e799 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_cucumber_4.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -156,6 +157,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6-java) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -181,6 +183,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile b/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile index 9050053c..bd99a42e 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile +++ b/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile.lock b/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile.lock index 12bc41d9..526814ee 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_cucumber_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -156,6 +157,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6-java) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -181,6 +183,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile b/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile index 75eb8323..9b5a94ad 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile +++ b/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile.lock b/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile.lock index 3bef169e..fe990d8e 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_cucumber_6.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -160,6 +161,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6-java) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -185,6 +187,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile b/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile index b425e539..310fd418 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile +++ b/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile.lock b/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile.lock index 6da1c070..2da09f58 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_cucumber_7.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -142,6 +143,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -165,6 +167,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile b/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile index 1f065ae6..9dcc7fc6 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile +++ b/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile.lock b/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile.lock index ebf38b35..9cc64944 100644 --- a/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_cucumber_8.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -136,6 +137,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -159,6 +161,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_minitest_5.gemfile b/gemfiles/jruby_9.4.0.0_minitest_5.gemfile index b1f7cdf8..d03bd893 100644 --- a/gemfiles/jruby_9.4.0.0_minitest_5.gemfile +++ b/gemfiles/jruby_9.4.0.0_minitest_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_minitest_5.gemfile.lock b/gemfiles/jruby_9.4.0.0_minitest_5.gemfile.lock index d8d27c91..6fadfb6f 100644 --- a/gemfiles/jruby_9.4.0.0_minitest_5.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_minitest_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -106,6 +107,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -129,6 +131,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/jruby_9.4.0.0_rspec_3.gemfile b/gemfiles/jruby_9.4.0.0_rspec_3.gemfile index d59f8d57..8c8e2b81 100644 --- a/gemfiles/jruby_9.4.0.0_rspec_3.gemfile +++ b/gemfiles/jruby_9.4.0.0_rspec_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec", "~> 3" gem "os" gem "climate_control" +gem "rspec", "~> 3" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/jruby_9.4.0.0_rspec_3.gemfile.lock b/gemfiles/jruby_9.4.0.0_rspec_3.gemfile.lock index d320e99b..5c78a90a 100644 --- a/gemfiles/jruby_9.4.0.0_rspec_3.gemfile.lock +++ b/gemfiles/jruby_9.4.0.0_rspec_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -105,6 +106,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -127,6 +129,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_cucumber_3.gemfile b/gemfiles/ruby_2.7.6_cucumber_3.gemfile index e333024b..be1f65fc 100644 --- a/gemfiles/ruby_2.7.6_cucumber_3.gemfile +++ b/gemfiles/ruby_2.7.6_cucumber_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_cucumber_3.gemfile.lock b/gemfiles/ruby_2.7.6_cucumber_3.gemfile.lock index f8d057d9..750d41ea 100644 --- a/gemfiles/ruby_2.7.6_cucumber_3.gemfile.lock +++ b/gemfiles/ruby_2.7.6_cucumber_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -123,6 +124,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -146,6 +148,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_cucumber_4.gemfile b/gemfiles/ruby_2.7.6_cucumber_4.gemfile index 118961f1..5e26524b 100644 --- a/gemfiles/ruby_2.7.6_cucumber_4.gemfile +++ b/gemfiles/ruby_2.7.6_cucumber_4.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_cucumber_4.gemfile.lock b/gemfiles/ruby_2.7.6_cucumber_4.gemfile.lock index 3fc6c2f4..b755182f 100644 --- a/gemfiles/ruby_2.7.6_cucumber_4.gemfile.lock +++ b/gemfiles/ruby_2.7.6_cucumber_4.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_cucumber_5.gemfile b/gemfiles/ruby_2.7.6_cucumber_5.gemfile index 9050053c..bd99a42e 100644 --- a/gemfiles/ruby_2.7.6_cucumber_5.gemfile +++ b/gemfiles/ruby_2.7.6_cucumber_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_cucumber_5.gemfile.lock b/gemfiles/ruby_2.7.6_cucumber_5.gemfile.lock index 26a3fdf1..6596bbd2 100644 --- a/gemfiles/ruby_2.7.6_cucumber_5.gemfile.lock +++ b/gemfiles/ruby_2.7.6_cucumber_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_cucumber_6.gemfile b/gemfiles/ruby_2.7.6_cucumber_6.gemfile index 75eb8323..9b5a94ad 100644 --- a/gemfiles/ruby_2.7.6_cucumber_6.gemfile +++ b/gemfiles/ruby_2.7.6_cucumber_6.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_cucumber_6.gemfile.lock b/gemfiles/ruby_2.7.6_cucumber_6.gemfile.lock index 2791348a..39434a0c 100644 --- a/gemfiles/ruby_2.7.6_cucumber_6.gemfile.lock +++ b/gemfiles/ruby_2.7.6_cucumber_6.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -157,6 +158,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -182,6 +184,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_cucumber_7.gemfile b/gemfiles/ruby_2.7.6_cucumber_7.gemfile index b425e539..310fd418 100644 --- a/gemfiles/ruby_2.7.6_cucumber_7.gemfile +++ b/gemfiles/ruby_2.7.6_cucumber_7.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_cucumber_7.gemfile.lock b/gemfiles/ruby_2.7.6_cucumber_7.gemfile.lock index dfd43dda..f83ffd71 100644 --- a/gemfiles/ruby_2.7.6_cucumber_7.gemfile.lock +++ b/gemfiles/ruby_2.7.6_cucumber_7.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -139,6 +140,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -162,6 +164,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_cucumber_8.gemfile b/gemfiles/ruby_2.7.6_cucumber_8.gemfile index 1f065ae6..9dcc7fc6 100644 --- a/gemfiles/ruby_2.7.6_cucumber_8.gemfile +++ b/gemfiles/ruby_2.7.6_cucumber_8.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_cucumber_8.gemfile.lock b/gemfiles/ruby_2.7.6_cucumber_8.gemfile.lock index b057632e..f9a7073c 100644 --- a/gemfiles/ruby_2.7.6_cucumber_8.gemfile.lock +++ b/gemfiles/ruby_2.7.6_cucumber_8.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -133,6 +134,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -156,6 +158,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_minitest_5.gemfile b/gemfiles/ruby_2.7.6_minitest_5.gemfile index b1f7cdf8..d03bd893 100644 --- a/gemfiles/ruby_2.7.6_minitest_5.gemfile +++ b/gemfiles/ruby_2.7.6_minitest_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_minitest_5.gemfile.lock b/gemfiles/ruby_2.7.6_minitest_5.gemfile.lock index 56c9d29a..ac4f4509 100644 --- a/gemfiles/ruby_2.7.6_minitest_5.gemfile.lock +++ b/gemfiles/ruby_2.7.6_minitest_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -103,6 +104,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -126,6 +128,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_2.7.6_rspec_3.gemfile b/gemfiles/ruby_2.7.6_rspec_3.gemfile index d59f8d57..8c8e2b81 100644 --- a/gemfiles/ruby_2.7.6_rspec_3.gemfile +++ b/gemfiles/ruby_2.7.6_rspec_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec", "~> 3" gem "os" gem "climate_control" +gem "rspec", "~> 3" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_2.7.6_rspec_3.gemfile.lock b/gemfiles/ruby_2.7.6_rspec_3.gemfile.lock index d57b57aa..723237d3 100644 --- a/gemfiles/ruby_2.7.6_rspec_3.gemfile.lock +++ b/gemfiles/ruby_2.7.6_rspec_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -102,6 +103,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -124,6 +126,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_cucumber_3.gemfile b/gemfiles/ruby_3.0.4_cucumber_3.gemfile index e333024b..be1f65fc 100644 --- a/gemfiles/ruby_3.0.4_cucumber_3.gemfile +++ b/gemfiles/ruby_3.0.4_cucumber_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_cucumber_3.gemfile.lock b/gemfiles/ruby_3.0.4_cucumber_3.gemfile.lock index f8d057d9..750d41ea 100644 --- a/gemfiles/ruby_3.0.4_cucumber_3.gemfile.lock +++ b/gemfiles/ruby_3.0.4_cucumber_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -123,6 +124,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -146,6 +148,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_cucumber_4.gemfile b/gemfiles/ruby_3.0.4_cucumber_4.gemfile index 118961f1..5e26524b 100644 --- a/gemfiles/ruby_3.0.4_cucumber_4.gemfile +++ b/gemfiles/ruby_3.0.4_cucumber_4.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_cucumber_4.gemfile.lock b/gemfiles/ruby_3.0.4_cucumber_4.gemfile.lock index 3fc6c2f4..b755182f 100644 --- a/gemfiles/ruby_3.0.4_cucumber_4.gemfile.lock +++ b/gemfiles/ruby_3.0.4_cucumber_4.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_cucumber_5.gemfile b/gemfiles/ruby_3.0.4_cucumber_5.gemfile index 9050053c..bd99a42e 100644 --- a/gemfiles/ruby_3.0.4_cucumber_5.gemfile +++ b/gemfiles/ruby_3.0.4_cucumber_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_cucumber_5.gemfile.lock b/gemfiles/ruby_3.0.4_cucumber_5.gemfile.lock index 26a3fdf1..6596bbd2 100644 --- a/gemfiles/ruby_3.0.4_cucumber_5.gemfile.lock +++ b/gemfiles/ruby_3.0.4_cucumber_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_cucumber_6.gemfile b/gemfiles/ruby_3.0.4_cucumber_6.gemfile index 75eb8323..9b5a94ad 100644 --- a/gemfiles/ruby_3.0.4_cucumber_6.gemfile +++ b/gemfiles/ruby_3.0.4_cucumber_6.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_cucumber_6.gemfile.lock b/gemfiles/ruby_3.0.4_cucumber_6.gemfile.lock index 2791348a..39434a0c 100644 --- a/gemfiles/ruby_3.0.4_cucumber_6.gemfile.lock +++ b/gemfiles/ruby_3.0.4_cucumber_6.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -157,6 +158,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -182,6 +184,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_cucumber_7.gemfile b/gemfiles/ruby_3.0.4_cucumber_7.gemfile index b425e539..310fd418 100644 --- a/gemfiles/ruby_3.0.4_cucumber_7.gemfile +++ b/gemfiles/ruby_3.0.4_cucumber_7.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_cucumber_7.gemfile.lock b/gemfiles/ruby_3.0.4_cucumber_7.gemfile.lock index dfd43dda..f83ffd71 100644 --- a/gemfiles/ruby_3.0.4_cucumber_7.gemfile.lock +++ b/gemfiles/ruby_3.0.4_cucumber_7.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -139,6 +140,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -162,6 +164,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_cucumber_8.gemfile b/gemfiles/ruby_3.0.4_cucumber_8.gemfile index 1f065ae6..9dcc7fc6 100644 --- a/gemfiles/ruby_3.0.4_cucumber_8.gemfile +++ b/gemfiles/ruby_3.0.4_cucumber_8.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_cucumber_8.gemfile.lock b/gemfiles/ruby_3.0.4_cucumber_8.gemfile.lock index b057632e..f9a7073c 100644 --- a/gemfiles/ruby_3.0.4_cucumber_8.gemfile.lock +++ b/gemfiles/ruby_3.0.4_cucumber_8.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -133,6 +134,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -156,6 +158,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_minitest_5.gemfile b/gemfiles/ruby_3.0.4_minitest_5.gemfile index b1f7cdf8..d03bd893 100644 --- a/gemfiles/ruby_3.0.4_minitest_5.gemfile +++ b/gemfiles/ruby_3.0.4_minitest_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_minitest_5.gemfile.lock b/gemfiles/ruby_3.0.4_minitest_5.gemfile.lock index 56c9d29a..ac4f4509 100644 --- a/gemfiles/ruby_3.0.4_minitest_5.gemfile.lock +++ b/gemfiles/ruby_3.0.4_minitest_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -103,6 +104,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -126,6 +128,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.0.4_rspec_3.gemfile b/gemfiles/ruby_3.0.4_rspec_3.gemfile index d59f8d57..8c8e2b81 100644 --- a/gemfiles/ruby_3.0.4_rspec_3.gemfile +++ b/gemfiles/ruby_3.0.4_rspec_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec", "~> 3" gem "os" gem "climate_control" +gem "rspec", "~> 3" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.0.4_rspec_3.gemfile.lock b/gemfiles/ruby_3.0.4_rspec_3.gemfile.lock index d57b57aa..723237d3 100644 --- a/gemfiles/ruby_3.0.4_rspec_3.gemfile.lock +++ b/gemfiles/ruby_3.0.4_rspec_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -102,6 +103,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -124,6 +126,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_cucumber_3.gemfile b/gemfiles/ruby_3.1.2_cucumber_3.gemfile index e333024b..be1f65fc 100644 --- a/gemfiles/ruby_3.1.2_cucumber_3.gemfile +++ b/gemfiles/ruby_3.1.2_cucumber_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_cucumber_3.gemfile.lock b/gemfiles/ruby_3.1.2_cucumber_3.gemfile.lock index f8d057d9..750d41ea 100644 --- a/gemfiles/ruby_3.1.2_cucumber_3.gemfile.lock +++ b/gemfiles/ruby_3.1.2_cucumber_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -123,6 +124,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -146,6 +148,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_cucumber_4.gemfile b/gemfiles/ruby_3.1.2_cucumber_4.gemfile index 118961f1..5e26524b 100644 --- a/gemfiles/ruby_3.1.2_cucumber_4.gemfile +++ b/gemfiles/ruby_3.1.2_cucumber_4.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_cucumber_4.gemfile.lock b/gemfiles/ruby_3.1.2_cucumber_4.gemfile.lock index 3fc6c2f4..b755182f 100644 --- a/gemfiles/ruby_3.1.2_cucumber_4.gemfile.lock +++ b/gemfiles/ruby_3.1.2_cucumber_4.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_cucumber_5.gemfile b/gemfiles/ruby_3.1.2_cucumber_5.gemfile index 9050053c..bd99a42e 100644 --- a/gemfiles/ruby_3.1.2_cucumber_5.gemfile +++ b/gemfiles/ruby_3.1.2_cucumber_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_cucumber_5.gemfile.lock b/gemfiles/ruby_3.1.2_cucumber_5.gemfile.lock index 26a3fdf1..6596bbd2 100644 --- a/gemfiles/ruby_3.1.2_cucumber_5.gemfile.lock +++ b/gemfiles/ruby_3.1.2_cucumber_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_cucumber_6.gemfile b/gemfiles/ruby_3.1.2_cucumber_6.gemfile index 75eb8323..9b5a94ad 100644 --- a/gemfiles/ruby_3.1.2_cucumber_6.gemfile +++ b/gemfiles/ruby_3.1.2_cucumber_6.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_cucumber_6.gemfile.lock b/gemfiles/ruby_3.1.2_cucumber_6.gemfile.lock index 2791348a..39434a0c 100644 --- a/gemfiles/ruby_3.1.2_cucumber_6.gemfile.lock +++ b/gemfiles/ruby_3.1.2_cucumber_6.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -157,6 +158,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -182,6 +184,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_cucumber_7.gemfile b/gemfiles/ruby_3.1.2_cucumber_7.gemfile index b425e539..310fd418 100644 --- a/gemfiles/ruby_3.1.2_cucumber_7.gemfile +++ b/gemfiles/ruby_3.1.2_cucumber_7.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_cucumber_7.gemfile.lock b/gemfiles/ruby_3.1.2_cucumber_7.gemfile.lock index dfd43dda..f83ffd71 100644 --- a/gemfiles/ruby_3.1.2_cucumber_7.gemfile.lock +++ b/gemfiles/ruby_3.1.2_cucumber_7.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -139,6 +140,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -162,6 +164,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_cucumber_8.gemfile b/gemfiles/ruby_3.1.2_cucumber_8.gemfile index 1f065ae6..9dcc7fc6 100644 --- a/gemfiles/ruby_3.1.2_cucumber_8.gemfile +++ b/gemfiles/ruby_3.1.2_cucumber_8.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_cucumber_8.gemfile.lock b/gemfiles/ruby_3.1.2_cucumber_8.gemfile.lock index b057632e..f9a7073c 100644 --- a/gemfiles/ruby_3.1.2_cucumber_8.gemfile.lock +++ b/gemfiles/ruby_3.1.2_cucumber_8.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -133,6 +134,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -156,6 +158,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_minitest_5.gemfile b/gemfiles/ruby_3.1.2_minitest_5.gemfile index b1f7cdf8..d03bd893 100644 --- a/gemfiles/ruby_3.1.2_minitest_5.gemfile +++ b/gemfiles/ruby_3.1.2_minitest_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_minitest_5.gemfile.lock b/gemfiles/ruby_3.1.2_minitest_5.gemfile.lock index 56c9d29a..ac4f4509 100644 --- a/gemfiles/ruby_3.1.2_minitest_5.gemfile.lock +++ b/gemfiles/ruby_3.1.2_minitest_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -103,6 +104,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -126,6 +128,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.1.2_rspec_3.gemfile b/gemfiles/ruby_3.1.2_rspec_3.gemfile index d59f8d57..8c8e2b81 100644 --- a/gemfiles/ruby_3.1.2_rspec_3.gemfile +++ b/gemfiles/ruby_3.1.2_rspec_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec", "~> 3" gem "os" gem "climate_control" +gem "rspec", "~> 3" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.1.2_rspec_3.gemfile.lock b/gemfiles/ruby_3.1.2_rspec_3.gemfile.lock index d57b57aa..723237d3 100644 --- a/gemfiles/ruby_3.1.2_rspec_3.gemfile.lock +++ b/gemfiles/ruby_3.1.2_rspec_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -102,6 +103,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -124,6 +126,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_cucumber_3.gemfile b/gemfiles/ruby_3.2.0_cucumber_3.gemfile index e333024b..be1f65fc 100644 --- a/gemfiles/ruby_3.2.0_cucumber_3.gemfile +++ b/gemfiles/ruby_3.2.0_cucumber_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_cucumber_3.gemfile.lock b/gemfiles/ruby_3.2.0_cucumber_3.gemfile.lock index f8d057d9..750d41ea 100644 --- a/gemfiles/ruby_3.2.0_cucumber_3.gemfile.lock +++ b/gemfiles/ruby_3.2.0_cucumber_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -123,6 +124,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -146,6 +148,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_cucumber_4.gemfile b/gemfiles/ruby_3.2.0_cucumber_4.gemfile index 118961f1..5e26524b 100644 --- a/gemfiles/ruby_3.2.0_cucumber_4.gemfile +++ b/gemfiles/ruby_3.2.0_cucumber_4.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_cucumber_4.gemfile.lock b/gemfiles/ruby_3.2.0_cucumber_4.gemfile.lock index 3fc6c2f4..b755182f 100644 --- a/gemfiles/ruby_3.2.0_cucumber_4.gemfile.lock +++ b/gemfiles/ruby_3.2.0_cucumber_4.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_cucumber_5.gemfile b/gemfiles/ruby_3.2.0_cucumber_5.gemfile index 9050053c..bd99a42e 100644 --- a/gemfiles/ruby_3.2.0_cucumber_5.gemfile +++ b/gemfiles/ruby_3.2.0_cucumber_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_cucumber_5.gemfile.lock b/gemfiles/ruby_3.2.0_cucumber_5.gemfile.lock index 26a3fdf1..6596bbd2 100644 --- a/gemfiles/ruby_3.2.0_cucumber_5.gemfile.lock +++ b/gemfiles/ruby_3.2.0_cucumber_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_cucumber_6.gemfile b/gemfiles/ruby_3.2.0_cucumber_6.gemfile index 75eb8323..9b5a94ad 100644 --- a/gemfiles/ruby_3.2.0_cucumber_6.gemfile +++ b/gemfiles/ruby_3.2.0_cucumber_6.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_cucumber_6.gemfile.lock b/gemfiles/ruby_3.2.0_cucumber_6.gemfile.lock index 2791348a..39434a0c 100644 --- a/gemfiles/ruby_3.2.0_cucumber_6.gemfile.lock +++ b/gemfiles/ruby_3.2.0_cucumber_6.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -157,6 +158,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -182,6 +184,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_cucumber_7.gemfile b/gemfiles/ruby_3.2.0_cucumber_7.gemfile index b425e539..310fd418 100644 --- a/gemfiles/ruby_3.2.0_cucumber_7.gemfile +++ b/gemfiles/ruby_3.2.0_cucumber_7.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_cucumber_7.gemfile.lock b/gemfiles/ruby_3.2.0_cucumber_7.gemfile.lock index dfd43dda..f83ffd71 100644 --- a/gemfiles/ruby_3.2.0_cucumber_7.gemfile.lock +++ b/gemfiles/ruby_3.2.0_cucumber_7.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -139,6 +140,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -162,6 +164,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_cucumber_8.gemfile b/gemfiles/ruby_3.2.0_cucumber_8.gemfile index 1f065ae6..9dcc7fc6 100644 --- a/gemfiles/ruby_3.2.0_cucumber_8.gemfile +++ b/gemfiles/ruby_3.2.0_cucumber_8.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_cucumber_8.gemfile.lock b/gemfiles/ruby_3.2.0_cucumber_8.gemfile.lock index b057632e..f9a7073c 100644 --- a/gemfiles/ruby_3.2.0_cucumber_8.gemfile.lock +++ b/gemfiles/ruby_3.2.0_cucumber_8.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -133,6 +134,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -156,6 +158,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_minitest_5.gemfile b/gemfiles/ruby_3.2.0_minitest_5.gemfile index b1f7cdf8..d03bd893 100644 --- a/gemfiles/ruby_3.2.0_minitest_5.gemfile +++ b/gemfiles/ruby_3.2.0_minitest_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_minitest_5.gemfile.lock b/gemfiles/ruby_3.2.0_minitest_5.gemfile.lock index 56c9d29a..ac4f4509 100644 --- a/gemfiles/ruby_3.2.0_minitest_5.gemfile.lock +++ b/gemfiles/ruby_3.2.0_minitest_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -103,6 +104,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -126,6 +128,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.2.0_rspec_3.gemfile b/gemfiles/ruby_3.2.0_rspec_3.gemfile index d59f8d57..8c8e2b81 100644 --- a/gemfiles/ruby_3.2.0_rspec_3.gemfile +++ b/gemfiles/ruby_3.2.0_rspec_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec", "~> 3" gem "os" gem "climate_control" +gem "rspec", "~> 3" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.2.0_rspec_3.gemfile.lock b/gemfiles/ruby_3.2.0_rspec_3.gemfile.lock index d57b57aa..723237d3 100644 --- a/gemfiles/ruby_3.2.0_rspec_3.gemfile.lock +++ b/gemfiles/ruby_3.2.0_rspec_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -102,6 +103,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -124,6 +126,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_cucumber_3.gemfile b/gemfiles/ruby_3.3.0_cucumber_3.gemfile index e333024b..be1f65fc 100644 --- a/gemfiles/ruby_3.3.0_cucumber_3.gemfile +++ b/gemfiles/ruby_3.3.0_cucumber_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_cucumber_3.gemfile.lock b/gemfiles/ruby_3.3.0_cucumber_3.gemfile.lock index f1157e86..8d5dfc8b 100644 --- a/gemfiles/ruby_3.3.0_cucumber_3.gemfile.lock +++ b/gemfiles/ruby_3.3.0_cucumber_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -123,6 +124,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -146,6 +148,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_cucumber_4.gemfile b/gemfiles/ruby_3.3.0_cucumber_4.gemfile index 118961f1..5e26524b 100644 --- a/gemfiles/ruby_3.3.0_cucumber_4.gemfile +++ b/gemfiles/ruby_3.3.0_cucumber_4.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_cucumber_4.gemfile.lock b/gemfiles/ruby_3.3.0_cucumber_4.gemfile.lock index 88623370..29e4f448 100644 --- a/gemfiles/ruby_3.3.0_cucumber_4.gemfile.lock +++ b/gemfiles/ruby_3.3.0_cucumber_4.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -149,6 +150,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -174,6 +176,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_cucumber_5.gemfile b/gemfiles/ruby_3.3.0_cucumber_5.gemfile index 9050053c..bd99a42e 100644 --- a/gemfiles/ruby_3.3.0_cucumber_5.gemfile +++ b/gemfiles/ruby_3.3.0_cucumber_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_cucumber_5.gemfile.lock b/gemfiles/ruby_3.3.0_cucumber_5.gemfile.lock index 99c7a32b..15141f9a 100644 --- a/gemfiles/ruby_3.3.0_cucumber_5.gemfile.lock +++ b/gemfiles/ruby_3.3.0_cucumber_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -153,6 +154,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -178,6 +180,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_cucumber_6.gemfile b/gemfiles/ruby_3.3.0_cucumber_6.gemfile index 75eb8323..9b5a94ad 100644 --- a/gemfiles/ruby_3.3.0_cucumber_6.gemfile +++ b/gemfiles/ruby_3.3.0_cucumber_6.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_cucumber_6.gemfile.lock b/gemfiles/ruby_3.3.0_cucumber_6.gemfile.lock index 0f99035c..3206d933 100644 --- a/gemfiles/ruby_3.3.0_cucumber_6.gemfile.lock +++ b/gemfiles/ruby_3.3.0_cucumber_6.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -157,6 +158,7 @@ GEM ffi (~> 1.1) thor (1.2.2) thread_safe (0.3.6) + timecop (0.9.8) tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.4.2) @@ -182,6 +184,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_cucumber_7.gemfile b/gemfiles/ruby_3.3.0_cucumber_7.gemfile index b425e539..310fd418 100644 --- a/gemfiles/ruby_3.3.0_cucumber_7.gemfile +++ b/gemfiles/ruby_3.3.0_cucumber_7.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_cucumber_7.gemfile.lock b/gemfiles/ruby_3.3.0_cucumber_7.gemfile.lock index 6ff9ea89..dea59a78 100644 --- a/gemfiles/ruby_3.3.0_cucumber_7.gemfile.lock +++ b/gemfiles/ruby_3.3.0_cucumber_7.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -139,6 +140,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -162,6 +164,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_cucumber_8.gemfile b/gemfiles/ruby_3.3.0_cucumber_8.gemfile index 1f065ae6..9dcc7fc6 100644 --- a/gemfiles/ruby_3.3.0_cucumber_8.gemfile +++ b/gemfiles/ruby_3.3.0_cucumber_8.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_cucumber_8.gemfile.lock b/gemfiles/ruby_3.3.0_cucumber_8.gemfile.lock index 3c780d69..35be2d1b 100644 --- a/gemfiles/ruby_3.3.0_cucumber_8.gemfile.lock +++ b/gemfiles/ruby_3.3.0_cucumber_8.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -133,6 +134,7 @@ GEM sys-uname (1.2.3) ffi (~> 1.1) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -156,6 +158,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_minitest_5.gemfile b/gemfiles/ruby_3.3.0_minitest_5.gemfile index b1f7cdf8..d03bd893 100644 --- a/gemfiles/ruby_3.3.0_minitest_5.gemfile +++ b/gemfiles/ruby_3.3.0_minitest_5.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec" gem "os" gem "climate_control" +gem "rspec" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_minitest_5.gemfile.lock b/gemfiles/ruby_3.3.0_minitest_5.gemfile.lock index 0a6a6365..4a7ed582 100644 --- a/gemfiles/ruby_3.3.0_minitest_5.gemfile.lock +++ b/gemfiles/ruby_3.3.0_minitest_5.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -103,6 +104,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -126,6 +128,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/gemfiles/ruby_3.3.0_rspec_3.gemfile b/gemfiles/ruby_3.3.0_rspec_3.gemfile index d59f8d57..8c8e2b81 100644 --- a/gemfiles/ruby_3.3.0_rspec_3.gemfile +++ b/gemfiles/ruby_3.3.0_rspec_3.gemfile @@ -5,12 +5,13 @@ source "https://rubygems.org" gem "ddtrace" gem "pry" gem "rake" -gem "rspec", "~> 3" gem "os" gem "climate_control" +gem "rspec", "~> 3" gem "rspec-collection_matchers" gem "rspec_junit_formatter" gem "appraisal" +gem "timecop" gem "standard", "~> 1.31.0" gem "yard" gem "webrick" diff --git a/gemfiles/ruby_3.3.0_rspec_3.gemfile.lock b/gemfiles/ruby_3.3.0_rspec_3.gemfile.lock index 825e55c0..3a1a2252 100644 --- a/gemfiles/ruby_3.3.0_rspec_3.gemfile.lock +++ b/gemfiles/ruby_3.3.0_rspec_3.gemfile.lock @@ -2,6 +2,7 @@ PATH remote: .. specs: datadog-ci (0.1.1) + msgpack GEM remote: https://rubygems.org/ @@ -102,6 +103,7 @@ GEM lint_roller (~> 1.1) rubocop-performance (~> 1.19.0) thor (1.2.2) + timecop (0.9.8) unicode-display_width (2.4.2) webrick (1.8.1) yard (0.9.34) @@ -124,6 +126,7 @@ DEPENDENCIES simplecov simplecov-cobertura (~> 2.1.0) standard (~> 1.31.0) + timecop webrick yard diff --git a/lib/datadog/ci/configuration/components.rb b/lib/datadog/ci/configuration/components.rb index aafad49f..6108b48d 100644 --- a/lib/datadog/ci/configuration/components.rb +++ b/lib/datadog/ci/configuration/components.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true -require_relative "../flush" +require_relative "../test_visibility/flush" +require_relative "../test_visibility/transport" module Datadog module CI @@ -16,15 +17,60 @@ def initialize(settings) end def activate_ci!(settings) + agentless_transport = nil + + if settings.ci.agentless_mode_enabled + if settings.api_key.nil? + # agentless mode is requested but no API key is provided - + # we cannot continue and log an error + # Tests are running without CI visibility enabled + + Datadog.logger.error( + "DATADOG CONFIGURATION - CI VISIBILITY - ATTENTION - " \ + "Agentless mode was enabled but DD_API_KEY is not set: CI visibility is disabled. " \ + "Please make sure to set valid api key in DD_API_KEY environment variable" + ) + + settings.ci.enabled = false + return + else + agentless_transport = build_agentless_transport(settings) + end + end + + # Deactivate telemetry + settings.telemetry.enabled = false + + # Deactivate remote configuration + settings.remote.enabled = false + # Activate underlying tracing test mode settings.tracing.test_mode.enabled = true # Choose user defined TraceFlush or default to CI TraceFlush - settings.tracing.test_mode.trace_flush = settings.ci.trace_flush \ - || CI::Flush::Finished.new + settings.tracing.test_mode.trace_flush = settings.ci.trace_flush || CI::TestVisibility::Flush::Finished.new + + writer_options = settings.ci.writer_options + if agentless_transport + writer_options[:transport] = agentless_transport + writer_options[:shutdown_timeout] = 60 + + settings.tracing.test_mode.async = true + end + + settings.tracing.test_mode.writer_options = writer_options + end + + def build_agentless_transport(settings) + dd_site = settings.site || "datadoghq.com" + agentless_url = settings.ci.agentless_url || + "https://#{Ext::Transport::TEST_VISIBILITY_INTAKE_HOST_PREFIX}.#{dd_site}:443" - # Pass through any other options - settings.tracing.test_mode.writer_options = settings.ci.writer_options + Datadog::CI::TestVisibility::Transport.new( + api_key: settings.api_key, + url: agentless_url, + env: settings.env + ) end end end diff --git a/lib/datadog/ci/configuration/settings.rb b/lib/datadog/ci/configuration/settings.rb index fc3c38f9..eb92f4b9 100644 --- a/lib/datadog/ci/configuration/settings.rb +++ b/lib/datadog/ci/configuration/settings.rb @@ -5,8 +5,10 @@ module Datadog module CI module Configuration - # Adds CI behavior to Datadog trace settings + # Adds CI behavior to ddtrace settings module Settings + InvalidIntegrationError = Class.new(StandardError) + def self.extended(base) base = base.singleton_class unless base.is_a?(Class) add_settings!(base) @@ -21,16 +23,37 @@ def self.add_settings!(base) o.default false end - # DEV: Alias to Datadog::Tracing::Contrib::Extensions::Configuration::Settings#instrument. - # DEV: Should be removed when CI implement its own `c.ci.instrument`. + option :agentless_mode_enabled do |o| + o.type :bool + o.env CI::Ext::Settings::ENV_AGENTLESS_MODE_ENABLED + o.default false + end + + option :agentless_url do |o| + o.type :string, nilable: true + o.env CI::Ext::Settings::ENV_AGENTLESS_URL + end + define_method(:instrument) do |integration_name, options = {}, &block| - Datadog.configuration.tracing.instrument(integration_name, options, &block) + return unless enabled + + integration = fetch_integration(integration_name) + integration.configure(options, &block) + + return unless integration.enabled + + patch_results = integration.patch + next if patch_results == true + + error_message = <<-ERROR + Available?: #{patch_results[:available]}, Loaded?: #{patch_results[:loaded]}, + Compatible?: #{patch_results[:compatible]}, Patchable?: #{patch_results[:patchable]}" + ERROR + Datadog.logger.warn("Unable to patch #{integration_name} (#{error_message})") end - # DEV: Alias to Datadog::Tracing::Contrib::Extensions::Configuration::Settings#instrument. - # DEV: Should be removed when CI implement its own `c.ci[]`. - define_method(:[]) do |integration_name, key = :default| - Datadog.configuration.tracing[integration_name, key] + define_method(:[]) do |integration_name| + fetch_integration(integration_name).configuration end # TODO: Deprecate in the next major version, as `instrument` better describes this method's purpose @@ -42,6 +65,11 @@ def self.add_settings!(base) o.type :hash o.default({}) end + + define_method(:fetch_integration) do |name| + Datadog::CI::Contrib::Integration.registry[name] || + raise(InvalidIntegrationError, "'#{name}' is not a valid integration.") + end end end end diff --git a/lib/datadog/ci/contrib/cucumber/configuration/settings.rb b/lib/datadog/ci/contrib/cucumber/configuration/settings.rb index 713094e6..da7bd635 100644 --- a/lib/datadog/ci/contrib/cucumber/configuration/settings.rb +++ b/lib/datadog/ci/contrib/cucumber/configuration/settings.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true require_relative "../ext" - -require "datadog/tracing/contrib/configuration/settings" +require_relative "../../settings" module Datadog module CI @@ -11,7 +10,7 @@ module Cucumber module Configuration # Custom settings for the Cucumber integration # TODO: mark as `@public_api` when GA - class Settings < Datadog::Tracing::Contrib::Configuration::Settings + class Settings < Datadog::CI::Contrib::Settings option :enabled do |o| o.type :bool o.env Ext::ENV_ENABLED diff --git a/lib/datadog/ci/contrib/cucumber/formatter.rb b/lib/datadog/ci/contrib/cucumber/formatter.rb index 150218ac..b41cded5 100644 --- a/lib/datadog/ci/contrib/cucumber/formatter.rb +++ b/lib/datadog/ci/contrib/cucumber/formatter.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require_relative "../../test" -require_relative "../../ext/app_types" -require_relative "../../ext/environment" +require_relative "../../recorder" require_relative "../../ext/test" require_relative "ext" @@ -30,7 +28,7 @@ def bind_events(config) end def on_test_case_started(event) - @current_feature_span = CI::Test.trace( + @current_feature_span = CI::Recorder.trace( configuration[:operation_name], { span_options: { @@ -50,11 +48,11 @@ def on_test_case_finished(event) return if @current_feature_span.nil? if event.result.skipped? - CI::Test.skipped!(@current_feature_span) + CI::Recorder.skipped!(@current_feature_span) elsif event.result.ok? - CI::Test.passed!(@current_feature_span) + CI::Recorder.passed!(@current_feature_span) elsif event.result.failed? - CI::Test.failed!(@current_feature_span) + CI::Recorder.failed!(@current_feature_span) end @current_feature_span.finish @@ -72,11 +70,11 @@ def on_test_step_finished(event) return if @current_step_span.nil? if event.result.skipped? - CI::Test.skipped!(@current_step_span, event.result.exception) + CI::Recorder.skipped!(@current_step_span, event.result.exception) elsif event.result.ok? - CI::Test.passed!(@current_step_span) + CI::Recorder.passed!(@current_step_span) elsif event.result.failed? - CI::Test.failed!(@current_step_span, event.result.exception) + CI::Recorder.failed!(@current_step_span, event.result.exception) end @current_step_span.finish diff --git a/lib/datadog/ci/contrib/cucumber/integration.rb b/lib/datadog/ci/contrib/cucumber/integration.rb index 69b2e0fd..1739a13c 100644 --- a/lib/datadog/ci/contrib/cucumber/integration.rb +++ b/lib/datadog/ci/contrib/cucumber/integration.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require "datadog/tracing/contrib" -require "datadog/tracing/contrib/integration" - +require_relative "../integration" require_relative "configuration/settings" require_relative "patcher" @@ -12,11 +10,11 @@ module Contrib module Cucumber # Description of Cucumber integration class Integration - include Datadog::Tracing::Contrib::Integration + include Datadog::CI::Contrib::Integration MINIMUM_VERSION = Gem::Version.new("3.0.0") - register_as :cucumber, auto_patch: true + register_as :cucumber def self.version Gem.loaded_specs["cucumber"] \ diff --git a/lib/datadog/ci/contrib/integration.rb b/lib/datadog/ci/contrib/integration.rb new file mode 100644 index 00000000..24021ae9 --- /dev/null +++ b/lib/datadog/ci/contrib/integration.rb @@ -0,0 +1,149 @@ +# frozen_string_literal: true + +require_relative "settings" + +module Datadog + module CI + module Contrib + module Integration + @registry = {} + + def self.included(base) + base.extend(ClassMethods) + base.include(InstanceMethods) + end + + def self.register(klass, name) + registry[name] = klass.new + end + + def self.registry + @registry + end + + # Class-level methods for Integration + module ClassMethods + def register_as(name) + Integration.register(self, name) + end + + # Version of the integration target code in the environment. + # + # This is the gem version, when the instrumentation target is a Ruby gem. + # + # If the target for instrumentation has concept of versioning, override {.version}, + # otherwise override {.available?} and implement a custom target presence check. + # @return [Object] the target version + def version + nil + end + + # Is the target available to be instrumented? (e.g. gem installed?) + # + # The target doesn't have to be loaded (e.g. `require`) yet, but needs to be able + # to be loaded before instrumentation can commence. + # + # By default, {.available?} checks if {.version} returned a non-nil object. + # + # If the target for instrumentation has concept of versioning, override {.version}, + # otherwise override {.available?} and implement a custom target presence check. + # @return [Boolean] is the target available for instrumentation in this Ruby environment? + def available? + !version.nil? + end + + # Is the target loaded into the application? (e.g. gem required? Constant defined?) + # + # The target's objects should be ready to be referenced by the instrumented when {.loaded} + # returns `true`. + # + # @return [Boolean] is the target ready to be referenced during instrumentation? + def loaded? + true + end + + # Is this instrumentation compatible with the available target? (e.g. minimum version met?) + # @return [Boolean] is the available target compatible with this instrumentation? + def compatible? + available? + end + + # Can the patch for this integration be applied? + # + # By default, this is equivalent to {#available?}, {#loaded?}, and {#compatible?} + # all being truthy. + def patchable? + available? && loaded? && compatible? + end + end + + module InstanceMethods + # returns the configuration instance. + def configuration + @configuration ||= new_configuration + end + + def configure(options = {}, &block) + configuration.configure(options, &block) + configuration + end + + # Resets all configuration options + def reset_configuration! + @configuration = nil + end + + def enabled + configuration.enabled + end + + # The patcher module to inject instrumented objects into the instrumentation target. + # + # {Contrib::Patcher} includes the basic functionality of a patcher. `include`ing + # {Contrib::Patcher} into a new module is the recommend way to create a custom patcher. + # + # @return [Contrib::Patcher] a module that `include`s {Contrib::Patcher} + def patcher + nil + end + + # @!visibility private + def patch + # @type var patcher_klass: untyped + patcher_klass = patcher + if !self.class.patchable? || patcher_klass.nil? + return { + available: self.class.available?, + loaded: self.class.loaded?, + compatible: self.class.compatible?, + patchable: self.class.patchable? + } + end + + patcher_klass.patch + true + end + + # Can the patch for this integration be applied automatically? + # @return [Boolean] can the tracer activate this instrumentation without explicit user input? + def auto_instrument? + true + end + + protected + + # Returns a new configuration object for this integration. + # + # This method normally needs to be overridden for each integration + # as their settings, defaults and environment variables are + # specific for each integration. + # + # @return [Datadog::CI::Contrib::Settings] a new, integration-specific settings object + def new_configuration + Datadog::CI::Contrib::Settings.new + end + end + end + end + end +end diff --git a/lib/datadog/ci/contrib/minitest/configuration/settings.rb b/lib/datadog/ci/contrib/minitest/configuration/settings.rb index f412978e..a631ad43 100644 --- a/lib/datadog/ci/contrib/minitest/configuration/settings.rb +++ b/lib/datadog/ci/contrib/minitest/configuration/settings.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true require_relative "../ext" - -require "datadog/tracing/contrib/configuration/settings" +require_relative "../../settings" module Datadog module CI @@ -11,7 +10,7 @@ module Minitest module Configuration # Custom settings for the Minitest integration # TODO: mark as `@public_api` when GA - class Settings < Datadog::Tracing::Contrib::Configuration::Settings + class Settings < Datadog::CI::Contrib::Settings option :enabled do |o| o.type :bool o.env Ext::ENV_ENABLED diff --git a/lib/datadog/ci/contrib/minitest/hooks.rb b/lib/datadog/ci/contrib/minitest/hooks.rb index 0bb61195..41253cde 100644 --- a/lib/datadog/ci/contrib/minitest/hooks.rb +++ b/lib/datadog/ci/contrib/minitest/hooks.rb @@ -1,5 +1,9 @@ # frozen_string_literal: true +require_relative "../../recorder" +require_relative "../../ext/test" +require_relative "ext" + module Datadog module CI module Contrib @@ -15,7 +19,7 @@ def before_setup path, = method(name).source_location test_suite = Pathname.new(path.to_s).relative_path_from(Pathname.pwd).to_s - span = CI::Test.trace( + span = CI::Recorder.trace( configuration[:operation_name], { span_options: { @@ -41,11 +45,11 @@ def after_teardown case result_code when "." - CI::Test.passed!(span) + CI::Recorder.passed!(span) when "E", "F" - CI::Test.failed!(span, failure) + CI::Recorder.failed!(span, failure) when "S" - CI::Test.skipped!(span) + CI::Recorder.skipped!(span) span.set_tag(CI::Ext::Test::TAG_SKIP_REASON, failure.message) end diff --git a/lib/datadog/ci/contrib/minitest/integration.rb b/lib/datadog/ci/contrib/minitest/integration.rb index 557289ff..66382afb 100644 --- a/lib/datadog/ci/contrib/minitest/integration.rb +++ b/lib/datadog/ci/contrib/minitest/integration.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require "datadog/tracing/contrib" -require "datadog/tracing/contrib/integration" - +require_relative "../integration" require_relative "configuration/settings" require_relative "patcher" @@ -12,11 +10,11 @@ module Contrib module Minitest # Description of Minitest integration class Integration - include Datadog::Tracing::Contrib::Integration + include Datadog::CI::Contrib::Integration MINIMUM_VERSION = Gem::Version.new("5.0.0") - register_as :minitest, auto_patch: true + register_as :minitest def self.version Gem.loaded_specs["minitest"] && Gem.loaded_specs["minitest"].version diff --git a/lib/datadog/ci/contrib/rspec/configuration/settings.rb b/lib/datadog/ci/contrib/rspec/configuration/settings.rb index c573b9e5..82a12a01 100644 --- a/lib/datadog/ci/contrib/rspec/configuration/settings.rb +++ b/lib/datadog/ci/contrib/rspec/configuration/settings.rb @@ -1,8 +1,7 @@ # frozen_string_literal: true require_relative "../ext" - -require "datadog/tracing/contrib/configuration/settings" +require_relative "../../settings" module Datadog module CI @@ -11,7 +10,7 @@ module RSpec module Configuration # Custom settings for the RSpec integration # TODO: mark as `@public_api` when GA - class Settings < Datadog::Tracing::Contrib::Configuration::Settings + class Settings < Datadog::CI::Contrib::Settings option :enabled do |o| o.type :bool o.env Ext::ENV_ENABLED diff --git a/lib/datadog/ci/contrib/rspec/example.rb b/lib/datadog/ci/contrib/rspec/example.rb index e45e26c6..e695cd0a 100644 --- a/lib/datadog/ci/contrib/rspec/example.rb +++ b/lib/datadog/ci/contrib/rspec/example.rb @@ -1,9 +1,6 @@ # frozen_string_literal: true -require_relative "../../test" - -require_relative "../../ext/app_types" -require_relative "../../ext/environment" +require_relative "../../recorder" require_relative "../../ext/test" require_relative "ext" @@ -28,7 +25,7 @@ def run(example_group_instance, reporter) test_name += " #{description}" end - CI::Test.trace( + CI::Recorder.trace( configuration[:operation_name], { span_options: { @@ -46,11 +43,11 @@ def run(example_group_instance, reporter) case execution_result.status when :passed - CI::Test.passed!(span) + CI::Recorder.passed!(span) when :failed - CI::Test.failed!(span, execution_result.exception) + CI::Recorder.failed!(span, execution_result.exception) else - CI::Test.skipped!(span, execution_result.exception) if execution_result.example_skipped? + CI::Recorder.skipped!(span, execution_result.exception) if execution_result.example_skipped? end result diff --git a/lib/datadog/ci/contrib/rspec/integration.rb b/lib/datadog/ci/contrib/rspec/integration.rb index ac076b90..dbd625cc 100644 --- a/lib/datadog/ci/contrib/rspec/integration.rb +++ b/lib/datadog/ci/contrib/rspec/integration.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true -require "datadog/tracing/contrib" -require "datadog/tracing/contrib/integration" - +require_relative "../integration" require_relative "configuration/settings" require_relative "patcher" @@ -12,11 +10,11 @@ module Contrib module RSpec # Description of RSpec integration class Integration - include Datadog::Tracing::Contrib::Integration + include Datadog::CI::Contrib::Integration MINIMUM_VERSION = Gem::Version.new("3.0.0") - register_as :rspec, auto_patch: true + register_as :rspec def self.version Gem.loaded_specs["rspec-core"] \ diff --git a/lib/datadog/ci/contrib/settings.rb b/lib/datadog/ci/contrib/settings.rb new file mode 100644 index 00000000..41878252 --- /dev/null +++ b/lib/datadog/ci/contrib/settings.rb @@ -0,0 +1,33 @@ +require "datadog/core/configuration/base" + +module Datadog + module CI + module Contrib + # Common settings for all integrations + # @public_api + class Settings + include Core::Configuration::Base + + option :enabled, default: true + option :service_name + option :operation_name + + def configure(options = {}) + self.class.options.each do |name, _value| + self[name] = options[name] if options.key?(name) + end + + yield(self) if block_given? + end + + def [](name) + respond_to?(name) ? send(name) : get_option(name) + end + + def []=(name, value) + respond_to?("#{name}=") ? send("#{name}=", value) : set_option(name, value) + end + end + end + end +end diff --git a/lib/datadog/ci/ext/settings.rb b/lib/datadog/ci/ext/settings.rb index d74dbbd6..c08a5f56 100644 --- a/lib/datadog/ci/ext/settings.rb +++ b/lib/datadog/ci/ext/settings.rb @@ -6,6 +6,8 @@ module Ext # Defines constants for test tags module Settings ENV_MODE_ENABLED = "DD_TRACE_CI_ENABLED" + ENV_AGENTLESS_MODE_ENABLED = "DD_CIVISIBILITY_AGENTLESS_ENABLED" + ENV_AGENTLESS_URL = "DD_CIVISIBILITY_AGENTLESS_URL" end end end diff --git a/lib/datadog/ci/ext/transport.rb b/lib/datadog/ci/ext/transport.rb new file mode 100644 index 00000000..ece118eb --- /dev/null +++ b/lib/datadog/ci/ext/transport.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Datadog + module CI + module Ext + module Transport + HEADER_DD_API_KEY = "DD-API-KEY" + HEADER_CONTENT_TYPE = "Content-Type" + HEADER_CONTENT_ENCODING = "Content-Encoding" + + TEST_VISIBILITY_INTAKE_HOST_PREFIX = "citestcycle-intake" + TEST_VISIBILITY_INTAKE_PATH = "/api/v2/citestcycle" + + CONTENT_TYPE_MESSAGEPACK = "application/msgpack" + CONTENT_ENCODING_GZIP = "gzip" + end + end + end +end diff --git a/lib/datadog/ci/flush.rb b/lib/datadog/ci/flush.rb deleted file mode 100644 index 5fabf41e..00000000 --- a/lib/datadog/ci/flush.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require "datadog/tracing/metadata/ext" -require "datadog/tracing/flush" - -module Datadog - module CI - module Flush - # Common behavior for CI flushing - module Tagging - # Decorate a trace with CI tags - def get_trace(trace_op) - trace = trace_op.flush! - - # Origin tag is required on every span - trace.spans.each do |span| - span.set_tag( - Tracing::Metadata::Ext::Distributed::TAG_ORIGIN, - trace.origin - ) - end - - trace - end - end - - # Consumes only completed traces (where all spans have finished) - class Finished < Tracing::Flush::Finished - prepend Tagging - end - - # Performs partial trace flushing to avoid large traces residing in memory for too long - class Partial < Tracing::Flush::Partial - prepend Tagging - end - end - end -end diff --git a/lib/datadog/ci/test.rb b/lib/datadog/ci/recorder.rb similarity index 89% rename from lib/datadog/ci/test.rb rename to lib/datadog/ci/recorder.rb index 2548c062..a298f2ad 100644 --- a/lib/datadog/ci/test.rb +++ b/lib/datadog/ci/recorder.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "datadog/tracing" require "datadog/tracing/contrib/analytics" require_relative "ext/app_types" @@ -11,7 +12,7 @@ module Datadog module CI # Common behavior for CI tests - module Test + module Recorder # Creates a new span for a CI test def self.trace(span_name, options = {}) span_options = { @@ -19,13 +20,13 @@ def self.trace(span_name, options = {}) }.merge(options[:span_options] || {}) if block_given? - Tracing.trace(span_name, **span_options) do |span, trace| + ::Datadog::Tracing.trace(span_name, **span_options) do |span, trace| set_tags!(trace, span, options) yield(span, trace) end else - span = Tracing.trace(span_name, **span_options) - trace = Tracing.active_trace + span = ::Datadog::Tracing.trace(span_name, **span_options) + trace = ::Datadog::Tracing.active_trace set_tags!(trace, span, options) span end @@ -37,7 +38,7 @@ def self.set_tags!(trace, span, tags = {}) # Set default tags trace.origin = Ext::Test::CONTEXT_ORIGIN if trace - Datadog::Tracing::Contrib::Analytics.set_measured(span) + ::Datadog::Tracing::Contrib::Analytics.set_measured(span) span.set_tag(Ext::Test::TAG_SPAN_KIND, Ext::AppTypes::TYPE_TEST) # Set environment tags diff --git a/lib/datadog/ci/test_visibility/flush.rb b/lib/datadog/ci/test_visibility/flush.rb new file mode 100644 index 00000000..36660e7c --- /dev/null +++ b/lib/datadog/ci/test_visibility/flush.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "datadog/tracing/metadata/ext" +require "datadog/tracing/flush" + +module Datadog + module CI + module TestVisibility + module Flush + # Common behavior for CI flushing + module Tagging + # Decorate a trace with CI tags + def get_trace(trace_op) + trace = trace_op.flush! + + # Origin tag is required on every span + trace.spans.each do |span| + span.set_tag( + Tracing::Metadata::Ext::Distributed::TAG_ORIGIN, + trace.origin + ) + end + + trace + end + end + + # Consumes only completed traces (where all spans have finished) + class Finished < Tracing::Flush::Finished + prepend Tagging + end + + # Performs partial trace flushing to avoid large traces residing in memory for too long + class Partial < Tracing::Flush::Partial + prepend Tagging + end + end + end + end +end diff --git a/lib/datadog/ci/test_visibility/serializers/base.rb b/lib/datadog/ci/test_visibility/serializers/base.rb new file mode 100644 index 00000000..04e20e72 --- /dev/null +++ b/lib/datadog/ci/test_visibility/serializers/base.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +module Datadog + module CI + module TestVisibility + module Serializers + class Base + MINIMUM_TIMESTAMP_NANO = 946684800000000000 + MINIMUM_DURATION_NANO = 0 + MAXIMUM_DURATION_NANO = 9223372036854775807 + + attr_reader :trace, :span + + def initialize(trace, span) + @trace = trace + @span = span + end + + def to_msgpack(packer = nil) + packer ||= MessagePack::Packer.new + + packer.write_map_header(3) + + write_field(packer, "type") + write_field(packer, "version") + + packer.write("content") + packer.write_map_header(content_map_size) + + content_fields.each do |field| + if field.is_a?(Hash) + field.each do |field_name, method| + write_field(packer, field_name, method) + end + else + write_field(packer, field) + end + end + end + + # validates according to citestcycle json schema + def valid? + required_fields_present? && valid_start_time? && valid_duration? + end + + def content_fields + [] + end + + def content_map_size + 0 + end + + def runtime_id + @trace.runtime_id + end + + def trace_id + @trace.id + end + + def span_id + @span.id + end + + def parent_id + @span.parent_id + end + + def type + end + + def version + 1 + end + + def span_type + @span.type + end + + def name + @span.name + end + + def resource + @span.resource + end + + def service + @span.service + end + + def start + @start ||= time_nano(@span.start_time) + end + + def duration + @duration ||= duration_nano(@span.duration) + end + + def meta + @span.meta + end + + def metrics + @span.metrics + end + + def error + @span.status + end + + def self.calculate_content_map_size(fields_list) + fields_list.reduce(0) do |size, field| + if field.is_a?(Hash) + size + field.size + else + size + 1 + end + end + end + + private + + def valid_start_time? + !start.nil? && start >= MINIMUM_TIMESTAMP_NANO + end + + def valid_duration? + !duration.nil? && duration >= MINIMUM_DURATION_NANO && duration <= MAXIMUM_DURATION_NANO + end + + def required_fields_present? + required_fields.all? { |field| !send(field).nil? } + end + + def required_fields + [] + end + + def write_field(packer, field_name, method = nil) + method ||= field_name + + packer.write(field_name) + packer.write(send(method)) + end + + # in nanoseconds since Epoch + def time_nano(time) + time.to_i * 1000000000 + time.nsec + end + + # in nanoseconds + def duration_nano(duration) + (duration * 1e9).to_i + end + end + end + end + end +end diff --git a/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb b/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb new file mode 100644 index 00000000..d2cfe1ae --- /dev/null +++ b/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require_relative "../test_v1" +require_relative "../span" + +module Datadog + module CI + module TestVisibility + module Serializers + module Factories + # This factory takes care of creating citestcycle serializers when test-level visibility is enabled + # NOTE: citestcycle is a protocol Datadog uses to submit test execution tracing information to CI visibility + # backend + module TestLevel + module_function + + def serializer(trace, span) + case span.type + when Datadog::CI::Ext::AppTypes::TYPE_TEST + Serializers::TestV1.new(trace, span) + else + Serializers::Span.new(trace, span) + end + end + end + end + end + end + end +end diff --git a/lib/datadog/ci/test_visibility/serializers/span.rb b/lib/datadog/ci/test_visibility/serializers/span.rb new file mode 100644 index 00000000..5ac657ff --- /dev/null +++ b/lib/datadog/ci/test_visibility/serializers/span.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require_relative "base" + +module Datadog + module CI + module TestVisibility + module Serializers + class Span < Base + CONTENT_FIELDS = [ + "trace_id", "span_id", "parent_id", + "name", "resource", "service", + "error", "start", "duration", + "meta", "metrics", + "type" => "span_type" + ].freeze + + CONTENT_MAP_SIZE = calculate_content_map_size(CONTENT_FIELDS) + + REQUIRED_FIELDS = [ + "trace_id", + "span_id", + "error", + "name", + "resource", + "start", + "duration" + ].freeze + + def content_fields + CONTENT_FIELDS + end + + def content_map_size + CONTENT_MAP_SIZE + end + + def type + "span" + end + + private + + def required_fields + REQUIRED_FIELDS + end + end + end + end + end +end diff --git a/lib/datadog/ci/test_visibility/serializers/test_v1.rb b/lib/datadog/ci/test_visibility/serializers/test_v1.rb new file mode 100644 index 00000000..c2547234 --- /dev/null +++ b/lib/datadog/ci/test_visibility/serializers/test_v1.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +require_relative "base" +require_relative "../../ext/test" + +module Datadog + module CI + module TestVisibility + module Serializers + class TestV1 < Base + CONTENT_FIELDS = [ + "trace_id", "span_id", + "name", "resource", "service", + "error", "start", "duration", + "meta", "metrics", + "type" => "span_type" + ].freeze + + CONTENT_MAP_SIZE = calculate_content_map_size(CONTENT_FIELDS) + + REQUIRED_FIELDS = [ + "trace_id", + "span_id", + "error", + "name", + "resource", + "start", + "duration" + ].freeze + + def content_fields + CONTENT_FIELDS + end + + def content_map_size + CONTENT_MAP_SIZE + end + + def type + "test" + end + + def name + "#{@span.get_tag(Ext::Test::TAG_FRAMEWORK)}.test" + end + + def resource + "#{@span.get_tag(Ext::Test::TAG_SUITE)}.#{@span.get_tag(Ext::Test::TAG_NAME)}" + end + + private + + def required_fields + REQUIRED_FIELDS + end + end + end + end + end +end diff --git a/lib/datadog/ci/test_visibility/transport.rb b/lib/datadog/ci/test_visibility/transport.rb new file mode 100644 index 00000000..472280b8 --- /dev/null +++ b/lib/datadog/ci/test_visibility/transport.rb @@ -0,0 +1,169 @@ +# frozen_string_literal: true + +require "msgpack" +require "uri" + +require "datadog/core/encoding" +require "datadog/core/environment/identity" +require "datadog/core/chunker" + +require_relative "serializers/factories/test_level" +require_relative "../ext/transport" +require_relative "../transport/http" + +module Datadog + module CI + module TestVisibility + class Transport + # CI test cycle intake's limit is 5.1MB uncompressed + # We will use a bit more conservative value 5MB + DEFAULT_MAX_PAYLOAD_SIZE = 5 * 1024 * 1024 + + attr_reader :serializers_factory, + :api_key, + :max_payload_size, + :http, + :env + + def initialize( + api_key:, + url:, + env: nil, + serializers_factory: Datadog::CI::TestVisibility::Serializers::Factories::TestLevel, + max_payload_size: DEFAULT_MAX_PAYLOAD_SIZE + ) + @serializers_factory = serializers_factory + @api_key = api_key + @max_payload_size = max_payload_size + @env = env + + uri = URI.parse(url) + + raise "Invalid agentless mode URL: #{url}" if uri.host.nil? + + @http = Datadog::CI::Transport::HTTP.new( + host: uri.host, + port: uri.port, + ssl: uri.scheme == "https" || uri.port == 443, + compress: true + ) + end + + def send_traces(traces) + return [] if traces.nil? || traces.empty? + + Datadog.logger.debug { "Sending #{traces.count} traces..." } + + encoded_events = encode_traces(traces) + if encoded_events.empty? + Datadog.logger.debug { "Empty encoded events list, skipping send" } + return [] + end + + responses = [] + Datadog::Core::Chunker.chunk_by_size(encoded_events, max_payload_size).map do |chunk| + encoded_payload = pack_events(chunk) + Datadog.logger.debug do + "Send chunk of #{chunk.count} events; payload size #{encoded_payload.size}" + end + + response = send_payload(encoded_payload) + + Datadog.logger.debug do + "Received server response: #{response.inspect}" + end + + responses << response + end + + responses + end + + private + + def send_payload(encoded_payload) + http.request( + path: Datadog::CI::Ext::Transport::TEST_VISIBILITY_INTAKE_PATH, + payload: encoded_payload, + headers: { + Ext::Transport::HEADER_DD_API_KEY => api_key, + Ext::Transport::HEADER_CONTENT_TYPE => Ext::Transport::CONTENT_TYPE_MESSAGEPACK + } + ) + end + + def encode_traces(traces) + traces.flat_map do |trace| + spans = trace.spans + # TODO: remove condition when 1.0 is released + if spans.respond_to?(:filter_map) + spans.filter_map { |span| encode_span(trace, span) } + else + trace.spans.map { |span| encode_span(trace, span) }.reject(&:nil?) + end + end + end + + def encode_span(trace, span) + serializer = serializers_factory.serializer(trace, span) + + if serializer.valid? + encoded = encoder.encode(serializer) + + if encoded.size > max_payload_size + # This single event is too large, we can't flush it + Datadog.logger.debug { "Dropping test event. Payload too large: '#{span.inspect}'" } + Datadog.logger.debug { encoded } + + return nil + end + + encoded + else + Datadog.logger.debug { "Invalid span skipped: #{span}" } + nil + end + end + + def encoder + Datadog::Core::Encoding::MsgpackEncoder + end + + def pack_events(encoded_events) + packer = MessagePack::Packer.new + + packer.write_map_header(3) # Set header with how many elements in the map + + packer.write("version") + packer.write(1) + + packer.write("metadata") + packer.write_map_header(1) + + packer.write("*") + metadata_fields_count = env ? 4 : 3 + packer.write_map_header(metadata_fields_count) + + if env + packer.write("env") + packer.write(env) + end + + packer.write("runtime-id") + packer.write(Datadog::Core::Environment::Identity.id) + + packer.write("language") + packer.write(Datadog::Core::Environment::Identity.lang) + + packer.write("library_version") + packer.write(Datadog::CI::VERSION::STRING) + + packer.write("events") + packer.write_array_header(encoded_events.size) + + (packer.buffer.to_a + encoded_events).join + end + end + end + end +end diff --git a/lib/datadog/ci/transport/gzip.rb b/lib/datadog/ci/transport/gzip.rb new file mode 100644 index 00000000..f8c9aa98 --- /dev/null +++ b/lib/datadog/ci/transport/gzip.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "zlib" +require "stringio" + +module Datadog + module CI + module Transport + module Gzip + module_function + + def compress(input) + gzip_writer = Zlib::GzipWriter.new(StringIO.new) + gzip_writer << input + gzip_writer.close.string + end + end + end + end +end diff --git a/lib/datadog/ci/transport/http.rb b/lib/datadog/ci/transport/http.rb new file mode 100644 index 00000000..a59ffb2f --- /dev/null +++ b/lib/datadog/ci/transport/http.rb @@ -0,0 +1,153 @@ +# frozen_string_literal: true + +require "net/http" + +require_relative "gzip" +require_relative "../ext/transport" + +module Datadog + module CI + module Transport + class HTTP + attr_reader \ + :host, + :port, + :ssl, + :timeout, + :compress + + DEFAULT_TIMEOUT = 30 + + def initialize(host:, timeout: DEFAULT_TIMEOUT, port: nil, ssl: true, compress: false) + @host = host + @port = port + @timeout = timeout + @ssl = ssl.nil? ? true : ssl + @compress = compress.nil? ? false : compress + end + + def request(path:, payload:, headers:, method: "post") + raise "Unknown method #{method}" unless respond_to?(method, true) + + if compress + headers[Ext::Transport::HEADER_CONTENT_ENCODING] = Ext::Transport::CONTENT_ENCODING_GZIP + payload = Gzip.compress(payload) + end + + Datadog.logger.debug do + "Sending #{method} request: host=#{host}; port=#{port}; ssl_enabled=#{ssl}; " \ + "compression_enabled=#{compress}; path=#{path}; payload_size=#{payload.size}" + end + + send(method, path: path, payload: payload, headers: headers) + end + + private + + def open(&block) + req = ::Net::HTTP.new(@host, @port) + + req.use_ssl = @ssl + req.open_timeout = req.read_timeout = @timeout + + req.start(&block) + end + + def post(path:, headers:, payload:) + post = ::Net::HTTP::Post.new(path, headers) + post.body = payload + + http_response = open do |http| + http.request(post) + end + + Response.new(http_response) + rescue => e + Datadog.logger.debug("Unable to send events: #{e}") + + InternalErrorResponse.new(e) + end + + # Data structure for an HTTP Response + class Response + attr_reader :http_response + + def initialize(http_response) + @http_response = http_response + end + + def payload + http_response.body + end + + def code + http_response.code.to_i + end + + def ok? + code.between?(200, 299) + end + + def unsupported? + code == 415 + end + + def not_found? + code == 404 + end + + def client_error? + code.between?(400, 499) + end + + def server_error? + code.between?(500, 599) + end + + def internal_error? + false + end + + def trace_count + 0 + end + + def inspect + "#{self.class} ok?:#{ok?} unsupported?:#{unsupported?}, " \ + "not_found?:#{not_found?}, client_error?:#{client_error?}, " \ + "server_error?:#{server_error?}, internal_error?:#{internal_error?}, " \ + "payload:#{payload}" + end + end + + class InternalErrorResponse < Response + class DummyNetHTTPResponse + def body + "" + end + + def code + "-1" + end + end + + attr_reader :error + + def initialize(error) + super(DummyNetHTTPResponse.new) + + @error = error + end + + def internal_error? + true + end + + def inspect + "#{super}, error_class:#{error.class}, error:#{error}" + end + end + end + end + end +end diff --git a/sig/datadog/ci/configuration/components.rbs b/sig/datadog/ci/configuration/components.rbs index c716f604..6d7569e6 100644 --- a/sig/datadog/ci/configuration/components.rbs +++ b/sig/datadog/ci/configuration/components.rbs @@ -5,6 +5,8 @@ module Datadog def initialize: (untyped settings) -> void def activate_ci!: (untyped settings) -> untyped + + def build_agentless_transport: (untyped settings) -> Datadog::CI::TestVisibility::Transport end end end diff --git a/sig/datadog/ci/configuration/settings.rbs b/sig/datadog/ci/configuration/settings.rbs index 3d887b0f..d6498cb7 100644 --- a/sig/datadog/ci/configuration/settings.rbs +++ b/sig/datadog/ci/configuration/settings.rbs @@ -3,6 +3,8 @@ module Datadog module Configuration module Settings extend Datadog::Core::Configuration::Options::ClassMethods + include Datadog::Core::Configuration::Options::InstanceMethods + extend Datadog::Core::Configuration::Base::ClassMethods def self.extended: (untyped base) -> untyped diff --git a/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs b/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs index f14bef8a..def7f7a4 100644 --- a/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs +++ b/sig/datadog/ci/contrib/cucumber/configuration/settings.rbs @@ -3,7 +3,7 @@ module Datadog module Contrib module Cucumber module Configuration - class Settings < Datadog::Tracing::Contrib::Configuration::Settings + class Settings < Datadog::CI::Contrib::Settings end end end diff --git a/sig/datadog/ci/contrib/cucumber/integration.rbs b/sig/datadog/ci/contrib/cucumber/integration.rbs index 1dd57b16..9aeee479 100644 --- a/sig/datadog/ci/contrib/cucumber/integration.rbs +++ b/sig/datadog/ci/contrib/cucumber/integration.rbs @@ -3,7 +3,8 @@ module Datadog module Contrib module Cucumber class Integration - extend Datadog::Tracing::Contrib::Integration + extend Datadog::CI::Contrib::Integration::ClassMethods + include Datadog::CI::Contrib::Integration::InstanceMethods MINIMUM_VERSION: Gem::Version diff --git a/sig/datadog/ci/contrib/integration.rbs b/sig/datadog/ci/contrib/integration.rbs new file mode 100644 index 00000000..09908ce6 --- /dev/null +++ b/sig/datadog/ci/contrib/integration.rbs @@ -0,0 +1,44 @@ +module Datadog + module CI + module Contrib + module Integration + self.@registry: Hash[Symbol, untyped] + + def self.included: (Module base) -> void + + def self.register: (untyped integration, Symbol name) -> void + + def self.registry: () -> Hash[Symbol, Struct[untyped]] + + module ClassMethods + def register_as: (Symbol name) -> void + + def version: () -> Gem::Version? + + def available?: () -> bool + + def loaded?: () -> bool + + def compatible?: () -> bool + + def patchable?: () -> bool + end + + module InstanceMethods + extend ClassMethods + @configuration: Datadog::CI::Contrib::Settings? + + def configuration: () -> Datadog::CI::Contrib::Settings + + def configure: (?::Hash[Symbol, untyped] options) ?{ (Datadog::CI::Contrib::Settings) -> Datadog::CI::Contrib::Settings } -> Datadog::CI::Contrib::Settings + + def reset_configuration!: () -> void + + def patcher: () -> Datadog::Tracing::Contrib::Patcher? + + def new_configuration: () -> Datadog::CI::Contrib::Settings + end + end + end + end +end diff --git a/sig/datadog/ci/contrib/minitest/configuration/settings.rbs b/sig/datadog/ci/contrib/minitest/configuration/settings.rbs index bd45cfde..1d4d86ed 100644 --- a/sig/datadog/ci/contrib/minitest/configuration/settings.rbs +++ b/sig/datadog/ci/contrib/minitest/configuration/settings.rbs @@ -3,7 +3,7 @@ module Datadog module Contrib module Minitest module Configuration - class Settings < Datadog::Tracing::Contrib::Configuration::Settings + class Settings < Datadog::CI::Contrib::Settings end end end diff --git a/sig/datadog/ci/contrib/minitest/integration.rbs b/sig/datadog/ci/contrib/minitest/integration.rbs index c4ea0821..ce86c736 100644 --- a/sig/datadog/ci/contrib/minitest/integration.rbs +++ b/sig/datadog/ci/contrib/minitest/integration.rbs @@ -3,13 +3,14 @@ module Datadog module Contrib module Minitest class Integration - extend Datadog::Tracing::Contrib::Integration + extend Datadog::CI::Contrib::Integration::ClassMethods + include Datadog::CI::Contrib::Integration::InstanceMethods - MINIMUM_VERSION: untyped + MINIMUM_VERSION: Gem::Version def self.version: () -> untyped - def self.loaded?: () -> untyped + def self.loaded?: () -> bool def compatible?: () -> bool diff --git a/sig/datadog/ci/contrib/rspec/configuration/settings.rbs b/sig/datadog/ci/contrib/rspec/configuration/settings.rbs index 055a9a86..9217aad3 100644 --- a/sig/datadog/ci/contrib/rspec/configuration/settings.rbs +++ b/sig/datadog/ci/contrib/rspec/configuration/settings.rbs @@ -3,7 +3,7 @@ module Datadog module Contrib module RSpec module Configuration - class Settings < Datadog::Tracing::Contrib::Configuration::Settings + class Settings < Datadog::CI::Contrib::Settings end end end diff --git a/sig/datadog/ci/contrib/rspec/integration.rbs b/sig/datadog/ci/contrib/rspec/integration.rbs index 22f8a820..c56a528f 100644 --- a/sig/datadog/ci/contrib/rspec/integration.rbs +++ b/sig/datadog/ci/contrib/rspec/integration.rbs @@ -3,9 +3,10 @@ module Datadog module Contrib module RSpec class Integration - extend Datadog::Tracing::Contrib::Integration + extend Datadog::CI::Contrib::Integration::ClassMethods + include Datadog::CI::Contrib::Integration::InstanceMethods - MINIMUM_VERSION: untyped + MINIMUM_VERSION: Gem::Version def self.version: () -> untyped diff --git a/sig/datadog/ci/contrib/settings.rbs b/sig/datadog/ci/contrib/settings.rbs new file mode 100644 index 00000000..ca38c544 --- /dev/null +++ b/sig/datadog/ci/contrib/settings.rbs @@ -0,0 +1,25 @@ +module Datadog + module CI + module Contrib + class Settings + include Core::Configuration::Base + extend Datadog::Core::Configuration::Options::ClassMethods + include Datadog::Core::Configuration::Options::InstanceMethods + + extend Datadog::Core::Configuration::Base::ClassMethods + + def configure: (?::Hash[Symbol, untyped] options) ?{ (Datadog::CI::Contrib::Settings) -> Datadog::CI::Contrib::Settings } -> Datadog::CI::Contrib::Settings? + + def []: (Symbol name) -> Datadog::CI::Contrib::Settings + + def []=: (untyped name, untyped value) -> untyped + + # default configuration options + # + def enabled: () -> bool + def service_name: () -> String + def operation_name: () -> String + end + end + end +end diff --git a/sig/datadog/ci/ext/settings.rbs b/sig/datadog/ci/ext/settings.rbs index b133c8d0..5d76a0cd 100644 --- a/sig/datadog/ci/ext/settings.rbs +++ b/sig/datadog/ci/ext/settings.rbs @@ -3,6 +3,8 @@ module Datadog module Ext module Settings ENV_MODE_ENABLED: String + ENV_AGENTLESS_MODE_ENABLED: String + ENV_AGENTLESS_URL: String end end end diff --git a/sig/datadog/ci/ext/transport.rbs b/sig/datadog/ci/ext/transport.rbs new file mode 100644 index 00000000..6c40b3b6 --- /dev/null +++ b/sig/datadog/ci/ext/transport.rbs @@ -0,0 +1,21 @@ +module Datadog + module CI + module Ext + module Transport + HEADER_DD_API_KEY: "DD-API-KEY" + + HEADER_CONTENT_TYPE: "Content-Type" + + HEADER_CONTENT_ENCODING: "Content-Encoding" + + TEST_VISIBILITY_INTAKE_HOST_PREFIX: "citestcycle-intake" + + TEST_VISIBILITY_INTAKE_PATH: "/api/v2/citestcycle" + + CONTENT_TYPE_MESSAGEPACK: "application/msgpack" + + CONTENT_ENCODING_GZIP: "gzip" + end + end + end +end diff --git a/sig/datadog/ci/flush.rbs b/sig/datadog/ci/flush.rbs deleted file mode 100644 index a9c80605..00000000 --- a/sig/datadog/ci/flush.rbs +++ /dev/null @@ -1,15 +0,0 @@ -module Datadog - module CI - module Flush - module Tagging - def get_trace: (untyped trace_op) -> untyped - end - class Finished < Tracing::Flush::Finished - prepend Tagging - end - class Partial < Tracing::Flush::Partial - prepend Tagging - end - end - end -end diff --git a/sig/datadog/ci/test.rbs b/sig/datadog/ci/recorder.rbs similarity index 96% rename from sig/datadog/ci/test.rbs rename to sig/datadog/ci/recorder.rbs index 7943c29a..248cfe9b 100644 --- a/sig/datadog/ci/test.rbs +++ b/sig/datadog/ci/recorder.rbs @@ -1,6 +1,6 @@ module Datadog module CI - module Test + module Recorder self.@environment_tags: Hash[String, String] def self.trace: (untyped span_name, ?::Hash[untyped, untyped] options) ?{ (untyped, untyped) -> untyped } -> untyped diff --git a/sig/datadog/ci/test_visibility/flush.rbs b/sig/datadog/ci/test_visibility/flush.rbs new file mode 100644 index 00000000..6d1f153a --- /dev/null +++ b/sig/datadog/ci/test_visibility/flush.rbs @@ -0,0 +1,17 @@ +module Datadog + module CI + module TestVisibility + module Flush + module Tagging + def get_trace: (Datadog::Tracing::TraceOperation trace_op) -> untyped + end + class Finished < Tracing::Flush::Finished + prepend Tagging + end + class Partial < Tracing::Flush::Partial + prepend Tagging + end + end + end + end +end diff --git a/sig/datadog/ci/test_visibility/serializers/base.rbs b/sig/datadog/ci/test_visibility/serializers/base.rbs new file mode 100644 index 00000000..908b19d6 --- /dev/null +++ b/sig/datadog/ci/test_visibility/serializers/base.rbs @@ -0,0 +1,73 @@ +module Datadog + module CI + module TestVisibility + module Serializers + class Base + MINIMUM_TIMESTAMP_NANO: 946684800000000000 + MINIMUM_DURATION_NANO: 0 + MAXIMUM_DURATION_NANO: 9223372036854775807 + + @content_fields_count: Integer + @start: Integer + @duration: Integer + + attr_reader trace: Datadog::Tracing::TraceSegment + attr_reader span: Datadog::Tracing::Span + + def initialize: (Datadog::Tracing::TraceSegment trace, Datadog::Tracing::Span span) -> void + + def to_msgpack: (?untyped? packer) -> untyped + + def valid?: () -> bool + def content_fields: () -> ::Array[String | Hash[String, String]] + def content_map_size: () -> Integer + + def runtime_id: () -> String + + def trace_id: () -> String + + def span_id: () -> String + + def parent_id: () -> String + + def type: () -> nil + + def version: () -> 1 + + def span_type: () -> String + + def name: () -> String + + def resource: () -> String + + def service: () -> String + + def start: () -> Integer + + def duration: () -> Integer + + def meta: () -> Hash[String, untyped] + + def metrics: () -> Hash[String, untyped] + + def error: () -> Integer + + def self.calculate_content_map_size: (Array[String | Hash[String, String]] fields_list) -> Integer + + private + + def valid_start_time?: () -> bool + def valid_duration?: () -> bool + def required_fields_present?: () -> bool + def required_fields: () -> Array[String] + + def write_field: (untyped packer, String field_name, ?String? method) -> untyped + def time_nano: (Time time) -> Integer + def duration_nano: (Float duration) -> Integer + + def content_fields_count: () -> Integer + end + end + end + end +end diff --git a/sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs b/sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs new file mode 100644 index 00000000..ac7b6c13 --- /dev/null +++ b/sig/datadog/ci/test_visibility/serializers/factories/test_level.rbs @@ -0,0 +1,13 @@ +module Datadog + module CI + module TestVisibility + module Serializers + module Factories + module TestLevel + def self?.serializer: (Datadog::Tracing::TraceSegment trace, Datadog::Tracing::Span span) -> Datadog::CI::TestVisibility::Serializers::Base + end + end + end + end + end +end diff --git a/sig/datadog/ci/test_visibility/serializers/span.rbs b/sig/datadog/ci/test_visibility/serializers/span.rbs new file mode 100644 index 00000000..4b1c449c --- /dev/null +++ b/sig/datadog/ci/test_visibility/serializers/span.rbs @@ -0,0 +1,18 @@ +module Datadog + module CI + module TestVisibility + module Serializers + class Span < Base + CONTENT_FIELDS: Array[String | Hash[String, String]] + CONTENT_MAP_SIZE: Integer + REQUIRED_FIELDS: Array[String] + + def required_fields: () -> Array[String] + def content_fields: () -> Array[String | Hash[String, String]] + def content_map_size: () -> Integer + def type: () -> "span" + end + end + end + end +end diff --git a/sig/datadog/ci/test_visibility/serializers/test_v1.rbs b/sig/datadog/ci/test_visibility/serializers/test_v1.rbs new file mode 100644 index 00000000..a590d687 --- /dev/null +++ b/sig/datadog/ci/test_visibility/serializers/test_v1.rbs @@ -0,0 +1,23 @@ +module Datadog + module CI + module TestVisibility + module Serializers + class TestV1 < Base + CONTENT_FIELDS: Array[String | Hash[String, String]] + CONTENT_MAP_SIZE: Integer + REQUIRED_FIELDS: Array[String] + + def required_fields: () -> Array[String] + def content_fields: () -> Array[String | Hash[String, String]] + def content_map_size: () -> Integer + + def type: () -> "test" + + def name: () -> ::String + + def resource: () -> ::String + end + end + end + end +end diff --git a/sig/datadog/ci/test_visibility/transport.rbs b/sig/datadog/ci/test_visibility/transport.rbs new file mode 100644 index 00000000..c1058380 --- /dev/null +++ b/sig/datadog/ci/test_visibility/transport.rbs @@ -0,0 +1,39 @@ +module Datadog + module CI + module TestVisibility + class Transport + DEFAULT_MAX_PAYLOAD_SIZE: Integer + + attr_reader serializers_factory: singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestLevel) + attr_reader api_key: String + attr_reader env: String? + attr_reader http: Datadog::CI::Transport::HTTP + attr_reader max_payload_size: Integer + + @api_key: String + @env: String? + @http: Datadog::CI::Transport::HTTP + @serializers_factory: singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestLevel) + @max_payload_size: Integer + + def initialize: ( + api_key: String, + url: ::String, + ?env: ::String?, + ?serializers_factory: singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestLevel), + ?max_payload_size: Integer + ) -> void + + def send_traces: (Array[Datadog::Tracing::TraceSegment] traces) -> ::Array[Datadog::CI::Transport::HTTP::Response] + + private + + def send_payload: (String encoded_payload) -> Datadog::CI::Transport::HTTP::Response + def pack_events: (Array[String] encoded_events) -> String + def encode_traces: (Array[Datadog::Tracing::TraceSegment] traces) -> ::Array[String] + def encode_span: (Datadog::Tracing::TraceSegment trace, Datadog::Tracing::Span span) -> String? + def encoder: () -> singleton(Datadog::Core::Encoding::MsgpackEncoder) + end + end + end +end diff --git a/sig/datadog/ci/transport/gzip.rbs b/sig/datadog/ci/transport/gzip.rbs new file mode 100644 index 00000000..303b96c1 --- /dev/null +++ b/sig/datadog/ci/transport/gzip.rbs @@ -0,0 +1,9 @@ +module Datadog + module CI + module Transport + module Gzip + def self?.compress: (String input) -> String + end + end + end +end diff --git a/sig/datadog/ci/transport/http.rbs b/sig/datadog/ci/transport/http.rbs new file mode 100644 index 00000000..bab7f203 --- /dev/null +++ b/sig/datadog/ci/transport/http.rbs @@ -0,0 +1,61 @@ +module Datadog + module CI + module Transport + class HTTP + attr_reader host: String + attr_reader port: Integer? + attr_reader ssl: bool + attr_reader timeout: Integer + attr_reader compress: bool + + DEFAULT_TIMEOUT: 30 + + def initialize: (host: String, ?port: Integer?, ?ssl: bool, ?timeout: Integer, ?compress: bool) -> void + + def request: (?method: String, payload: String, headers: Hash[String, String], path: String) -> Response + + private + + def open: () { (::Net::HTTP) -> Net::HTTPResponse } -> Net::HTTPResponse + + def post: (payload: String, headers: Hash[String, String], path: String) -> Response + + class Response + attr_reader http_response: (Net::HTTPResponse | InternalErrorResponse::DummyNetHTTPResponse) + + def initialize: ((Net::HTTPResponse | InternalErrorResponse::DummyNetHTTPResponse) http_response) -> void + + def payload: () -> String + + def code: () -> Integer + + def ok?: () -> bool + + def unsupported?: () -> bool + + def not_found?: () -> bool + + def client_error?: () -> bool + + def server_error?: () -> bool + + def internal_error?: () -> bool + + def inspect: () -> ::String + end + + class InternalErrorResponse < Response + class DummyNetHTTPResponse + def body: () -> "" + def code: () -> "-1" + end + + attr_reader error: StandardError + @error: StandardError + + def initialize: (StandardError error) -> void + end + end + end + end +end diff --git a/spec/datadog/ci/configuration/components_spec.rb b/spec/datadog/ci/configuration/components_spec.rb index 26efcc97..9f5283d2 100644 --- a/spec/datadog/ci/configuration/components_spec.rb +++ b/spec/datadog/ci/configuration/components_spec.rb @@ -38,6 +38,22 @@ .to receive(:enabled) .and_return(enabled) + allow(settings.ci) + .to receive(:agentless_mode_enabled) + .and_return(agentless_enabled) + + allow(settings.ci) + .to receive(:agentless_url) + .and_return(agentless_url) + + allow(settings) + .to receive(:site) + .and_return(dd_site) + + allow(settings) + .to receive(:api_key) + .and_return(api_key) + # Spy on test mode behavior allow(settings.tracing.test_mode) .to receive(:enabled=) @@ -48,33 +64,112 @@ allow(settings.tracing.test_mode) .to receive(:writer_options=) + allow(settings.tracing.test_mode) + .to receive(:async=) + + allow(settings.ci) + .to receive(:enabled=) + + allow(Datadog.logger) + .to receive(:error) + components end + let(:api_key) { nil } + let(:agentless_url) { nil } + let(:dd_site) { nil } + context "is enabled" do let(:enabled) { true } - it do - expect(settings.tracing.test_mode) - .to have_received(:enabled=) - .with(true) - end - - it do - expect(settings.tracing.test_mode) - .to have_received(:trace_flush=) - .with(settings.ci.trace_flush || kind_of(Datadog::CI::Flush::Finished)) - end - - it do - expect(settings.tracing.test_mode) - .to have_received(:writer_options=) - .with(settings.ci.writer_options) + context "and when #agentless_mode" do + context "is disabled" do + let(:agentless_enabled) { false } + + it do + expect(settings.tracing.test_mode) + .to have_received(:enabled=) + .with(true) + end + + it do + expect(settings.tracing.test_mode) + .to have_received(:trace_flush=) + .with(settings.ci.trace_flush || kind_of(Datadog::CI::TestVisibility::Flush::Finished)) + end + + it do + expect(settings.tracing.test_mode) + .to have_received(:writer_options=) + .with(settings.ci.writer_options) + end + end + + context "is enabled" do + let(:agentless_enabled) { true } + + context "when api key is set" do + let(:api_key) { "api_key" } + + it "sets async for test mode and provides transport and shutdown timeout to the write" do + expect(settings.tracing.test_mode) + .to have_received(:async=) + .with(true) + + expect(settings.tracing.test_mode).to have_received(:writer_options=) do |options| + expect(options[:transport]).to be_kind_of(Datadog::CI::TestVisibility::Transport) + expect(options[:shutdown_timeout]).to eq(60) + + http_client = options[:transport].http + expect(http_client.host).to eq("citestcycle-intake.datadoghq.com") + expect(http_client.port).to eq(443) + expect(http_client.ssl).to eq(true) + end + end + + context "when agentless_url is provided" do + let(:agentless_url) { "http://localhost:5555" } + + it "configures transport to use intake URL from settings" do + expect(settings.tracing.test_mode).to have_received(:writer_options=) do |options| + http_client = options[:transport].http + expect(http_client.host).to eq("localhost") + expect(http_client.port).to eq(5555) + expect(http_client.ssl).to eq(false) + end + end + end + + context "when dd_site is provided" do + let(:dd_site) { "eu.datadoghq.com" } + + it "construct intake url using provided host" do + expect(settings.tracing.test_mode).to have_received(:writer_options=) do |options| + http_client = options[:transport].http + expect(http_client.host).to eq("citestcycle-intake.eu.datadoghq.com") + expect(http_client.port).to eq(443) + expect(http_client.ssl).to eq(true) + end + end + end + end + + context "when api key is not set" do + let(:api_key) { nil } + + it "logs an error message and disables CI visibility" do + expect(Datadog.logger).to have_received(:error) + expect(settings.ci).to have_received(:enabled=).with(false) + end + end + end end end context "is disabled" do let(:enabled) { false } + let(:agentless_enabled) { false } it do expect(settings.tracing.test_mode) diff --git a/spec/datadog/ci/configuration/settings_spec.rb b/spec/datadog/ci/configuration/settings_spec.rb index 5ff2dcc3..e58fcab7 100644 --- a/spec/datadog/ci/configuration/settings_spec.rb +++ b/spec/datadog/ci/configuration/settings_spec.rb @@ -1,3 +1,46 @@ +# Dummy Integration +class FakeIntegration + include Datadog::CI::Contrib::Integration + + register_as :fake + + module Patcher + module_function + + def patched? + @patched + end + + def patch + @patched = true + end + + def reset + @patched = nil + end + end + + def self.version + "0.1" + end + + def self.loaded? + true + end + + def self.compatible? + true + end + + def self.auto_instrument? + false + end + + def patcher + Patcher + end +end + RSpec.describe Datadog::CI::Configuration::Settings do context "when used to extend Datadog::Core::Configuration::Settings" do subject(:settings) do @@ -53,6 +96,177 @@ end end + describe "#agentless_mode_enabled" do + subject(:agentless_mode_enabled) { settings.ci.agentless_mode_enabled } + + it { is_expected.to be false } + + context "when #{Datadog::CI::Ext::Settings::ENV_AGENTLESS_MODE_ENABLED}" do + around do |example| + ClimateControl.modify(Datadog::CI::Ext::Settings::ENV_AGENTLESS_MODE_ENABLED => enable) do + example.run + end + end + + context "is not defined" do + let(:enable) { nil } + + it { is_expected.to be false } + end + + context "is set to true" do + let(:enable) { "true" } + + it { is_expected.to be true } + end + + context "is set to false" do + let(:enable) { "false" } + + it { is_expected.to be false } + end + end + end + + describe "#agentless_mode_enabled=" do + it "updates the #enabled setting" do + expect { settings.ci.agentless_mode_enabled = true } + .to change { settings.ci.agentless_mode_enabled } + .from(false) + .to(true) + end + end + + describe "#agentless_url" do + subject(:agentless_url) { settings.ci.agentless_url } + + it { is_expected.to be nil } + + context "when #{Datadog::CI::Ext::Settings::ENV_AGENTLESS_URL}" do + around do |example| + ClimateControl.modify(Datadog::CI::Ext::Settings::ENV_AGENTLESS_URL => agentless_url) do + example.run + end + end + + context "is not defined" do + let(:agentless_url) { nil } + + it { is_expected.to be nil } + end + + context "is set to some value" do + let(:agentless_url) { "example.com" } + + it { is_expected.to eq agentless_url } + end + end + end + + describe "#agentless_url=" do + it "updates the #enabled setting" do + expect { settings.ci.agentless_url = "example.com" } + .to change { settings.ci.agentless_url } + .from(nil) + .to("example.com") + end + end + + describe "#instrument" do + let(:integration_name) { :fake } + + let(:integration) { FakeIntegration.new } + let(:enabled) { true } + + subject(:instrument) { settings.ci.instrument(integration_name, enabled: enabled) } + + before do + settings.ci.enabled = ci_enabled + end + + after do + FakeIntegration::Patcher.reset + end + + context "ci enabled" do + let(:ci_enabled) { true } + + context "when integration exists" do + it "patches the integration" do + expect(FakeIntegration::Patcher).to receive(:patch) + + instrument + end + + context "when called multiple times" do + it "does not patch the integration multiple times" do + expect(FakeIntegration::Patcher).to receive(:patch).and_call_original.once + + instrument + instrument + end + end + + context "when not loaded" do + before { allow(FakeIntegration).to receive(:loaded?).and_return(false) } + + it "does not patch the integration" do + expect(FakeIntegration::Patcher).to_not receive(:patch) + + instrument + end + end + + context "when not available" do + before { allow(FakeIntegration).to receive(:available?).and_return(false) } + + it "does not patch the integration" do + expect(FakeIntegration::Patcher).to_not receive(:patch) + + instrument + end + end + + context "when not compatible" do + before { allow(FakeIntegration).to receive(:compatible?).and_return(false) } + + it "does not patch the integration" do + expect(FakeIntegration::Patcher).to_not receive(:patch) + + instrument + end + end + + context "when not enabled" do + let(:enabled) { false } + + it "does not patch the integration" do + expect(FakeIntegration::Patcher).to_not receive(:patch) + + instrument + end + end + end + + context "when integration does not exist" do + let(:integration_name) { :not_existing } + + it "does not patch the integration" do + expect { instrument }.to raise_error(Datadog::CI::Configuration::Settings::InvalidIntegrationError) + end + end + + context "ci is not enabled" do + let(:ci_enabled) { false } + + it "does not patch the integration" do + expect(FakeIntegration::Patcher).to_not receive(:patch) + instrument + end + end + end + end + describe "#trace_flush" do subject(:trace_flush) { settings.ci.trace_flush } diff --git a/spec/datadog/ci/contrib/cucumber/formatter_spec.rb b/spec/datadog/ci/contrib/cucumber/instrumentation_spec.rb similarity index 89% rename from spec/datadog/ci/contrib/cucumber/formatter_spec.rb rename to spec/datadog/ci/contrib/cucumber/instrumentation_spec.rb index 2bdebfb2..7a7adcb7 100644 --- a/spec/datadog/ci/contrib/cucumber/formatter_spec.rb +++ b/spec/datadog/ci/contrib/cucumber/instrumentation_spec.rb @@ -1,12 +1,13 @@ -require_relative "../support/spec_helper" - require "stringio" require "cucumber" RSpec.describe "Cucumber formatter" do extend ConfigurationHelpers - include_context "CI mode activated" + include_context "CI mode activated" do + let(:integration_name) { :cucumber } + let(:integration_options) { {service_name: "jalapenos"} } + end # Cucumber runtime setup let(:existing_runtime) { Cucumber::Runtime.new(runtime_options) } @@ -27,12 +28,6 @@ end end - before do - Datadog.configure do |c| - c.ci.instrument :cucumber, service_name: "jalapenos" - end - end - context "executing a test suite" do let(:args) { ["spec/datadog/ci/contrib/cucumber/cucumber.features"] } diff --git a/spec/datadog/ci/contrib/cucumber/integration_spec.rb b/spec/datadog/ci/contrib/cucumber/integration_spec.rb index 8a5a0321..be1a4e81 100644 --- a/spec/datadog/ci/contrib/cucumber/integration_spec.rb +++ b/spec/datadog/ci/contrib/cucumber/integration_spec.rb @@ -1,9 +1,7 @@ -require_relative "../support/spec_helper" - RSpec.describe Datadog::CI::Contrib::Cucumber::Integration do extend ConfigurationHelpers - let(:integration) { described_class.new(:cucumber) } + let(:integration) { described_class.new } describe ".version" do subject(:version) { described_class.version } @@ -62,8 +60,8 @@ it { is_expected.to be(false) } end - describe "#default_configuration" do - subject(:default_configuration) { integration.default_configuration } + describe "#configuration" do + subject(:configuration) { integration.configuration } it { is_expected.to be_a_kind_of(Datadog::CI::Contrib::Cucumber::Configuration::Settings) } end diff --git a/spec/datadog/ci/contrib/cucumber/patcher_spec.rb b/spec/datadog/ci/contrib/cucumber/patcher_spec.rb index a778e237..d7de0ec1 100644 --- a/spec/datadog/ci/contrib/cucumber/patcher_spec.rb +++ b/spec/datadog/ci/contrib/cucumber/patcher_spec.rb @@ -1,5 +1,3 @@ -require_relative "../support/spec_helper" - RSpec.describe Datadog::CI::Contrib::Cucumber::Patcher do describe ".patch" do subject!(:patch) { described_class.patch } diff --git a/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb b/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb index fd93f86f..d6ea4286 100644 --- a/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb +++ b/spec/datadog/ci/contrib/minitest/instrumentation_spec.rb @@ -2,18 +2,15 @@ require "minitest" require "minitest/spec" -require_relative "../support/spec_helper" - RSpec.describe "Minitest hooks" do - include_context "CI mode activated" + include_context "CI mode activated" do + let(:integration_name) { :minitest } + let(:integration_options) { {service_name: "ltest"} } + end before do # required to call .runnable_methods Minitest.seed = 1 - - Datadog.configure do |c| - c.ci.instrument :minitest, service_name: "ltest" - end end it "creates span for test" do diff --git a/spec/datadog/ci/contrib/minitest/integration_spec.rb b/spec/datadog/ci/contrib/minitest/integration_spec.rb index b0ce04a3..5de418f6 100644 --- a/spec/datadog/ci/contrib/minitest/integration_spec.rb +++ b/spec/datadog/ci/contrib/minitest/integration_spec.rb @@ -1,9 +1,7 @@ -require_relative "../support/spec_helper" - RSpec.describe Datadog::CI::Contrib::Minitest::Integration do extend ConfigurationHelpers - let(:integration) { described_class.new(:minitest) } + let(:integration) { described_class.new } describe ".version" do subject(:version) { described_class.version } @@ -54,8 +52,8 @@ it { is_expected.to be(false) } end - describe "#default_configuration" do - subject(:default_configuration) { integration.default_configuration } + describe "#configuration" do + subject(:configuration) { integration.configuration } it { is_expected.to be_a_kind_of(Datadog::CI::Contrib::Minitest::Configuration::Settings) } end diff --git a/spec/datadog/ci/contrib/minitest/patcher_spec.rb b/spec/datadog/ci/contrib/minitest/patcher_spec.rb index 1107bcef..313c144f 100644 --- a/spec/datadog/ci/contrib/minitest/patcher_spec.rb +++ b/spec/datadog/ci/contrib/minitest/patcher_spec.rb @@ -1,5 +1,3 @@ -require_relative "../support/spec_helper" - require "minitest" RSpec.describe Datadog::CI::Contrib::Minitest::Patcher do diff --git a/spec/datadog/ci/contrib/rspec/instrumentation_spec.rb b/spec/datadog/ci/contrib/rspec/instrumentation_spec.rb index 715ca2f4..5638451e 100644 --- a/spec/datadog/ci/contrib/rspec/instrumentation_spec.rb +++ b/spec/datadog/ci/contrib/rspec/instrumentation_spec.rb @@ -1,14 +1,9 @@ require "time" -require_relative "../support/spec_helper" - RSpec.describe "RSpec hooks" do - include_context "CI mode activated" - - before do - Datadog.configure do |c| - c.ci.instrument :rspec, service_name: "lspec" - end + include_context "CI mode activated" do + let(:integration_name) { :rspec } + let(:integration_options) { {service_name: "lspec"} } end # Yields to a block in a new RSpec global context. All RSpec diff --git a/spec/datadog/ci/contrib/rspec/integration_spec.rb b/spec/datadog/ci/contrib/rspec/integration_spec.rb index 6221e82f..3588365e 100644 --- a/spec/datadog/ci/contrib/rspec/integration_spec.rb +++ b/spec/datadog/ci/contrib/rspec/integration_spec.rb @@ -1,9 +1,7 @@ -require_relative "../support/spec_helper" - RSpec.describe Datadog::CI::Contrib::RSpec::Integration do extend ConfigurationHelpers - let(:integration) { described_class.new(:rspec) } + let(:integration) { described_class.new } describe ".version" do subject(:version) { described_class.version } @@ -54,8 +52,8 @@ it { is_expected.to be(false) } end - describe "#default_configuration" do - subject(:default_configuration) { integration.default_configuration } + describe "#configuration" do + subject(:configuration) { integration.configuration } it { is_expected.to be_a_kind_of(Datadog::CI::Contrib::RSpec::Configuration::Settings) } end diff --git a/spec/datadog/ci/contrib/rspec/patcher_spec.rb b/spec/datadog/ci/contrib/rspec/patcher_spec.rb index 54ad8876..60e15a86 100644 --- a/spec/datadog/ci/contrib/rspec/patcher_spec.rb +++ b/spec/datadog/ci/contrib/rspec/patcher_spec.rb @@ -1,5 +1,3 @@ -require_relative "../support/spec_helper" - RSpec.describe Datadog::CI::Contrib::RSpec::Patcher do describe ".patch" do subject!(:patch) { described_class.patch } diff --git a/spec/datadog/ci/contrib/support/mode_helpers.rb b/spec/datadog/ci/contrib/support/mode_helpers.rb deleted file mode 100644 index 434f9afa..00000000 --- a/spec/datadog/ci/contrib/support/mode_helpers.rb +++ /dev/null @@ -1,17 +0,0 @@ -RSpec.shared_context "CI mode activated" do - let(:settings) do - Datadog::Core::Configuration::Settings.new.tap do |settings| - settings.ci.enabled = true - end - end - - let(:components) { Datadog::Core::Configuration::Components.new(settings) } - - before do - allow(Datadog::Tracing) - .to receive(:tracer) - .and_return(components.tracer) - end - - after { components.shutdown! } -end diff --git a/spec/datadog/ci/contrib/support/spec_helper.rb b/spec/datadog/ci/contrib/support/spec_helper.rb deleted file mode 100644 index afc41b0c..00000000 --- a/spec/datadog/ci/contrib/support/spec_helper.rb +++ /dev/null @@ -1,32 +0,0 @@ -require_relative "mode_helpers" -require_relative "tracer_helpers" - -if defined?(Warning.ignore) - # Caused by https://github.com/cucumber/cucumber-ruby/blob/47c8e2d7c97beae8541c895a43f9ccb96324f0f1/lib/cucumber/encoding.rb#L5-L6 - Gem.path.each do |path| - Warning.ignore(/setting Encoding.default_external/, path) - Warning.ignore(/setting Encoding.default_internal/, path) - end -end - -RSpec.configure do |config| - config.include Contrib::TracerHelpers - - # Raise error when patching an integration fails. - # This can be disabled by unstubbing +CommonMethods#on_patch_error+ - require "datadog/tracing/contrib/patcher" - config.before do - allow_any_instance_of(Datadog::Tracing::Contrib::Patcher::CommonMethods).to(receive(:on_patch_error)) { |_, e| raise e } - end - - # Ensure tracer environment is clean before running tests. - # - # This is done :before and not :after because doing so after - # can create noise for test assertions. For example: - # +expect(Datadog).to receive(:shutdown!).once+ - config.before do - Datadog.shutdown! - # without_warnings { Datadog.configuration.reset! } - Datadog.configuration.reset! - end -end diff --git a/spec/datadog/ci/contrib/support/tracer_helpers.rb b/spec/datadog/ci/contrib/support/tracer_helpers.rb deleted file mode 100644 index 03c7c3a3..00000000 --- a/spec/datadog/ci/contrib/support/tracer_helpers.rb +++ /dev/null @@ -1,101 +0,0 @@ -module Contrib - # Contrib-specific tracer helpers. - # For contrib, we only allow one tracer to be active: - # the global tracer in +Datadog::Tracing+. - module TracerHelpers - # Returns the current tracer instance - def tracer - Datadog::Tracing.send(:tracer) - end - - # Returns spans and caches it (similar to +let(:spans)+). - def traces - @traces ||= fetch_traces - end - - # Returns spans and caches it (similar to +let(:spans)+). - def spans - @spans ||= fetch_spans - end - - # Retrieves all traces in the current tracer instance. - # This method does not cache its results. - def fetch_traces(tracer = self.tracer) - tracer.instance_variable_get(:@traces) || [] - end - - # Retrieves and sorts all spans in the current tracer instance. - # This method does not cache its results. - def fetch_spans(tracer = self.tracer) - traces = fetch_traces(tracer) - traces.collect(&:spans).flatten.sort! do |a, b| - if a.name == b.name - if a.resource == b.resource - if a.start_time == b.start_time - a.end_time <=> b.end_time - else - a.start_time <=> b.start_time - end - else - a.resource <=> b.resource - end - else - a.name <=> b.name - end - end - end - - # Remove all traces from the current tracer instance and - # busts cache of +#spans+ and +#span+. - def clear_traces! - tracer.instance_variable_set(:@traces, []) - - @traces = nil - @trace = nil - @spans = nil - @span = nil - end - - RSpec.configure do |config| - # Capture spans from the global tracer - config.before do - # DEV `*_any_instance_of` has concurrency issues when running with parallelism (e.g. JRuby). - # DEV Single object `allow` and `expect` work as intended with parallelism. - allow(Datadog::Tracing::Tracer).to receive(:new).and_wrap_original do |method, **args, &block| - instance = method.call(**args, &block) - - # The mutex must be eagerly initialized to prevent race conditions on lazy initialization - write_lock = Mutex.new - allow(instance).to receive(:write) do |trace| - instance.instance_exec do - write_lock.synchronize do - @traces ||= [] - @traces << trace - end - end - end - - instance - end - end - - # Execute shutdown! after the test has finished - # teardown and mock verifications. - # - # Changing this to `config.after(:each)` would - # put shutdown! inside the test scope, interfering - # with mock assertions. - config.around do |example| - example.run.tap do - Datadog::Tracing.shutdown! - end - end - end - - # Useful for integration testing. - def use_real_tracer! - @use_real_tracer = true - allow(Datadog::Tracing::Tracer).to receive(:new).and_call_original - end - end -end diff --git a/spec/datadog/ci/test_spec.rb b/spec/datadog/ci/recorder_spec.rb similarity index 99% rename from spec/datadog/ci/test_spec.rb rename to spec/datadog/ci/recorder_spec.rb index fefcbcae..cee9efac 100644 --- a/spec/datadog/ci/test_spec.rb +++ b/spec/datadog/ci/recorder_spec.rb @@ -1,4 +1,4 @@ -RSpec.describe Datadog::CI::Test do +RSpec.describe Datadog::CI::Recorder do let(:trace_op) { instance_double(Datadog::Tracing::TraceOperation) } let(:span_name) { "span name" } diff --git a/spec/datadog/ci/flush_spec.rb b/spec/datadog/ci/test_visibility/flush_spec.rb similarity index 92% rename from spec/datadog/ci/flush_spec.rb rename to spec/datadog/ci/test_visibility/flush_spec.rb index 3de76ccf..034dd006 100644 --- a/spec/datadog/ci/flush_spec.rb +++ b/spec/datadog/ci/test_visibility/flush_spec.rb @@ -33,7 +33,7 @@ end end -RSpec.describe Datadog::CI::Flush::Finished do +RSpec.describe Datadog::CI::TestVisibility::Flush::Finished do subject(:trace_flush) { described_class.new } describe "#consume" do @@ -44,7 +44,7 @@ end end -RSpec.describe Datadog::CI::Flush::Partial do +RSpec.describe Datadog::CI::TestVisibility::Flush::Partial do subject(:trace_flush) { described_class.new(min_spans_before_partial_flush: min_spans_for_partial) } let(:min_spans_for_partial) { 2 } diff --git a/spec/datadog/ci/test_visibility/serializers/factories/test_level_spec.rb b/spec/datadog/ci/test_visibility/serializers/factories/test_level_spec.rb new file mode 100644 index 00000000..72555693 --- /dev/null +++ b/spec/datadog/ci/test_visibility/serializers/factories/test_level_spec.rb @@ -0,0 +1,21 @@ +require_relative "../../../../../../lib/datadog/ci/test_visibility/serializers/factories/test_level" +require_relative "../../../../../../lib/datadog/ci/test_visibility/serializers/test_v1" +require_relative "../../../../../../lib/datadog/ci/recorder" + +RSpec.describe Datadog::CI::TestVisibility::Serializers::Factories::TestLevel do + include_context "CI mode activated" do + let(:integration_name) { :rspec } + end + + subject { described_class.serializer(trace, span) } + + describe ".convert_trace_to_serializable_events" do + context "traced a single test execution with Recorder" do + before do + produce_test_trace + end + + it { is_expected.to be_kind_of(Datadog::CI::TestVisibility::Serializers::TestV1) } + end + end +end diff --git a/spec/datadog/ci/test_visibility/serializers/span_spec.rb b/spec/datadog/ci/test_visibility/serializers/span_spec.rb new file mode 100644 index 00000000..cb3fe02a --- /dev/null +++ b/spec/datadog/ci/test_visibility/serializers/span_spec.rb @@ -0,0 +1,67 @@ +require_relative "../../../../../lib/datadog/ci/test_visibility/serializers/span" +require_relative "../../../../../lib/datadog/ci/recorder" + +RSpec.describe Datadog::CI::TestVisibility::Serializers::Span do + include_context "CI mode activated" do + let(:integration_name) { :rspec } + end + + include_context "Test visibility event serialized" do + subject { described_class.new(trace, tracer_span) } + end + + let(:test_span) do + spans.find { |span| span.type == "test" } + end + + let(:tracer_span) do + spans.find { |span| span.type != "test" } + end + + describe "#to_msgpack" do + context "traced a single test execution with Recorder" do + before do + produce_test_trace(with_http_span: true) + end + + it "serializes test event to messagepack" do + expect_event_header(type: "span") + expect(content).to include( + { + "trace_id" => trace.id, + "span_id" => first_other_span.id, + "parent_id" => first_test_span.id, + "name" => "http-call", + "service" => "net-http", + "type" => "http", + "error" => 0, + "resource" => "http-call" + } + ) + expect(content).to include("start", "duration") + + expect(meta).to include( + { + "custom_tag" => "custom_tag_value", + "_dd.origin" => "ciapp-test" + } + ) + expect(metrics).to eq({"_dd.top_level" => 1.0, "custom_metric" => 42}) + end + end + end + + describe "valid?" do + context "required fields" do + context "when not present" do + before do + produce_test_trace(with_http_span: true) + + tracer_span.name = nil + end + + it { is_expected.not_to be_valid } + end + end + end +end diff --git a/spec/datadog/ci/test_visibility/serializers/test_v1_spec.rb b/spec/datadog/ci/test_visibility/serializers/test_v1_spec.rb new file mode 100644 index 00000000..8d33044b --- /dev/null +++ b/spec/datadog/ci/test_visibility/serializers/test_v1_spec.rb @@ -0,0 +1,122 @@ +require_relative "../../../../../lib/datadog/ci/test_visibility/serializers/test_v1" +require_relative "../../../../../lib/datadog/ci/recorder" + +RSpec.describe Datadog::CI::TestVisibility::Serializers::TestV1 do + include_context "CI mode activated" do + let(:integration_name) { :rspec } + end + + include_context "Test visibility event serialized" do + subject { described_class.new(trace, span) } + end + + describe "#to_msgpack" do + context "traced a single test execution with Recorder" do + before do + produce_test_trace + end + + it "serializes test event to messagepack" do + expect_event_header + + expect(content).to include( + { + "trace_id" => trace.id, + "span_id" => span.id, + "name" => "rspec.test", + "service" => "rspec-test-suite", + "type" => "test", + "resource" => "calculator_tests.test_add" + } + ) + + expect(meta).to include( + { + "test.framework" => "rspec", + "test.status" => "pass", + "_dd.origin" => "ciapp-test", + "test_owner" => "my_team" + } + ) + expect(metrics).to eq( + {"_dd.measured" => 1.0, "_dd.top_level" => 1.0, "memory_allocations" => 16} + ) + end + end + + context "trace a failed test" do + before do + produce_test_trace(result: "FAILED", exception: StandardError.new("1 + 2 are not equal to 5")) + end + + it "has error" do + expect_event_header + + expect(content).to include({"error" => 1}) + expect(meta).to include({"test.status" => "fail"}) + end + end + + context "with time and duration expectations" do + let(:start_time) { Time.now } + let(:duration_seconds) { 3 } + + before do + produce_test_trace(start_time: start_time, duration_seconds: duration_seconds) + end + + it "correctly serializes start and duration in nanoseconds" do + expect(content).to include({ + "start" => start_time.to_i * 1_000_000_000 + start_time.nsec, + "duration" => 3 * 1_000_000_000 + }) + end + end + end + + describe "#valid?" do + context "duration" do + before do + produce_test_trace + span.duration = duration + end + + context "when positive number" do + let(:duration) { 42 } + + it { is_expected.to be_valid } + end + + context "when negative number" do + let(:duration) { -1 } + + it { is_expected.not_to be_valid } + end + + context "when too big" do + let(:duration) { Datadog::CI::TestVisibility::Serializers::Base::MAXIMUM_DURATION_NANO + 1 } + + it { is_expected.not_to be_valid } + end + end + + context "start" do + before do + produce_test_trace + span.start_time = start_time + end + + context "when now" do + let(:start_time) { Time.now } + + it { is_expected.to be_valid } + end + + context "when far in the past" do + let(:start_time) { Time.at(0) } + + it { is_expected.not_to be_valid } + end + end + end +end diff --git a/spec/datadog/ci/test_visibility/transport_spec.rb b/spec/datadog/ci/test_visibility/transport_spec.rb new file mode 100644 index 00000000..a775da46 --- /dev/null +++ b/spec/datadog/ci/test_visibility/transport_spec.rb @@ -0,0 +1,164 @@ +require_relative "../../../../lib/datadog/ci/test_visibility/transport" + +RSpec.describe Datadog::CI::TestVisibility::Transport do + include_context "CI mode activated" do + let(:integration_name) { :rspec } + end + + subject do + described_class.new( + api_key: api_key, + env: env, + url: url, + serializers_factory: serializers_factory, + max_payload_size: max_payload_size + ) + end + + let(:api_key) { "api_key" } + let(:env) { nil } + let(:url) { "https://citestcycle-intake.datad0ghq.com:443" } + let(:serializers_factory) { Datadog::CI::TestVisibility::Serializers::Factories::TestLevel } + let(:max_payload_size) { 4 * 1024 * 1024 } + + let(:http) { spy(:http) } + + before do + expect(Datadog::CI::Transport::HTTP).to receive(:new).with( + host: "citestcycle-intake.datad0ghq.com", + port: 443, + ssl: true, + compress: true + ).and_return(http) + end + + describe "#send_traces" do + context "with a single trace and a single span" do + before do + produce_test_trace + end + + it "sends correct payload" do + subject.send_traces([trace]) + + expect(http).to have_received(:request) do |args| + expect(args[:path]).to eq("/api/v2/citestcycle") + expect(args[:headers]).to eq({ + "DD-API-KEY" => "api_key", + "Content-Type" => "application/msgpack" + }) + + payload = MessagePack.unpack(args[:payload]) + expect(payload["version"]).to eq(1) + + metadata = payload["metadata"]["*"] + expect(metadata).to include("runtime-id", "library_version") + expect(metadata["language"]).to eq("ruby") + + events = payload["events"] + expect(events.count).to eq(1) + expect(events.first["content"]["resource"]).to include("calculator_tests") + end + end + end + + context "with env defined" do + let(:env) { "ci" } + before do + produce_test_trace + end + + it "sends correct payload including env" do + subject.send_traces([trace]) + + expect(http).to have_received(:request) do |args| + payload = MessagePack.unpack(args[:payload]) + + metadata = payload["metadata"]["*"] + expect(metadata["env"]).to eq("ci") + end + end + end + + context "multiple traces with 2 spans each" do + let(:traces_count) { 2 } + let(:expected_events_count) { 4 } + + before do + 2.times { produce_test_trace(with_http_span: true) } + end + + it "sends event for each of spans" do + subject.send_traces(traces) + + expect(http).to have_received(:request) do |args| + payload = MessagePack.unpack(args[:payload]) + events = payload["events"] + expect(events.count).to eq(expected_events_count) + end + end + + context "when some spans are broken" do + let(:expected_events_count) { 3 } + + before do + http_span = spans.find { |span| span.type == "http" } + http_span.start_time = Time.at(0) + end + + it "filters out invalid events" do + subject.send_traces(traces) + + expect(http).to have_received(:request) do |args| + payload = MessagePack.unpack(args[:payload]) + + events = payload["events"] + expect(events.count).to eq(expected_events_count) + + span_events = events.filter { |e| e["type"] == "span" } + expect(span_events.count).to eq(1) + end + end + end + + context "when chunking is used" do + # one test event is approximately 1000 bytes currently + # ATTENTION: might break if more data is added to test spans in #produce_test_trace method + let(:max_payload_size) { 2000 } + + it "filters out invalid events" do + responses = subject.send_traces(traces) + + expect(http).to have_received(:request).twice + expect(responses.count).to eq(2) + end + end + + context "when max_payload-size is too small" do + # one test event is approximately 1000 bytes currently + # ATTENTION: might break if more data is added to test spans in #produce_test_trace method + let(:max_payload_size) { 1 } + + it "does not send events that are larger than max size" do + subject.send_traces(traces) + + expect(http).not_to have_received(:request) + end + end + end + + context "when all events are invalid" do + before do + produce_test_trace + + span.start_time = Time.at(0) + end + + it "does not send anything" do + subject.send_traces(traces) + + expect(http).not_to have_received(:request) + end + end + end +end diff --git a/spec/datadog/ci/transport/gzip_spec.rb b/spec/datadog/ci/transport/gzip_spec.rb new file mode 100644 index 00000000..f51ca322 --- /dev/null +++ b/spec/datadog/ci/transport/gzip_spec.rb @@ -0,0 +1,34 @@ +require_relative "../../../../lib/datadog/ci/transport/gzip" + +RSpec.describe Datadog::CI::Transport::Gzip do + subject { described_class.compress(input) } + + describe ".compress" do + let(:input) do + <<-LOREM + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc et est eu dui dignissim tempus. Aliquam + scelerisque posuere odio id sollicitudin. Etiam dolor lorem, interdum sed mollis consectetur, sagittis a massa. + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec gravida, libero ac gravida ornare, leo elit + facilisis nunc, in pharetra odio lectus sit amet augue. Cras fermentum interdum tortor, pulvinar laoreet massa + mollis non. Vestibulum pulvinar dolor nec ante facilisis, in scelerisque tortor maximus. + + Cras pellentesque odio at mauris venenatis efficitur. Mauris pretium, est eu convallis sagittis, felis purus ] + ullamcorper turpis, vel hendrerit justo massa at nulla. Maecenas hendrerit ante ligula. Maecenas blandit porta + magna. Proin volutpat vestibulum diam quis malesuada. Aliquam at porttitor turpis. Aliquam ut tellus ultricies, + commodo sapien vel, consequat felis. Aenean velit turpis, rhoncus in ipsum ut, tempor iaculis nisi. Fusce + faucibus consequat blandit. Nam maximus augue quis tellus cursus eleifend. Suspendisse auctor, orci sit amet + vehicula molestie, magna nibh rutrum metus, eget sagittis felis mauris eu quam. Vivamus ut vulputate est. + LOREM + end + + it "compresses" do + expect(input.size).to be > subject.size + end + + it "can be decompressed with gzip" do + Zlib::GzipReader.new(StringIO.new(subject)) do |gzip| + expect(gzip.read).to eq(input) + end + end + end +end diff --git a/spec/datadog/ci/transport/http_spec.rb b/spec/datadog/ci/transport/http_spec.rb new file mode 100644 index 00000000..09009b2e --- /dev/null +++ b/spec/datadog/ci/transport/http_spec.rb @@ -0,0 +1,360 @@ +require_relative "../../../../lib/datadog/ci/transport/http" + +RSpec.describe Datadog::CI::Transport::HTTP do + subject(:transport) { described_class.new(host: host, **options) } + + let(:host) { "datadog-host" } + let(:port) { 8132 } + let(:timeout) { 10 } + let(:ssl) { true } + let(:options) { {} } + + shared_context "HTTP connection stub" do + let(:http_connection) { instance_double(::Net::HTTP) } + + before do + allow(::Net::HTTP).to receive(:new) + .with( + transport.host, + transport.port + ).and_return(http_connection) + + allow(http_connection).to receive(:open_timeout=).with(transport.timeout) + allow(http_connection).to receive(:read_timeout=).with(transport.timeout) + allow(http_connection).to receive(:use_ssl=).with(transport.ssl) + + allow(http_connection).to receive(:start).and_yield(http_connection) + end + end + + describe "#initialize" do + context "given no options" do + let(:options) { {} } + + it do + is_expected.to have_attributes( + host: host, + port: nil, + timeout: Datadog::CI::Transport::HTTP::DEFAULT_TIMEOUT, + ssl: true + ) + end + end + + context "given a :port option" do + let(:options) { {timeout: port} } + + it { is_expected.to have_attributes(timeout: port) } + end + + context "given a :timeout option" do + let(:options) { {timeout: timeout} } + + it { is_expected.to have_attributes(timeout: timeout) } + end + + context "given a :ssl option" do + let(:options) { {ssl: ssl} } + + context "with nil" do + let(:ssl) { nil } + + it { is_expected.to have_attributes(ssl: true) } + end + + context "with false" do + let(:ssl) { false } + + it { is_expected.to have_attributes(ssl: false) } + end + end + + context "given a :compress option" do + let(:options) { {compress: compress} } + + context "with nil" do + let(:compress) { nil } + + it { is_expected.to have_attributes(compress: false) } + end + + context "with false" do + let(:compress) { true } + + it { is_expected.to have_attributes(compress: true) } + end + end + end + + describe "#request" do + include_context "HTTP connection stub" + + let(:path) { "/api/v1/intake" } + let(:payload) { '{ "key": "value" }' } + let(:headers) { {"Content-Type" => "application/json"} } + let(:request_options) { {} } + + let(:http_response) { double("http_response") } + + subject(:request) { transport.request(path: path, payload: payload, headers: headers, **request_options) } + + context "when request is successful" do + before { expect(http_connection).to receive(:request).and_return(http_response) } + + it "produces a response" do + is_expected.to be_a_kind_of(described_class::Response) + + expect(request.http_response).to be(http_response) + end + end + + context "when error in connecting to server" do + before { expect(http_connection).to receive(:request).and_raise(StandardError) } + + it { expect(request).to be_a_kind_of(described_class::InternalErrorResponse) } + end + + context "when method is unknown" do + let(:request_options) { {method: "delete"} } + + it { expect { request }.to raise_error("Unknown method delete") } + end + + context "when compressing payload" do + let(:headers) { {"Content-Type" => "application/json"} } + let(:expected_headers) { {"Content-Type" => "application/json", "Content-Encoding" => "gzip"} } + let(:options) { {compress: true} } + let(:post_request) { double(:post_request) } + + before do + expect(::Net::HTTP::Post).to receive(:new).with(path, expected_headers).and_return(post_request) + expect(post_request).to receive(:body=).with(Datadog::CI::Transport::Gzip.compress(payload)) + expect(http_connection).to receive(:request).with(post_request).and_return(http_response) + end + + it { expect(request.http_response).to be(http_response) } + end + end +end + +RSpec.describe Datadog::CI::Transport::HTTP::Response do + subject(:response) { described_class.new(http_response) } + + let(:http_response) { instance_double(::Net::HTTPResponse) } + + describe "#initialize" do + it { is_expected.to have_attributes(http_response: http_response) } + end + + describe "#payload" do + subject(:payload) { response.payload } + + let(:http_response) { instance_double(::Net::HTTPResponse, body: double("body")) } + + it { is_expected.to be(http_response.body) } + end + + describe "#code" do + subject(:code) { response.code } + + let(:http_response) { instance_double(::Net::HTTPResponse, code: "200") } + + it { is_expected.to eq(200) } + end + + describe "#ok?" do + subject(:ok?) { response.ok? } + + let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } + + context "when code not 2xx" do + let(:code) { 199 } + + it { is_expected.to be false } + end + + context "when code is 200" do + let(:code) { 200 } + + it { is_expected.to be true } + end + + context "when code is 299" do + let(:code) { 299 } + + it { is_expected.to be true } + end + + context "when code is greater than 299" do + let(:code) { 300 } + + it { is_expected.to be false } + end + end + + describe "#unsupported?" do + subject(:unsupported?) { response.unsupported? } + + let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } + + context "when code is 400" do + let(:code) { 400 } + + it { is_expected.to be false } + end + + context "when code is 415" do + let(:code) { 415 } + + it { is_expected.to be true } + end + end + + describe "#not_found?" do + subject(:not_found?) { response.not_found? } + + let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } + + context "when code is 400" do + let(:code) { 400 } + + it { is_expected.to be false } + end + + context "when code is 404" do + let(:code) { 404 } + + it { is_expected.to be true } + end + end + + describe "#client_error?" do + subject(:client_error?) { response.client_error? } + + let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } + + context "when code is 399" do + let(:code) { 399 } + + it { is_expected.to be false } + end + + context "when code is 400" do + let(:code) { 400 } + + it { is_expected.to be true } + end + + context "when code is 499" do + let(:code) { 499 } + + it { is_expected.to be true } + end + + context "when code is 500" do + let(:code) { 500 } + + it { is_expected.to be false } + end + end + + describe "#server_error?" do + subject(:server_error?) { response.server_error? } + + let(:http_response) { instance_double(::Net::HTTPResponse, code: code) } + + context "when code is 499" do + let(:code) { 499 } + + it { is_expected.to be false } + end + + context "when code is 500" do + let(:code) { 500 } + + it { is_expected.to be true } + end + + context "when code is 599" do + let(:code) { 599 } + + it { is_expected.to be true } + end + + context "when code is 600" do + let(:code) { 600 } + + it { is_expected.to be false } + end + end + + describe "#internal_error?" do + subject(:internal_error?) { response.internal_error? } + + it { is_expected.to be false } + end +end + +RSpec.describe Datadog::CI::Transport::HTTP::InternalErrorResponse do + subject(:response) { described_class.new(error) } + + let(:error) { instance_double(StandardError, class: "StandardError", to_s: "error message") } + + describe "#initialize" do + it { is_expected.to have_attributes(error: error) } + end + + describe "#payload" do + subject(:payload) { response.payload } + + it { is_expected.to eq("") } + end + + describe "#code" do + subject(:code) { response.code } + + it { is_expected.to eq(-1) } + end + + describe "#ok?" do + subject(:ok?) { response.ok? } + + it { is_expected.to be false } + end + + describe "#unsupported?" do + subject(:unsupported?) { response.unsupported? } + + it { is_expected.to be false } + end + + describe "#not_found?" do + subject(:not_found?) { response.not_found? } + + it { is_expected.to be false } + end + + describe "#client_error?" do + subject(:client_error?) { response.client_error? } + + it { is_expected.to be false } + end + + describe "#server_error?" do + subject(:server_error?) { response.server_error? } + + it { is_expected.to be false } + end + + describe "#internal_error?" do + subject(:internal_error?) { response.internal_error? } + + it { is_expected.to be true } + end + + describe "#inspect" do + subject(:inspect) { response.inspect } + + it { is_expected.to include("error_class:StandardError, error:error message") } + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c55d75cd..f566167d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -12,21 +12,29 @@ require_relative "support/log_helpers" require_relative "support/tracer_helpers" require_relative "support/span_helpers" -require_relative "support/test_helpers" require_relative "support/platform_helpers" require_relative "support/git_helpers" require_relative "support/provider_test_helpers" +require_relative "support/ci_mode_helpers" +require_relative "support/test_visibility_event_serialized" require "rspec/collection_matchers" require "climate_control" +require "timecop" + +if defined?(Warning.ignore) + # Caused by https://github.com/cucumber/cucumber-ruby/blob/47c8e2d7c97beae8541c895a43f9ccb96324f0f1/lib/cucumber/encoding.rb#L5-L6 + Gem.path.each do |path| + Warning.ignore(/setting Encoding.default_external/, path) + Warning.ignore(/setting Encoding.default_internal/, path) + end +end RSpec.configure do |config| config.include ConfigurationHelpers config.include TracerHelpers config.include SpanHelpers - config.include TestHelpers::RSpec::Integration, :integration - # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = ".rspec_status" @@ -36,4 +44,22 @@ config.expect_with :rspec do |c| c.syntax = :expect end + + # Raise error when patching an integration fails. + # This can be disabled by unstubbing +CommonMethods#on_patch_error+ + require "datadog/tracing/contrib/patcher" + config.before do + allow_any_instance_of(Datadog::Tracing::Contrib::Patcher::CommonMethods).to(receive(:on_patch_error)) { |_, e| raise e } + end + + # Ensure tracer environment is clean before running tests. + # + # This is done :before and not :after because doing so after + # can create noise for test assertions. For example: + # +expect(Datadog).to receive(:shutdown!).once+ + config.before do + Datadog.shutdown! + # without_warnings { Datadog.configuration.reset! } + Datadog.configuration.reset! + end end diff --git a/spec/support/ci_mode_helpers.rb b/spec/support/ci_mode_helpers.rb new file mode 100644 index 00000000..9cdf6730 --- /dev/null +++ b/spec/support/ci_mode_helpers.rb @@ -0,0 +1,15 @@ +RSpec.shared_context "CI mode activated" do + let(:integration_name) { :override_me } + let(:integration_options) { {} } + + before do + Datadog.configure do |c| + c.ci.enabled = true + c.ci.instrument integration_name, integration_options + end + end + + after do + ::Datadog::Tracing.shutdown! + end +end diff --git a/spec/support/faux_transport.rb b/spec/support/faux_transport.rb deleted file mode 100644 index 1474343f..00000000 --- a/spec/support/faux_transport.rb +++ /dev/null @@ -1,16 +0,0 @@ -require "ddtrace/transport/http" - -# FauxTransport is a dummy Datadog::Transport that doesn't send data to an agent. -class FauxTransport < Datadog::Transport::HTTP::Client - def initialize(*) - end - - def send_traces(*) - # Emulate an OK response - [Datadog::Transport::HTTP::Traces::Response.new( - Datadog::Transport::HTTP::Adapters::Net::Response.new( - Net::HTTPResponse.new(1.0, 200, "OK") - ) - )] - end -end diff --git a/spec/support/faux_writer.rb b/spec/support/faux_writer.rb deleted file mode 100644 index ebba7e36..00000000 --- a/spec/support/faux_writer.rb +++ /dev/null @@ -1,54 +0,0 @@ -require_relative "faux_transport" - -# FauxWriter is a dummy writer that buffers spans locally. -class FauxWriter < Datadog::Tracing::Writer - def initialize(options = {}) - options[:transport] ||= FauxTransport.new - options[:call_original] ||= true - @options = options - - super if options[:call_original] - @mutex = Mutex.new - - # easy access to registered components - @traces = [] - end - - def write(trace) - @mutex.synchronize do - super(trace) if @options[:call_original] - @traces << trace - end - end - - def traces(action = :clear) - traces = @traces - @traces = [] if action == :clear - traces - end - - def spans(action = :clear) - @mutex.synchronize do - traces = @traces - @traces = [] if action == :clear - spans = traces.collect(&:spans).flatten - # sort the spans to avoid test flakiness - - spans.sort! do |a, b| - if a.name == b.name - if a.resource == b.resource - if a.start_time == b.start_time - a.end_time <=> b.end_time - else - a.start_time <=> b.start_time - end - else - a.resource <=> b.resource - end - else - a.name <=> b.name - end - end - end - end -end diff --git a/spec/support/test_helpers.rb b/spec/support/test_helpers.rb deleted file mode 100644 index 037ecf6f..00000000 --- a/spec/support/test_helpers.rb +++ /dev/null @@ -1,31 +0,0 @@ -module TestHelpers - module_function - - # Integration tests are normally expensive (time-wise or resource-wise). - # They run in CI by default. - def run_integration_tests? - ENV["TEST_DATADOG_INTEGRATION"] - end - - module RSpec - # RSpec extension to allow for declaring integration tests - # using example group parameters: - # - # ```ruby - # describe 'end-to-end foo test', :integration do - # ... - # end - # ``` - module Integration - def self.included(base) - base.class_exec do - before do - unless run_integration_tests? - skip("Integration tests can be enabled by setting the environment variable `TEST_DATADOG_INTEGRATION=1`") - end - end - end - end - end - end -end diff --git a/spec/support/test_visibility_event_serialized.rb b/spec/support/test_visibility_event_serialized.rb new file mode 100644 index 00000000..af6de9c2 --- /dev/null +++ b/spec/support/test_visibility_event_serialized.rb @@ -0,0 +1,17 @@ +RSpec.shared_context "Test visibility event serialized" do + subject {} + + let(:msgpack_json) { MessagePack.unpack(MessagePack.pack(subject)) } + let(:content) { msgpack_json["content"] } + let(:meta) { content["meta"] } + let(:metrics) { content["metrics"] } + + def expect_event_header(version: 1, type: "test") + expect(msgpack_json).to include( + { + "version" => version, + "type" => type + } + ) + end +end diff --git a/spec/support/tracer_helpers.rb b/spec/support/tracer_helpers.rb index a2e5a42e..71583c6c 100644 --- a/spec/support/tracer_helpers.rb +++ b/spec/support/tracer_helpers.rb @@ -1,66 +1,82 @@ -require_relative "faux_writer" +require "datadog/tracing" +# For contrib, we only allow one tracer to be active: +# the global tracer in +Datadog::Tracing+. module TracerHelpers - # Return a test tracer instance with a faux writer. + # Returns the current tracer instance def tracer - @tracer ||= new_tracer + Datadog::Tracing.send(:tracer) end - def new_tracer(options = {}) - writer = FauxWriter.new( - transport: Datadog::Transport::HTTP.default do |t| - t.adapter :test - end + def produce_test_trace( + framework: "rspec", operation: "rspec.example", + test_name: "test_add", test_suite: "calculator_tests", + service: "rspec-test-suite", result: "PASSED", exception: nil, + start_time: Time.now, duration_seconds: 2, + with_http_span: false + ) + # each time monotonic clock is called it will return a number that is + # by `duration_seconds` bigger than the previous + allow(Process).to receive(:clock_gettime).and_return( + 0, duration_seconds, 2 * duration_seconds, 3 * duration_seconds ) - - options = {writer: writer}.merge(options) - Datadog::Tracing::Tracer.new(**options) - end - - def get_test_writer(options = {}) - options = { - transport: Datadog::Transport::HTTP.default do |t| - t.adapter :test + Timecop.freeze(start_time) + + Datadog::CI::Recorder.trace( + operation, + { + span_options: { + resource: test_name, + service: service + }, + framework: framework, + framework_version: "1.0.0", + test_name: test_name, + test_suite: test_suite, + test_type: "test" + } + ) do |span| + if with_http_span + Datadog::Tracing.trace("http-call", type: "http", service: "net-http") do |span, trace| + span.set_tag("custom_tag", "custom_tag_value") + span.set_metric("custom_metric", 42) + end end - }.merge(options) - - FauxWriter.new(options) - end - - # Return some test traces - def get_test_traces(n, service: "test-app", resource: "/traces", type: "web") - traces = [] - n.times do - trace_op = Datadog::Tracing::TraceOperation.new + Datadog::Tracing.active_span.set_tag("test_owner", "my_team") + Datadog::Tracing.active_span.set_metric("memory_allocations", 16) - trace_op.measure("client.testing", service: service, resource: resource, type: type) do - trace_op.measure("client.testing", service: service, resource: resource, type: type) do - end + case result + when "FAILED" + Datadog::CI::Recorder.failed!(span, exception) + when "SKIPPED" + Datadog::CI::Recorder.skipped!(span, exception) + else + Datadog::CI::Recorder.passed!(span) end - traces << trace_op.flush! + Timecop.travel(start_time + duration_seconds) end - traces + Timecop.return end - # Return some test services - def get_test_services - {"rest-api" => {"app" => "rails", "app_type" => "web"}, - "master" => {"app" => "postgres", "app_type" => "db"}} + def first_test_span + spans.find { |span| span.type == "test" } end - def writer - tracer.writer + def first_other_span + spans.find { |span| span.type != "test" } end + # Returns spans and caches it (similar to +let(:spans)+). def traces - @traces ||= writer.traces + @traces ||= fetch_traces end + # Returns spans and caches it (similar to +let(:spans)+). def spans - @spans ||= writer.spans + @spans ||= fetch_spans end # Returns the only trace in the current tracer writer. @@ -87,8 +103,37 @@ def span end end + # Retrieves all traces in the current tracer instance. + # This method does not cache its results. + def fetch_traces(tracer = self.tracer) + tracer.instance_variable_get(:@traces) || [] + end + + # Retrieves and sorts all spans in the current tracer instance. + # This method does not cache its results. + def fetch_spans(tracer = self.tracer) + traces = fetch_traces(tracer) + traces.collect(&:spans).flatten.sort! do |a, b| + if a.name == b.name + if a.resource == b.resource + if a.start_time == b.start_time + a.end_time <=> b.end_time + else + a.start_time <=> b.start_time + end + else + a.resource <=> b.resource + end + else + a.name <=> b.name + end + end + end + + # Remove all traces from the current tracer instance and + # busts cache of +#spans+ and +#span+. def clear_traces! - writer.spans(:clear) + tracer.instance_variable_set(:@traces, []) @traces = nil @trace = nil @@ -96,14 +141,45 @@ def clear_traces! @span = nil end - def tracer_shutdown! - if defined?(@use_real_tracer) && @use_real_tracer - Datadog::Tracing.shutdown! - elsif defined?(@tracer) && @tracer - @tracer.shutdown! - @tracer = nil + RSpec.configure do |config| + # Capture spans from the global tracer + config.before do + # DEV `*_any_instance_of` has concurrency issues when running with parallelism (e.g. JRuby). + # DEV Single object `allow` and `expect` work as intended with parallelism. + allow(Datadog::Tracing::Tracer).to receive(:new).and_wrap_original do |method, **args, &block| + instance = method.call(**args, &block) + + # The mutex must be eagerly initialized to prevent race conditions on lazy initialization + write_lock = Mutex.new + allow(instance).to receive(:write) do |trace| + instance.instance_exec do + write_lock.synchronize do + @traces ||= [] + @traces << trace + end + end + end + + instance + end end - without_warnings { Datadog.send(:reset!) } + # Execute shutdown! after the test has finished + # teardown and mock verifications. + # + # Changing this to `config.after(:each)` would + # put shutdown! inside the test scope, interfering + # with mock assertions. + config.around do |example| + example.run.tap do + Datadog::Tracing.shutdown! + end + end + end + + # Useful for integration testing. + def use_real_tracer! + @use_real_tracer = true + allow(Datadog::Tracing::Tracer).to receive(:new).and_call_original end end diff --git a/vendor/rbs/ddtrace/0/datadog/core/chunket.rbs b/vendor/rbs/ddtrace/0/datadog/core/chunket.rbs new file mode 100644 index 00000000..69b01010 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/chunket.rbs @@ -0,0 +1,7 @@ +module Datadog + module Core + module Chunker + def self?.chunk_by_size: (untyped list, untyped max_chunk_size) -> untyped + end + end +end diff --git a/vendor/rbs/ddtrace/0/datadog/core/encoding.rbs b/vendor/rbs/ddtrace/0/datadog/core/encoding.rbs new file mode 100644 index 00000000..63ec5d88 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/encoding.rbs @@ -0,0 +1,9 @@ +module Datadog + module Core + module Encoding + module MsgpackEncoder + def self.encode: (untyped payload) -> String + end + end + end +end diff --git a/vendor/rbs/ddtrace/0/datadog/core/environment/identity.rbs b/vendor/rbs/ddtrace/0/datadog/core/environment/identity.rbs new file mode 100644 index 00000000..6494a483 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/environment/identity.rbs @@ -0,0 +1,10 @@ +module Datadog + module Core + module Environment + module Identity + def self.lang: () -> "ruby" + def self.id: () -> String + end + end + end +end diff --git a/vendor/rbs/ddtrace/0/datadog/tracing/contrib/configuration/settings.rbs b/vendor/rbs/ddtrace/0/datadog/tracing/contrib/configuration/settings.rbs deleted file mode 100644 index acd3fd9a..00000000 --- a/vendor/rbs/ddtrace/0/datadog/tracing/contrib/configuration/settings.rbs +++ /dev/null @@ -1,18 +0,0 @@ -module Datadog - module Tracing - module Contrib - module Configuration - class Settings - include Core::Configuration::Base - extend Core::Configuration::Options::ClassMethods - - def configure: (?::Hash[untyped, untyped] options) { (untyped) -> untyped } -> untyped - - def []: (untyped name) -> untyped - - def []=: (untyped name, untyped value) -> untyped - end - end - end - end -end diff --git a/vendor/rbs/ddtrace/0/datadog/tracing/contrib/integration.rbs b/vendor/rbs/ddtrace/0/datadog/tracing/contrib/integration.rbs deleted file mode 100644 index 5d148b75..00000000 --- a/vendor/rbs/ddtrace/0/datadog/tracing/contrib/integration.rbs +++ /dev/null @@ -1,13 +0,0 @@ -module Datadog - module Tracing - module Contrib - module Integration - def self.included: (untyped base) -> untyped - - def compatible?: () -> bool - - def register_as: (Symbol key, Hash[Symbol, untyped] options) -> untyped - end - end - end -end diff --git a/vendor/rbs/ddtrace/0/datadog/tracing/span.rbs b/vendor/rbs/ddtrace/0/datadog/tracing/span.rbs index 9adb31f1..b8bb2171 100644 --- a/vendor/rbs/ddtrace/0/datadog/tracing/span.rbs +++ b/vendor/rbs/ddtrace/0/datadog/tracing/span.rbs @@ -4,6 +4,20 @@ module Datadog attr_accessor span_id: Integer def set_tag: (String key, ?untyped? value) -> void + def get_tag: (String key) -> String? + def type: () -> String + def id: () -> String + def trace_id: () -> String + def parent_id: () -> String + def name: () -> String + def resource: () -> String + def service: () -> String + def status: () -> Integer + def start_time: () -> Time + def end_time: () -> Time + def duration: () -> Float + def meta: () -> Hash[String, untyped] + def metrics: () -> Hash[String, untyped] end end end diff --git a/vendor/rbs/ddtrace/0/datadog/tracing/span_operation.rbs b/vendor/rbs/ddtrace/0/datadog/tracing/span_operation.rbs index 11f3c61d..ff5277fb 100644 --- a/vendor/rbs/ddtrace/0/datadog/tracing/span_operation.rbs +++ b/vendor/rbs/ddtrace/0/datadog/tracing/span_operation.rbs @@ -54,7 +54,7 @@ module Datadog def finished?: () -> untyped - def duration: () -> untyped + def duration: () -> Float def set_error: (untyped e) -> untyped diff --git a/vendor/rbs/ddtrace/0/datadog/tracing/trace_operation.rbs b/vendor/rbs/ddtrace/0/datadog/tracing/trace_operation.rbs new file mode 100644 index 00000000..06cdf5e8 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/tracing/trace_operation.rbs @@ -0,0 +1,84 @@ +module Datadog + module Tracing + class TraceOperation + include Metadata::Tagging + + DEFAULT_MAX_LENGTH: ::Integer + + attr_accessor agent_sample_rate: untyped + attr_accessor hostname: untyped + attr_accessor origin: untyped + attr_accessor rate_limiter_rate: untyped + attr_accessor rule_sample_rate: untyped + attr_accessor sample_rate: untyped + attr_accessor sampling_priority: untyped + attr_reader active_span_count: untyped + attr_reader active_span: untyped + attr_reader id: untyped + attr_reader max_length: untyped + attr_reader parent_span_id: untyped + attr_writer name: untyped + attr_writer resource: untyped + attr_writer sampled: untyped + attr_writer service: untyped + + def initialize: (?agent_sample_rate: untyped?, ?events: untyped?, ?hostname: untyped?, ?id: untyped?, ?max_length: untyped, ?name: untyped?, ?origin: untyped?, ?parent_span_id: untyped?, ?rate_limiter_rate: untyped?, ?resource: untyped?, ?rule_sample_rate: untyped?, ?sample_rate: untyped?, ?sampled: untyped?, ?sampling_priority: untyped?, ?service: untyped?, ?tags: untyped?, ?metrics: untyped?) -> void + def full?: () -> untyped + def finished_span_count: () -> untyped + def finished?: () -> untyped + def sampled?: () -> bool + def priority_sampled?: () -> bool + def keep!: () -> untyped + def reject!: () -> untyped + def name: () -> untyped + def resource: () -> untyped + def resource_override?: () -> bool + def service: () -> untyped + def measure: (untyped op_name, ?events: untyped?, ?on_error: untyped?, ?resource: untyped?, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?type: untyped?) { (untyped, untyped) -> untyped } -> untyped + def build_span: (untyped op_name, ?events: untyped?, ?on_error: untyped?, ?resource: untyped?, ?service: untyped?, ?start_time: untyped?, ?tags: untyped?, ?type: untyped?) -> untyped + def flush!: () ?{ (untyped) -> untyped } -> untyped + def to_digest: () -> untyped + def fork_clone: () -> untyped + + class Events + include Tracing::Events + + attr_reader span_before_start: untyped + attr_reader span_finished: untyped + attr_reader trace_finished: untyped + + def initialize: () -> void + + class SpanBeforeStart < Tracing::Event + def initialize: () -> void + end + + class SpanFinished < Tracing::Event + def initialize: () -> void + end + + class TraceFinished < Tracing::Event + def initialize: () -> void + + def deactivate_trace_subscribed?: () -> untyped + def subscribe_deactivate_trace: () ?{ () -> untyped } -> untyped + end + end + + private + + attr_reader events: untyped + attr_reader root_span: untyped + + def activate_span!: (untyped span_op) -> untyped + def deactivate_span!: (untyped span_op) -> untyped + def start_span: (untyped span_op) -> untyped + def finish_span: (untyped span, untyped span_op, untyped parent) -> untyped + + def set_root_span!: (untyped span) -> (nil | untyped) + + def build_trace: (untyped spans, ?bool partial) -> untyped + def distributed_tags: () -> untyped + end + end +end diff --git a/vendor/rbs/msgpack/0/message_pack/packer.rbs b/vendor/rbs/msgpack/0/message_pack/packer.rbs new file mode 100644 index 00000000..e5eaccd4 --- /dev/null +++ b/vendor/rbs/msgpack/0/message_pack/packer.rbs @@ -0,0 +1,14 @@ +module MessagePack + class Buffer + def to_a: () -> Array[String] + end + + class Packer + def initialize: () -> void + + def write: (Object input) -> self + def write_map_header: (Integer keys_number) -> self + def write_array_header: (Integer keys_number) -> self + def buffer: () -> Buffer + end +end