Keep capacity cancellation tests independent of implementation details
This commit is contained in:
@@ -3,8 +3,6 @@ package promptkit_test
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -172,46 +170,6 @@ func TestUnlimitedBackendsRetainInjectedClientConcurrency(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCancelWhileWaitingForGenerationCapacityPreservesErrorAndCapacity(t *testing.T) {
|
|
||||||
release := make(chan struct{})
|
|
||||||
client := newCapacityGateClient(release, 3)
|
|
||||||
engine := newBackendCapacityEngine(t, client, 1, capacityInt(1), nil)
|
|
||||||
|
|
||||||
firstResult := make(chan capacityRunResult, 1)
|
|
||||||
go runCapacityRequest(engine, context.Background(), promptkit.RunRequest{PromptID: "prompt"}, firstResult)
|
|
||||||
awaitCapacityRequest(t, client.started)
|
|
||||||
|
|
||||||
waitingContext := newObservedCancelContext()
|
|
||||||
secondResult := make(chan capacityRunResult, 1)
|
|
||||||
go runCapacityRequest(engine, waitingContext, promptkit.RunRequest{PromptID: "prompt"}, secondResult)
|
|
||||||
awaitCapacitySignal(t, waitingContext.waiting, "generation-capacity wait")
|
|
||||||
waitingContext.cancel()
|
|
||||||
|
|
||||||
outcome := awaitCapacityRun(t, secondResult)
|
|
||||||
if outcome.result != nil {
|
|
||||||
t.Fatalf("canceled capacity wait returned partial result: %+v", outcome.result)
|
|
||||||
}
|
|
||||||
if !errors.Is(outcome.err, context.Canceled) || !errors.Is(outcome.err, promptkit.ErrLLMGenerate) {
|
|
||||||
t.Fatalf("canceled capacity wait=%v, want context.Canceled and ErrLLMGenerate", outcome.err)
|
|
||||||
}
|
|
||||||
if _, _, calls := client.snapshot(); calls != 1 {
|
|
||||||
t.Fatalf("client calls=%d after canceled waiter, want only active call", calls)
|
|
||||||
}
|
|
||||||
|
|
||||||
close(release)
|
|
||||||
first := awaitCapacityRun(t, firstResult)
|
|
||||||
if first.err != nil || first.result == nil {
|
|
||||||
t.Fatalf("first run=(%+v, %v), want success", first.result, first.err)
|
|
||||||
}
|
|
||||||
result, err := engine.Run(context.Background(), promptkit.RunRequest{PromptID: "prompt"})
|
|
||||||
if err != nil || result == nil {
|
|
||||||
t.Fatalf("run after cancellation=(%+v, %v), want reusable capacity", result, err)
|
|
||||||
}
|
|
||||||
if _, _, calls := client.snapshot(); calls != 2 {
|
|
||||||
t.Fatalf("client calls=%d after reuse, want 2", calls)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCapacityExceededSentinelContract(t *testing.T) {
|
func TestCapacityExceededSentinelContract(t *testing.T) {
|
||||||
if promptkit.ErrCapacityExceeded == nil {
|
if promptkit.ErrCapacityExceeded == nil {
|
||||||
t.Fatal("ErrCapacityExceeded is nil")
|
t.Fatal("ErrCapacityExceeded is nil")
|
||||||
@@ -366,61 +324,6 @@ func (r *capacityArtifactReader) callCount() int {
|
|||||||
return r.calls
|
return r.calls
|
||||||
}
|
}
|
||||||
|
|
||||||
type observedCancelContext struct {
|
|
||||||
done chan struct{}
|
|
||||||
waiting chan struct{}
|
|
||||||
once sync.Once
|
|
||||||
}
|
|
||||||
|
|
||||||
func newObservedCancelContext() *observedCancelContext {
|
|
||||||
return &observedCancelContext{
|
|
||||||
done: make(chan struct{}),
|
|
||||||
waiting: make(chan struct{}, 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *observedCancelContext) Deadline() (time.Time, bool) {
|
|
||||||
return time.Time{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *observedCancelContext) Done() <-chan struct{} {
|
|
||||||
var callers [8]uintptr
|
|
||||||
frames := runtime.CallersFrames(callers[:runtime.Callers(2, callers[:])])
|
|
||||||
for {
|
|
||||||
frame, more := frames.Next()
|
|
||||||
if strings.Contains(frame.Function, "internal/capacity.(*pool).acquire") {
|
|
||||||
select {
|
|
||||||
case c.waiting <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if !more {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return c.done
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *observedCancelContext) Err() error {
|
|
||||||
select {
|
|
||||||
case <-c.done:
|
|
||||||
return context.Canceled
|
|
||||||
default:
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *observedCancelContext) Value(any) any {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *observedCancelContext) cancel() {
|
|
||||||
c.once.Do(func() {
|
|
||||||
close(c.done)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func awaitCapacityRequest(
|
func awaitCapacityRequest(
|
||||||
t *testing.T,
|
t *testing.T,
|
||||||
requests <-chan promptkit.GenerateRequest,
|
requests <-chan promptkit.GenerateRequest,
|
||||||
|
|||||||
@@ -94,4 +94,7 @@ The [runner tests](../../internal/usecase/runner_test.go) own early admission,
|
|||||||
lease lifetime, failure release, and shared initial/repair scheduling. The
|
lease lifetime, failure release, and shared initial/repair scheduling. The
|
||||||
[external package capacity tests](../../capacity_contract_test.go) own the
|
[external package capacity tests](../../capacity_contract_test.go) own the
|
||||||
assembled public-engine behavior for configured limits, capacity errors,
|
assembled public-engine behavior for configured limits, capacity errors,
|
||||||
endpoint identity, engine independence, cancellation, and injected clients.
|
endpoint identity, engine independence, and injected clients. The
|
||||||
|
[root error-boundary tests](../../errors_internal_test.go) own preservation of
|
||||||
|
the public generation category and context identity when generation is
|
||||||
|
canceled.
|
||||||
|
|||||||
22
errors_internal_test.go
Normal file
22
errors_internal_test.go
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package promptkit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"gitea.maximumdirect.net/eric/promptkit/internal/usecase"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMapPublicErrorPreservesGenerationCancellation(t *testing.T) {
|
||||||
|
internalErr := fmt.Errorf("%w: %w", usecase.ErrLLMGenerate, context.Canceled)
|
||||||
|
|
||||||
|
err := mapPublicError(internalErr)
|
||||||
|
if !errors.Is(err, ErrLLMGenerate) {
|
||||||
|
t.Fatalf("mapped error=%v, want ErrLLMGenerate", err)
|
||||||
|
}
|
||||||
|
if !errors.Is(err, context.Canceled) {
|
||||||
|
t.Fatalf("mapped error=%v, want context.Canceled", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user