Files
magpie/internal/provider/subscription_usage_test.go
T
Bo Zou f56d105495 provider: a Copilot account signed in from magpie gets its allowance card, the editors' sign-in or not (#386)
The Usage page's Copilot card was asked only when copilotLogin(cfg) found the
editors' or the standalone CLI's own GitHub sign-in
(~/.config/github-copilot/apps.json or hosts.json, else ~/.copilot/config.json).
An account signed in from magpie itself is kept in logins.json, and
copilotLogin never looks there: with no editor and no Copilot CLI signed in on
the machine, no Copilot allowance was fetched at all. The Usage page, the menu
bar's usage picker and the gateway's GET /v1/magpie/quotas had no Copilot card,
while `magpie accounts` did show one (that path reads copilotLoginList through
builtinLogins, login_usage.go) and quotas.json kept a fresh reading written by
it — the account was known and readable, only never asked where the page looks.

copilotLoginList() covers both the editors'/CLI's own account and the accounts
magpie signed in (copilot_accounts.go), and loginQuota resolves each account's
own token by user, so the fetch is scheduled from that list now, as Kiro's,
ZCode's, Qoder's, Zed's and WorkBuddy's already are. The single-account branch
this replaces called copilotSubscriptionUsage with the editors' token for the
same account, which perLogin reaches the same way.

