Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
b14c465
Add DynamicSignalTypes to PolicyTemplate struct
MichelLosier Mar 4, 2026
0375356
Refactor scenarioTest to have dataStreams slice
MichelLosier Mar 4, 2026
7987669
Add discoverDataStreams
MichelLosier Mar 4, 2026
5821366
Incorporate datastream discovery for dynamic_signal_types into prepar…
MichelLosier Mar 4, 2026
fcd8e0f
Loop over datastreams in validateTestScenario
MichelLosier Mar 4, 2026
7e97891
Have system tests artifact creation handle multiple datastreams
MichelLosier Mar 11, 2026
6c7d972
Add support for in system tests to narrow test scope
MichelLosier Mar 12, 2026
0e42d25
Add tests for discoverDataStreams
MichelLosier Mar 12, 2026
29a8599
Add helper for building datastream name with explicit type
MichelLosier Mar 12, 2026
800a16c
Extract scenarioDataStream building into own method on tester
MichelLosier Mar 12, 2026
9db5c35
Add test coverage for tester.buildDataStreamScenarios
MichelLosier Mar 12, 2026
c1fc11f
Update tests, fix index template name bug
MichelLosier Mar 13, 2026
f59adbe
Update system_testing.md with signal_types config opt
MichelLosier Mar 13, 2026
4a100ee
Add sql_server_input_otel as test package
MichelLosier Mar 13, 2026
362cb71
Simplify dataStreamDataType string extraction
MichelLosier Mar 17, 2026
40bc8a7
Add debug line that lists all datastreams to be tested together
MichelLosier Mar 17, 2026
ab697ed
Remove use of in policy for sql_server_input_otel test package
MichelLosier Mar 17, 2026
3e431e3
Add ForDuration in wait module
MichelLosier Mar 17, 2026
64e7d1f
Discover datastreams in two phases
MichelLosier Mar 17, 2026
3ec5d6f
Have dynamicSignalTypesTTL be configurable
MichelLosier Mar 17, 2026
1720977
Add tests waitForAllDataStreams
MichelLosier Mar 17, 2026
50be1b6
Move scenario.deprecationWarnings into scenarioDataStream
MichelLosier Mar 18, 2026
b8f860d
Extract datastream doc verification into own method
MichelLosier Mar 18, 2026
03c3f09
Add discoverDataStreamsCh to enable concurrent stream discovery and v…
MichelLosier Mar 18, 2026
3932568
Add discoverAndVerifyDataStreams to concurrently run ds discovery and…
MichelLosier Mar 18, 2026
932a022
Incorporate discoverAndVerifyDataStreams into prepareScenario
MichelLosier Mar 18, 2026
94b72ac
Have sync package as direct dep
MichelLosier Mar 18, 2026
7e93b2b
Formatting
MichelLosier Mar 18, 2026
12ef3c7
Add test-all-signals system test for sql_server_input_otel test package
MichelLosier Mar 18, 2026
a1a3bad
Handle multiple sample_events for docs
MichelLosier Mar 18, 2026
2307067
Add getSampleEventPaths
MichelLosier Mar 18, 2026
83c3253
Update verifySampleEvent to handle multiple sample events
MichelLosier Mar 18, 2026
86444e1
Add test for multiple sample event handling for docs
MichelLosier Mar 18, 2026
fb71771
Add tests for multi sample file handling in static test runner
MichelLosier Mar 18, 2026
2a0aab0
Have datastream discovery and verification synchronous
MichelLosier Mar 20, 2026
2b8b0b4
Remove unused imports
MichelLosier Mar 20, 2026
cf11b62
Match only on namespace when doing datastream discovery
MichelLosier Mar 20, 2026
b2a129f
Have sync as indirect dep
MichelLosier Mar 20, 2026
65d7882
When signal_types is used, use discovery with wildcard dataset
MichelLosier Mar 20, 2026
cced5a1
Have dynamic_signal_types discovery run through 5s of search after ea…
MichelLosier Mar 22, 2026
2add311
Formatting
MichelLosier Mar 22, 2026
4157304
Edit logging wording
MichelLosier Mar 22, 2026
e4c2b26
Increase discovery TTL count to 10
MichelLosier Mar 22, 2026
eb74594
Use underscore as separator for sample event types
MichelLosier Mar 23, 2026
ca09f4a
Extract iterated ops in verifySampleEvents into own method, update st…
MichelLosier Mar 23, 2026
c2c7f42
Remove old dot separated sample events
MichelLosier Mar 23, 2026
5df7435
Formatting
MichelLosier Mar 23, 2026
b54f341
Change DynamicSignalTypesSearchPollCount to friendlier WaitForDynamic…
MichelLosier Mar 23, 2026
ccb6cad
Update docs handling of sample events
MichelLosier Mar 23, 2026
e6509ad
Remove unused request mock
MichelLosier Mar 24, 2026
f2187f5
Handle having both plain and named sample events for readme rendering
MichelLosier Mar 24, 2026
b080c76
Add event template in ReadMe for sql_server_input_otel
MichelLosier Mar 24, 2026
036f8de
Rename sampleEventSignalType to sampleEventName, add sort
MichelLosier Mar 24, 2026
70467ae
Add test case for rendering both named and unnamed sample events
MichelLosier Mar 24, 2026
1d4fbb8
Restore docs sample_event handling with main
MichelLosier Mar 24, 2026
e8a3144
Render only one sample_event, with fallback to named sample events
MichelLosier Mar 24, 2026
4787e2b
Update readme tests
MichelLosier Mar 24, 2026
86cd021
Remove all streams in namespace for scenario cleanup
MichelLosier Mar 24, 2026
74974e1
Update Readme with sample event for sql_server_input_otel
MichelLosier Mar 24, 2026
670b486
Use datastream discovery when input package is otelcol and type traces
MichelLosier Mar 25, 2026
74d56a5
Add debug logging
MichelLosier Mar 26, 2026
275bf5b
Formatting
MichelLosier Mar 26, 2026
366e17a
Merge branch 'main' into add-dynamic-signal-types-system-test-support
MichelLosier Mar 26, 2026
10beacf
Merge branch 'main' into add-dynamic-signal-types-system-test-support
MichelLosier Mar 30, 2026
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
2 changes: 2 additions & 0 deletions docs/howto/system_testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,12 +534,14 @@ for system tests.
| policy_template | string | | Name of policy template associated with the data stream and input. Required when multiple policy templates include the input being tested. |
| service | string | | Name of a specific Docker service to setup for the test. |
| service_notify_signal | string | | Signal name to send to 'service' when the test policy has been applied to the Agent. This can be used to trigger the service after the Agent is ready to receive data. |
| signal_types | []string | | For otel packages with dynamic_signal_types, this specifies which signal data streams to assert on. Otherwise all detected data streams for the dataset will be asserted against.
| skip.link | URL | | URL linking to an issue about why the test is skipped. |
| skip.reason | string | | Reason to skip the test. If specified the test will not execute. |
| skip_ignored_fields | array string | | List of fields to be skipped when performing validation of fields ignored during ingestion. |
| skip_transform_validation | boolean | | Disable or enable the transforms validation performed in system tests. |
| vars | dictionary | | Package level variables to set (i.e. declared in `$package_root/manifest.yml`). If not specified the defaults from the manifest are used. |
| wait_for_data_timeout | duration | | Amount of time to wait for data to be present in Elasticsearch. Defaults to 10m. |
| wait_for_dynamic_streams_stable | duration | | For packages with dynamic data streams (e.g. `dynamic_signal_types`), minimum time the discovered data stream count must stay unchanged after the first stream appears before discovery completes. Defaults to 10s. |

