一键导入
write-agent-health-e2e
Create an E2E lifecycle test for a new health platform issue in test/new-e2e/tests/agent-health/
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Create an E2E lifecycle test for a new health platform issue in test/new-e2e/tests/agent-health/
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Generate the Agent Supply Chain newsletter by researching team activity on GitHub and Confluence, then creating a Confluence draft and Gmail draft
Give your AI agents something more useful than a prompt. Velocity through clarity.
Extract an Allium specification from an existing codebase. Use when the user has existing code and wants to distil behaviour into a spec, reverse engineer a specification from implementation, generate a spec from code, turn implementation into a behavioural specification, or document what a codebase does in Allium terms.
Run a structured discovery session to build an Allium specification through conversation. Use when the user wants to create a new spec from scratch, elicit or gather requirements, capture domain behaviour, specify a feature or system, define what a system should do, or is describing functionality and needs help shaping it into a specification.
Generate tests from Allium specifications. Use when the user wants to propagate tests, generate test files from a spec, write tests for a specification, create property-based tests, produce state machine tests, check test coverage against spec obligations, or understand what tests a specification requires.
Autonomously work on Jira backlog tickets, creating PRs and shepherding them to merge
| name | write-agent-health-e2e |
| description | Create an E2E lifecycle test for a new health platform issue in test/new-e2e/tests/agent-health/ |
| allowed-tools | Read, Write, Edit, Glob, Grep, Bash |
| argument-hint | <issue-module-name> e.g. rofspermissions, invalidconfig, dockerpermissions |
$ARGUMENTS must be the name of an existing issue module directory under comp/healthplatform/issues/.
If $ARGUMENTS is empty or not provided, stop immediately and ask:
Which health platform issue module should I write the E2E test for? Available modules: run
ls comp/healthplatform/issues/to list them.
Once you have the module name, set:
MODULE = $ARGUMENTS (e.g. rofspermissions)MODULE_PATH = comp/healthplatform/issues/$MODULE/TEST_FILE = test/new-e2e/tests/agent-health/{module}_test.goVerify $MODULE_PATH exists before proceeding:
ls comp/healthplatform/issues/$MODULE/
If the directory does not exist, stop and tell the user which modules are available.
Read both files before writing anything:
$MODULE_PATH/module.go — extract:
IssueID constant → used as const issueID in the testIssueName constant → asserted as issue.IssueName in the testIssueType constant → asserted as issue.IssueType in the test. It is a fixed const set explicitly by the module (IssueName lowercased, spaces replaced by underscores) — not computed by the agent at runtime, so read it directly rather than deriving it yourself.BuiltInPeriodicHealthCheck() returns non-nil → agent runs the check on a scheduleBuiltInStartupHealthCheck() returns non-nil → check runs once at startupnil → issues are pushed externally by another component (like admissionprobe)$MODULE_PATH/issue.go — extract the exact values returned by BuildIssue():
Category, Source, Location, Severity, Tags → assert these in IssueDetectionRemediation is non-nil and has Summary/Steps → assert those tooIf a check.go file exists in the module, read it to understand how the issue is triggered (what system condition causes it).
| Scenario | Environment |
|---|---|
| Standard host issue (most cases) | environments.Host + awshost.Provisioner |
| Issue requires Docker daemon | Custom env struct with Docker *components.RemoteHostDocker + Pulumi provisioner |
Do not implement Diagnose() on the env — all assertions go through fakeintake only.
Check whether another test file in this package already embeds a suitable base agent config (e.g. a short forwarder interval) before adding your own — reuse it if so, following the fixture rules in Step 4 otherwise.
$TEST_FILE// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2025-present Datadog, Inc.
package agenthealth
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/DataDog/agent-payload/v5/healthplatform"
"github.com/DataDog/datadog-agent/test/e2e-framework/components/datadog/agentparams"
"github.com/DataDog/datadog-agent/test/e2e-framework/scenarios/aws/ec2"
"github.com/DataDog/datadog-agent/test/e2e-framework/testing/e2e"
"github.com/DataDog/datadog-agent/test/e2e-framework/testing/environments"
awshost "github.com/DataDog/datadog-agent/test/e2e-framework/testing/provisioners/aws/host"
)
type {module}Suite struct {
e2e.BaseSuite[environments.Host]
}
func Test{Module}Suite(t *testing.T) {
t.Parallel()
e2e.Run(t, &{module}Suite{},
e2e.WithProvisioner(awshost.Provisioner(
awshost.WithRunOptions(
ec2.WithAgentOptions(
agentparams.WithAgentConfig({module}AgentConfig),
// add agentparams here to pre-configure the issue trigger if needed
),
),
)),
)
}
// Test{Module}IssueLifecycle verifies that <describe trigger condition> is detected
// in fakeintake as NEW, and that <describe fix> causes the issue to stop being
// reported (or be reported as RESOLVED).
//
// Cross-restart persistence is tested separately in TestResilienceSuite.
func (suite *{module}Suite) Test{Module}IssueLifecycle() {
// only declare host/agent if needed for runtime trigger/fix steps
fakeIntake := suite.Env().FakeIntake.Client()
const issueID = "<IssueID from module.go>"
suite.T().Run("IssueDetection", func(t *testing.T) {
// if the issue is not pre-configured by the provisioner, trigger it here:
// suite.Env().RemoteHost.MustExecute("sudo ...")
var issues []*healthplatform.Issue
require.EventuallyWithT(t, func(ct *assert.CollectT) {
payloads, err := fakeIntake.GetAgentHealth()
assert.NoError(ct, err)
issues = nil
for _, p := range payloads {
for _, iss := range findIssuesByID(t, p, issueID) {
if iss.PersistedIssue != nil && iss.PersistedIssue.State == healthplatform.IssueState_ISSUE_STATE_ACTIVE {
issues = append(issues, iss)
}
}
}
assert.NotEmpty(ct, issues, "issue not found as ACTIVE in fakeintake")
}, defaultIssueTimeout, defaultIssuePollInterval, "issue not detected as ACTIVE in fakeintake")
require.NotEmpty(t, issues)
issue := issues[0]
// assert values read from issue.go:
assert.Equal(t, "<IssueName>", issue.IssueName)
assert.Equal(t, "<issue_type>", issue.IssueType) // IssueName lowercased, spaces -> underscores
assert.Equal(t, "<Category>", issue.Category)
assert.Equal(t, "<Source>", issue.Source)
// assert.Equal(t, "<Location>", issue.Location) // if Location is set
assert.Contains(t, issue.Tags, "<tag>")
require.NotNil(t, issue.Remediation)
assert.NotEmpty(t, issue.Remediation.Summary)
})
suite.T().Run("Resolution", func(t *testing.T) {
// Option A — fix via config change (preferred): UpdateEnv handles restart + wait
suite.UpdateEnv(awshost.Provisioner(
awshost.WithRunOptions(
ec2.WithAgentOptions(
agentparams.WithAgentConfig({module}AgentConfig),
// agentparams that represent the fixed state
),
),
))
require.NoError(t, fakeIntake.FlushServerAndResetAggregators())
// Option B — fix via runtime action (e.g. chmod), then manual restart:
// suite.Env().RemoteHost.MustExecute("sudo ...")
// require.NoError(t, suite.Env().Agent.Client.Restart())
// require.EventuallyWithT(t, func(ct *assert.CollectT) {
// assert.True(ct, suite.Env().Agent.Client.IsReady())
// }, 2*time.Minute, 10*time.Second, "agent not ready after fix")
// require.NoError(t, fakeIntake.FlushServerAndResetAggregators())
require.Never(t, func() bool {
payloads, _ := fakeIntake.GetAgentHealth()
for _, p := range payloads {
for _, iss := range findIssuesByID(t, p, issueID) {
if iss.PersistedIssue == nil || iss.PersistedIssue.State != healthplatform.IssueState_ISSUE_STATE_RESOLVED {
return true
}
}
}
return false
}, defaultIssueAbsenceWindow, defaultIssuePollInterval,
"issue still reported as non-resolved after fix")
})
}
If the issue ID includes a runtime-generated suffix (e.g. an issue ID with a hash appended per instance):
add a findIssuesByPrefix helper to helpers_test.go (mirroring findIssuesByID but using strings.HasPrefix) and use it in place of findIssuesByID(t, p, issueID).
| Content | How to include |
|---|---|
| Short YAML (≤ 10 lines) | const string literal inline in the test file |
| Python file | //go:embed fixtures/{module}.py → var {module}Py string |
| Long YAML | //go:embed fixtures/{module}_config.yaml → var {module}Config string |
Before adding a new agent config fixture, check whether an existing one in the package already fits — reuse it instead of declaring a near-duplicate.
UpdateEnv first → wait for agent ready → FlushServerAndResetAggregators(). Never flush before restarting.RestartResilience is covered once in TestResilienceSuite. Do not add it here.ISSUE_STATE_ACTIVE, ISSUE_STATE_RESOLVED).IssueDetection, Resolution) are always inline in the test method.Diagnose() on the env struct.t.Parallel(): the first line of every Test{Module}Suite function must be t.Parallel() so the suite can run concurrently with others.dda inv linter.go --targets=test/new-e2e/tests/agent-health
dda inv new-e2e-tests.run --targets=./tests/agent-health/... --run=^Test{Module}Suite$
Tell the user:
dda inv new-e2e-tests.run --targets=./tests/agent-health/... --run=^Test{Module}Suite$fixtures/