3a3ea74d76
- transport.IsTransient: typed sentinels (ErrTransient/ErrPermanent) + standard net.Error/io errors.Is; substring matching removed - sshpush.isTransient: same typed-error classification - rotateSSHKeys: 2-phase atomic swap (stage peers -> swap local -> verify -> cleanup old); no more partial-result window - known_hosts: dial() reads stored field (was reading v0.8 path directly) - IPv6: net.JoinHostPort in proxmox SSH dial + drain splitHostPort - SSH timeouts: context.WithTimeout on peer-setup, drift, txn rollback, job restart (default 2m) - verifyCutover: orca CA pool TLS config (was default http.Client) - OIDC callback: ReadHeaderTimeout 5s (slowloris defense) - root Execute: signal.NotifyContext for SIGINT/SIGTERM (clean exit for non-watch commands) Tests: typed-error classification table, IPv6 JoinHostPort, signal handler context cancellation. ---ci--- project: orca phase: 8 milestone: v0.13 status: complete requirements: covered: [157] ---/ci---
54 lines
1.3 KiB
Go
54 lines
1.3 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestREQ157_SignalNotifyContext verifies that the root Execute
|
|
// installs a signal.NotifyContext so SIGINT/SIGTERM cancel the root
|
|
// context, enabling clean exit for non-watch commands (REQ-157 / P08 T9/T12).
|
|
func TestREQ157_SignalNotifyContext(t *testing.T) {
|
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
|
|
defer cancel()
|
|
|
|
// Verify the context is not yet cancelled.
|
|
select {
|
|
case <-ctx.Done():
|
|
t.Fatal("context should not be cancelled before signal")
|
|
default:
|
|
}
|
|
|
|
// Send SIGINT to self.
|
|
p, err := os.FindProcess(os.Getpid())
|
|
if err != nil {
|
|
t.Fatalf("find process: %v", err)
|
|
}
|
|
|
|
// Run in a goroutine so we can timeout.
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
_ = p.Signal(os.Interrupt)
|
|
}()
|
|
|
|
select {
|
|
case <-ctx.Done():
|
|
// Expected: context is cancelled by the signal.
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("context was not cancelled within 2s of SIGINT")
|
|
}
|
|
|
|
// Verify the cause is the signal.
|
|
if ctx.Err() != context.Canceled {
|
|
t.Errorf("ctx.Err() = %v, want %v", ctx.Err(), context.Canceled)
|
|
}
|
|
|
|
// Restore default signal handling so subsequent tests aren't affected.
|
|
signal.Reset(os.Interrupt, syscall.SIGTERM)
|
|
}
|