feat: execute durable client commands through supervisor
This commit is contained in:
+81
-2
@@ -86,6 +86,8 @@ func runCLI(ctx context.Context, args []string) error {
|
||||
return validateCoverageInventory(filepath.Join(repoRoot, "test", "coverage.toml"))
|
||||
case "integration":
|
||||
return h.integration(ctx, args[1:])
|
||||
case "e2e":
|
||||
return h.e2e(ctx, args[1:])
|
||||
case "status", "logs", "collect", "recover", "reuse", "stop", "reset", "purge":
|
||||
return h.environmentCommand(args[0], args[1:])
|
||||
default:
|
||||
@@ -94,7 +96,7 @@ func runCLI(ctx context.Context, args []string) error {
|
||||
}
|
||||
|
||||
func usageError() error {
|
||||
return errors.New("usage: harness doctor|coverage|integration|status|logs|collect|recover|reuse|stop|reset|purge")
|
||||
return errors.New("usage: harness doctor|coverage|integration|e2e|status|logs|collect|recover|reuse|stop|reset|purge")
|
||||
}
|
||||
|
||||
func (h *harness) doctor(repoRoot string) error {
|
||||
@@ -113,7 +115,7 @@ func (h *harness) doctor(repoRoot string) error {
|
||||
name := probe.Name()
|
||||
_ = probe.Close()
|
||||
_ = os.Remove(name)
|
||||
fmt.Fprintln(h.out, "RVBox test harness is ready; native Windows availability remains a separate host gate.")
|
||||
fmt.Fprintln(h.out, "RVBox test harness is ready; native Windows scenarios use the explicit scripts/windows/test-host.ps1 host lane.")
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -182,6 +184,83 @@ func (h *harness) integration(ctx context.Context, args []string) error {
|
||||
return h.transition(current, "completed", *suite+"-complete", *suite+" integration run completed")
|
||||
}
|
||||
|
||||
// e2e runs production-shaped Go scenarios against real local listeners and
|
||||
// durable stores. Native Windows work is an additional host lane invoked by
|
||||
// scripts/windows/test-host.ps1; it is never silently replaced by Wine or a
|
||||
// cross-compiled binary. The run manifest/journal makes every scenario
|
||||
// resumable and keeps artifacts bounded.
|
||||
func (h *harness) e2e(ctx context.Context, args []string) error {
|
||||
flags := flag.NewFlagSet("e2e", flag.ContinueOnError)
|
||||
flags.SetOutput(io.Discard)
|
||||
scenario := flags.String("scenario", "smoke", "scenario name: smoke, script, recovery, or all")
|
||||
runID := flags.String("run-id", "", "run ID")
|
||||
resume := flags.Bool("resume", false, "resume an existing run")
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
if *scenario != "smoke" && *scenario != "script" && *scenario != "recovery" && *scenario != "all" {
|
||||
return fmt.Errorf("scenario %q is not implemented yet; available: smoke, script, recovery, all", *scenario)
|
||||
}
|
||||
var current *manifest
|
||||
var err error
|
||||
if *resume {
|
||||
if *runID == "" {
|
||||
return errors.New("--resume requires --run-id")
|
||||
}
|
||||
current, err = h.load(*runID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if current.Layer != "e2e" || current.Suite != *scenario {
|
||||
return errors.New("run layer/scenario does not match resume request")
|
||||
}
|
||||
if current.Phase != "ready" && current.Phase != "interrupted" && current.Phase != "stopped" && current.Phase != "running" && current.Phase != "failed" {
|
||||
return fmt.Errorf("run in phase %q is not resumable; recover or reuse it first", current.Phase)
|
||||
}
|
||||
} else {
|
||||
current, err = h.create(*runID, "e2e", *scenario)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
fmt.Fprintln(h.out, current.RunID)
|
||||
if err := h.transition(current, "running", "e2e-start", "e2e scenario started"); err != nil {
|
||||
return err
|
||||
}
|
||||
scenarios := []string{*scenario}
|
||||
if *scenario == "all" {
|
||||
scenarios = []string{"smoke", "script", "recovery"}
|
||||
}
|
||||
for _, item := range scenarios {
|
||||
if err := h.runE2EScenario(ctx, current, item); err != nil {
|
||||
_ = h.transition(current, "failed", "e2e-"+item+"-failed", err.Error())
|
||||
return err
|
||||
}
|
||||
}
|
||||
return h.transition(current, "completed", "e2e-complete", "e2e scenario completed")
|
||||
}
|
||||
|
||||
func (h *harness) runE2EScenario(ctx context.Context, current *manifest, scenario string) error {
|
||||
if err := h.appendJournal(current.RunID, journalEntry{At: h.now(), Step: "e2e-" + scenario, Status: "running", Detail: "scenario started"}); err != nil {
|
||||
return err
|
||||
}
|
||||
var err error
|
||||
switch scenario {
|
||||
case "smoke":
|
||||
err = h.runGoSuite(ctx, current, "e2e-client-agent", "real client/server WebSocket and control flow", "./test/integration/clientagent")
|
||||
case "script":
|
||||
err = h.runGoSuite(ctx, current, "e2e-script-transfer", "durable script transfer and replay", "./internal/client/agent")
|
||||
case "recovery":
|
||||
err = h.runStoreSuite(ctx, current)
|
||||
default:
|
||||
err = fmt.Errorf("unknown e2e scenario %q", scenario)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return h.appendJournal(current.RunID, journalEntry{At: h.now(), Step: "e2e-" + scenario, Status: "passed", Detail: "scenario passed"})
|
||||
}
|
||||
|
||||
func (h *harness) runStoreSuite(ctx context.Context, current *manifest) error {
|
||||
if err := h.appendJournal(current.RunID, journalEntry{At: h.now(), Step: "store-real-sqlite", Status: "running", Detail: "running real SQLite/WAL and filesystem cases"}); err != nil {
|
||||
return err
|
||||
|
||||
@@ -112,6 +112,17 @@ func TestIntegrationResumeValidation_HP_CFG_01(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestE2EScenarioValidationAndRunIdentity_HP_E2E_01(t *testing.T) {
|
||||
t.Parallel()
|
||||
h := &harness{root: t.TempDir(), now: time.Now, out: &bytes.Buffer{}}
|
||||
if err := h.e2e(context.Background(), []string{"--scenario", "unknown"}); err == nil {
|
||||
t.Fatal("unknown e2e scenario accepted")
|
||||
}
|
||||
if err := h.e2e(context.Background(), []string{"--resume", "--scenario", "smoke"}); err == nil {
|
||||
t.Fatal("e2e resume without run ID accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoundedSuiteLogAndFailedRunRecovery_BH_STORE_03(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user