Skip to content

Commit 9259dcb

Browse files
test(repo): cover requested model repository semantics
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
1 parent 7ef933c commit 9259dcb

2 files changed

Lines changed: 104 additions & 27 deletions

File tree

backend/internal/repository/usage_log_repo_breakdown_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ func TestResolveModelDimensionExpression(t *testing.T) {
3434
modelType string
3535
want string
3636
}{
37-
{usagestats.ModelSourceRequested, "model"},
38-
{usagestats.ModelSourceUpstream, "COALESCE(NULLIF(TRIM(upstream_model), ''), model)"},
39-
{usagestats.ModelSourceMapping, "(model || ' -> ' || COALESCE(NULLIF(TRIM(upstream_model), ''), model))"},
40-
{"", "model"},
41-
{"invalid", "model"},
37+
{usagestats.ModelSourceRequested, "COALESCE(NULLIF(TRIM(requested_model), ''), model)"},
38+
{usagestats.ModelSourceUpstream, "COALESCE(NULLIF(TRIM(upstream_model), ''), COALESCE(NULLIF(TRIM(requested_model), ''), model))"},
39+
{usagestats.ModelSourceMapping, "(COALESCE(NULLIF(TRIM(requested_model), ''), model) || ' -> ' || COALESCE(NULLIF(TRIM(upstream_model), ''), COALESCE(NULLIF(TRIM(requested_model), ''), model)))"},
40+
{"", "COALESCE(NULLIF(TRIM(requested_model), ''), model)"},
41+
{"invalid", "COALESCE(NULLIF(TRIM(requested_model), ''), model)"},
4242
}
4343

