Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions internal/testdrive/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import (
"os"
"path/filepath"
"testing"

"github.com/DataDog/ddtest/internal/framework"
Expand Down Expand Up @@ -46,3 +47,13 @@
require.Equal(t, "npm", command)
require.Equal(t, []string{"run", "smoke", "--", "--config", "config with spaces.js"}, args)
}

func TestTestdriveMinitestRequiresExecutableRailsBinstub(t *testing.T) {
root := t.TempDir()
t.Chdir(root)
require.NoError(t, os.MkdirAll(filepath.Join(root, "bin"), 0755))
require.NoError(t, os.WriteFile(filepath.Join(root, "bin", "rails"), []byte("#!/bin/sh\n"), 0644))
command, args := framework.NewMinitest().Command()

Check failure on line 56 in internal/testdrive/command_test.go

View workflow job for this annotation

GitHub Actions / lint

not enough arguments in call to framework.NewMinitest

Check failure on line 56 in internal/testdrive/command_test.go

View workflow job for this annotation

GitHub Actions / test

not enough arguments in call to framework.NewMinitest
require.Equal(t, "bundle", command)
require.Equal(t, []string{"exec", "rake", "test"}, args)
}
2 changes: 2 additions & 0 deletions internal/testdrive/frameworks_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func TestPublicFrameworkTestdrives(t *testing.T) {
{"cucumber", `{"scripts":{"test":"cucumber-js"},"devDependencies":{"@cucumber/cucumber":"12.2.0"}}`, "npm test", map[string]string{"features/one.feature": "Feature: Arithmetic\n Scenario: Add\n Given addition works\n", "features/step_definitions/one.js": `const {Given} = require('@cucumber/cucumber'); Given('addition works', () => require('node:assert').equal(1+1,2));`}},
{"cypress", `{"scripts":{"test":"cypress run"},"devDependencies":{"cypress":"15.1.0"}}`, "npm test", map[string]string{"cypress.config.js": `module.exports={e2e:{supportFile:false,setupNodeEvents(on,config){on('task',{answer:()=>42});on('after:run',()=>{require('node:fs').writeFileSync('original-hook.txt','ran');});return config;}}};`, "cypress/e2e/one.cy.js": `it('preserves hooks', () => { cy.task('answer').should('equal',42); });`}},
{"pytest", "", "python -m pytest", map[string]string{"requirements.txt": "pytest==8.4.2\n", "pytest.ini": "[pytest]\n", "test_one.py": "def test_adds():\n assert 1 + 1 == 2\n"}},
{"rspec", "", "bundle exec rspec", map[string]string{"Gemfile": "source 'https://rubygems.org'\ngem 'rspec', '3.13.1'\n", "spec/one_spec.rb": "RSpec.describe 'addition' do\n it('adds') { expect(1 + 1).to eq(2) }\nend\n"}},
{"minitest", "", "bundle exec rake test", map[string]string{"Gemfile": "source 'https://rubygems.org'\ngem 'rake', '13.3.0'\ngem 'minitest', '5.25.5'\n", "Rakefile": "require 'rake/testtask'\nRake::TestTask.new(:test) { |t| t.pattern = 'test/**/*_test.rb' }\n", "test/one_test.rb": "require 'minitest/autorun'\nclass AdditionTest < Minitest::Test\n def test_adds\n assert_equal 2, 1 + 1\n end\nend\n"}},
}
for _, fixture := range fixtures {
t.Run(fixture.name, func(t *testing.T) {
Expand Down
24 changes: 21 additions & 3 deletions internal/testdrive/multiframework_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import (
"github.com/DataDog/ddtest/internal/settings"
"os"
"path/filepath"
"strings"
"testing"

"github.com/DataDog/ddtest/internal/testdrive/intake"
"github.com/stretchr/testify/require"
)

func TestPrepareAllSupportedFrameworks(t *testing.T) {
for _, name := range []string{"jest", "mocha", "vitest", "playwright", "cucumber", "cypress", "pytest"} {
for _, name := range []string{"jest", "mocha", "vitest", "playwright", "cucumber", "cypress", "pytest", "rspec", "minitest"} {
t.Run(name, func(t *testing.T) {
root := t.TempDir()
switch name {
Expand All @@ -43,6 +44,7 @@ func TestPrepareAllSupportedFrameworks(t *testing.T) {
installer := &fakeTracer{preloadPath: filepath.Join(root, "isolated")}
run.platform = installer
run.projectTracer = ""
run.projectTracer = ""
executor := &fakeTestdriveExecutor{}
run.executor = executor
run.startIntake = func(string) (localIntake, error) {
Expand All @@ -65,7 +67,8 @@ func TestPrepareAllSupportedFrameworks(t *testing.T) {
require.NotContains(t, executor.env, "NODE_OPTIONS")
case "ruby":
require.NotContains(t, executor.env, "BUNDLE_GEMFILE")
require.Contains(t, preview.String(), "Bundler updates the project Gemfile and lockfile")
require.Contains(t, preview.String(), "Bundler updates Gemfile and Gemfile.lock.")
require.NotContains(t, preview.String(), "It will not change")
require.Contains(t, output.String(), "datadog-ci · installed in project")
require.Contains(t, executor.env["RUBYOPT"], "datadog/ci/auto_instrument")
require.NotContains(t, executor.env, "NODE_OPTIONS")
Expand Down Expand Up @@ -125,7 +128,9 @@ func TestLanguageEnvironmentsPreserveCustomerOptions(t *testing.T) {
python := (&Testdrive{language: "python"}).environment("/session/python", "http://127.0.0.1:1234", "session")
require.Equal(t, "/session/python"+string(os.PathListSeparator)+"/customer/modules", python["PYTHONPATH"])
require.Equal(t, "-q --ddtrace", python["PYTEST_ADDOPTS"])

ruby := (&Testdrive{language: "ruby"}).environment("/session/Gemfile", "http://127.0.0.1:1234", "session")
require.True(t, strings.HasPrefix(ruby["RUBYOPT"], "-W0 "))
require.NotContains(t, ruby, "BUNDLE_PATH") // Inherit project Bundler configuration.
}

func TestCypressWrapperUsesExplicitConfigWithoutEditingIt(t *testing.T) {
Expand Down Expand Up @@ -187,3 +192,16 @@ func TestPythonProjectTracerPreservesImportEnvironment(t *testing.T) {
t.Fatal(env)
}
}

func TestRubyProjectTracerPreservesBundleEnvironment(t *testing.T) {
drive := &Testdrive{language: "ruby"}
env := drive.environment("", "http://127.0.0.1:1234", "session")
for _, key := range []string{"BUNDLE_GEMFILE", "BUNDLE_PATH", "BUNDLE_APP_CONFIG", "BUNDLE_FROZEN", "BUNDLE_WITHOUT"} {
if _, changed := env[key]; changed {
t.Fatal("project Bundler setting overridden", key)
}
}
if !strings.Contains(env["RUBYOPT"], "-rdatadog/ci/auto_instrument") {
t.Fatal(env)
}
}
12 changes: 12 additions & 0 deletions internal/testdrive/ruby.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package testdrive

import (
"os"
"strings"
)

func rubyEnvironment(path string) map[string]string {
env := map[string]string{}
env["RUBYOPT"] = strings.TrimSpace(os.Getenv("RUBYOPT") + " -rbundler/setup -rdatadog/ci/auto_instrument")
return env
}
15 changes: 10 additions & 5 deletions internal/testdrive/testdrive.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,6 @@ func Prepare(version string) (*Testdrive, error) {
return nil, err
}
language := detectedPlatform.Name()
switch runner.Name() {
case "jest", "mocha", "vitest", "playwright", "cucumber", "cypress", "pytest":
default:
return nil, fmt.Errorf("testdrive does not yet support %s", runner.Name())
}
command, args := runner.Command()
label := map[string]string{"javascript": "dd-trace", "python": "ddtrace", "ruby": "datadog-ci"}[language] + "@" + version

Expand Down Expand Up @@ -140,6 +135,10 @@ func (t *Testdrive) Preview(output io.Writer) {
}
_, _ = fmt.Fprintf(output, " - run: %s\n", shellquote.Join(append([]string{command}, args...)...))
_, _ = fmt.Fprintln(output)
if t.language == "ruby" && t.projectTracer == "" {
_, _ = fmt.Fprintln(output, "Bundler updates Gemfile and Gemfile.lock.")
return
}
patterns := map[string][]string{
"javascript": {"package.json", "package-lock.json", "npm-shrinkwrap.json", "yarn.lock", "pnpm-lock.yaml", "bun.lock", "bun.lockb"},
"python": {"pyproject.toml", "setup.py", "setup.cfg", "requirements*.txt", "requirements*.in", "Pipfile", "Pipfile.lock", "poetry.lock", "uv.lock", "pdm.lock", "pylock.toml"},
Expand Down Expand Up @@ -190,6 +189,10 @@ func (t *Testdrive) Run(ctx context.Context, output io.Writer) (runErr error) {
}

tracerLabel := t.tracerLabel
if t.language == "ruby" {
// Bundler owns the project dependency selection.
tracerLabel = "datadog-ci · installed in project"
}
if installation.Project {
tracerLabel = t.installedTracerLabel(t.projectTracer) + " · reused"
}
Expand Down Expand Up @@ -426,6 +429,8 @@ func (t *Testdrive) environment(path, intakeURL, sessionID string) map[string]st
maps.Copy(env, t.javascriptEnvironment(path))
case "python":
maps.Copy(env, pythonEnvironment(path))
case "ruby":
maps.Copy(env, rubyEnvironment(path))
}
return env
}
Expand Down
Loading