Skip to content

Conversation

@efd6
Copy link
Contributor

@efd6 efd6 commented Oct 21, 2025

This adds script testing for data streams. This is the MVP, future versions can be generalised to operate on input packages.

The current implementation supports:

  • pipeline testing
  • system testing
  • package upgrade testing (only to latest, not to arbitrary versions)
  • shared stack
  • independent stack
  • docker services

Not supported:

  • test coverage
  • report output configuration
  • k8s services
  • tf services (ish, these can be shimmed via docker)

Depends on a v3.5.1 of package-spec (currently shimmed in via a go.mod replace directive, to be removed)

Please take a look.

For #2944

@efd6 efd6 self-assigned this Oct 21, 2025
@efd6 efd6 added the enhancement New feature or request label Oct 21, 2025
@efd6 efd6 force-pushed the script_tests branch 5 times, most recently from 411a39e to bc55805 Compare October 21, 2025 04:31
@efd6
Copy link
Contributor Author

efd6 commented Oct 21, 2025

/test

@efd6 efd6 force-pushed the script_tests branch 2 times, most recently from d9e0c1a to 8433c67 Compare October 21, 2025 20:16
@efd6 efd6 marked this pull request as ready for review October 21, 2025 21:09
@efd6 efd6 requested a review from a team as a code owner October 21, 2025 21:09
test-check-packages-other:
PACKAGE_TEST_TYPE=other ./scripts/test-check-packages.sh

test-check-packages-independent-script:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not appear to run in the CI, but it's entirely unclear how to achieve that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to be executed in the CI, it needs to be updated this shell script that generates dynamically all the Buildkite steps to be executed:

https://github.com/elastic/elastic-package/blob/d5f73ab15af1dcf3547ed789f8ad4631257f4197/.buildkite/pipeline.trigger.integration.tests.sh

Depending on how it is required to launch these steps, all packages in one step or each package in its own CI step, it would be needed to do different modifications:

  • all packages in one step:
  • each package in its own CI step:
    • it would be needed to duplicate this code for the new Makefile target name
    • for instance, for packages under parallel folder:
      pushd test/packages/parallel > /dev/null
      while IFS= read -r -d '' package ; do
      package_name=$(basename "${package}")
      echo " - label: \":go: Integration test: ${package_name}\""
      echo " key: \"integration-parallel-${package_name}-agent\""
      echo " command: ./.buildkite/scripts/integration_tests.sh -t test-check-packages-parallel -p ${package_name}"
      echo " env:"
      echo " UPLOAD_SAFE_LOGS: 1"
      echo " agents:"
      echo " provider: \"gcp\""
      echo " image: \"${UBUNTU_X86_64_AGENT_IMAGE}\""
      echo " plugins:"
      echo " # See https://github.com/elastic/oblt-infra/blob/main/conf/resources/repos/integrations/01-gcp-buildkite-oidc.tf"
      echo " # This plugin authenticates to Google Cloud using the OIDC token."
      echo " - elastic/oblt-google-auth#v1.2.0:"
      echo " lifetime: 10800 # seconds"
      echo " project-id: \"elastic-observability-ci\""
      echo " project-number: \"911195782929\""
      echo " artifact_paths:"
      echo " - build/test-results/*.xml"
      echo " - build/test-coverage/coverage-*.xml" # these files should not be used to compute the final coverage of elastic-package
      done < <(find . -maxdepth 1 -mindepth 1 -type d -print0)

return cmd
}