TestCopilotQuotaWithoutEditorsSignIn fails without the change ("no Copilot
card: an account signed in from magpie alone was left out") and passes with it.
go vet ./internal/provider/ and the internal/provider package's tests pass.
2026-10-01 14:43:13 +08:00

177 lines
5.7 KiB
Go

package provider
import (
"context"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"time"
)
func TestCodexQuotaUsesProviderWindowDuration(t *testing.T) {
for _, tt := range []struct {
seconds int64
want string
}{{5 * 60 * 60, "5 hours"}, {7 * 24 * 60 * 60, "7 days"}, {0, "Allowance"}} {
if got := quotaDurationName(tt.seconds); got != tt.want {
t.Errorf("quotaDurationName(%d) = %q, want %q", tt.seconds, got, tt.want)
}
}
w := codexWindow{UsedPercent: 16, LimitWindowSecs: 7 * 24 * 60 * 60}
if got := w.window(); got.Name != "7 days" || got.Used != 16 {
t.Fatalf("Codex window = %+v", got)
}
}
func TestCompactQuotaNumber(t *testing.T) {
for _, tt := range []struct {
in float64
want string
}{{7, "7"}, {7.5, "7.5"}, {7.25, "7.25"}} {
if got := compactNumber(tt.in); got != tt.want {
t.Errorf("compactNumber(%v) = %q, want %q", tt.in, got, tt.want)
}
}
}
// A stale copy comes back at once while it refreshes, and accounts removed
// from magpie in the meantime are left out of it.
func TestSubscriptionUsageServesStale(t *testing.T) {
signIn(t)
if err := Delete("codex"); err != nil {
t.Fatal(err)
}
c := &subscriptionUsageCache
c.Lock()
c.at, c.pending = time.Now().Add(-time.Hour), nil
c.data = []SubscriptionQuota{{Provider: "claude", Name: "Claude Code"}, {Provider: "codex", Name: "Codex"}}
c.Unlock()
t.Cleanup(func() {
for {
c.Lock()
p := c.pending
if p == nil {
c.data, c.at = nil, time.Time{}
c.Unlock()
return
}
c.Unlock()
<-p
}
})
ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
defer cancel()
start := time.Now()
got := SubscriptionUsage(ctx)
if time.Since(start) > 40*time.Millisecond {
t.Fatalf("waited %v for a stale copy", time.Since(start))
}
if len(got) != 1 || got[0].Provider != "claude" {
t.Fatalf("got %+v, want only claude", got)
}
c.Lock()
refreshing := c.pending != nil || time.Since(c.at) < time.Minute
c.Unlock()
if !refreshing {
t.Fatal("a stale copy did not start a refresh")
}
// what showed the stale copy hears when the new one lands
landed := make(chan struct{}, 1)
OnSubscriptionUsage = func() { landed <- struct{}{} }
t.Cleanup(func() { OnSubscriptionUsage = nil })
c.Lock()
p := c.pending
c.at = time.Time{}
c.Unlock()
if p != nil {
<-p
}
SubscriptionUsage(context.Background())
select {
case <-landed:
case <-time.After(15 * time.Second):
t.Fatal("not told the refresh landed")
}
}
func TestChosenWindows(t *testing.T) {
ws := []QuotaWindow{{Name: "Gemini 3.8 Flash (High)", Model: "gemini-3.8-flash-high"}, {Name: "Gemini 3 Flash", Model: "gemini-3-flash"}, {Name: "Weekly"}}
got := chosenWindows(ws, map[string]bool{"gemini-3.8-flash-high": true}, nil)
if len(got) != 2 || got[0].Model != "gemini-3.8-flash-high" || got[1].Name != "Weekly" {
t.Fatalf("got %+v", got)
}
// ids the quota names that none of the enabled ones match: keep them all
if got := chosenWindows(ws[:2], map[string]bool{"gemini-pro-agent": true}, nil); len(got) != 2 {
t.Fatalf("kept %d of 2", len(got))
}
// Antigravity's levels of a model enabled as one (#150): each level's
// window is kept for the family picked, and only those
ag := []QuotaWindow{{Model: "gemini-3.7-flash-high"}, {Model: "gemini-3.7-flash-low"}, {Model: "gemini-3.1-pro-high"},
{Model: "claude-opus-4-6-thinking"}, {Model: "gemini-3.5-flash-lite"}}
base := func(id string) string {
if b, _, ok := antigravityBaseIn(antigravityListed, id); ok {
return b
}
return id
}
got = chosenWindows(ag, map[string]bool{"gemini-3.7-flash": true, "claude-opus-4-6-thinking": true}, base)
if len(got) != 3 || got[0].Model != "gemini-3.7-flash-high" || got[1].Model != "gemini-3.7-flash-low" || got[2].Model != "claude-opus-4-6-thinking" {
t.Fatalf("antigravity: %+v", got)
}
}
// A Copilot account magpie signed in itself gets its card even when the
// editors' own sign-in isn't on the machine: every account magpie knows is
// asked, not only the one the editors' or the CLI's token belongs to.
func TestCopilotQuotaWithoutEditorsSignIn(t *testing.T) {
home := signIn(t)
t.Setenv("COPILOT_HOME", filepath.Join(home, ".copilot")) // not the machine's own CLI, when it has one
// the editors' own sign-in gone: what magpie keeps is all that's left
for _, dir := range []string{filepath.Join(home, ".config", "github-copilot"), filepath.Join(home, ".copilot")} {
if err := os.RemoveAll(dir); err != nil {
t.Fatal(err)
}
}
if _, ok := copilotLogin(filepath.Join(home, ".config")); ok {
t.Fatal("the editors' own sign-in is still readable")
}
if err := addCopilotLogin("hubot", "Pro+", "ghu_hubot"); err != nil {
t.Fatal(err)
}
if ls := copilotLoginList(); len(ls) != 1 || ls[0].User != "hubot" {
t.Fatalf("copilotLoginList = %+v", ls)
}
old := CopilotUserURL
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Authorization") != "token ghu_hubot" {
w.WriteHeader(http.StatusUnauthorized)
return
}
w.Write([]byte(`{"copilot_plan":"individual_pro","quota_snapshots":{"premium_interactions":{"has_quota":true,"entitlement":7000,"quota_remaining":6996}}}`))
}))
defer srv.Close()
CopilotUserURL = srv.URL
t.Cleanup(func() { CopilotUserURL = old })
var card *SubscriptionQuota
for _, q := range fetchSubscriptionUsage() {
if q.Provider == "copilot" {
card = &q
}
}
if card == nil {
t.Fatal("no Copilot card: an account signed in from magpie alone was left out")
}
if card.User != "hubot" || card.Plan != "individual_pro" || len(card.Windows) != 1 {
t.Fatalf("Copilot card = %+v", *card)
}
if w := card.Windows[0]; w.Name != "Premium requests" || w.Display != "4 / 7000" {
t.Fatalf("window = %+v", w)
}
}