bc4eda926b
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017CDMFpFx84pfviCTVuGGhf
61 lines
2.0 KiB
Go
61 lines
2.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// F10/rc255 (CAMPAIGN-3): a guest-hook phase body that PANICS must never crash the process — the hook
|
|
// must return cleanly so the guest start proceeds (a nonzero exit blocks the start). runHookPhase
|
|
// recovers the panic and returns.
|
|
func TestRunHookPhase_PanicRecovered(t *testing.T) {
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
runHookPhase("9201", "pre-start", 5*time.Second, func(context.Context) {
|
|
panic("simulated heal panic (e.g. a future Heal bug)")
|
|
})
|
|
}()
|
|
select {
|
|
case <-done:
|
|
// returned cleanly — the guest start would proceed
|
|
case <-time.After(3 * time.Second):
|
|
t.Fatal("runHookPhase did not return after a panicking body (would have crashed the hook)")
|
|
}
|
|
}
|
|
|
|
// A phase body that overruns the timeout must be abandoned — the hook returns rather than hanging the
|
|
// PVE start task. (The body's context is cancelled; the hook does not wait for the body to notice.)
|
|
func TestRunHookPhase_TimeoutReturns(t *testing.T) {
|
|
bodyCtxCancelled := make(chan struct{}, 1)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer close(done)
|
|
runHookPhase("9201", "post-start", 20*time.Millisecond, func(ctx context.Context) {
|
|
<-ctx.Done() // simulate a body that respects cancellation eventually
|
|
bodyCtxCancelled <- struct{}{}
|
|
})
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(2 * time.Second):
|
|
t.Fatal("runHookPhase did not return after the timeout (would hang the guest start)")
|
|
}
|
|
select {
|
|
case <-bodyCtxCancelled:
|
|
// the body's context was cancelled at the deadline — the intended signal
|
|
case <-time.After(time.Second):
|
|
t.Fatal("the phase body's context was not cancelled at the timeout")
|
|
}
|
|
}
|
|
|
|
// A body that errors (returns normally, no panic) is fine — the hook returns cleanly.
|
|
func TestRunHookPhase_NormalBodyReturns(t *testing.T) {
|
|
ran := false
|
|
runHookPhase("9201", "pre-start", time.Second, func(context.Context) { ran = true })
|
|
if !ran {
|
|
t.Fatal("the phase body must run")
|
|
}
|
|
}
|