Files
tutti/services/tuttid/service/agent/commit_replay_integration_test.go
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

88 lines
3.1 KiB
Go

package agent
import (
"context"
"database/sql"
"encoding/json"
"errors"
"path/filepath"
"testing"
agenthost "github.com/tutti-os/tutti/packages/agent/host"
storesqlite "github.com/tutti-os/tutti/packages/agent/store-sqlite"
_ "modernc.org/sqlite"
)
type durableMarkerParticipant struct{}
func (durableMarkerParticipant) Participate(ctx context.Context, writer storesqlite.TransactionWriter, delta storesqlite.TransactionDelta) error {
payload, err := json.Marshal(delta)
if err != nil {
return err
}
_, err = writer.ExecContext(ctx, `INSERT INTO test_durable_outbox (transaction_id, payload_json, delivered) VALUES (?, ?, 0)`, delta.TransactionID, string(payload))
return err
}
type replayObserver struct {
fail bool
calls int
}
func (o *replayObserver) ObserveCommitted(context.Context, agenthost.CommittedDelta) error {
o.calls++
if o.fail {
return errors.New("observer unavailable")
}
return nil
}
func TestDurableMarkerSurvivesObserverFailureAndCanReplay(t *testing.T) {
db, err := sql.Open("sqlite", filepath.Join(t.TempDir(), "agent-store.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = db.Close() })
db.SetMaxOpenConns(1)
store := storesqlite.New(db, storesqlite.Options{TransactionParticipant: durableMarkerParticipant{}})
if err := store.Migrate(context.Background()); err != nil {
t.Fatal(err)
}
if _, err := db.Exec(`CREATE TABLE test_durable_outbox (transaction_id TEXT PRIMARY KEY, payload_json TEXT NOT NULL, delivered INTEGER NOT NULL)`); err != nil {
t.Fatal(err)
}
result, err := store.ReportSessionState(context.Background(), storesqlite.SessionStateReport{
WorkspaceID: "workspace-1", AgentSessionID: "session-1", Provider: "codex", OccurredAtUnixMS: 1,
})
if err != nil {
t.Fatal(err)
}
observer := &replayObserver{fail: true}
agenthost.NotifyCommitted(context.Background(), observer, agenthost.CanonicalDelta(result.CommitDelta))
var delivered int
var payloadJSON string
if err := db.QueryRow(`SELECT payload_json, delivered FROM test_durable_outbox WHERE transaction_id = ?`, result.TransactionID).Scan(&payloadJSON, &delivered); err != nil || delivered != 0 {
t.Fatalf("durable marker after observer failure delivered=%d error=%v", delivered, err)
}
var replayed storesqlite.TransactionDelta
if err := json.Unmarshal([]byte(payloadJSON), &replayed); err != nil {
t.Fatalf("decode durable committed delta: %v", err)
}
if replayed.TransactionID != result.TransactionID || len(replayed.Mutations) != len(result.CommitDelta.Mutations) {
t.Fatalf("replayed delta=%#v, committed=%#v", replayed, result.CommitDelta)
}
if _, found, err := store.GetSession(context.Background(), "workspace-1", "session-1"); err != nil || !found {
t.Fatalf("canonical fact after observer failure found=%v error=%v", found, err)
}
observer.fail = false
agenthost.NotifyCommitted(context.Background(), observer, agenthost.CanonicalDelta(replayed))
if observer.calls != 2 {
t.Fatalf("observer calls=%d, want failed delivery plus replay", observer.calls)
}
if _, err := db.Exec(`UPDATE test_durable_outbox SET delivered = 1 WHERE transaction_id = ?`, result.TransactionID); err != nil {
t.Fatal(err)
}
}