provider: a plugin account signed in again is read afresh, not answered by the reading kept from the refused sign-in (TestPluginLoginTakesTheLapseOff red after its first run in a process)

clearPluginLapse took the lapsed mark off a plugin account signed in
again, but its usage reading from before stayed in loginUsageCache for a
minute. The next LoginUsage was answered from it: the old sign-in's
"expired" shown for the renewed account, and no plugin asked, so nothing
marked or cleared. TestPluginLoginTakesTheLapseOff hit it from its second
run in one process (-count=3: pass, fail, fail): the run before's last
reading, still under a minute old, answered the "gone" account's read
under the new HOME, and the vendor's refusal never marked it.

A sign-in now drops the account's kept reading (StaleAllowance), on both
paths that finish one (pluginDone and PluginSignedIn).

TestPluginLoginReadsUsageAfresh fails without the change at -count=1
("signed in again, but the usage is the old sign-in's: ... Error:the
FakeCo sign-in has expired"). With it, -run TestPluginLogin passes at
-count=5 and with -race -count=5; go test -tags nogui . ./internal/provider
./internal/plugin pass under a temp HOME with pinned caches; go vet and
GOOS=linux/windows go vet -tags nogui pass on the two packages.
This commit is contained in:
yetone committed 2026-10-08 13:52:50 +08:00
1 parent 1b8c33b5e2
commit 8c0ffcc640
2 files changed
+43 -1

No files matched your search

+7 -1
View File
@@ -380,11 +380,17 @@ func lapsedText(pp plugin.Provider, user string) string {
// clearPluginLapse takes the mark off an account signed in again, and
// brings back the agent's own account removed in magpie, as signing in to
// the built-in's did (#320).
// the built-in's did (#320). The account's kept usage reading goes too: it
// was read under the old sign-in, and for a minute it would answer the
// next reading with the old sign-in's "expired" without asking the plugin,
// and so without marking or clearing anything.
func clearPluginLapse(saved plugin.Saved) {
for _, pp := range plugin.Cached() {
if pp.ID == saved.Provider {
notePluginLapse(pp, saved.Account, http.StatusOK)
if user := pluginLabels(pp)[saved.Account]; user != "" {
StaleAllowance(pluginAgent(pp), user)
}
}
}
loginsMu.Lock()
+36
View File
@@ -2,10 +2,14 @@ package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"os"
"os/exec"
"path/filepath"
"runtime"
"sync/atomic"
"testing"
"time"
@@ -64,6 +68,38 @@ func TestPluginLoginTakesTheLapseOff(t *testing.T) {
}
}
// An account signed in again is read afresh: the reading kept from the
// sign-in the vendor refused doesn't answer for the new one, saying its
// sign-in has expired for up to a minute after it was renewed.
func TestPluginLoginReadsUsageAfresh(t *testing.T) {
var status atomic.Int32
status.Store(http.StatusUnauthorized)
vendor := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(int(status.Load()))
fmt.Fprint(w, "Fake Pro")
}))
defer vendor.Close()
t.Setenv("FAKE_USAGE", vendor.URL)
ctx := fakeCoHome(t)
noBrowser(t)
login := func() {
t.Helper()
piped(t, "2", "2", "team", "good")
if out, err := said(t, func() error { return pluginCmd([]string{"plugin", "login", "fakeco"}) }); err != nil {
t.Fatalf("%v: %s", err, out)
}
}
login()
if q := provider.LoginUsage(ctx, "fakeco")["team@fake"]; q.Error == "" {
t.Fatalf("the vendor's 401 read clean: %+v", q)
}
status.Store(http.StatusOK)
login()
if q := provider.LoginUsage(ctx, "fakeco")["team@fake"]; q.Error != "" || q.Plan != "Fake Pro" {
t.Fatalf("signed in again, but the usage is the old sign-in's: %+v", q)
}
}
// fakeCoHome is a HOME of the test's own with the plugin host's test
// plugin (FakeCo) added, as TestAccountsOfAPlugin sets one up.
func fakeCoHome(t *testing.T) context.Context {