e2e

package
v0.30.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 6, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package e2e provides a Go test harness for end-to-end sync testing. It ports the bash harness (scripts/e2e/harness.sh) to Go, building real td and td-sync binaries, running a server, and authenticating multiple actors.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtractIssueID

func ExtractIssueID(output string) string

ExtractIssueID is the exported version for test use.

Types

type ActionDef

type ActionDef struct {
	Name   string
	Weight int
	Exec   func(engine *ChaosEngine, actor string) ActionResult
}

ActionDef defines a named action with weight and executor.

func SelectAction

func SelectAction(rng *rand.Rand) ActionDef

SelectAction picks a weighted random action definition.

type ActionResult

type ActionResult struct {
	Action  string
	Actor   string
	Target  string // issue ID or other target
	OK      bool
	ExpFail bool // expected failure (self-review, cycle, etc.)
	Skipped bool // no suitable target found
	Output  string
}

ActionResult records the outcome of a single chaos action.

type ActionStats

type ActionStats struct {
	OK        int
	ExpFail   int
	UnexpFail int
}

ActionStats tracks per-action-type outcomes.

type ChaosEngine

type ChaosEngine struct {
	Harness   *Harness
	Rng       *rand.Rand
	NumActors int

	// State tracking
	Issues      map[string]*IssueState // id -> state
	IssueOrder  []string               // ordered list of all created issue IDs
	Boards      []string
	DepPairs    map[string]bool            // "from_to" -> true
	ParentChild map[string]string          // childID -> parentID
	IssueFiles  map[string]string          // "issueID~filePath" -> role
	ActiveWS    map[string]string          // actor -> ws name
	WSTagged    map[string]map[string]bool // actor -> set of tagged issue IDs

	Stats ChaosStats
}

ChaosEngine drives random mutations against a Harness.

func NewChaosEngine

func NewChaosEngine(h *Harness, seed int64, numActors int) *ChaosEngine

NewChaosEngine creates a ChaosEngine with deterministic seed.

func (*ChaosEngine) RunAction

func (e *ChaosEngine) RunAction() ActionResult

RunAction selects a weighted random action and executes it. Returns the result and whether it was an unexpected failure.

func (*ChaosEngine) RunN

func (e *ChaosEngine) RunN(n int) []ActionResult

RunN executes n random actions and returns all results.

func (*ChaosEngine) Summary

func (e *ChaosEngine) Summary() string

Summary returns a human-readable stats summary.

func (*ChaosEngine) TrackCreatedIssue

func (e *ChaosEngine) TrackCreatedIssue(id, status, owner string)

TrackCreatedIssue manually adds an issue to the engine's state tracking.

type ChaosReport

type ChaosReport struct {
	Seed          int64                        `json:"seed"`
	Actions       int                          `json:"actions"`
	Duration      int64                        `json:"duration_ms"`
	Actors        int                          `json:"actors"`
	Results       ReportResults                `json:"results"`
	PerAction     map[string]ReportActionStats `json:"per_action"`
	Verifications []ReportVerification         `json:"verifications"`
	SyncStats     ReportSyncStats              `json:"sync_stats"`
	Pass          bool                         `json:"pass"`
}

ChaosReport is the JSON-serializable report for CI integration.

func BuildReport

func BuildReport(seed int64, eng *ChaosEngine, v *Verifier, elapsed time.Duration) ChaosReport

BuildReport constructs a ChaosReport from engine and verifier state.

type ChaosStats

type ChaosStats struct {
	ActionCount        int
	SyncCount          int
	Skipped            int
	ExpectedFailures   int
	UnexpectedFailures int
	FieldCollisions    int
	DeleteMutate       int
	BurstCount         int
	BurstActions       int
	EdgeDataUsed       int
	PerAction          map[string]*ActionStats
}

ChaosStats aggregates counters across all actions.

type Config

type Config struct {
	NumActors int  // 2 or 3 (alice, bob, optionally carol)
	AutoSync  bool // enable auto-sync on clients
	Debounce  string
	Interval  string
}

Config controls harness setup options.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns a Config with sensible defaults.

type Harness

type Harness struct {
	ServerURL string
	ProjectID string
	WorkDir   string

	TdBin   string
	SyncBin string
	// contains filtered or unexported fields
}

Harness manages a td-sync server and multiple td client environments.

func Setup

func Setup(t *testing.T, cfg Config) *Harness

Setup creates a new Harness: builds binaries, starts the server, authenticates actors, creates a project, and links all actors. When t is non-nil, t.Cleanup is used for teardown.

func (*Harness) ClientDir

func (h *Harness) ClientDir(actor string) string

ClientDir returns the working directory for an actor.

func (*Harness) DBPath

func (h *Harness) DBPath(actor string) string

DBPath returns the path to an actor's issues.db.

func (*Harness) HomeDir

func (h *Harness) HomeDir(actor string) string

HomeDir returns the HOME directory for an actor.

func (*Harness) ServerLogContents

func (h *Harness) ServerLogContents() string

ServerLogContents returns the server log file contents.

func (*Harness) SetServerEnv

func (h *Harness) SetServerEnv(envs ...string)

SetServerEnv adds environment variables for the server process. Can be called before Setup() or between StopServer()/StartServer() calls. Later values override earlier ones (appended after defaults).

func (*Harness) StartServer

func (h *Harness) StartServer() error

StartServer starts a new server process using the same data directory and port. Blocks until the server passes a health check.

func (*Harness) StopServer

func (h *Harness) StopServer() error

StopServer kills the running server process and waits for it to exit.

func (*Harness) SyncAll

func (h *Harness) SyncAll() error

SyncAll syncs all actors in round-robin for convergence. Performs 3 rounds of push+pull for each actor, with rate-limit retry.

func (*Harness) Td

func (h *Harness) Td(actor string, args ...string) (string, error)

Td runs the td binary as the given actor and returns combined output. For "init" commands, it pipes "n" to stdin to skip the sync prompt.

func (*Harness) TdA

func (h *Harness) TdA(args ...string) (string, error)

TdA runs td as alice.

func (*Harness) TdB

func (h *Harness) TdB(args ...string) (string, error)

TdB runs td as bob.

func (*Harness) TdC

func (h *Harness) TdC(args ...string) (string, error)

TdC runs td as carol.

func (*Harness) Teardown

func (h *Harness) Teardown()

Teardown kills the server and cleans up temp dirs.

type HistorySummary

type HistorySummary struct {
	TotalOps      int            `json:"total_ops"`
	ByResult      map[string]int `json:"by_result"`
	ByAction      map[string]int `json:"by_action"`
	ByActor       map[string]int `json:"by_actor"`
	AvgDuration   time.Duration  `json:"avg_duration_ns"`
	MaxDuration   time.Duration  `json:"max_duration_ns"`
	TotalDuration time.Duration  `json:"total_duration_ns"`
	UniqueIssues  int            `json:"unique_issues"`
}

HistorySummary holds aggregate stats over recorded operations.

type IssueState

type IssueState struct {
	ID      string
	Status  string // open, in_progress, in_review, closed, blocked
	Owner   string // actor who created it
	Minor   bool
	Deleted bool
}

IssueState tracks the engine's view of an issue.

type OperationHistory

type OperationHistory struct {
	// contains filtered or unexported fields
}

OperationHistory records all operations performed during a chaos run. Thread-safe for concurrent recording from multiple goroutines.

func NewOperationHistory

func NewOperationHistory() *OperationHistory

NewOperationHistory creates an empty history.

func (*OperationHistory) Filter

func (h *OperationHistory) Filter(fn func(OperationRecord) bool) []OperationRecord

Filter returns records matching a predicate.

func (*OperationHistory) ForActor

func (h *OperationHistory) ForActor(actor string) []OperationRecord

