Files
multica/server/pkg/agent/opencode_clock_unix_test.go
04931df162 MUL-8174: fix OpenCode resume after host clock rollback (#9460)
* fix(opencode): retire future-dated resume sessions (MUL-8174)

Co-authored-by: multica-agent <[email protected]>

* fix(opencode): check resume clocks across CLI versions

Co-authored-by: multica-agent <[email protected]>

---------

Co-authored-by: Sol-Boy <[email protected]>
Co-authored-by: multica-agent <[email protected]>
2026-10-23 14:41:13 +08:00

163 lines
6.5 KiB
Go

//go:build !windows
package agent
import (
"context"
"errors"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestCheckOpenCodeResumeClock(t *testing.T) {
for _, tc := range []struct {
name, body string
wantSkew, wantUnverified bool
}{
{"normal", `printf '%s' '{"info":{"id":"ses-test","time":{"updated":1}},"messages":[{"info":{"time":{"created":1}}}]}'`, false, false},
{"future", `printf '%s' '{"info":{"id":"ses-test","time":{"updated":9999999999999}},"messages":[]}'`, true, false},
{"failure with private stderr", `echo private-transcript >&2; exit 1`, false, true},
{"malformed", `echo private-transcript`, false, true},
{"oversized", `printf '{"ignored":"'; head -c 33554433 /dev/zero | LC_ALL=C tr '\000' x; printf '"}'`, false, true},
{"timeout", `sleep 30`, false, true},
} {
t.Run(tc.name, func(t *testing.T) {
cwd := t.TempDir()
bin := filepath.Join(t.TempDir(), "opencode")
// Assert storage/project selection without injecting task MCP servers
// or resolving an installed agent and its account.
body := `#!/bin/sh
[ "$1" = export ] && [ "$2" = ses-test ] && [ "$3" = --sanitize ] || exit 2
[ "$PWD" = "$CLOCK_EXPECTED_CWD" ] && [ "$(pwd)" = "$CLOCK_EXPECTED_CWD" ] || exit 3
[ "$XDG_DATA_HOME" = clock-test-storage ] || exit 4
[ -z "$OPENCODE_CONFIG_CONTENT" ] || exit 5
` + tc.body + "\n"
if err := os.WriteFile(bin, []byte(body), 0o755); err != nil {
t.Fatal(err)
}
cfg := Config{ExecutablePath: bin, BuiltinRuntime: true, CLIVersion: "1.18.30", Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), Env: map[string]string{"CLOCK_EXPECTED_CWD": cwd, "XDG_DATA_HOME": "clock-test-storage"}}
opts := ExecOptions{Cwd: cwd, ResumeSessionID: "ses-test", McpConfig: []byte(`{"mcpServers":{"clock-mcp":{"command":"unused"}}}`)}
ctx := context.Background()
if tc.name == "timeout" {
var cancel context.CancelFunc
ctx, cancel = context.WithTimeout(ctx, 100*time.Millisecond)
defer cancel()
}
err := CheckOpenCodeResumeClock(ctx, cfg, opts)
if errors.Is(err, ErrOpenCodeClockSkew) != tc.wantSkew || (err != nil && !errors.Is(err, ErrOpenCodeClockSkew)) != tc.wantUnverified {
t.Fatalf("check = %v, want skew=%v unverified=%v", err, tc.wantSkew, tc.wantUnverified)
}
if err != nil && strings.Contains(err.Error(), "private-transcript") {
t.Fatal("export contents leaked")
}
})
}
}
func TestCheckOpenCodeResumeClockUnsupportedContractsDoNotExecute(t *testing.T) {
for _, tc := range []struct {
version string
builtin bool
args []string
}{
{"1.18.30", false, nil},
{"1.18.30", true, []string{"--server=https://unused.invalid"}},
{"1.18.30", true, []string{"--attach", "https://unused.invalid"}},
{"1.18.30", true, []string{"--session", "other-session"}},
{"1.18.30", true, []string{"-sother-session"}},
{"1.18.30", true, []string{"-csother-session"}},
} {
cfg := Config{ExecutablePath: filepath.Join(t.TempDir(), "does-not-exist"), BuiltinRuntime: tc.builtin, CLIVersion: tc.version}
err := CheckOpenCodeResumeClock(context.Background(), cfg, ExecOptions{ResumeSessionID: "ses-test", CustomArgs: tc.args})
if err == nil || errors.Is(err, ErrOpenCodeClockSkew) || strings.Contains(err.Error(), "executable not found") {
t.Fatalf("unsupported contract must be rejected before lookup: %v", err)
}
}
}
func TestCheckOpenCodeResumeClockAcrossVersions(t *testing.T) {
for _, tc := range []struct {
version, command string
standalone bool
}{
{"1.1.54", "export", false}, {"1.18.30", "export", false}, {"1.18.31", "export", false}, {"1.99.0", "export", false},
{"opencode v2.0.10", "session", false}, {"opencode v2.1.0", "session", true}, {"", "export", false},
} {
t.Run(tc.version, func(t *testing.T) {
bin := filepath.Join(t.TempDir(), "opencode")
body := `#!/bin/sh
[ "$1" = "$CLOCK_EXPECTED_COMMAND" ] || exit 4
if [ "$1" = session ]; then
[ "$2" = export ] && [ "$3" = ses-test ] || exit 2
if [ "$CLOCK_STANDALONE" = true ]; then
[ "$4" = --standalone ] && [ "$5" = --sanitize ] || exit 5
else
[ "$4" = --sanitize ] || exit 6
fi
printf '%s' '{"info":{"id":"ses-test","time":{"updated":1}},"messages":[{"id":"msg-test","type":"user","time":{"created":9999999999999}}]}'
else
[ "$1" = export ] && [ "$2" = ses-test ] && [ "$3" = --sanitize ] || exit 3
printf '%s' '{"info":{"id":"ses-test","time":{"updated":1}},"messages":[{"info":{"time":{"created":9999999999999}}}]}'
fi
`
if err := os.WriteFile(bin, []byte(body), 0o755); err != nil {
t.Fatal(err)
}
cfg := Config{ExecutablePath: bin, BuiltinRuntime: true, CLIVersion: tc.version, Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), Env: map[string]string{"CLOCK_EXPECTED_COMMAND": tc.command, "CLOCK_STANDALONE": fmt.Sprint(tc.standalone)}}
opts := ExecOptions{ResumeSessionID: "ses-test"}
if tc.standalone {
opts.CustomArgs = []string{"--standalone"}
}
if err := CheckOpenCodeResumeClock(context.Background(), cfg, opts); !errors.Is(err, ErrOpenCodeClockSkew) {
t.Fatalf("version %q bypassed a future-dated session: %v", tc.version, err)
}
})
}
}
func TestCheckOpenCodeResumeClockWithoutSanitizeFlag(t *testing.T) {
for _, unsupported := range []bool{true, false} {
t.Run(fmt.Sprint(unsupported), func(t *testing.T) {
dir := t.TempDir()
bin, calls := filepath.Join(dir, "opencode"), filepath.Join(dir, "calls")
body := `#!/bin/sh
printf 'called\n' >> "$CLOCK_CALLS"
if [ -n "$3" ]; then
if [ "$CLOCK_UNSUPPORTED" = true ]; then
echo 'Unknown argument: sanitize' >&2
else
echo 'private export error' >&2
fi
exit 1
fi
printf '%s' '{"info":{"id":"ses-test","time":{"updated":1}},"messages":[{"info":{"time":{"created":9999999999999}},"parts":[{"text":"private transcript"}]}]}'
`
if err := os.WriteFile(bin, []byte(body), 0o755); err != nil {
t.Fatal(err)
}
cfg := Config{ExecutablePath: bin, BuiltinRuntime: true, CLIVersion: "1.1.54", Logger: slog.New(slog.NewTextHandler(io.Discard, nil)), Env: map[string]string{"CLOCK_CALLS": calls, "CLOCK_UNSUPPORTED": fmt.Sprint(unsupported)}}
err := CheckOpenCodeResumeClock(context.Background(), cfg, ExecOptions{ResumeSessionID: "ses-test"})
if errors.Is(err, ErrOpenCodeClockSkew) != unsupported || err == nil || strings.Contains(err.Error(), "private") {
t.Fatalf("old CLI export check = %v", err)
}
raw, readErr := os.ReadFile(calls)
if readErr != nil {
t.Fatal(readErr)
}
want := 1
if unsupported {
want = 2
}
if strings.Count(string(raw), "called") != want {
t.Fatalf("export calls = %s, want %d", raw, want)
}
})
}
}