CI / test (push) Successful in 9m51s
auth.gpu_hour_cost (and core_hour_cost) is what an hour of a GPU (a core) costs the operator. The leader keeps, by the hour, the GPU- and core-hours each model's replicas hold (held_1h); llmgw ctl prices suggest spreads a model's over the tokens it served, an output token counted as four input ones and a cached prompt token as a tenth of one, and --apply writes the prices into auth.pricing. Idle hours are in the cost, so a model held and little used comes out dear; one held and billed nothing is shown with what it held. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
36 lines
1.0 KiB
Go
36 lines
1.0 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestHeldAddsUpByHour(t *testing.T) {
|
|
s := capacityStore(t)
|
|
ctx := context.Background()
|
|
at := time.Date(2026, 11, 14, 10, 5, 0, 0, time.UTC)
|
|
for i := range 3 {
|
|
if err := s.AddHeld(ctx, at.Add(time.Duration(i)*5*time.Minute),
|
|
[]Held{{Model: "qwen", GPUSeconds: 300}, {Model: "small", CoreSeconds: 1200}, {Model: "idle"}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if err := s.AddHeld(ctx, at.Add(time.Hour), []Held{{Model: "qwen", GPUSeconds: 600}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, first, err := s.HeldSince(ctx, at.Add(-24*time.Hour))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(got) != 2 || got[0] != (Held{Model: "qwen", GPUSeconds: 1500}) || got[1] != (Held{Model: "small", CoreSeconds: 3600}) {
|
|
t.Errorf("held %+v", got)
|
|
}
|
|
if !first.Equal(at.Truncate(time.Hour)) {
|
|
t.Errorf("first %v", first)
|
|
}
|
|
if got, _, _ := s.HeldSince(ctx, at.Add(time.Hour)); len(got) != 1 || got[0].GPUSeconds != 600 {
|
|
t.Errorf("since the next hour: %+v", got)
|
|
}
|
|
}
|