Files
wehub-resource-sync f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:30:36 +08:00

268 lines
7.3 KiB
Go

package db
import (
"context"
"testing"
"go.kenn.io/agentsview/internal/config"
"go.kenn.io/agentsview/internal/export"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMigrationCreatesModelPricingTable(t *testing.T) {
d := testDB(t)
var count int
err := d.getReader().QueryRow(
`SELECT count(*) FROM pragma_table_info('model_pricing')`,
).Scan(&count)
require.NoError(t, err, "pragma_table_info")
assert.NotZero(t, count, "model_pricing table not created by schema")
}
func TestUpsertModelPricing(t *testing.T) {
d := testDB(t)
prices := []ModelPricing{
{
ModelPattern: "claude-sonnet-4",
InputPerMTok: 3.0,
OutputPerMTok: 15.0,
CacheCreationPerMTok: 3.75,
CacheReadPerMTok: 0.30,
},
}
err := d.UpsertModelPricing(prices)
require.NoError(t, err, "UpsertModelPricing")
got, err := d.GetModelPricing("claude-sonnet-4")
require.NoError(t, err, "GetModelPricing")
require.NotNil(t, got, "expected pricing")
assert.Equal(t, "claude-sonnet-4", got.ModelPattern)
assert.Equal(t, 3.0, got.InputPerMTok)
assert.Equal(t, 15.0, got.OutputPerMTok)
assert.Equal(t, 3.75, got.CacheCreationPerMTok)
assert.Equal(t, 0.30, got.CacheReadPerMTok)
assert.NotEmpty(t, got.UpdatedAt, "expected UpdatedAt to be set")
}
func TestUpsertModelPricingOverwrites(t *testing.T) {
d := testDB(t)
initial := []ModelPricing{
{
ModelPattern: "claude-opus-4",
InputPerMTok: 15.0,
OutputPerMTok: 75.0,
CacheCreationPerMTok: 18.75,
CacheReadPerMTok: 1.50,
},
}
err := d.UpsertModelPricing(initial)
require.NoError(t, err, "UpsertModelPricing initial")
updated := []ModelPricing{
{
ModelPattern: "claude-opus-4",
InputPerMTok: 10.0,
OutputPerMTok: 50.0,
CacheCreationPerMTok: 12.50,
CacheReadPerMTok: 1.00,
},
}
err = d.UpsertModelPricing(updated)
require.NoError(t, err, "UpsertModelPricing updated")
got, err := d.GetModelPricing("claude-opus-4")
require.NoError(t, err, "GetModelPricing after update")
require.NotNil(t, got, "expected pricing")
assert.Equal(t, 10.0, got.InputPerMTok)
assert.Equal(t, 50.0, got.OutputPerMTok)
assert.Equal(t, 12.50, got.CacheCreationPerMTok)
assert.Equal(t, 1.00, got.CacheReadPerMTok)
}
func TestFilterChangedModelPricingIgnoresUpdatedAtOnlyDifferences(t *testing.T) {
existing := []ModelPricing{
{
ModelPattern: "_fallback_version",
InputPerMTok: 0,
OutputPerMTok: 0,
CacheCreationPerMTok: 0,
CacheReadPerMTok: 0,
UpdatedAt: "v1",
},
{
ModelPattern: "same-model",
InputPerMTok: 1,
OutputPerMTok: 2,
CacheCreationPerMTok: 3,
CacheReadPerMTok: 4,
UpdatedAt: "old",
},
{
ModelPattern: "changed-model",
InputPerMTok: 1,
OutputPerMTok: 2,
CacheCreationPerMTok: 3,
CacheReadPerMTok: 4,
UpdatedAt: "old",
},
}
desired := []ModelPricing{
{
ModelPattern: "_fallback_version",
InputPerMTok: 0,
OutputPerMTok: 0,
CacheCreationPerMTok: 0,
CacheReadPerMTok: 0,
UpdatedAt: "v2",
},
{
ModelPattern: "same-model",
InputPerMTok: 1,
OutputPerMTok: 2,
CacheCreationPerMTok: 3,
CacheReadPerMTok: 4,
UpdatedAt: "new",
},
{
ModelPattern: "changed-model",
InputPerMTok: 1,
OutputPerMTok: 9,
CacheCreationPerMTok: 3,
CacheReadPerMTok: 4,
UpdatedAt: "new",
},
{
ModelPattern: "missing-model",
InputPerMTok: 5,
OutputPerMTok: 6,
CacheCreationPerMTok: 7,
CacheReadPerMTok: 8,
UpdatedAt: "new",
},
}
gotSummary, gotRows := FilterChangedModelPricing(existing, desired)
assert.Equal(t, PricingChangeSummary{
Total: 4,
Missing: 1,
Changed: 1,
Unchanged: 2,
}, gotSummary)
require.Len(t, gotRows, 2)
assert.Equal(t, "changed-model", gotRows[0].ModelPattern)
assert.Equal(t, "missing-model", gotRows[1].ModelPattern)
}
func TestPricingMeta(t *testing.T) {
d := testDB(t)
// Initially empty.
got, err := d.GetPricingMeta("_fallback_version")
require.NoError(t, err, "GetPricingMeta empty")
require.Empty(t, got)
// Set and read back.
require.NoError(t,
d.SetPricingMeta("_fallback_version", "v1"),
"SetPricingMeta v1")
got, err = d.GetPricingMeta("_fallback_version")
require.NoError(t, err, "GetPricingMeta v1")
require.Equal(t, "v1", got)
// Update overwrites.
require.NoError(t,
d.SetPricingMeta("_fallback_version", "v2"),
"SetPricingMeta v2")
got, err = d.GetPricingMeta("_fallback_version")
require.NoError(t, err, "GetPricingMeta v2")
require.Equal(t, "v2", got)
// Sentinel row does not interfere with model lookups.
p, err := d.GetModelPricing("_fallback_version")
require.NoError(t, err, "GetModelPricing sentinel")
if p != nil {
assert.Zero(t, p.InputPerMTok,
"sentinel should have zero pricing, got %+v", p)
}
}
func TestGetModelPricingNotFound(t *testing.T) {
d := testDB(t)
got, err := d.GetModelPricing("nonexistent-model")
require.NoError(t, err, "GetModelPricing not found")
assert.Nil(t, got, "expected nil")
}
func TestInsertMissingModelPricing_DoesNotOverwrite(t *testing.T) {
d := testDB(t)
// Seed an existing row (simulating a LiteLLM rate already present).
require.NoError(t, d.UpsertModelPricing([]ModelPricing{{
ModelPattern: "claude-opus-4-6",
InputPerMTok: 5.0,
OutputPerMTok: 25.0,
CacheCreationPerMTok: 6.25,
CacheReadPerMTok: 0.5,
}}), "UpsertModelPricing")
// Insert-missing with a DIFFERENT rate for the same pattern, plus a
// brand-new pattern.
err := d.InsertMissingModelPricing([]ModelPricing{
{ModelPattern: "claude-opus-4-6", InputPerMTok: 999.0, OutputPerMTok: 999.0},
{ModelPattern: "gpt-5.4", InputPerMTok: 2.5, OutputPerMTok: 15.0},
})
require.NoError(t, err, "InsertMissingModelPricing")
// Existing row is untouched.
opus, err := d.GetModelPricing("claude-opus-4-6")
require.NoError(t, err, "GetModelPricing opus")
require.NotNil(t, opus)
assert.Equal(t, 5.0, opus.InputPerMTok, "opus InputPerMTok not overwritten")
// New row was inserted.
gpt, err := d.GetModelPricing("gpt-5.4")
require.NoError(t, err, "GetModelPricing gpt")
require.NotNil(t, gpt)
assert.Equal(t, 2.5, gpt.InputPerMTok, "gpt-5.4 InputPerMTok inserted")
}
func TestLoadPricingMapKeepsCustomSourceWhenRatesMatchFallback(t *testing.T) {
d := testDB(t)
ctx := context.Background()
fallback := fallbackRateMap()
fallbackRates, ok := fallback["gpt-5.5"]
require.True(t, ok, "expected gpt-5.5 fallback rates")
d.SetCustomPricing(map[string]config.CustomModelRate{
"gpt-5.5": {
Input: fallbackRates.InputPerMTok,
Output: fallbackRates.OutputPerMTok,
CacheCreation: fallbackRates.CacheWritePerMTok,
CacheRead: fallbackRates.CacheReadPerMTok,
},
})
rows, err := d.loadPricingMap(ctx)
require.NoError(t, err, "loadPricingMap")
resolver := export.NewPricingResolver(rows)
lookup := resolver.Lookup("gpt-5.5")
require.True(t, lookup.OK, "lookup custom fallback-rate row")
assert.Equal(t, export.PricingRowSourceCustom,
lookup.Rates.Source)
block, err := resolver.BuildBlock()
require.NoError(t, err)
assert.Equal(t, "custom", block.Source)
assert.Equal(t, 1, block.CustomOverrideCount)
}