4444
for _, tc := range tests {

backend/internal/repository/usage_log_repo_request_type_test.go

Lines changed: 99 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package repository
33
import (
44
"context"
55
"database/sql"
6+
"database/sql/driver"
67
"fmt"
78
"reflect"
89
"testing"
@@ -21,20 +22,21 @@ func TestUsageLogRepositoryCreateSyncRequestTypeAndLegacyFields(t *testing.T) {
2122

2223
createdAt := time.Date(2025, 1, 1, 12, 0, 0, 0, time.UTC)
2324
log := &service.UsageLog{
24-
UserID: 1,
25-
APIKeyID: 2,
26-
AccountID: 3,
27-
RequestID: "req-1",
28-
Model: "gpt-5",
29-
InputTokens: 10,
30-
OutputTokens: 20,
31-
TotalCost: 1,
32-
ActualCost: 1,
33-
BillingType: service.BillingTypeBalance,
34-
RequestType: service.RequestTypeWSV2,
35-
Stream: false,
36-
OpenAIWSMode: false,
37-
CreatedAt: createdAt,
25+
UserID: 1,
26+
APIKeyID: 2,
27+
AccountID: 3,
28+
RequestID: "req-1",
29+
Model: "gpt-5",
30+
RequestedModel: "gpt-5",
31+
InputTokens: 10,
32+
OutputTokens: 20,
33+
TotalCost: 1,
34+
ActualCost: 1,
35+
BillingType: service.BillingTypeBalance,
36+
RequestType: service.RequestTypeWSV2,
37+
Stream: false,
38+
OpenAIWSMode: false,
39+
CreatedAt: createdAt,
3840
}
3941

4042
mock.ExpectQuery("INSERT INTO usage_logs").
@@ -44,6 +46,7 @@ func TestUsageLogRepositoryCreateSyncRequestTypeAndLegacyFields(t *testing.T) {
4446
log.AccountID,
4547
log.RequestID,
4648
log.Model,
49+
log.RequestedModel,
4750
sqlmock.AnyArg(), // upstream_model
4851
sqlmock.AnyArg(), // group_id
4952
sqlmock.AnyArg(), // subscription_id
@@ -99,13 +102,14 @@ func TestUsageLogRepositoryCreate_PersistsServiceTier(t *testing.T) {
99102
createdAt := time.Date(2025, 1, 2, 12, 0, 0, 0, time.UTC)
100103
serviceTier := "priority"
101104
log := &service.UsageLog{
102-
UserID: 1,
103-
APIKeyID: 2,
104-
AccountID: 3,
105-
RequestID: "req-service-tier",
106-
Model: "gpt-5.4",
107-
ServiceTier: &serviceTier,
108-
CreatedAt: createdAt,
105+
UserID: 1,
106+
APIKeyID: 2,
107+
AccountID: 3,
108+
RequestID: "req-service-tier",
109+
Model: "gpt-5.4",
110+
RequestedModel: "gpt-5.4",
111+
ServiceTier: &serviceTier,
112+
CreatedAt: createdAt,
109113
}
110114

111115
mock.ExpectQuery("INSERT INTO usage_logs").
@@ -115,6 +119,7 @@ func TestUsageLogRepositoryCreate_PersistsServiceTier(t *testing.T) {
115119
log.AccountID,
116120
log.RequestID,
117121
log.Model,
122+
log.RequestedModel,
118123
sqlmock.AnyArg(),
119124
sqlmock.AnyArg(),
120125
sqlmock.AnyArg(),
@@ -158,6 +163,75 @@ func TestUsageLogRepositoryCreate_PersistsServiceTier(t *testing.T) {
158163
require.NoError(t, mock.ExpectationsWereMet())
159164
}
160165

166+
func TestBuildUsageLogBestEffortInsertQuery_IncludesRequestedModelColumn(t *testing.T) {
167+
prepared := prepareUsageLogInsert(&service.UsageLog{
168+
UserID: 1,
169+
APIKeyID: 2,
170+
AccountID: 3,
171+
RequestID: "req-best-effort-query",
172+
Model: "gpt-5",
173+
RequestedModel: "gpt-5",
174+
CreatedAt: time.Date(2025, 1, 3, 12, 0, 0, 0, time.UTC),
175+
})
176+
177+
query, args := buildUsageLogBestEffortInsertQuery([]usageLogInsertPrepared{prepared})
178+
179+
require.Contains(t, query, "INSERT INTO usage_logs (")
180+
require.Contains(t, query, "\n\t\t\tmodel,\n\t\t\trequested_model,\n\t\t\tupstream_model,")
181+
require.Contains(t, query, "\n\t\t\trequest_id,\n\t\t\tmodel,\n\t\t\trequested_model,\n\t\t\tupstream_model,")
182+
require.Len(t, args, len(prepared.args))
183+
require.Equal(t, prepared.args[5], args[5])
184+
}
185+
186+
func TestExecUsageLogInsertNoResult_PersistsRequestedModel(t *testing.T) {
187+
db, mock := newSQLMock(t)
188+
prepared := prepareUsageLogInsert(&service.UsageLog{
189+
UserID: 1,
190+
APIKeyID: 2,
191+
AccountID: 3,
192+
RequestID: "req-best-effort-exec",
193+
Model: "gpt-5",
194+
RequestedModel: "gpt-5",
195+
CreatedAt: time.Date(2025, 1, 4, 12, 0, 0, 0, time.UTC),
196+
})
197+
198+
mock.ExpectExec("INSERT INTO usage_logs").
199+
WithArgs(anySliceToDriverValues(prepared.args)...).
200+
WillReturnResult(sqlmock.NewResult(0, 1))
201+
202+
err := execUsageLogInsertNoResult(context.Background(), db, prepared)
203+
require.NoError(t, err)
204+
require.NoError(t, mock.ExpectationsWereMet())
205+
}
206+
207+
func TestPrepareUsageLogInsert_ArgCountMatchesTypes(t *testing.T) {
208+
prepared := prepareUsageLogInsert(&service.UsageLog{
209+
UserID: 1,
210+
APIKeyID: 2,
211+
AccountID: 3,
212+
RequestID: "req-arg-count",
213+
Model: "gpt-5",
214+
RequestedModel: "gpt-5",
215+
CreatedAt: time.Date(2025, 1, 5, 12, 0, 0, 0, time.UTC),
216+
})
217+
218+
require.Len(t, prepared.args, len(usageLogInsertArgTypes))
219+
}
220+
221+
func TestCoalesceTrimmedString(t *testing.T) {
222+
require.Equal(t, "fallback", coalesceTrimmedString(sql.NullString{}, "fallback"))
223+
require.Equal(t, "fallback", coalesceTrimmedString(sql.NullString{Valid: true, String: " "}, "fallback"))
224+
require.Equal(t, "value", coalesceTrimmedString(sql.NullString{Valid: true, String: "value"}, "fallback"))
225+
}
226+
227+
func anySliceToDriverValues(values []any) []driver.Value {
228+
out := make([]driver.Value, 0, len(values))
229+
for _, value := range values {
230+
out = append(out, value)
231+
}
232+
return out
233+
}
234+
161235
func TestUsageLogRepositoryListWithFiltersRequestTypePriority(t *testing.T) {
162236
db, mock := newSQLMock(t)
163237
repo := &usageLogRepository{sql: db}
@@ -354,7 +428,8 @@ func TestScanUsageLogRequestTypeAndLegacyFallback(t *testing.T) {
354428
int64(20), // api_key_id
355429
int64(30), // account_id
356430
sql.NullString{Valid: true, String: "req-1"},
357-
"gpt-5", // model
431+
"gpt-5", // model
432+
sql.NullString{Valid: true, String: "gpt-5"}, // requested_model
358433
sql.NullString{}, // upstream_model
359434
sql.NullInt64{}, // group_id
360435
sql.NullInt64{}, // subscription_id
@@ -407,6 +482,7 @@ func TestScanUsageLogRequestTypeAndLegacyFallback(t *testing.T) {
407482
int64(31),
408483
sql.NullString{Valid: true, String: "req-2"},
409484
"gpt-5",
485+
sql.NullString{Valid: true, String: "gpt-5"},
410486
sql.NullString{},
411487
sql.NullInt64{},
412488
sql.NullInt64{},
@@ -449,6 +525,7 @@ func TestScanUsageLogRequestTypeAndLegacyFallback(t *testing.T) {
449525
int64(32),
450526
sql.NullString{Valid: true, String: "req-3"},
451527
"gpt-5.4",
528+
sql.NullString{Valid: true, String: "gpt-5.4"},
452529
sql.NullString{},
453530
sql.NullInt64{},
454531
sql.NullInt64{},

0 commit comments

Comments
 (0)