|
|
@@ -2,6 +2,7 @@ package model_test
|
|
|
|
|
|
import (
|
|
|
"testing"
|
|
|
+ "time"
|
|
|
|
|
|
"github.com/labring/aiproxy/core/model"
|
|
|
)
|
|
|
@@ -349,3 +350,371 @@ func TestPrice_ValidateConditionalPrices(t *testing.T) {
|
|
|
})
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+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))
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|