Files
wehub-resource-sync bf9395e022
CI / license-header (push) Has been skipped
CI / e2e-dry-run (push) Has been skipped
CI / fast-gate (push) Failing after 0s
Test PR Label Logic / test-pr-labels (push) Failing after 1s
Skill Format Check / check-format (push) Failing after 2s
CI / security (push) Failing after 5s
CI / unit-test (push) Has been skipped
CI / lint (push) Has been skipped
CI / script-test (push) Has been skipped
CI / deterministic-gate (push) Has been skipped
CI / coverage (push) Has been skipped
CI / results (push) Has been cancelled
CI / deadcode (push) Has been cancelled
CI / e2e-live (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:22:54 +08:00

111 lines
3.2 KiB
Go

// Copyright (c) 2026 Lark Technologies Pte. Ltd.
// SPDX-License-Identifier: MIT
package hook
import (
"context"
"errors"
"testing"
"github.com/larksuite/cli/extension/platform"
)
// A Startup handler returning a regular error must surface as a typed
// *LifecycleError with Panic=false so the cmd-layer guard can pick
// reason_code=lifecycle_failed.
func TestEmit_StartupHandlerError_TypedError(t *testing.T) {
reg := NewRegistry()
want := errors.New("backend down")
reg.AddLifecycle(LifecycleEntry{
Event: platform.Startup,
Name: "p.boot",
Fn: func(context.Context, *platform.LifecycleContext) error { return want },
})
got := Emit(context.Background(), reg, platform.Startup, nil)
if got == nil {
t.Fatal("expected error from Emit, got nil")
}
var le *LifecycleError
if !errors.As(got, &le) {
t.Fatalf("expected *LifecycleError, got %T %v", got, got)
}
if le.Panic {
t.Errorf("Panic = true, want false (returned error)")
}
if le.HookName != "p.boot" {
t.Errorf("HookName = %q, want p.boot", le.HookName)
}
if !errors.Is(got, want) {
t.Errorf("unwrap should reach original error")
}
}
// A Startup handler that panics must be recovered and surface as a
// typed *LifecycleError with Panic=true so the cmd-layer guard can
// pick reason_code=lifecycle_panic.
func TestEmit_StartupHandlerPanic_TypedError(t *testing.T) {
reg := NewRegistry()
reg.AddLifecycle(LifecycleEntry{
Event: platform.Startup,
Name: "p.boot",
Fn: func(context.Context, *platform.LifecycleContext) error { panic("boom") },
})
got := Emit(context.Background(), reg, platform.Startup, nil)
if got == nil {
t.Fatal("expected error from Emit, got nil")
}
var le *LifecycleError
if !errors.As(got, &le) {
t.Fatalf("expected *LifecycleError, got %T %v", got, got)
}
if !le.Panic {
t.Errorf("Panic = false, want true (recovered panic)")
}
if le.HookName != "p.boot" {
t.Errorf("HookName = %q, want p.boot", le.HookName)
}
}
// A Startup handler that succeeds returns nil; subsequent handlers run.
func TestEmit_StartupAllHandlersRun(t *testing.T) {
reg := NewRegistry()
var calls []string
reg.AddLifecycle(LifecycleEntry{
Event: platform.Startup, Name: "a",
Fn: func(context.Context, *platform.LifecycleContext) error {
calls = append(calls, "a")
return nil
},
})
reg.AddLifecycle(LifecycleEntry{
Event: platform.Startup, Name: "b",
Fn: func(context.Context, *platform.LifecycleContext) error {
calls = append(calls, "b")
return nil
},
})
if err := Emit(context.Background(), reg, platform.Startup, nil); err != nil {
t.Fatalf("Emit: %v", err)
}
if len(calls) != 2 || calls[0] != "a" || calls[1] != "b" {
t.Errorf("handlers fired in unexpected order: %v", calls)
}
}
// Shutdown handler errors are logged, not propagated; Emit returns nil.
func TestEmit_ShutdownErrorsSwallowed(t *testing.T) {
reg := NewRegistry()
reg.AddLifecycle(LifecycleEntry{
Event: platform.Shutdown, Name: "flush",
Fn: func(context.Context, *platform.LifecycleContext) error {
return errors.New("flush failed")
},
})
if err := Emit(context.Background(), reg, platform.Shutdown, nil); err != nil {
t.Errorf("Shutdown errors must NOT propagate, got: %v", err)
}
}