For example, the `apache/access` data stream's `test-access-log-config.yml` is
shown below.
Expand Down
140 changes: 110 additions & 30 deletions internal/docs/readme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package docs

import (
"fmt"
"os"
"path/filepath"
"testing"
Expand All @@ -16,6 +17,21 @@ import (
"github.com/elastic/elastic-package/internal/packages"
)

const (
sampleEventReadmeTemplateRel = "_dev/build/docs/README.md"
sampleEventReadmeDocPrefix = `{{- generatedHeader }}
# README
Introduction to the package
`
)

func readmeEventTemplateLine(eventTarget string) string {
if eventTarget == "" {
return "{{ event }}"
}
return fmt.Sprintf(`{{ event %q }}`, eventTarget)
}

func TestGenerateReadme(t *testing.T) {
cases := []struct {
title string
Expand Down Expand Up @@ -134,59 +150,107 @@ http://www.example.com/bar

func TestRenderReadmeWithSampleEvent(t *testing.T) {
cases := []struct {
title string
packageRoot string
templatePath string
dataStreamName string
readmeTemplateContents string
sampleEventJsonContents string
expected string
title string
eventTarget string // data stream folder and {{ event "…" }} argument; empty → package root and {{ event }}
plainValue string // if set, writes sample_event.json with JSON {"v": plainValue}
namedSamples []string
expected string
}{
{
title: "README with sample event",
packageRoot: t.TempDir(),
templatePath: "_dev/build/docs/README.md",
readmeTemplateContents: `
{{- generatedHeader }}
title: "README with sample event",
eventTarget: "example",
plainValue: "event1",
expected: `<!-- NOTICE: Do not edit this file manually.-->
<!-- This file is automatically generated by Elastic Package -->
# README
Introduction to the package
{{ event "example" }}`,
An example event for ` + "`example`" + ` looks as following:

` + "```json" + `
{
"v": "event1"
}
` + "```" + "\n",
},
{
title: "named sample_event_*.json only uses first lexicographic basename",
eventTarget: "otel_ds",
namedSamples: []string{"metrics", "logs"},
expected: `<!-- NOTICE: Do not edit this file manually.-->
<!-- This file is automatically generated by Elastic Package -->
# README
Introduction to the package
An example event for ` + "`example`" + ` looks as following:
An example event for ` + "`otel_ds`" + ` looks as following:

` + "```json" + `
{
"v": "logs"
}
` + "```" + "\n",
},
{
title: "sample_event.json is preferred over sample_event_*.json",
eventTarget: "otel_ds",
plainValue: "plain",
namedSamples: []string{"logs"},
expected: `<!-- NOTICE: Do not edit this file manually.-->
<!-- This file is automatically generated by Elastic Package -->
# README
Introduction to the package
An example event for ` + "`otel_ds`" + ` looks as following:

` + "```json" + `
{
"v": "plain"
}
` + "```" + "\n",
},
{
title: "package root: named sample_event_*.json files only uses first lexicographic basename",
eventTarget: "",
namedSamples: []string{"metrics", "logs"},
expected: `<!-- NOTICE: Do not edit this file manually.-->
<!-- This file is automatically generated by Elastic Package -->
# README
Introduction to the package
An example event looks as following:

` + "```json" + `
{
"id": "event1"
"v": "logs"
}
` + "```",
dataStreamName: "example",
sampleEventJsonContents: `{"id": "event1"}`,
` + "```" + "\n",
},
}

linksMap := newEmptyLinkMap()
urls := fields.SchemaURLs{}
for _, c := range cases {
t.Run(c.title, func(t *testing.T) {
filename := filepath.Base(c.templatePath)
templatePath := filepath.Join(c.packageRoot, c.templatePath)
packageRoot := t.TempDir()
filename := filepath.Base(sampleEventReadmeTemplateRel)
templatePath := filepath.Join(packageRoot, sampleEventReadmeTemplateRel)

createReadmeTemplateFile(t, c.packageRoot, c.readmeTemplateContents)
createSampleEventFile(t, c.packageRoot, c.dataStreamName, c.sampleEventJsonContents)
createManifestFile(t, c.packageRoot)
readmeContents := sampleEventReadmeDocPrefix + readmeEventTemplateLine(c.eventTarget) + "\n"
createReadmeTemplateFile(t, packageRoot, readmeContents)

root, err := os.OpenRoot(c.packageRoot)
ds := c.eventTarget
if c.plainValue != "" {
createSampleEventFile(t, packageRoot, ds, fmt.Sprintf(`{"v": %q}`, c.plainValue))
}
for _, name := range c.namedSamples {
createNamedSampleEventFile(t, packageRoot, ds, name, fmt.Sprintf(`{"v": %q}`, name))
}
createManifestFile(t, packageRoot)

root, err := os.OpenRoot(packageRoot)
require.NoError(t, err)
t.Cleanup(func() { root.Close() })

rendered, err := renderReadme(root, filename, c.packageRoot, templatePath, linksMap, urls)
rendered, err := renderReadme(root, filename, packageRoot, templatePath, linksMap, urls)
require.NoError(t, err)

renderedString := string(rendered)
assert.Equal(t, c.expected, renderedString)
assert.Equal(t, c.expected, string(rendered))
})
}
}
Expand Down Expand Up @@ -500,10 +564,26 @@ func createDocsFolder(t *testing.T, packageRoot string) string {

func createSampleEventFile(t *testing.T, packageRoot, dataStreamName, contents string) {
t.Helper()
dataStreamFolder := createDataStreamFolder(t, packageRoot, dataStreamName)
writeSampleEventAt(t, packageRoot, dataStreamName, sampleEventFile, contents)
}

// createNamedSampleEventFile writes sample_event_<name>.json.
func createNamedSampleEventFile(t *testing.T, packageRoot, dataStreamName, name, contents string) {
t.Helper()
require.NotEmpty(t, name, "sample event name is required")
writeSampleEventAt(t, packageRoot, dataStreamName, fmt.Sprintf("sample_event_%s.json", name), contents)
}

sampleEventFile := filepath.Join(dataStreamFolder, sampleEventFile)
err := os.WriteFile(sampleEventFile, []byte(contents), 0644)
func writeSampleEventAt(t *testing.T, packageRoot, dataStreamName, fileName, contents string) {
t.Helper()
var dir string
if dataStreamName == "" {
dir = packageRoot
} else {
dir = createDataStreamFolder(t, packageRoot, dataStreamName)
}
path := filepath.Join(dir, fileName)
err := os.WriteFile(path, []byte(contents), 0644)
require.NoError(t, err)
}

Expand Down
32 changes: 29 additions & 3 deletions internal/docs/sample_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"strings"

"github.com/Masterminds/semver/v3"
Expand All @@ -20,11 +21,15 @@ import (
const sampleEventFile = "sample_event.json"

func renderSampleEvent(packageRoot, dataStreamName string) (string, error) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not totally sure about how these sample event files should be rendered. For now, I'd prefer to keep rendering just one file (as it is currently done in main) and let's see how this could be improved later in some follow-up.

As a proposal, it could be rendered the sample_event.json file if that file exists (as it is done currently in main). If that file does not exist but other sample_event_*.json files exist, then render just the first of those files ordered lexicographical by name. This will ensure that the same sample_event file is picked up always to render the README.

In any case, static tests must keep validating all of them as it is performed in this PR now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, seems like if we wanted to support multiple renderings later, it maybe be better addressed during or after the implementing story for elastic/package-spec#582

var eventPath string
var dir string
if dataStreamName == "" {
eventPath = filepath.Join(packageRoot, sampleEventFile)
dir = packageRoot
} else {
eventPath = filepath.Join(packageRoot, "data_stream", dataStreamName, sampleEventFile)
dir = filepath.Join(packageRoot, "data_stream", dataStreamName)
}
eventPath, err := resolveSampleEventPath(dir)
if err != nil {
return "", err
}

body, err := os.ReadFile(eventPath)
Expand Down Expand Up @@ -60,6 +65,27 @@ func renderSampleEvent(packageRoot, dataStreamName string) (string, error) {
return builder.String(), nil
}

func resolveSampleEventPath(dir string) (string, error) {
plain := filepath.Join(dir, sampleEventFile)
if info, err := os.Stat(plain); err == nil && !info.IsDir() {
return plain, nil
}

matches, err := filepath.Glob(filepath.Join(dir, "sample_event_*.json"))
if err != nil {
return "", fmt.Errorf("glob sample_event_*.json failed (dir: %s): %w", dir, err)
}
if len(matches) == 0 {
return "", fmt.Errorf("sample event file not found (looked for %s and sample_event_*.json under %s): %w",
sampleEventFile, dir, os.ErrNotExist)
}

sort.Slice(matches, func(i, j int) bool {
return filepath.Base(matches[i]) < filepath.Base(matches[j])
})
return matches[0], nil
}

func stripDataStreamFolderSuffix(dataStreamName string) string {
dataStreamName = strings.ReplaceAll(dataStreamName, "_metrics", "")
dataStreamName = strings.ReplaceAll(dataStreamName, "_logs", "")
Expand Down
9 changes: 5 additions & 4 deletions internal/packages/packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,11 @@ type PolicyTemplate struct {
Inputs []Input `config:"inputs,omitempty" json:"inputs,omitempty" yaml:"inputs,omitempty"`

// For purposes of "input packages"
Input string `config:"input,omitempty" json:"input,omitempty" yaml:"input,omitempty"`
Type string `config:"type,omitempty" json:"type,omitempty" yaml:"type,omitempty"`
TemplatePath string `config:"template_path,omitempty" json:"template_path,omitempty" yaml:"template_path,omitempty"`
Vars []Variable `config:"vars,omitempty" json:"vars,omitempty" yaml:"vars,omitempty"`
Input string `config:"input,omitempty" json:"input,omitempty" yaml:"input,omitempty"`
Type string `config:"type,omitempty" json:"type,omitempty" yaml:"type,omitempty"`
DynamicSignalTypes bool `config:"dynamic_signal_types,omitempty" json:"dynamic_signal_types,omitempty" yaml:"dynamic_signal_types,omitempty"`
TemplatePath string `config:"template_path,omitempty" json:"template_path,omitempty" yaml:"template_path,omitempty"`
Vars []Variable `config:"vars,omitempty" json:"vars,omitempty" yaml:"vars,omitempty"`
}

// Owner defines package owners, either a single person or a team.
Expand Down
1 change: 1 addition & 0 deletions internal/testrunner/runners/static/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
TestType testrunner.TestType = "static"

sampleEventJSON = "sample_event.json"
sampleEventGlob = "sample_event*.json"
)

type runner struct {
Expand Down
Loading