diff --git a/backend/internal/repository/account_repo_opencode_go_usage.go b/backend/internal/repository/account_repo_opencode_go_usage.go index 63de8e3ed6..f02ed3ea4b 100644 --- a/backend/internal/repository/account_repo_opencode_go_usage.go +++ b/backend/internal/repository/account_repo_opencode_go_usage.go @@ -106,9 +106,9 @@ func (r *accountRepository) UpdateOpenCodeGoUsageSnapshot(ctx context.Context, a if r == nil || r.client == nil || !service.IsOpenCodeGoUsageAccount(account) { return service.ErrOpenCodeGoUsageUnavailable } - payload := openCodeGoUsageManagedPayload(account) - payload[service.OpenCodeGoUsageSnapshotExtraKey] = snapshot - return r.updateOpenCodeGoUsageGroup(ctx, account, payload, nil, true) + return r.updateOpenCodeGoUsageGroup(ctx, account, map[string]any{ + service.OpenCodeGoUsageSnapshotExtraKey: snapshot, + }, nil, true) } // DisableOpenCodeGoUsageAutoRefresh is group-scoped and retains the loaded diff --git a/backend/internal/repository/account_repo_opencode_go_usage_test.go b/backend/internal/repository/account_repo_opencode_go_usage_test.go new file mode 100644 index 0000000000..5ff0b87764 --- /dev/null +++ b/backend/internal/repository/account_repo_opencode_go_usage_test.go @@ -0,0 +1,68 @@ +package repository + +import ( + "context" + "encoding/json" + "regexp" + "testing" + "time" + + "github.com/DATA-DOG/go-sqlmock" + "github.com/Wei-Shaw/sub2api/internal/service" + "github.com/stretchr/testify/require" +) + +func openCodeGoUsageRepositoryAccount() *service.Account { + previousAttempt := time.Date(2026, time.August, 15, 8, 0, 0, 0, time.UTC) + return &service.Account{ + ID: 17, + Platform: service.PlatformOpenAI, + Type: service.AccountTypeAPIKey, + Credentials: map[string]any{"api_key": "key", "base_url": "https://opencode.ai/zen/go/v1"}, + Extra: map[string]any{ + service.OpenCodeGoUsageAutoRefreshExtraKey: true, + service.OpenCodeGoUsageSnapshotExtraKey: &service.OpenCodeGoUsageSnapshot{ + Status: service.OpenCodeGoUsageStatusOK, + LastAttemptAt: previousAttempt, + NextRefreshAt: previousAttempt.Add(time.Hour), + }, + }, + } +} + +func TestUpdateOpenCodeGoUsageSnapshotWritesSnapshotOnly(t *testing.T) { + client, mock := newOllamaCloudUsageRepositoryTestClient(t) + account := openCodeGoUsageRepositoryAccount() + previousSnapshotJSON, err := json.Marshal(account.Extra[service.OpenCodeGoUsageSnapshotExtraKey]) + require.NoError(t, err) + + attemptedAt := time.Date(2026, time.August, 15, 9, 0, 0, 0, time.UTC) + snapshot := &service.OpenCodeGoUsageSnapshot{ + Status: service.OpenCodeGoUsageStatusOK, + LastAttemptAt: attemptedAt, + NextRefreshAt: attemptedAt.Add(time.Hour), + } + expectedPayload, err := json.Marshal(map[string]any{ + service.OpenCodeGoUsageSnapshotExtraKey: snapshot, + }) + require.NoError(t, err) + require.NotContains(t, string(expectedPayload), service.OpenCodeGoUsageAutoRefreshExtraKey) + + credentials, err := json.Marshal(normalizeJSONMap(account.Credentials)) + require.NoError(t, err) + mock.ExpectBegin() + mock.ExpectQuery(`(?s)`+regexp.QuoteMeta("SELECT")+`.*`+regexp.QuoteMeta("FOR NO KEY UPDATE")). + WithArgs("key", account.ID, account.Platform, account.Type, string(credentials), nil). + WillReturnRows(sqlmock.NewRows([]string{"id", "anchor_matches", "auto_refresh", "snapshot"}). + AddRow(account.ID, true, `true`, string(previousSnapshotJSON))) + mock.ExpectExec(`(?s)`+regexp.QuoteMeta("UPDATE accounts")). + WithArgs(string(expectedPayload), "key", sqlmock.AnyArg()). + WillReturnResult(sqlmock.NewResult(0, 1)) + mock.ExpectCommit() + repo := newAccountRepositoryWithSQL(client, nil, nil) + + err = repo.UpdateOpenCodeGoUsageSnapshot(context.Background(), account, snapshot) + + require.NoError(t, err) + require.NoError(t, mock.ExpectationsWereMet()) +} diff --git a/backend/internal/service/opencode_go_usage_test.go b/backend/internal/service/opencode_go_usage_test.go index e173a4cc28..ccf1038d82 100644 --- a/backend/internal/service/opencode_go_usage_test.go +++ b/backend/internal/service/opencode_go_usage_test.go @@ -279,6 +279,7 @@ func TestOpenCodeGoUsageParseJSONLenient(t *testing.T) { func TestOpenCodeGoUsageRefresh200Success(t *testing.T) { account := openCodeGoUsageAccount(7) + account.Extra[OpenCodeGoUsageAutoRefreshExtraKey] = true repo := &openCodeGoUsageTestRepo{accounts: map[int64]*Account{7: account}} stub := &openCodeGoUsageHTTPStub{body: []byte(openCodeGoUsageFixture)} svc := newOpenCodeGoUsageTestService(t, repo, stub, &upstreamBillingProbeSettingRepo{}) @@ -286,6 +287,8 @@ func TestOpenCodeGoUsageRefresh200Success(t *testing.T) { state, err := svc.Refresh(context.Background(), 7) require.NoError(t, err) require.True(t, state.Eligible) + require.True(t, state.AutoRefreshEnabled) + require.True(t, openCodeGoUsageAutoRefreshEnabled(account)) require.Equal(t, OpenCodeGoUsageStatusOK, state.Snapshot.Status) require.Equal(t, 6.0, state.Snapshot.Data.Rolling.Percent) require.Equal(t, 2.0, state.Snapshot.Data.Weekly.Percent)