yao/sui/test/runner_test.go
Max 12e88943e9 feat(sandbox): add opencode runner and enhance attachment processing
- Registered new "opencode" and "opencode/cli" runners in the sandbox initialization.
- Updated the `runExecStep` function to use the computer's working directory and set the HOME environment variable.
- Refactored attachment processing in the Claude package to delegate to shared functionality, improving code maintainability.
2026-04-24 23:26:55 +08:00

362 lines
8.2 KiB
Go

package test_test
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/yaoapp/gou/application"
"github.com/yaoapp/yao/config"
"github.com/yaoapp/yao/engine"
"github.com/yaoapp/yao/sui/core"
suitest "github.com/yaoapp/yao/sui/test"
"github.com/yaoapp/yao/test"
)
func prepare(t *testing.T) {
t.Helper()
test.Prepare(t, config.Conf)
_, err := engine.Load(config.Conf, engine.LoadOption{Action: "sui.test"})
require.NoError(t, err)
}
func requireAgentSUI(t *testing.T) {
t.Helper()
_, has := core.SUIs["agent"]
if !has {
t.Skip("no 'agent' SUI loaded")
}
}
// --- happy path (dashboard page: 4 pass) ---
func TestDiscoverPageTests(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/dashboard",
JSON: true,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
require.NotNil(t, report)
assert.Greater(t, report.Summary.Total, 0)
assert.Greater(t, len(report.Pages), 0)
}
func TestRunnerExecuteTests(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/dashboard",
JSON: true,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, "sui_backend_test", report.Type)
assert.False(t, report.Metadata.StartedAt.IsZero())
assert.False(t, report.Metadata.CompletedAt.IsZero())
assert.Equal(t, 4, report.Summary.Total)
assert.Equal(t, 4, report.Summary.Passed)
assert.Equal(t, 0, report.Summary.Failed)
assert.False(t, report.HasFailures())
}
func TestRunnerWithRunFilter(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/dashboard",
Run: "TestGetDashboard",
JSON: true,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 1, report.Summary.Total)
assert.Equal(t, 1, report.Summary.Passed)
}
func TestRunnerFailFast(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/dashboard",
FailFast: true,
JSON: true,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 0, report.Summary.Failed)
}
// --- errors page: covers fail, skip, setAuthorized, reset ---
func TestRunnerWithFailAndSkip(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/errors",
JSON: true,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
require.NotNil(t, report)
assert.Equal(t, 6, report.Summary.Total)
assert.Equal(t, 2, report.Summary.Failed, "TestAlwaysFail + TestRuntimeError should fail")
assert.Equal(t, 1, report.Summary.Skipped, "TestAlwaysSkip should be skipped")
assert.Equal(t, 3, report.Summary.Passed)
assert.True(t, report.HasFailures())
page := report.Pages[0]
for _, r := range page.Results {
switch r.Name {
case "TestAlwaysFail":
assert.Equal(t, "failed", r.Status)
assert.NotEmpty(t, r.Error)
case "TestAlwaysSkip":
assert.Equal(t, "skipped", r.Status)
case "TestSetAuthorizedAndReset":
assert.Equal(t, "passed", r.Status)
case "TestCallWithRequestRender":
assert.Equal(t, "passed", r.Status)
case "TestCallNonExistentMethod":
assert.Equal(t, "passed", r.Status)
case "TestRuntimeError":
assert.Equal(t, "error", r.Status)
assert.Contains(t, r.Error, "deliberate execution error")
}
}
}
func TestRunnerFailFastStopsOnError(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/errors",
FailFast: true,
JSON: true,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 1, report.Summary.Failed)
// fail-fast: executed only up to the first failure
executed := report.Summary.Passed + report.Summary.Failed + report.Summary.Skipped
assert.Less(t, executed, report.Summary.Total, "fail-fast should stop before running all tests")
}
// --- verbose (non-JSON) output: covers printHeader, printTestResult, printSummary ---
func TestRunnerVerboseAllPass(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/dashboard",
Verbose: true,
JSON: false,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 4, report.Summary.Passed)
}
func TestRunnerVerboseWithFailures(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/errors",
Verbose: true,
JSON: false,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Greater(t, report.Summary.Failed, 0)
}
func TestRunnerVerboseWithData(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
opts := &suitest.Options{
SUIID: "agent",
Template: "agent",
Page: "tests.sui-pages/dashboard",
Data: `{"key":"value"}`,
Verbose: true,
JSON: false,
}
runner, err := suitest.NewRunner(opts)
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 4, report.Summary.Passed)
}
// --- error paths ---
func TestNewRunnerInvalidSUI(t *testing.T) {
prepare(t)
defer test.Clean()
_, err := suitest.NewRunner(&suitest.Options{SUIID: "nonexistent", Template: "default"})
assert.Error(t, err)
assert.Contains(t, err.Error(), "not found")
}
func TestNewRunnerInvalidTemplate(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
_, err := suitest.NewRunner(&suitest.Options{SUIID: "agent", Template: "nonexistent-tmpl"})
assert.Error(t, err)
}
func TestRunnerNoMatchingPage(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
runner, err := suitest.NewRunner(&suitest.Options{
SUIID: "agent", Template: "agent", Page: "xyz-no-match", JSON: true,
})
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 0, report.Summary.Total)
}
func TestRunnerNoMatchingRun(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
runner, err := suitest.NewRunner(&suitest.Options{
SUIID: "agent", Template: "agent", Page: "tests.sui-pages/dashboard",
Run: "NoSuchFunction", JSON: true,
})
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 0, report.Summary.Total)
}
func TestRunnerVerboseNoTestFiles(t *testing.T) {
prepare(t)
defer test.Clean()
requireAgentSUI(t)
runner, err := suitest.NewRunner(&suitest.Options{
SUIID: "agent", Template: "agent", Page: "xyz-no-match", JSON: false,
})
require.NoError(t, err)
report, err := runner.Run()
require.NoError(t, err)
assert.Equal(t, 0, report.Summary.Total)
}
// --- types ---
func TestLoadPageConfig(t *testing.T) {
prepare(t)
defer test.Clean()
cfgFile := "assistants/tests/sui-pages/pages/dashboard/dashboard.config"
exists, _ := application.App.Exists(cfgFile)
if !exists {
t.Skipf("test config %s not found", cfgFile)
}
cfg, err := suitest.LoadPageConfig(cfgFile)
assert.NoError(t, err)
assert.NotNil(t, cfg)
assert.Equal(t, "Test Dashboard", cfg.Title)
}
func TestLoadPageConfigNotExist(t *testing.T) {
prepare(t)
defer test.Clean()
cfg, err := suitest.LoadPageConfig("nonexistent/path.config")
assert.NoError(t, err)
assert.Nil(t, cfg)
}
func TestReportHasFailures(t *testing.T) {
r := &suitest.Report{Summary: &suitest.TestSummary{Failed: 1}}
assert.True(t, r.HasFailures())
r.Summary.Failed = 0
assert.False(t, r.HasFailures())
}