internal/testrunner/runners/policy/tester.go (147 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.
package policy
import (
"context"
"path/filepath"
"strings"
"github.com/elastic/go-resource"
"github.com/elastic/elastic-package/internal/kibana"
"github.com/elastic/elastic-package/internal/logger"
"github.com/elastic/elastic-package/internal/resources"
"github.com/elastic/elastic-package/internal/testrunner"
)
type tester struct {
testFolder testrunner.TestFolder
packageRootPath string
kibanaClient *kibana.Client
testPath string
generateTestResult bool
globalTestConfig testrunner.GlobalRunnerTestConfig
withCoverage bool
coverageType string
resourcesManager *resources.Manager
}
// Ensures that runner implements testrunner.Tester interface
var _ testrunner.Tester = new(tester)
type PolicyTesterOptions struct {
TestFolder testrunner.TestFolder
TestPath string
KibanaClient *kibana.Client
PackageRootPath string
GenerateTestResult bool
GlobalTestConfig testrunner.GlobalRunnerTestConfig
WithCoverage bool
CoverageType string
}
func NewPolicyTester(options PolicyTesterOptions) *tester {
tester := tester{
kibanaClient: options.KibanaClient,
testFolder: options.TestFolder,
packageRootPath: options.PackageRootPath,
generateTestResult: options.GenerateTestResult,
testPath: options.TestPath,
globalTestConfig: options.GlobalTestConfig,
withCoverage: options.WithCoverage,
coverageType: options.CoverageType,
}
tester.resourcesManager = resources.NewManager()
tester.resourcesManager.RegisterProvider(resources.DefaultKibanaProviderName, &resources.KibanaProvider{Client: tester.kibanaClient})
return &tester
}
func (r *tester) Type() testrunner.TestType {
return TestType
}
func (r *tester) String() string {
return string(TestType)
}
// Parallel indicates if this tester can run in parallel or not.
func (r tester) Parallel() bool {
// Not supported yet parallel tests even if it is indicated in the global config r.globalTestConfig
return false
}
func (r *tester) Run(ctx context.Context) ([]testrunner.TestResult, error) {
var results []testrunner.TestResult
result, err := r.runTest(ctx, r.resourcesManager, r.testPath)
if err != nil {
logger.Error(err)
}
results = append(results, result...)
return results, nil
}
func (r *tester) runTest(ctx context.Context, manager *resources.Manager, testPath string) ([]testrunner.TestResult, error) {
result := testrunner.NewResultComposer(testrunner.TestResult{
TestType: TestType,
Name: filepath.Base(testPath),
Package: r.testFolder.Package,
DataStream: r.testFolder.DataStream,
})
testConfig, err := readTestConfig(testPath)
if err != nil {
return result.WithErrorf("failed to read test config from %s: %w", testPath, err)
}
testName := testNameFromPath(testPath)
if skip := testrunner.AnySkipConfig(testConfig.Skip, r.globalTestConfig.Skip); skip != nil {
logger.Warnf("skipping %s test for %s/%s: %s (details: %s)",
TestType, r.testFolder.Package, r.testFolder.DataStream,
skip.Reason, skip.Link)
return result.WithSkip(skip)
}
policy := resources.FleetAgentPolicy{
Name: testName,
Namespace: "ep",
PackagePolicies: []resources.FleetPackagePolicy{
{
Name: testName + "-" + r.testFolder.Package,
RootPath: r.packageRootPath,
DataStreamName: r.testFolder.DataStream,
InputName: testConfig.Input,
Vars: testConfig.Vars,
DataStreamVars: testConfig.DataStream.Vars,
},
},
}
resources := resource.Resources{&policy}
_, testErr := manager.ApplyCtx(ctx, resources)
if testErr == nil {
if r.generateTestResult {
testErr = dumpExpectedAgentPolicy(ctx, r.kibanaClient, testPath, policy.ID)
} else {
testErr = assertExpectedAgentPolicy(ctx, r.kibanaClient, testPath, policy.ID)
}
}
// Cleanup
policy.Absent = true
_, err = manager.ApplyCtx(ctx, resources)
if err != nil {
if testErr != nil {
return result.WithErrorf("cleanup failed with %w after test failed: %w", err, testErr)
}
return result.WithErrorf("cleanup failed: %w", err)
}
if r.withCoverage {
coverage, err := generateCoverageReport(result.CoveragePackageName(), r.packageRootPath, r.testFolder.DataStream, r.coverageType)
if err != nil {
return result.WithErrorf("coverage report generation failed: %w", err)
}
result = result.WithCoverage(coverage)
}
if testErr != nil {
return result.WithError(testErr)
}
return result.WithSuccess()
}
// generateCoverageReport generates a coverage report that includes the manifests and template files in the package or data stream.
// TODO: For manifests, mark as covered only the variables used.
// TODO: For templates, mark as covered only the parts used, but this requires introspection in handlebars.
func generateCoverageReport(pkgName, rootPath, dataStream, coverageType string) (testrunner.CoverageReport, error) {
dsPattern := "*"
if dataStream != "" {
dsPattern = dataStream
}
// This list of patterns includes patterns for all types of packages. It should not be a problem if some path doesn't exist.
patterns := []string{
filepath.Join(rootPath, "manifest.yml"),
filepath.Join(rootPath, "agent", "input", "*.yml.hbs"),
filepath.Join(rootPath, "data_stream", dsPattern, "manifest.yml"),
filepath.Join(rootPath, "data_stream", dsPattern, "agent", "stream", "*.yml.hbs"),
}
return testrunner.GenerateBaseFileCoverageReportGlob(pkgName, patterns, coverageType, true)
}
func testNameFromPath(path string) string {
ext := filepath.Ext(path)
return strings.TrimSuffix(filepath.Base(path), ext)
}
func (r *tester) TearDown(ctx context.Context) error {
return nil
}