package api import ( "context" "errors" "testing" "time" hub "git.ipao.vip/rogee/creator-hub/internal/environment" ) type runtimeUseTestStore struct { lease hub.RuntimeUseLease acquireErr error renewErr error releaseErr error renewCalls int releaseCount int } func (s *runtimeUseTestStore) AcquireRuntimeUse(context.Context, string, string, string, string) (hub.RuntimeUseLease, error) { return s.lease, s.acquireErr } func (s *runtimeUseTestStore) RenewRuntimeUse(context.Context, string) (hub.RuntimeUseLease, error) { s.renewCalls++ return s.lease, s.renewErr } func (s *runtimeUseTestStore) ReleaseRuntimeUse(context.Context, string) error { s.releaseCount++ return s.releaseErr } func TestBeginRuntimeUseForEnvironmentFencesGenerationAndClosesIdempotently(t *testing.T) { store := &runtimeUseTestStore{lease: hub.RuntimeUseLease{ Token: "runtime-use-token", RuntimeInstanceID: "runtime-instance", OwnerID: "owner", Purpose: "task", LeaseUntil: time.Now().Add(time.Minute), }} environment := hub.EnvironmentContext{Env: hub.Env{Alias: "browser"}, RuntimeInstanceID: "runtime-instance"} useCtx, handle, err := beginRuntimeUseForEnvironment(context.Background(), store, environment, "task", "owner") if err != nil || handle == nil || useCtx == nil { t.Fatalf("begin runtime use: handle=%#v err=%v", handle, err) } if err := handle.Close(); err != nil { t.Fatal(err) } if err := handle.Close(); err != nil { t.Fatal(err) } if store.releaseCount != 1 { t.Fatalf("runtime-use close released %d times", store.releaseCount) } store.lease.RuntimeInstanceID = "successor-runtime" if _, handle, err := beginRuntimeUseForEnvironment(context.Background(), store, environment, "task", "owner"); !errors.Is(err, hub.ErrConflict) || handle != nil { t.Fatalf("stale runtime lease was accepted: handle=%#v err=%v", handle, err) } if store.releaseCount != 2 { t.Fatalf("mismatched lease was not released: %d", store.releaseCount) } if _, handle, err := beginRuntimeUseForEnvironment(context.Background(), store, hub.EnvironmentContext{}, "task", "owner"); !errors.Is(err, hub.ErrConflict) || handle != nil { t.Fatalf("missing runtime generation was accepted: handle=%#v err=%v", handle, err) } var nilHandle *runtimeUseHandle if err := nilHandle.Close(); err != nil { t.Fatal(err) } } func TestRuntimeUseRenewalCancelsWorkWhenRenewalFails(t *testing.T) { previousInterval := runtimeUseRenewInterval runtimeUseRenewInterval = time.Millisecond defer func() { runtimeUseRenewInterval = previousInterval }() renewErr := errors.New("renewal failed") store := &runtimeUseTestStore{ lease: hub.RuntimeUseLease{ Token: "runtime-use-token", RuntimeInstanceID: "runtime-instance", Purpose: "task", }, renewErr: renewErr, } useCtx, handle, err := beginRuntimeUse(context.Background(), store, "browser", "task", "owner") if err != nil { t.Fatal(err) } select { case <-useCtx.Done(): case <-time.After(time.Second): t.Fatal("runtime-use context was not canceled after renewal failure") } if err := handle.Close(); !errors.Is(err, renewErr) { t.Fatalf("close error = %v, want %v", err, renewErr) } if store.renewCalls == 0 { t.Fatal("runtime-use renewal was not attempted") } } func TestRuntimeUseCloseReportsReleaseFailure(t *testing.T) { releaseErr := errors.New("release failed") store := &runtimeUseTestStore{ lease: hub.RuntimeUseLease{Token: "token", RuntimeInstanceID: "runtime"}, releaseErr: releaseErr, } _, handle, err := beginRuntimeUse(context.Background(), store, "browser", "task", "owner") if err != nil { t.Fatal(err) } if err := handle.Close(); !errors.Is(err, releaseErr) { t.Fatalf("close error = %v, want %v", err, releaseErr) } } func TestBeginRuntimeUseReportsUnavailableStoreAndAcquireFailure(t *testing.T) { if _, handle, err := beginRuntimeUse(context.Background(), struct{}{}, "browser", "task", "owner"); err == nil || handle != nil { t.Fatalf("unavailable lease store: handle=%#v err=%v", handle, err) } acquireErr := errors.New("acquire failed") store := &runtimeUseTestStore{acquireErr: acquireErr} if _, handle, err := beginRuntimeUse(context.Background(), store, "browser", "task", "owner"); !errors.Is(err, acquireErr) || handle != nil { t.Fatalf("acquire error: handle=%#v err=%v", handle, err) } }