internal/pluginhost/auth_provider_oauth_provider_test.go
Rama main · réplica de lectura del repositorio original.
package pluginhost
import (
"bytes"
"context"
"os"
"path/filepath"
"testing"
"github.com/MoeForge/moego/ojson"
coreauth "github.com/router-for-me/CLIProxyAPI/v8/sdk/cliproxy/auth"
"github.com/router-for-me/CLIProxyAPI/v8/sdk/pluginapi"
)
func TestHasMultipleAuthProviders(t *testing.T) {
host := newHostWithRecords(
capabilityRecord{id: "multi", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{identifier: "vendor", identifiers: []string{"vendor", "vendor-cn"}, credentialProviders: []string{"canonical-vendor"}},
}}},
capabilityRecord{id: "separate", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProviders: []pluginapi.AuthProvider{fakeAuthProvider{identifier: "portal-one"}, fakeAuthProvider{identifier: "portal-two"}},
}}},
capabilityRecord{id: "single", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{identifier: "single-vendor", identifiers: []string{"single-vendor", " SINGLE-VENDOR ", ""}},
}}},
)
for _, tc := range []struct {
provider string
want bool
}{
{provider: " Vendor ", want: true},
{provider: "vendor-cn", want: true},
{provider: "canonical-vendor", want: true},
{provider: "portal-one", want: true},
{provider: "portal-two", want: true},
{provider: "single-vendor"},
{provider: "missing"},
{},
} {
if got := host.HasMultipleAuthProviders(tc.provider); got != tc.want {
t.Errorf("HasMultipleAuthProviders(%q) = %t, want %t", tc.provider, got, tc.want)
}
}
var nilHost *Host
if nilHost.HasMultipleAuthProviders("vendor") {
t.Fatal("nil host reports multiple auth providers")
}
record := host.authProviderRecord("vendor")
setHostSnapshotForTest(host, false)
if host.HasMultipleAuthProviders("vendor") || host.recordHasMultipleAuthProviders(*record) {
t.Fatal("inactive plugin reports multiple auth providers")
}
}
func TestParseAuthsOAuthProviderAttribution(t *testing.T) {
for _, tc := range []struct {
name string
request string
identifiers []string
data pluginapi.AuthData
raw []byte
want string
}{
{name: "multi-primary", request: "vendor", identifiers: []string{"vendor", "vendor-cn"}, want: "vendor"},
{name: "multi-secondary", request: "vendor-cn", identifiers: []string{"vendor", "vendor-cn"}, want: "vendor-cn"},
{name: "single-without-hint", request: "vendor", identifiers: []string{"vendor"}},
{name: "attribute-hint", request: "vendor", identifiers: []string{"vendor"}, data: pluginapi.AuthData{Attributes: map[string]string{"oauth_provider": " Vendor "}}, want: "vendor"},
{name: "metadata-hint", request: "vendor", identifiers: []string{"vendor"}, data: pluginapi.AuthData{Metadata: map[string]any{"portal": " Vendor "}}, want: "vendor"},
{name: "storage-hint", request: "vendor", identifiers: []string{"vendor"}, data: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":" Vendor "}`)}, want: "vendor"},
{name: "raw-hint", request: "vendor", identifiers: []string{"vendor"}, raw: []byte(`{"oauth_provider":" Vendor "}`), want: "vendor"},
{
name: "data-over-storage-over-request", request: "vendor", identifiers: []string{"vendor", "vendor-cn"},
data: pluginapi.AuthData{Attributes: map[string]string{"portal": " Vendor "}, StorageJSON: []byte(`{"oauth_provider":"vendor-cn"}`)},
raw: []byte(`{"oauth_provider":"vendor-cn"}`), want: "vendor",
},
{
name: "storage-over-request", request: "vendor", identifiers: []string{"vendor", "vendor-cn"},
data: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":true,"portal":" Vendor-CN "}`)},
raw: []byte(`{"oauth_provider":"vendor"}`), want: "vendor-cn",
},
} {
t.Run(tc.name, func(t *testing.T) {
host := newHostWithRecords(capabilityRecord{id: "vendor-plugin", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{
identifier: "vendor", identifiers: tc.identifiers,
parseAuth: func(context.Context, pluginapi.AuthParseRequest) (pluginapi.AuthParseResponse, error) {
data := tc.data
data.Provider = "vendor"
return pluginapi.AuthParseResponse{Handled: true, Auth: data}, nil
},
},
}}})
auths, handled, errParse := host.ParseAuths(context.Background(), pluginapi.AuthParseRequest{Provider: tc.request, RawJSON: tc.raw})
if errParse != nil || !handled || len(auths) != 1 {
t.Fatalf("ParseAuths() handled=%t count=%d error=%v", handled, len(auths), errParse)
}
auth := auths[0]
if tc.want == "" {
if _, exists := auth.Metadata["oauth_provider"]; exists {
t.Errorf("single-entry parser added metadata: %#v", auth.Metadata)
}
if _, exists := auth.Attributes["oauth_provider"]; exists {
t.Errorf("single-entry parser added attributes: %#v", auth.Attributes)
}
} else if auth.Metadata["oauth_provider"] != tc.want || auth.Attributes["oauth_provider"] != tc.want {
t.Errorf("parsed attribution metadata=%#v attributes=%#v, want %q", auth.Metadata, auth.Attributes, tc.want)
}
})
}
}
func TestPluginAuthDataToCoreAuthStorageOAuthProvider(t *testing.T) {
for _, tc := range []struct {
name string
data pluginapi.AuthData
want string
}{
{name: "raw-oauth", data: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":" Vendor "}`)}, want: "vendor"},
{name: "raw-portal", data: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":" ","portal":" Vendor-CN "}`)}, want: "vendor-cn"},
{name: "invalid-hints-fall-back", data: pluginapi.AuthData{Metadata: map[string]any{"oauth_provider": 42}, Attributes: map[string]string{"portal": " "}, StorageJSON: []byte(`{"oauth_provider":"vendor"}`)}, want: "vendor"},
{name: "metadata-wins", data: pluginapi.AuthData{Metadata: map[string]any{"portal": "vendor-cn"}, StorageJSON: []byte(`{"oauth_provider":"vendor"}`)}, want: "vendor-cn"},
{name: "attributes-win", data: pluginapi.AuthData{Attributes: map[string]string{"oauth_provider": "vendor-cn"}, StorageJSON: []byte(`{"oauth_provider":"vendor"}`)}, want: "vendor-cn"},
{name: "invalid-json", data: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":"vendor"`)}},
{name: "non-string", data: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":42,"portal":true}`)}},
} {
t.Run(tc.name, func(t *testing.T) {
tc.data.Provider = "vendor"
auth := pluginAuthDataToCoreAuth(tc.data, "", "", "")
hint := authDataPortalHint(&pluginapi.AuthData{Metadata: auth.Metadata, Attributes: auth.Attributes})
if hint != tc.want {
t.Fatalf("converted hint = %q, want %q", hint, tc.want)
}
if tc.want != "" {
if auth.Metadata["oauth_provider"] != tc.want || auth.Attributes["oauth_provider"] != tc.want {
t.Fatalf("hint not copied to both maps: metadata=%#v attributes=%#v", auth.Metadata, auth.Attributes)
}
}
})
}
}
func TestPreserveAuthOAuthProviderFromAttributes(t *testing.T) {
for _, key := range []string{"oauth_provider", "portal"} {
for _, metadata := range []map[string]any{nil, {"access_token": "old"}, {"oauth_provider": " ", "portal": true}} {
t.Run(key, func(t *testing.T) {
auth := &coreauth.Auth{Provider: "vendor", Metadata: metadata, Attributes: map[string]string{key: " Vendor "}}
data := pluginapi.AuthData{Provider: "vendor", Metadata: map[string]any{"access_token": "new"}}
preserveAuthOAuthProviderMetadata(&data, auth, authDataPortalHint(&data))
if data.Metadata["oauth_provider"] != "vendor" || data.Attributes["oauth_provider"] != "vendor" {
t.Fatalf("preserved metadata=%#v attributes=%#v, want vendor", data.Metadata, data.Attributes)
}
if data.Metadata["access_token"] != "new" {
t.Fatal("preservation replaced refreshed token")
}
})
}
}
}
func TestPollLoginRecordsEffectiveOAuthProvider(t *testing.T) {
for _, responseKind := range []string{"auth", "auths"} {
t.Run(responseKind, func(t *testing.T) {
host := newHostWithRecords(capabilityRecord{id: "vendor-plugin", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{
identifier: "vendor", identifiers: []string{"vendor", "vendor-cn"},
pollLogin: func(context.Context, pluginapi.AuthLoginPollRequest) (pluginapi.AuthLoginPollResponse, error) {
data := pluginapi.AuthData{Provider: "vendor", StorageJSON: []byte(`{"oauth_provider":"vendor-cn"}`)}
resp := pluginapi.AuthLoginPollResponse{Status: pluginapi.AuthLoginStatusSuccess}
if responseKind == "auths" {
resp.Auths = []pluginapi.AuthData{data}
} else {
resp.Auth = data
}
return resp, nil
},
},
}}})
_, handled, errPoll := host.PollLogin(context.Background(), "vendor", "state")
if errPoll != nil || !handled {
t.Fatalf("PollLogin() handled=%t error=%v", handled, errPoll)
}
if got, ok := host.recordedCredentialProviderPortal("vendor-plugin", "vendor"); !ok || got != "vendor-cn" {
t.Fatalf("recorded portal = %q, present=%t, want vendor-cn", got, ok)
}
})
}
}
func TestExecutorAdapterRefreshOAuthProvider(t *testing.T) {
for _, tc := range []struct {
name string
metadata map[string]any
want string
}{
{name: "existing-effective-hint", metadata: map[string]any{"access_token": "new"}, want: "vendor"},
{name: "plugin-explicit-hint", metadata: map[string]any{"oauth_provider": "vendor-cn", "access_token": "new"}, want: "vendor-cn"},
} {
t.Run(tc.name, func(t *testing.T) {
record := normalizeTestCapabilityRecord(capabilityRecord{id: "vendor-plugin", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{
identifier: "vendor", identifiers: []string{"vendor", "vendor-cn"},
refreshAuth: func(context.Context, pluginapi.AuthRefreshRequest) (pluginapi.AuthRefreshResponse, error) {
return pluginapi.AuthRefreshResponse{Auth: pluginapi.AuthData{Metadata: tc.metadata}}, nil
},
},
}}})
host := newHostWithRecords(record)
adapter := newExecutorAdapterForRecordForTest(host, record, &fakeExecutor{}, nil, nil)
refreshed, errRefresh := adapter.Refresh(context.Background(), &coreauth.Auth{
Provider: "vendor", Attributes: map[string]string{"oauth_provider": "vendor"}, Metadata: map[string]any{"portal": "vendor-cn"},
})
if errRefresh != nil || refreshed == nil {
t.Fatalf("Refresh() auth=%#v error=%v", refreshed, errRefresh)
}
if refreshed.Metadata["oauth_provider"] != tc.want || refreshed.Attributes["oauth_provider"] != tc.want {
t.Fatalf("refreshed attribution metadata=%#v attributes=%#v, want %q", refreshed.Metadata, refreshed.Attributes, tc.want)
}
storage := refreshed.Storage.(interface{ RawJSON() []byte })
if got := ojson.GetBytes(storage.RawJSON(), "oauth_provider").String(); got != tc.want {
t.Fatalf("refreshed storage hint = %q, want %q", got, tc.want)
}
})
}
}
func TestRefreshAuthOAuthProviderSurvivesColdStart(t *testing.T) {
for _, tc := range []struct {
name string
attributes map[string]string
metadata map[string]any
rawJSON []byte
response pluginapi.AuthData
want string
}{
{
name: "attribute-oauth-over-metadata-portal",
attributes: map[string]string{"oauth_provider": "vendor"},
metadata: map[string]any{"portal": "vendor-cn"},
want: "vendor",
},
{
name: "metadata-oauth-over-attribute-portal",
attributes: map[string]string{"portal": "vendor-cn"},
metadata: map[string]any{"oauth_provider": "vendor"},
want: "vendor",
},
{
name: "attribute-portal-over-metadata-portal",
attributes: map[string]string{"portal": "vendor"},
metadata: map[string]any{"portal": "vendor-cn"},
want: "vendor",
},
{
name: "metadata-portal-over-storage-oauth",
metadata: map[string]any{"portal": "vendor"},
rawJSON: []byte(`{"oauth_provider":"vendor-cn"}`),
want: "vendor",
},
{
name: "storage-only",
rawJSON: []byte(`{"oauth_provider":"vendor-cn"}`),
want: "vendor-cn",
},
{
name: "plugin-attribute-oauth-over-existing",
attributes: map[string]string{"oauth_provider": "vendor"},
metadata: map[string]any{"oauth_provider": "vendor"},
response: pluginapi.AuthData{Attributes: map[string]string{"oauth_provider": "vendor-cn"}},
want: "vendor-cn",
},
{
name: "plugin-metadata-oauth-over-existing",
attributes: map[string]string{"oauth_provider": "vendor"},
response: pluginapi.AuthData{Metadata: map[string]any{"oauth_provider": "vendor-cn"}},
want: "vendor-cn",
},
{
name: "plugin-attribute-portal-over-existing",
attributes: map[string]string{"oauth_provider": "vendor"},
response: pluginapi.AuthData{Attributes: map[string]string{"portal": "vendor-cn"}},
want: "vendor-cn",
},
{
name: "plugin-metadata-portal-over-existing",
attributes: map[string]string{"oauth_provider": "vendor"},
response: pluginapi.AuthData{Metadata: map[string]any{"portal": "vendor-cn"}},
want: "vendor-cn",
},
{
name: "plugin-storage-oauth-over-existing",
attributes: map[string]string{"oauth_provider": "vendor"},
metadata: map[string]any{"portal": "vendor"},
response: pluginapi.AuthData{StorageJSON: []byte(`{"oauth_provider":"vendor-cn"}`)},
want: "vendor-cn",
},
} {
t.Run(tc.name, func(t *testing.T) {
newHost := func() *Host {
return newHostWithRecords(capabilityRecord{id: "vendor-plugin", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{
identifier: "vendor", identifiers: []string{"vendor", "vendor-cn"},
refreshAuth: func(context.Context, pluginapi.AuthRefreshRequest) (pluginapi.AuthRefreshResponse, error) {
data := tc.response
data.Metadata = cloneAnyMap(data.Metadata)
if data.Metadata == nil {
data.Metadata = make(map[string]any)
}
data.Metadata["access_token"] = "new"
return pluginapi.AuthRefreshResponse{Auth: data}, nil
},
parseAuth: func(_ context.Context, req pluginapi.AuthParseRequest) (pluginapi.AuthParseResponse, error) {
return pluginapi.AuthParseResponse{Handled: true, Auth: pluginapi.AuthData{Provider: "vendor", StorageJSON: req.RawJSON}}, nil
},
},
}}})
}
auth := &coreauth.Auth{Provider: "vendor", Attributes: tc.attributes, Metadata: tc.metadata}
if len(tc.rawJSON) > 0 {
auth.Storage = &pluginTokenStorage{provider: "vendor", rawJSON: tc.rawJSON}
}
refreshed, handled, errRefresh := newHost().RefreshAuth(context.Background(), auth)
if errRefresh != nil || !handled || refreshed == nil {
t.Fatalf("RefreshAuth() handled=%t auth=%#v error=%v", handled, refreshed, errRefresh)
}
if refreshed.Metadata["oauth_provider"] != tc.want || refreshed.Attributes["oauth_provider"] != tc.want {
t.Fatalf("refreshed attribution metadata=%#v attributes=%#v, want %q", refreshed.Metadata, refreshed.Attributes, tc.want)
}
if refreshed.Metadata["access_token"] != "new" {
t.Fatal("refresh lost the updated token")
}
raw := refreshed.Storage.(interface{ RawJSON() []byte }).RawJSON()
if got := ojson.GetBytes(raw, "oauth_provider").String(); got != tc.want {
t.Fatalf("refreshed storage hint = %q, want %q", got, tc.want)
}
path := filepath.Join(t.TempDir(), "account.json")
if errSaveTokenToFile := refreshed.Storage.SaveTokenToFile(path); errSaveTokenToFile != nil {
t.Fatalf("save refreshed auth: %v", errSaveTokenToFile)
}
saved, errRead := os.ReadFile(path)
if errRead != nil {
t.Fatalf("read saved auth: %v", errRead)
}
if !bytes.Equal(saved, raw) {
t.Fatalf("saved payload = %s, want RawJSON() %s", saved, raw)
}
reloaded, handled, errParse := newHost().ParseAuth(context.Background(), pluginapi.AuthParseRequest{
Provider: "vendor", Path: path, FileName: "account.json", RawJSON: saved,
})
if errParse != nil || !handled || reloaded == nil {
t.Fatalf("cold-start ParseAuth() handled=%t auth=%#v error=%v", handled, reloaded, errParse)
}
if reloaded.Metadata["oauth_provider"] != tc.want || reloaded.Attributes["oauth_provider"] != tc.want {
t.Fatalf("cold-start attribution metadata=%#v attributes=%#v, want %q", reloaded.Metadata, reloaded.Attributes, tc.want)
}
})
}
}
func TestRefreshAuthPreservesAttributeOnlyOAuthProvider(t *testing.T) {
host := newHostWithRecords(capabilityRecord{id: "vendor-plugin", plugin: pluginapi.Plugin{Capabilities: pluginapi.Capabilities{
AuthProvider: fakeAuthProvider{
identifier: "vendor", identifiers: []string{"vendor", "vendor-cn"},
refreshAuth: func(context.Context, pluginapi.AuthRefreshRequest) (pluginapi.AuthRefreshResponse, error) {
return pluginapi.AuthRefreshResponse{Auth: pluginapi.AuthData{Provider: "vendor", Metadata: map[string]any{"access_token": "new"}}}, nil
},
},
}}})
auth, handled, errRefresh := host.RefreshAuth(context.Background(), &coreauth.Auth{
Provider: "vendor", Attributes: map[string]string{"portal": " Vendor "},
})
if errRefresh != nil || !handled || auth == nil {
t.Fatalf("RefreshAuth() handled=%t auth=%#v error=%v", handled, auth, errRefresh)
}
if auth.Metadata["oauth_provider"] != "vendor" || auth.Attributes["oauth_provider"] != "vendor" {
t.Fatalf("refreshed attribution metadata=%#v attributes=%#v", auth.Metadata, auth.Attributes)
}
storage := auth.Storage.(interface{ RawJSON() []byte })
if got := ojson.GetBytes(storage.RawJSON(), "oauth_provider").String(); got != "vendor" {
t.Fatalf("refreshed storage hint = %q, want vendor", got)
}
}