ForActor returns records for a specific actor.

func (*OperationHistory) ForIssue

func (h *OperationHistory) ForIssue(issueID string) []OperationRecord

ForIssue returns records targeting a specific issue ID.

func (*OperationHistory) Len

func (h *OperationHistory) Len() int

Len returns the number of recorded operations.

func (*OperationHistory) Record

func (h *OperationHistory) Record(rec OperationRecord)

Record adds an operation to the history. It assigns a monotonic sequence number and timestamps the record if Timestamp is zero.

func (*OperationHistory) Records

func (h *OperationHistory) Records() []OperationRecord

Records returns a snapshot of all recorded operations.

func (*OperationHistory) Summary

func (h *OperationHistory) Summary() HistorySummary

Summary returns aggregate stats over all recorded operations.

func (*OperationHistory) WriteJSON

func (h *OperationHistory) WriteJSON(path string) error

WriteJSON writes the full history as JSON to the given path.

func (*OperationHistory) WriteReport

func (h *OperationHistory) WriteReport(w io.Writer)

WriteReport writes a human-readable summary to w.

type OperationRecord

type OperationRecord struct {
	Seq       int           `json:"seq"`
	Timestamp time.Time     `json:"timestamp"`
	Action    string        `json:"action"`
	Actor     string        `json:"actor"`
	TargetID  string        `json:"target_id,omitempty"`
	Args      []string      `json:"args"`
	Output    string        `json:"output"`
	Result    string        `json:"result"` // "ok", "expected_fail", "unexpected_fail", "skip"
	Duration  time.Duration `json:"duration_ns"`
	Error     string        `json:"error,omitempty"`
}

OperationRecord captures a single action performed during a chaos run.

type ReportActionStats

type ReportActionStats struct {
	OK        int `json:"ok"`
	ExpFail   int `json:"expected_fail"`
	UnexpFail int `json:"unexpected_fail"`
}

ReportActionStats tracks per-action-type outcomes.

type ReportResults

type ReportResults struct {
	Total     int `json:"total"`
	OK        int `json:"ok"`
	ExpFail   int `json:"expected_fail"`
	UnexpFail int `json:"unexpected_fail"`
	Skipped   int `json:"skipped"`
}

ReportResults aggregates action outcomes.

type ReportSyncStats

type ReportSyncStats struct {
	Count int `json:"count"`
}

ReportSyncStats tracks sync operations.

type ReportVerification

type ReportVerification struct {
	Name    string `json:"name"`
	Passed  bool   `json:"passed"`
	Details string `json:"details,omitempty"`
}

ReportVerification records the outcome of a single verification.

type Verifier

type Verifier struct {
	// contains filtered or unexported fields
}

Verifier runs correctness checks against harness databases.

func NewVerifier

func NewVerifier(h *Harness) *Verifier

NewVerifier creates a Verifier for the given harness.

func (*Verifier) AllPassed

func (v *Verifier) AllPassed() bool

AllPassed returns true if every result passed.

func (*Verifier) FailedResults

func (v *Verifier) FailedResults() []VerifyResult

FailedResults returns only failing results.

func (*Verifier) Results

func (v *Verifier) Results() []VerifyResult

Results returns accumulated results.

func (*Verifier) Summary

func (v *Verifier) Summary() string

Summary returns a human-readable summary.

func (*Verifier) VerifyActionLogConvergence

func (v *Verifier) VerifyActionLogConvergence(actorA, actorB string) []VerifyResult

VerifyActionLogConvergence checks that both actors agree on canonical event order. Compares events by server_seq: for each seq present on both sides, the (entity_type, action_type, entity_id) tuple must match. Clients may have different total event counts due to built-in entity creation during init.

func (*Verifier) VerifyCausalOrdering

func (v *Verifier) VerifyCausalOrdering(actor string) []VerifyResult

VerifyCausalOrdering checks that create events precede updates/transitions for same entity.

func (*Verifier) VerifyConvergence

func (v *Verifier) VerifyConvergence(actorA, actorB string) []VerifyResult

VerifyConvergence compares all synced tables between two actors.

func (*Verifier) VerifyEventCounts

func (v *Verifier) VerifyEventCounts(actorA, actorB string) []VerifyResult

VerifyEventCounts checks synced event counts and distributions match.

func (*Verifier) VerifyFieldLevelMerge

func (v *Verifier) VerifyFieldLevelMerge(actorA, actorB string) []VerifyResult

VerifyFieldLevelMerge tests that concurrent updates to different fields both survive. This is a targeted test: creates an issue, has two actors update different fields without syncing between, then syncs and verifies both changes are preserved.

func (*Verifier) VerifyIdempotency

func (v *Verifier) VerifyIdempotency(rounds int) []VerifyResult

VerifyIdempotency hashes DB content, runs N sync rounds, verifies hashes unchanged.

func (*Verifier) VerifyMonotonicSequence

func (v *Verifier) VerifyMonotonicSequence(actor string) []VerifyResult

VerifyMonotonicSequence checks server_seq is strictly increasing with no duplicates. Gaps are expected (each client only has a subset of server events) and are noted but not failures.

func (*Verifier) VerifyReadYourWrites

func (v *Verifier) VerifyReadYourWrites(engine *ChaosEngine) []VerifyResult

VerifyReadYourWrites checks that after sync, each actor can see issues they created.

type VerifyResult

type VerifyResult struct {
	Name    string
	Passed  bool
	Details string // explanation on failure
}

VerifyResult records the outcome of a single verification check.

func ScenarioBurstNoSync

func ScenarioBurstNoSync(h *Harness, rng *rand.Rand) []VerifyResult

ScenarioBurstNoSync: actor A performs 20 sequential mutations on one issue without any sync, then syncs, and verifies actor B sees the final state.

func ScenarioCascadeConflict

func ScenarioCascadeConflict(h *Harness) []VerifyResult

ScenarioCascadeConflict: parent + children in_progress, actor A moves parent to in_review (cascading children), actor B independently closes one child.

func ScenarioDependencyCycle

func ScenarioDependencyCycle(h *Harness) []VerifyResult

ScenarioDependencyCycle: tests distributed cycle detection during sync. Both actors add conflicting dependencies simultaneously (A->B and B->A), then sync. The sync should detect the cycle and skip one of the deps.

func ScenarioMultiFieldCollision

func ScenarioMultiFieldCollision(h *Harness) []VerifyResult

ScenarioMultiFieldCollision: both actors update different fields on the same issue concurrently. After sync, both changes should survive (field-level merge).

func ScenarioPartitionRecovery

func ScenarioPartitionRecovery(h *Harness, rng *rand.Rand) []VerifyResult

ScenarioPartitionRecovery: both actors perform 50+ mutations without syncing, then sync and verify convergence.

func ScenarioRapidCreateDelete

func ScenarioRapidCreateDelete(h *Harness) []VerifyResult

ScenarioRapidCreateDelete: create 10, sync, delete all, sync, restore 5, sync, delete 3, sync -- verify convergence at each stage.

func ScenarioServerRestart

func ScenarioServerRestart(h *Harness) []VerifyResult

ScenarioServerRestart tests sync resilience across a server restart. 1. Create issues on both actors, sync to convergence 2. Kill the server process 3. Both actors perform local mutations (these queue locally) 4. Restart the server (new process, same data dir) 5. Sync all actors 6. Verify convergence

func ScenarioThunderingHerd

func ScenarioThunderingHerd(h *Harness) []VerifyResult

ScenarioThunderingHerd: both actors make mutations, then both sync SIMULTANEOUSLY using goroutines, then verify convergence.

func ScenarioUndoSync

func ScenarioUndoSync(h *Harness) []VerifyResult

ScenarioUndoSync: actor A creates issue, syncs, actor B sees it, actor A undoes, syncs, verify convergence.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL