diff --git a/Steepfile b/Steepfile index adcd6d99..6571d23e 100644 --- a/Steepfile +++ b/Steepfile @@ -15,6 +15,7 @@ target :lib do library "securerandom" library "tmpdir" library "fileutils" + library "socket" repo_path "vendor/rbs" library "ddtrace" diff --git a/lib/datadog/ci/configuration/components.rb b/lib/datadog/ci/configuration/components.rb index 4c8414f3..329f9546 100644 --- a/lib/datadog/ci/configuration/components.rb +++ b/lib/datadog/ci/configuration/components.rb @@ -96,6 +96,8 @@ def activate_ci!(settings) ) itr = ITR::Runner.new( + api: test_visibility_api, + dd_env: settings.env, coverage_writer: coverage_writer, enabled: settings.ci.enabled && settings.ci.itr_enabled ) diff --git a/lib/datadog/ci/ext/test.rb b/lib/datadog/ci/ext/test.rb index 7c548203..0d219b96 100644 --- a/lib/datadog/ci/ext/test.rb +++ b/lib/datadog/ci/ext/test.rb @@ -43,6 +43,7 @@ module Test # Environment runtime tags TAG_OS_ARCHITECTURE = "os.architecture" TAG_OS_PLATFORM = "os.platform" + TAG_OS_VERSION = "os.version" TAG_RUNTIME_NAME = "runtime.name" TAG_RUNTIME_VERSION = "runtime.version" diff --git a/lib/datadog/ci/itr/runner.rb b/lib/datadog/ci/itr/runner.rb index 85d23bf6..44d72877 100644 --- a/lib/datadog/ci/itr/runner.rb +++ b/lib/datadog/ci/itr/runner.rb @@ -10,6 +10,7 @@ require_relative "../utils/parsing" require_relative "coverage/event" +require_relative "skippable" module Datadog module CI @@ -18,20 +19,30 @@ module ITR # Integrates with backend to provide test impact analysis data and # skip tests that are not impacted by the changes class Runner + attr_reader :correlation_id, :skippable_tests + def initialize( + dd_env:, + api: nil, coverage_writer: nil, enabled: false ) @enabled = enabled + @api = api + @dd_env = dd_env + @test_skipping_enabled = false @code_coverage_enabled = false @coverage_writer = coverage_writer + @correlation_id = nil + @skippable_tests = [] + Datadog.logger.debug("ITR Runner initialized with enabled: #{@enabled}") end - def configure(remote_configuration, test_session) + def configure(remote_configuration, test_session:, git_tree_upload_worker:) Datadog.logger.debug("Configuring ITR Runner with remote configuration: #{remote_configuration}") @enabled = Utils::Parsing.convert_to_bool( @@ -55,6 +66,8 @@ def configure(remote_configuration, test_session) load_datadog_cov! if @code_coverage_enabled Datadog.logger.debug("Configured ITR Runner with enabled: #{@enabled}, skipping_tests: #{@test_skipping_enabled}, code_coverage: #{@code_coverage_enabled}") + + fetch_skippable_tests(test_session: test_session, git_tree_upload_worker: git_tree_upload_worker) end def enabled? @@ -129,6 +142,20 @@ def ensure_test_source_covered(test_source_file, coverage) coverage[absolute_test_source_file_path] = true end + + def fetch_skippable_tests(test_session:, git_tree_upload_worker:) + return unless skipping_tests? + + # we can only request skippable tests if git metadata is already uploaded + git_tree_upload_worker.wait_until_done + + skippable_response = Skippable.new(api: @api, dd_env: @dd_env).fetch_skippable_tests(test_session) + @correlation_id = skippable_response.correlation_id + @skippable_tests = skippable_response.tests + + Datadog.logger.debug { "Fetched skippable tests: \n #{@skippable_tests}" } + Datadog.logger.debug { "ITR correlation ID: #{@correlation_id}" } + end end end end diff --git a/lib/datadog/ci/itr/skippable.rb b/lib/datadog/ci/itr/skippable.rb index 0c155227..28b3c1b6 100644 --- a/lib/datadog/ci/itr/skippable.rb +++ b/lib/datadog/ci/itr/skippable.rb @@ -4,24 +4,12 @@ require_relative "../ext/transport" require_relative "../ext/test" +require_relative "../utils/test_run" module Datadog module CI module ITR class Skippable - class Test - attr_reader :name, :suite - - def initialize(name:, suite:) - @name = name - @suite = suite - end - - def ==(other) - name == other.name && suite == other.suite - end - end - class Response def initialize(http_response) @http_response = http_response @@ -38,13 +26,17 @@ def correlation_id end def tests + res = Set.new + payload.fetch("data", []) - .filter_map do |test_data| + .each do |test_data| next unless test_data["type"] == Ext::Test::ITR_TEST_SKIPPING_MODE attrs = test_data["attributes"] || {} - Test.new(name: attrs["name"], suite: attrs["suite"]) + res << Utils::TestRun.test_full_name(attrs["name"], attrs["suite"]) end + + res end private @@ -65,7 +57,7 @@ def payload end end - def initialize(api: nil, dd_env: nil) + def initialize(dd_env:, api: nil) @api = api @dd_env = dd_env end @@ -98,10 +90,11 @@ def payload(test_session) "repository_url" => test_session.git_repository_url, "sha" => test_session.git_commit_sha, "configurations" => { - "os.platform" => test_session.os_platform, - "os.architecture" => test_session.os_architecture, - "runtime.name" => test_session.runtime_name, - "runtime.version" => test_session.runtime_version + Ext::Test::TAG_OS_PLATFORM => test_session.os_platform, + Ext::Test::TAG_OS_ARCHITECTURE => test_session.os_architecture, + Ext::Test::TAG_OS_VERSION => test_session.os_version, + Ext::Test::TAG_RUNTIME_NAME => test_session.runtime_name, + Ext::Test::TAG_RUNTIME_VERSION => test_session.runtime_version } } } diff --git a/lib/datadog/ci/span.rb b/lib/datadog/ci/span.rb index 481c87e1..097fc710 100644 --- a/lib/datadog/ci/span.rb +++ b/lib/datadog/ci/span.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "datadog/core/environment/platform" + require_relative "ext/test" require_relative "utils/test_run" @@ -151,6 +153,12 @@ def os_platform tracer_span.get_tag(Ext::Test::TAG_OS_PLATFORM) end + # Returns the OS version extracted from the environment. + # @return [String] OS version. + def os_version + tracer_span.get_tag(Ext::Test::TAG_OS_VERSION) + end + # Returns the runtime name extracted from the environment. # @return [String] runtime name. def runtime_name @@ -166,6 +174,7 @@ def runtime_version def set_environment_runtime_tags tracer_span.set_tag(Ext::Test::TAG_OS_ARCHITECTURE, ::RbConfig::CONFIG["host_cpu"]) tracer_span.set_tag(Ext::Test::TAG_OS_PLATFORM, ::RbConfig::CONFIG["host_os"]) + tracer_span.set_tag(Ext::Test::TAG_OS_VERSION, Core::Environment::Platform.kernel_release) tracer_span.set_tag(Ext::Test::TAG_RUNTIME_NAME, Core::Environment::Ext::LANG_ENGINE) tracer_span.set_tag(Ext::Test::TAG_RUNTIME_VERSION, Core::Environment::Ext::ENGINE_VERSION) tracer_span.set_tag(Ext::Test::TAG_COMMAND, Utils::TestRun.command) diff --git a/lib/datadog/ci/test_visibility/recorder.rb b/lib/datadog/ci/test_visibility/recorder.rb index 83b92e44..d3a07414 100644 --- a/lib/datadog/ci/test_visibility/recorder.rb +++ b/lib/datadog/ci/test_visibility/recorder.rb @@ -229,7 +229,12 @@ def configure_library(test_session) Datadog.logger.debug { "git metadata upload did not complete in time when configuring library" } end end - @itr.configure(remote_configuration.payload, test_session) + + @itr.configure( + remote_configuration.payload, + test_session: test_session, + git_tree_upload_worker: @git_tree_upload_worker + ) end def skip_tracing(block = nil) diff --git a/lib/datadog/ci/test_visibility/transport.rb b/lib/datadog/ci/test_visibility/transport.rb index 572de392..4116d513 100644 --- a/lib/datadog/ci/test_visibility/transport.rb +++ b/lib/datadog/ci/test_visibility/transport.rb @@ -14,7 +14,7 @@ class Transport < Datadog::CI::Transport::EventPlatformTransport def initialize( api:, - dd_env: nil, + dd_env:, serializers_factory: Datadog::CI::TestVisibility::Serializers::Factories::TestLevel, max_payload_size: DEFAULT_MAX_PAYLOAD_SIZE ) diff --git a/lib/datadog/ci/transport/http.rb b/lib/datadog/ci/transport/http.rb index a8ece315..a00ff3bf 100644 --- a/lib/datadog/ci/transport/http.rb +++ b/lib/datadog/ci/transport/http.rb @@ -4,6 +4,7 @@ require "datadog/core/transport/http/adapters/net" require "datadog/core/transport/http/env" require "datadog/core/transport/request" +require "socket" require_relative "gzip" require_relative "../ext/transport" @@ -20,6 +21,8 @@ class HTTP :compress DEFAULT_TIMEOUT = 30 + MAX_RETRIES = 3 + INITIAL_BACKOFF = 1 def initialize(host:, timeout: DEFAULT_TIMEOUT, port: nil, ssl: true, compress: false) @host = host @@ -29,7 +32,7 @@ def initialize(host:, timeout: DEFAULT_TIMEOUT, port: nil, ssl: true, compress: @compress = compress.nil? ? false : compress end - def request(path:, payload:, headers:, verb: "post") + def request(path:, payload:, headers:, verb: "post", retries: MAX_RETRIES, backoff: INITIAL_BACKOFF) if compress headers[Ext::Transport::HEADER_CONTENT_ENCODING] = Ext::Transport::CONTENT_ENCODING_GZIP payload = Gzip.compress(payload) @@ -41,9 +44,7 @@ def request(path:, payload:, headers:, verb: "post") end response = ResponseDecorator.new( - adapter.call( - build_env(path: path, payload: payload, headers: headers, verb: verb) - ) + perform_http_call(path: path, payload: payload, headers: headers, verb: verb, retries: retries, backoff: backoff) ) Datadog.logger.debug do @@ -55,6 +56,25 @@ def request(path:, payload:, headers:, verb: "post") private + def perform_http_call(path:, payload:, headers:, verb:, retries: MAX_RETRIES, backoff: INITIAL_BACKOFF) + adapter.call( + build_env(path: path, payload: payload, headers: headers, verb: verb) + ) + rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, SocketError, Net::HTTPBadResponse => e + Datadog.logger.debug("Failed to send request with #{e} (#{e.message})") + + if retries.positive? + sleep(backoff) + + perform_http_call( + path: path, payload: payload, headers: headers, verb: verb, retries: retries - 1, backoff: backoff * 2 + ) + else + Datadog.logger.error("Failed to send request after #{MAX_RETRIES} retries") + raise e + end + end + def build_env(path:, payload:, headers:, verb:) env = Datadog::Core::Transport::HTTP::Env.new( Datadog::Core::Transport::Request.new diff --git a/lib/datadog/ci/transport/remote_settings_api.rb b/lib/datadog/ci/transport/remote_settings_api.rb index ec252bd0..03ac76a1 100644 --- a/lib/datadog/ci/transport/remote_settings_api.rb +++ b/lib/datadog/ci/transport/remote_settings_api.rb @@ -51,7 +51,7 @@ def default_payload end end - def initialize(api: nil, dd_env: nil) + def initialize(dd_env:, api: nil) @api = api @dd_env = dd_env end @@ -86,10 +86,11 @@ def payload(test_session) "sha" => test_session.git_commit_sha, "test_level" => Ext::Test::ITR_TEST_SKIPPING_MODE, "configurations" => { - "os.platform" => test_session.os_platform, - "os.arch" => test_session.os_architecture, - "runtime.name" => test_session.runtime_name, - "runtime.version" => test_session.runtime_version + Ext::Test::TAG_OS_PLATFORM => test_session.os_platform, + Ext::Test::TAG_OS_ARCHITECTURE => test_session.os_architecture, + Ext::Test::TAG_OS_VERSION => test_session.os_version, + Ext::Test::TAG_RUNTIME_NAME => test_session.runtime_name, + Ext::Test::TAG_RUNTIME_VERSION => test_session.runtime_version } } } diff --git a/lib/datadog/ci/utils/test_run.rb b/lib/datadog/ci/utils/test_run.rb index 1e41715e..65a4c0e3 100644 --- a/lib/datadog/ci/utils/test_run.rb +++ b/lib/datadog/ci/utils/test_run.rb @@ -9,6 +9,10 @@ def self.command @command = "#{$0} #{ARGV.join(" ")}" end + + def self.test_full_name(test_name, suite) + "#{suite}.#{test_name}" + end end end end diff --git a/sig/datadog/ci/ext/test.rbs b/sig/datadog/ci/ext/test.rbs index 5b235a54..dc308af1 100644 --- a/sig/datadog/ci/ext/test.rbs +++ b/sig/datadog/ci/ext/test.rbs @@ -52,6 +52,8 @@ module Datadog TAG_OS_PLATFORM: "os.platform" + TAG_OS_VERSION: "os.version" + TAG_RUNTIME_NAME: "runtime.name" TAG_RUNTIME_VERSION: "runtime.version" diff --git a/sig/datadog/ci/itr/runner.rbs b/sig/datadog/ci/itr/runner.rbs index 1e4f8b53..180d7aca 100644 --- a/sig/datadog/ci/itr/runner.rbs +++ b/sig/datadog/ci/itr/runner.rbs @@ -5,12 +5,16 @@ module Datadog @enabled: bool @test_skipping_enabled: bool @code_coverage_enabled: bool - @api: Datadog::CI::Transport::Api::Base? + @correlation_id: String + @skippable_tests: Array[Datadog::CI::ITR::Skippable::Test] @coverage_writer: Datadog::CI::ITR::Coverage::Writer? - def initialize: (?enabled: bool, coverage_writer: Datadog::CI::ITR::Coverage::Writer?) -> void + @api: Datadog::CI::Transport::Api::Base? + @dd_env: String? + + def initialize: (dd_env: String?, ?enabled: bool, coverage_writer: Datadog::CI::ITR::Coverage::Writer?, api: Datadog::CI::Transport::Api::Base?) -> void - def configure: (Hash[String, untyped] remote_configuration, Datadog::CI::TestSession test_session) -> void + def configure: (Hash[String, untyped] remote_configuration, test_session: Datadog::CI::TestSession, git_tree_upload_worker: Datadog::CI::Worker) -> void def enabled?: () -> bool @@ -33,6 +37,8 @@ module Datadog def write: (Datadog::CI::ITR::Coverage::Event event) -> void def ensure_test_source_covered: (String test_source_file, Hash[String, untyped] coverage) -> void + + def fetch_skippable_tests: (test_session: Datadog::CI::TestSession, git_tree_upload_worker: Datadog::CI::Worker) -> void end end end diff --git a/sig/datadog/ci/itr/skippable.rbs b/sig/datadog/ci/itr/skippable.rbs index bb06b5e9..15c81922 100644 --- a/sig/datadog/ci/itr/skippable.rbs +++ b/sig/datadog/ci/itr/skippable.rbs @@ -5,18 +5,6 @@ module Datadog @api: Datadog::CI::Transport::Api::Base? @dd_env: String? - class Test - @name: String? - - @suite: String? - - attr_reader name: String? - - attr_reader suite: String? - - def initialize: (name: String?, suite: String?) -> void - end - class Response @http_response: Datadog::Core::Transport::HTTP::Adapters::Net::Response? @json: Hash[String, untyped]? @@ -27,14 +15,14 @@ module Datadog def correlation_id: () -> String? - def tests: () -> Array[Test] + def tests: () -> Set[String] private def payload: () -> Hash[String, untyped] end - def initialize: (?api: Datadog::CI::Transport::Api::Base?, ?dd_env: String?) -> void + def initialize: (?api: Datadog::CI::Transport::Api::Base?, dd_env: String?) -> void def fetch_skippable_tests: (Datadog::CI::TestSession test_session) -> Response diff --git a/sig/datadog/ci/span.rbs b/sig/datadog/ci/span.rbs index cbf14fff..6be70c73 100644 --- a/sig/datadog/ci/span.rbs +++ b/sig/datadog/ci/span.rbs @@ -55,6 +55,8 @@ module Datadog def os_platform: () -> String? + def os_version: () -> String? + def runtime_name: () -> String? def runtime_version: () -> String? diff --git a/sig/datadog/ci/test_visibility/transport.rbs b/sig/datadog/ci/test_visibility/transport.rbs index 7eeba0b0..77ec7a0b 100644 --- a/sig/datadog/ci/test_visibility/transport.rbs +++ b/sig/datadog/ci/test_visibility/transport.rbs @@ -10,7 +10,7 @@ module Datadog def initialize: ( api: Datadog::CI::Transport::Api::Base, - ?dd_env: ::String?, + dd_env: ::String?, ?serializers_factory: singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestLevel) | singleton(Datadog::CI::TestVisibility::Serializers::Factories::TestSuiteLevel), ?max_payload_size: Integer ) -> void diff --git a/sig/datadog/ci/transport/http.rbs b/sig/datadog/ci/transport/http.rbs index 9b02cebb..69154763 100644 --- a/sig/datadog/ci/transport/http.rbs +++ b/sig/datadog/ci/transport/http.rbs @@ -14,10 +14,12 @@ module Datadog attr_reader compress: bool DEFAULT_TIMEOUT: 30 + MAX_RETRIES: 3 + INITIAL_BACKOFF: 1 def initialize: (host: String, ?port: Integer?, ?ssl: bool, ?timeout: Integer, ?compress: bool) -> void - def request: (?verb: String, payload: String, headers: Hash[String, String], path: String) -> ResponseDecorator + def request: (?verb: String, payload: String, headers: Hash[String, String], path: String, ?retries: Integer, ?backoff: Integer) -> ResponseDecorator private @@ -25,6 +27,8 @@ module Datadog def build_env: (payload: String, headers: Hash[String, String], path: String, verb: String) -> Datadog::Core::Transport::HTTP::Env + def perform_http_call: (payload: String, headers: Hash[String, String], path: String, verb: String, ?retries: Integer, ?backoff: Integer) -> Datadog::Core::Transport::Response + class AdapterSettings attr_reader hostname: String attr_reader port: Integer? diff --git a/sig/datadog/ci/transport/remote_settings_api.rbs b/sig/datadog/ci/transport/remote_settings_api.rbs index 15086cc4..1c655f0e 100644 --- a/sig/datadog/ci/transport/remote_settings_api.rbs +++ b/sig/datadog/ci/transport/remote_settings_api.rbs @@ -22,7 +22,7 @@ module Datadog @api: Datadog::CI::Transport::Api::Base? @dd_env: String? - def initialize: (?api: Datadog::CI::Transport::Api::Base?, ?dd_env: String?) -> void + def initialize: (?api: Datadog::CI::Transport::Api::Base?, dd_env: String?) -> void def fetch_library_settings: (Datadog::CI::TestSession test_session) -> Response diff --git a/sig/datadog/ci/utils/test_run.rbs b/sig/datadog/ci/utils/test_run.rbs index c985f99b..66fef457 100644 --- a/sig/datadog/ci/utils/test_run.rbs +++ b/sig/datadog/ci/utils/test_run.rbs @@ -5,6 +5,8 @@ module Datadog self.@command: String def self.command: () -> String + + def self.test_full_name: (String test_name, String test_suite) -> String end end end diff --git a/spec/datadog/ci/itr/runner_spec.rb b/spec/datadog/ci/itr/runner_spec.rb index 80add74d..6fd53fd5 100644 --- a/spec/datadog/ci/itr/runner_spec.rb +++ b/spec/datadog/ci/itr/runner_spec.rb @@ -4,34 +4,41 @@ RSpec.describe Datadog::CI::ITR::Runner do let(:itr_enabled) { true } + + let(:api) { double("api") } let(:writer) { spy("writer") } + let(:git_worker) { spy("git_worker") } + let(:tracer_span) { Datadog::Tracing::SpanOperation.new("session") } let(:test_session) { Datadog::CI::TestSession.new(tracer_span) } - subject(:runner) { described_class.new(coverage_writer: writer, enabled: itr_enabled) } + subject(:runner) { described_class.new(api: api, dd_env: "dd_env", coverage_writer: writer, enabled: itr_enabled) } + let(:configure) { runner.configure(remote_configuration, test_session: test_session, git_tree_upload_worker: git_worker) } before do allow(writer).to receive(:write) end describe "#configure" do - before do - runner.configure(remote_configuration, test_session) - end - context "when remote configuration call failed" do let(:remote_configuration) { {"itr_enabled" => false} } it "configures the runner and test session" do + configure + expect(runner.enabled?).to be false expect(runner.skipping_tests?).to be false expect(runner.code_coverage?).to be false end end - context "when remote configuration call returned correct response" do + context "when remote configuration call returned correct response without tests skipping" do let(:remote_configuration) { {"itr_enabled" => true, "code_coverage" => true, "tests_skipping" => false} } + before do + configure + end + it "configures the runner" do expect(runner.enabled?).to be true expect(runner.skipping_tests?).to be false @@ -47,10 +54,41 @@ end end + context "when remote configuration call returned correct response with tests skipping" do + let(:remote_configuration) { {"itr_enabled" => true, "code_coverage" => true, "tests_skipping" => true} } + let(:skippable) do + instance_double( + Datadog::CI::ITR::Skippable, + fetch_skippable_tests: instance_double( + Datadog::CI::ITR::Skippable::Response, + correlation_id: "42", + tests: Set.new(["suite.test"]) + ) + ) + end + + before do + expect(Datadog::CI::ITR::Skippable).to receive(:new).and_return(skippable) + configure + end + + it "configures the runner" do + expect(runner.enabled?).to be true + expect(runner.skipping_tests?).to be true + + expect(runner.correlation_id).to eq("42") + expect(runner.skippable_tests).to eq(Set.new(["suite.test"])) + + expect(git_worker).to have_received(:wait_until_done) + end + end + context "when remote configuration call returned correct response with strings instead of bools" do let(:remote_configuration) { {"itr_enabled" => "true", "code_coverage" => "true", "tests_skipping" => "false"} } it "configures the runner" do + configure + expect(runner.enabled?).to be true expect(runner.skipping_tests?).to be false expect(runner.code_coverage?).to be(!PlatformHelpers.jruby?) # code coverage is not supported in JRuby @@ -61,6 +99,8 @@ let(:remote_configuration) { {} } it "configures the runner" do + configure + expect(runner.enabled?).to be false expect(runner.skipping_tests?).to be false expect(runner.code_coverage?).to be false @@ -73,7 +113,7 @@ let(:test_span) { Datadog::CI::Test.new(tracer_span) } before do - runner.configure(remote_configuration, test_session) + configure end context "when code coverage is disabled" do @@ -140,7 +180,8 @@ before do skip("Code coverage is not supported in JRuby") if PlatformHelpers.jruby? - runner.configure(remote_configuration, test_session) + configure + expect(test_span).to receive(:id).and_return(1) expect(test_span).to receive(:test_suite_id).and_return(2) expect(test_span).to receive(:test_session_id).and_return(3) diff --git a/spec/datadog/ci/itr/skippable_spec.rb b/spec/datadog/ci/itr/skippable_spec.rb index be209ba5..32bef886 100644 --- a/spec/datadog/ci/itr/skippable_spec.rb +++ b/spec/datadog/ci/itr/skippable_spec.rb @@ -18,6 +18,7 @@ "git.commit.sha" => "commit_sha", "os.platform" => "platform", "os.architecture" => "arch", + "os.version" => "version", "runtime.name" => "runtime_name", "runtime.version" => "runtime_version" }) @@ -47,6 +48,7 @@ configurations = attributes["configurations"] expect(configurations["os.platform"]).to eq("platform") expect(configurations["os.architecture"]).to eq("arch") + expect(configurations["os.version"]).to eq("version") expect(configurations["runtime.name"]).to eq("runtime_name") expect(configurations["runtime.version"]).to eq("runtime_version") end @@ -94,9 +96,7 @@ it "parses the response" do expect(response.ok?).to be true expect(response.correlation_id).to eq("correlation_id_123") - expect(response.tests.first).to eq( - Datadog::CI::ITR::Skippable::Test.new(name: "test_name", suite: "test_suite_name") - ) + expect(response.tests).to eq(Set.new(["test_suite_name.test_name"])) end end diff --git a/spec/datadog/ci/span_spec.rb b/spec/datadog/ci/span_spec.rb index b7709d34..c352f2f3 100644 --- a/spec/datadog/ci/span_spec.rb +++ b/spec/datadog/ci/span_spec.rb @@ -191,6 +191,7 @@ it "sets the environment runtime tags" do expect(tracer_span).to receive(:set_tag).with("os.architecture", ::RbConfig::CONFIG["host_cpu"]) expect(tracer_span).to receive(:set_tag).with("os.platform", ::RbConfig::CONFIG["host_os"]) + expect(tracer_span).to receive(:set_tag).with("os.version", Datadog::Core::Environment::Platform.kernel_release) expect(tracer_span).to receive(:set_tag).with("runtime.name", Datadog::Core::Environment::Ext::LANG_ENGINE) expect(tracer_span).to receive(:set_tag).with("runtime.version", Datadog::Core::Environment::Ext::ENGINE_VERSION) expect(tracer_span).to receive(:set_tag).with("test.command", test_command) @@ -253,6 +254,14 @@ end end + describe "#os_version" do + it "returns the OS version" do + expect(tracer_span).to receive(:get_tag).with("os.version").and_return("version") + + expect(span.os_version).to eq("version") + end + end + describe "#runtime_name" do it "returns the runtime name" do expect(tracer_span).to receive(:get_tag).with("runtime.name").and_return("name") diff --git a/spec/datadog/ci/transport/http_spec.rb b/spec/datadog/ci/transport/http_spec.rb index f9e6cf5b..e9eca4d6 100644 --- a/spec/datadog/ci/transport/http_spec.rb +++ b/spec/datadog/ci/transport/http_spec.rb @@ -145,5 +145,32 @@ expect(response.code).to eq(200) end end + + context "when request fails" do + let(:request_options) { {backoff: 0} } + + context "when succeeds after retries" do + before do + expect(adapter).to receive(:call).and_raise(Errno::ECONNRESET).exactly(described_class::MAX_RETRIES).times + expect(adapter).to receive(:call).and_return(http_response) + end + + it "produces a response" do + is_expected.to be_a_kind_of(described_class::ResponseDecorator) + + expect(response.code).to eq(200) + end + end + + context "when retries are exhausted" do + before do + expect(adapter).to receive(:call).and_raise(Errno::ECONNRESET).exactly(described_class::MAX_RETRIES + 1).times + end + + it "raises" do + expect { response }.to raise_error(Errno::ECONNRESET) + end + end + end end end diff --git a/spec/datadog/ci/transport/remote_settings_api_spec.rb b/spec/datadog/ci/transport/remote_settings_api_spec.rb index a46f30c9..f9c52091 100644 --- a/spec/datadog/ci/transport/remote_settings_api_spec.rb +++ b/spec/datadog/ci/transport/remote_settings_api_spec.rb @@ -18,6 +18,7 @@ "git.commit.sha" => "commit_sha", "os.platform" => "platform", "os.architecture" => "arch", + "os.version" => "version", "runtime.name" => "runtime_name", "runtime.version" => "runtime_version" }) @@ -48,7 +49,8 @@ configurations = attributes["configurations"] expect(configurations["os.platform"]).to eq("platform") - expect(configurations["os.arch"]).to eq("arch") + expect(configurations["os.architecture"]).to eq("arch") + expect(configurations["os.version"]).to eq("version") expect(configurations["runtime.name"]).to eq("runtime_name") expect(configurations["runtime.version"]).to eq("runtime_version") end diff --git a/spec/support/contexts/ci_mode.rb b/spec/support/contexts/ci_mode.rb index 8f232668..54b02054 100644 --- a/spec/support/contexts/ci_mode.rb +++ b/spec/support/contexts/ci_mode.rb @@ -23,6 +23,18 @@ let(:git_metadata_upload_enabled) { false } let(:require_git) { false } + let(:itr_correlation_id) { "itr_correlation_id" } + let(:itr_skippable_tests) { [] } + + let(:skippable_tests_response) do + instance_double( + Datadog::CI::ITR::Skippable::Response, + ok?: true, + correlation_id: itr_correlation_id, + tests: itr_skippable_tests + ) + end + let(:recorder) { Datadog.send(:components).ci_recorder } before do @@ -35,8 +47,8 @@ allow(Datadog::CI::Utils::TestRun).to receive(:command).and_return(test_command) allow_any_instance_of(Datadog::CI::Transport::RemoteSettingsApi).to receive(:fetch_library_settings).and_return( - double( - "remote_settings_api_response", + instance_double( + Datadog::CI::Transport::RemoteSettingsApi::Response, payload: { "itr_enabled" => itr_enabled, "code_coverage" => code_coverage_enabled, @@ -45,8 +57,8 @@ require_git?: require_git ), # This is for the second call to fetch_library_settings - double( - "remote_settings_api_response", + instance_double( + Datadog::CI::Transport::RemoteSettingsApi::Response, payload: { "itr_enabled" => itr_enabled, "code_coverage" => !code_coverage_enabled, @@ -55,7 +67,7 @@ require_git?: !require_git ) ) - + allow_any_instance_of(Datadog::CI::ITR::Skippable).to receive(:fetch_skippable_tests).and_return(skippable_tests_response) allow_any_instance_of(Datadog::CI::ITR::Coverage::Transport).to receive(:send_events).and_return([]) Datadog.configure do |c| diff --git a/vendor/rbs/ddtrace/0/datadog/core/environment/platform.rbs b/vendor/rbs/ddtrace/0/datadog/core/environment/platform.rbs new file mode 100644 index 00000000..ebfd15d0 --- /dev/null +++ b/vendor/rbs/ddtrace/0/datadog/core/environment/platform.rbs @@ -0,0 +1,12 @@ +module Datadog + module Core + module Environment + module Platform + def self?.hostname: () -> (untyped | nil) + def self?.kernel_name: () -> untyped + def self?.kernel_release: () -> (untyped | untyped | nil) + def self?.kernel_version: () -> (untyped | nil) + end + end + end +end