| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720 |
- package model_test
- import (
- "testing"
- "time"
- "github.com/labring/aiproxy/core/model"
- )
- func TestPrice_ValidateConditionalPrices(t *testing.T) {
- tests := []struct {
- name string
- price model.Price
- wantErr bool
- }{
- {
- name: "Empty conditional prices",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{},
- },
- wantErr: false,
- },
- {
- name: "Nil conditional prices",
- price: model.Price{
- ConditionalPrices: nil,
- },
- wantErr: false,
- },
- {
- name: "Valid single condition",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- OutputTokenMin: 0,
- OutputTokenMax: 200,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Valid multiple conditions - doubao-seed-1.6 example",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- OutputTokenMin: 0,
- OutputTokenMax: 200,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- OutputTokenMin: 201,
- OutputTokenMax: 16000,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.008,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 32001,
- InputTokenMax: 128000,
- },
- Price: model.Price{
- InputPrice: 0.0012,
- OutputPrice: 0.016,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 128001,
- InputTokenMax: 256000,
- },
- Price: model.Price{
- InputPrice: 0.0024,
- OutputPrice: 0.024,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Invalid input token range - min > max",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 32000,
- InputTokenMax: 1000, // min > max
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: true,
- },
- {
- name: "Invalid output token range - min > max",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- OutputTokenMin: 1000,
- OutputTokenMax: 500, // min > max
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: true,
- },
- {
- name: "Overlapping input ranges with overlapping output ranges",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- OutputTokenMin: 0,
- OutputTokenMax: 500,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 20000, // overlaps with previous
- InputTokenMax: 50000,
- OutputTokenMin: 200, // overlaps with previous
- OutputTokenMax: 1000,
- },
- Price: model.Price{
- InputPrice: 0.0012,
- OutputPrice: 0.008,
- },
- },
- },
- },
- wantErr: true,
- },
- {
- name: "Overlapping input ranges but non-overlapping output ranges (valid)",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- OutputTokenMin: 0,
- OutputTokenMax: 200,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0, // same input range
- InputTokenMax: 32000, // same input range
- OutputTokenMin: 201, // non-overlapping output range
- OutputTokenMax: 16000,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.008,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Improperly ordered conditions",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 32001,
- InputTokenMax: 128000,
- },
- Price: model.Price{
- InputPrice: 0.0012,
- OutputPrice: 0.016,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000, // should come before the previous one
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: true,
- },
- {
- name: "Valid consecutive ranges",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 32001, // consecutive with previous
- InputTokenMax: 128000,
- },
- Price: model.Price{
- InputPrice: 0.0012,
- OutputPrice: 0.016,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Gap between ranges (valid)",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 50000, // gap between 32000 and 50000
- InputTokenMax: 128000,
- },
- Price: model.Price{
- InputPrice: 0.0012,
- OutputPrice: 0.016,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Unbounded ranges (zero values)",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0, // unbounded min
- InputTokenMax: 0, // unbounded max
- },
- Price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Mixed bounded and unbounded ranges",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 32001,
- InputTokenMax: 0, // unbounded max
- },
- Price: model.Price{
- InputPrice: 0.0012,
- OutputPrice: 0.016,
- },
- },
- },
- },
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := tt.price.ValidateConditionalPrices()
- if tt.wantErr {
- if err == nil {
- t.Errorf("%s: ValidateConditionalPrices() expected error but got nil", tt.name)
- }
- return
- }
- if err != nil {
- t.Errorf("%s: ValidateConditionalPrices() unexpected error = %v", tt.name, err)
- }
- })
- }
- }
- func TestPrice_ValidateConditionalPrices_WithTime(t *testing.T) {
- now := time.Now().Unix()
- future := now + 3600 // 1 hour from now
- past := now - 3600 // 1 hour ago
- tests := []struct {
- name string
- price model.Price
- wantErr bool
- }{
- {
- name: "Valid time range - future time window",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: now,
- EndTime: future,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Valid time range - no end time",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: now,
- EndTime: 0, // no end limit
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Valid time range - no start time",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: 0, // no start limit
- EndTime: future,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: false,
- },
- {
- name: "Invalid time range - start time >= end time",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: future,
- EndTime: now, // end before start
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: true,
- },
- {
- name: "Invalid time range - start time equals end time",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: now,
- EndTime: now, // same time
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- },
- },
- wantErr: true,
- },
- {
- name: "Multiple conditions with different time ranges",
- price: model.Price{
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: past,
- EndTime: now,
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.002,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: now,
- EndTime: future,
- },
- Price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.003,
- },
- },
- },
- },
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- err := tt.price.ValidateConditionalPrices()
- if tt.wantErr {
- if err == nil {
- t.Errorf("%s: ValidateConditionalPrices() expected error but got nil", tt.name)
- }
- return
- }
- if err != nil {
- t.Errorf("%s: ValidateConditionalPrices() unexpected error = %v", tt.name, err)
- }
- })
- }
- }
- func TestPrice_SelectConditionalPrice_WithTime(t *testing.T) {
- now := time.Now().Unix()
- past := now - 3600 // 1 hour ago
- future := now + 3600 // 1 hour from now
- farFuture := now + 7200 // 2 hours from now
- tests := []struct {
- name string
- price model.Price
- usage model.Usage
- expectedInput float64
- expectedOutput float64
- }{
- {
- name: "Select price within active time range",
- price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: past,
- EndTime: future,
- },
- Price: model.Price{
- InputPrice: 0.0005,
- OutputPrice: 0.001,
- },
- },
- },
- },
- usage: model.Usage{
- InputTokens: 1000,
- OutputTokens: 500,
- },
- expectedInput: 0.0005,
- expectedOutput: 0.001,
- },
- {
- name: "Fallback to default price when time range not active (before start)",
- price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: future,
- EndTime: farFuture,
- },
- Price: model.Price{
- InputPrice: 0.0005,
- OutputPrice: 0.001,
- },
- },
- },
- },
- usage: model.Usage{
- InputTokens: 1000,
- OutputTokens: 500,
- },
- expectedInput: 0.001,
- expectedOutput: 0.002,
- },
- {
- name: "Fallback to default price when time range expired",
- price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: past - 7200, // 3 hours ago
- EndTime: past, // 1 hour ago
- },
- Price: model.Price{
- InputPrice: 0.0005,
- OutputPrice: 0.001,
- },
- },
- },
- },
- usage: model.Usage{
- InputTokens: 1000,
- OutputTokens: 500,
- },
- expectedInput: 0.001,
- expectedOutput: 0.002,
- },
- {
- name: "Select first matching price with multiple time-based conditions",
- price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: past,
- EndTime: future,
- },
- Price: model.Price{
- InputPrice: 0.0005,
- OutputPrice: 0.001,
- },
- },
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: past,
- EndTime: farFuture, // broader time range
- },
- Price: model.Price{
- InputPrice: 0.0008,
- OutputPrice: 0.0015,
- },
- },
- },
- },
- usage: model.Usage{
- InputTokens: 1000,
- OutputTokens: 500,
- },
- expectedInput: 0.0005,
- expectedOutput: 0.001,
- },
- {
- name: "Time range with no end time (ongoing promotion)",
- price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: past,
- EndTime: 0, // no end time
- },
- Price: model.Price{
- InputPrice: 0.0005,
- OutputPrice: 0.001,
- },
- },
- },
- },
- usage: model.Usage{
- InputTokens: 1000,
- OutputTokens: 500,
- },
- expectedInput: 0.0005,
- expectedOutput: 0.001,
- },
- {
- name: "Time range with no start time (promotion until end)",
- price: model.Price{
- InputPrice: 0.001,
- OutputPrice: 0.002,
- ConditionalPrices: []model.ConditionalPrice{
- {
- Condition: model.PriceCondition{
- InputTokenMin: 0,
- InputTokenMax: 32000,
- StartTime: 0, // no start time
- EndTime: future,
- },
- Price: model.Price{
- InputPrice: 0.0005,
- OutputPrice: 0.001,
- },
- },
- },
- },
- usage: model.Usage{
- InputTokens: 1000,
- OutputTokens: 500,
- },
- expectedInput: 0.0005,
- expectedOutput: 0.001,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- selectedPrice := tt.price.SelectConditionalPrice(tt.usage)
- if float64(selectedPrice.InputPrice) != tt.expectedInput {
- t.Errorf("%s: expected input price %v, got %v",
- tt.name, tt.expectedInput, float64(selectedPrice.InputPrice))
- }
- if float64(selectedPrice.OutputPrice) != tt.expectedOutput {
- t.Errorf("%s: expected output price %v, got %v",
- tt.name, tt.expectedOutput, float64(selectedPrice.OutputPrice))
- }
- })
- }
- }
|