test: comprehensive test coverage for 5 packages (#351)
* test(util): add comprehensive tests for atomic write functions Add tests for: - File permissions - Empty data handling - Various JSON types (string, int, float, bool, null, array, nested) - Unmarshallable types error handling - Read-only directory permission errors - Concurrent writes - Original content preservation on failure - Struct serialization/deserialization - Large data (1MB) * test(connection): add edge case tests for address parsing Add comprehensive test coverage for ParseAddress edge cases: - Empty/whitespace/slash-only inputs - Leading/trailing slash handling - Machine prefix edge cases (colons, empty machine) - Multiple slashes in polecat name (SplitN behavior) - Unicode and emoji support - Very long addresses - Special characters (hyphens, underscores, dots) - Whitespace in components Also adds tests for MustParseAddress panic behavior and RigPath method. Closes: gt-xgjyp * test(checkpoint): add comprehensive test coverage for checkpoint package Tests all public functions: Read, Write, Remove, Capture, WithMolecule, WithHookedBead, WithNotes, Age, IsStale, Summary, Path. Edge cases covered: missing file, corrupted JSON, stale detection. Closes: gt-09yn1 * test(lock): add comprehensive tests for lock package Add lock_test.go with tests covering: - LockInfo.IsStale() with valid/invalid PIDs - Lock.Acquire/Release lifecycle - Re-acquiring own lock (session refresh) - Stale lock cleanup during Acquire - Lock.Read() for missing/invalid/valid files - Lock.Check() for unlocked/owned/stale scenarios - Lock.Status() string formatting - Lock.ForceRelease() - processExists() helper - FindAllLocks() directory scanning - CleanStaleLocks() with mocked tmux - getActiveTmuxSessions() parsing - splitOnColon() and splitLines() helpers - DetectCollisions() for stale/orphaned locks Coverage: 84.4% * test(keepalive): add example tests demonstrating usage patterns Add ExampleTouchInWorkspace, ExampleRead, and ExampleState_Age to serve as documentation for how to use the keepalive package. * fix(test): correct boundary test timing race in checkpoint_test.go The 'exactly threshold' test case was flaky due to timing: by the time time.Since() runs after setting Timestamp, microseconds have passed, making age > threshold. Changed expectation to true since at-threshold is effectively stale. --------- Co-authored-by: slit <gt@gastown.local>
This commit is contained in:
@@ -76,3 +76,63 @@ func TestDirectoryCreation(t *testing.T) {
|
||||
t.Error("expected .runtime directory to be created")
|
||||
}
|
||||
}
|
||||
|
||||
// Example functions demonstrate keepalive usage patterns.
|
||||
|
||||
func ExampleTouchInWorkspace() {
|
||||
// TouchInWorkspace signals agent activity in a specific workspace.
|
||||
// This is the core function - use it when you know the workspace root.
|
||||
|
||||
workspaceRoot := "/path/to/workspace"
|
||||
|
||||
// Signal that "gt status" was run
|
||||
TouchInWorkspace(workspaceRoot, "gt status")
|
||||
|
||||
// Signal a command with arguments
|
||||
TouchInWorkspace(workspaceRoot, "gt sling bd-abc123 ai-platform")
|
||||
|
||||
// All errors are silently ignored (best-effort design).
|
||||
// This is intentional - keepalive failures should never break commands.
|
||||
}
|
||||
|
||||
func ExampleRead() {
|
||||
// Read retrieves the current keepalive state for a workspace.
|
||||
// Returns nil if no keepalive file exists or it can't be read.
|
||||
|
||||
workspaceRoot := "/path/to/workspace"
|
||||
state := Read(workspaceRoot)
|
||||
|
||||
if state == nil {
|
||||
// No keepalive found - agent may not have run any commands yet
|
||||
return
|
||||
}
|
||||
|
||||
// Access the last command that was run
|
||||
_ = state.LastCommand // e.g., "gt status"
|
||||
|
||||
// Access when the command was run
|
||||
_ = state.Timestamp // time.Time in UTC
|
||||
}
|
||||
|
||||
func ExampleState_Age() {
|
||||
// Age() returns how long ago the keepalive was updated.
|
||||
// This is useful for detecting idle or stuck agents.
|
||||
|
||||
workspaceRoot := "/path/to/workspace"
|
||||
state := Read(workspaceRoot)
|
||||
|
||||
// Age() is nil-safe - returns ~1 year for nil state
|
||||
age := state.Age()
|
||||
|
||||
// Check if agent was active recently (within 5 minutes)
|
||||
if age < 5*time.Minute {
|
||||
// Agent is active
|
||||
_ = "active"
|
||||
}
|
||||
|
||||
// Check if agent might be stuck (no activity for 30+ minutes)
|
||||
if age > 30*time.Minute {
|
||||
// Agent may need attention
|
||||
_ = "possibly stuck"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user