func testRunnerScriptCommandAction(cmd *cobra.Command, args []string) error {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The logic in this function is rudimentary and only intended to allow an MVP to be demonstrated. In order to be able to render reports and redirect to file output this needs enhancement.

@efd6 efd6 requested a review from andrewkroh October 21, 2025 21:14
@jsoriano jsoriano self-requested a review October 23, 2025 08:34
@efd6 efd6 force-pushed the script_tests branch 5 times, most recently from 7f4815f to fdc0951 Compare October 28, 2025 06:46
Copy link
Member

@jsoriano jsoriano left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This is a pretty interesting feature.

I haven't checked the implementation in detail, added by now some comments and questions about the overall behavior.

[!exec:jq] skip 'Skipping test requiring absent jq command'
# Register running stack.
use_stack -profile ${CONFIG_PROFILES}/default
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should not [allow to] hard-code profiles in scripts. The user may want to use a different one. If needed here, we could pass the current profile as another environment variable.

If we want to allow testing with multiple profiles or something like this, maybe we can provide some function to create and use temporary profiles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really like this. The point of the testing here is to make it more hermetic, but I think that game is already lost. So, done.

if err != nil {
return err
}
workRoot := filepath.Join(home, filepath.FromSlash(".elastic-package/tmp/script_tests"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config directory should be obtained using the location manager. It can be customized with ELASTIC_PACKAGE_DATA_HOME.

The tmp directory can be obtained with loc.TempDir().

Suggested change
workRoot := filepath.Join(home, filepath.FromSlash(".elastic-package/tmp/script_tests"))
workRoot := filepath.Join(home, loc.TempDir(), "script_tests")

// root is non-zero, so just let testscript put it where it wants in the
// case that we have not requested work to be retained. This will be in
// os.MkdirTemp(os.Getenv("GOTMPDIR"), "go-test-script") which on most
// systems will be /tmp/go-test-script. However, due to… decisions, we
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What decisions are you referring to? 🙂

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of file system jailing prevents using other paths.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be part of the coment 🙂

Comment on lines +22 to +28
Tests are written as [txtar format](https://pkg.go.dev/golang.org/x/tools/txtar#hdr-Txtar_format)
files in a data stream's \_dev/test/scripts directory. The logic for the test is
written in the txtar file's initial comment section and any additional resource
files are included in the txtar file's files sections.

The standard commands and behaviors for testscript scripts are documented in
the [testscript package documentation](https://pkg.go.dev/github.com/rogpeppe/go-internal/testscript).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the record. Could you comment, maybe in the description of the PR, why you chose testscript instead of some other embedded language?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the only well maintained and extensively used script testing package available with the features that we need. It's not a language, so I'm not sure what competing systems you are referring to.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the only well maintained and extensively used script testing package available with the features that we need.

I see it as a great tool for black-box testing of commands. Here we are exposing many functions as commands, that could stay as functions if we were using some embedded scripting language.

It's not a language

Well, I can agree that it is not a turing-complete language, but it is something that along with txtar is used to express scripts, what could very well called language, but I guess this is a semantic discussion 🙂

so I'm not sure what competing systems you are referring to.

There are other embeddable systems that can be used to express scripts, from goja to CEL, starlark, or even a yaml list.

I agree with the choice, only that it wouldn't have been the first option I would have thought of for this, and I would like to have the decision stored in some place.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added it to the issue that this is addressing.

# Only run the test if --external-stack=true.
[!external_stack] skip 'Skipping external stack test.'
# Only run the test if the jq executable is in $PATH. This is needed for a test below.
[!exec:jq] skip 'Skipping test requiring absent jq command'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like several sample tests here depend on jq to compare fields in JSON files. If this is going to be frequent, maybe we can provide a function to do these comparisons? And maybe the same with basic http requests to avoid depending on curl.

This would help to avoid depending on external tools that may not be installed, specially on Windows.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it becomes necessary, those can be added. Both curl and jq are available in CI and I'd prefer to avoid having to reimplement and maintain tools that already exist.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree this can be added later.

I am not so worried about the current CI, but about future deployments where we might forget to install jq (or curl in Windows) if not required for other things, and the test would be silently skipped. Same thing for local execution.

jq and curl are powerful tools, in no case I would think on re-implement them, but the limited logic that we need, also reusing existing tools, in the form of Go code.

Copy link
Contributor

@mrodm mrodm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a comment about CI scripts.

The error raised in the latest Buildkite build should be fixed now, since it has been merged the new spec version #3049

PACKAGE_TEST_TYPE=other ./scripts/test-check-packages.sh

test-check-packages-independent-script:
elastic-package test script -C test/packages/other/with_script --external-stack=false --defer-cleanup 1s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would create a new folder for this test package like test/packages/independent-script/ and just keep (move) that new test package into this new folder.

The reasoning of this is that currently, this with_script test package is being tested twice in Buildkite builds. In these two steps:

  • Integration test: other
  • Integration test: independent-script

So, I would create a new folder and update this target:

Suggested change
elastic-package test script -C test/packages/other/with_script --external-stack=false --defer-cleanup 1s
elastic-package test script -C test/packages/independent-script/with_script --external-stack=false --defer-cleanup 1s

- `--run`: run only tests matching the regular expression
- `--scripts`: path to directory containing test scripts (advanced use only)
- `--update`: update archive file if a cmp fails
- `--verbose-scripts`: verbose script test output (show all script logging)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably start using named loggers, and some global selector flag. cc @mrodm

At least, the current logger set in elastic-package supports to set -v and -vv (or -v -v). With the former, DEBUG messages are shown. With the latter, TRACE messages are shown too.

func Trace(a ...interface{}) {
if !IsTraceMode() {
return
}
logMessage("TRACE", a...)
}
// Tracef method logs message with "trace" level and formats it.
func Tracef(format string, a ...interface{}) {
if !IsTraceMode() {
return
}
logMessagef("TRACE", format, a...)
}

@efd6 efd6 force-pushed the script_tests branch 3 times, most recently from 3514b1d to 31b44ff Compare November 5, 2025 21:29
@elasticmachine
Copy link
Collaborator

💚 Build Succeeded

History

cc @efd6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants