152 lines
5.4 KiB
Go
152 lines
5.4 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"sub2api-cn-relay-manager/internal/store/sqlite"
|
|
)
|
|
|
|
func TestAPIListProviderAccountsReturnsRows(t *testing.T) {
|
|
handler := NewAPIHandler("secret-token", ActionSet{
|
|
ListProviderAccounts: func(_ context.Context, req ListProviderAccountsRequest) ([]ProviderAccountInfo, error) {
|
|
if req.ProviderID != "deepseek-official" {
|
|
t.Fatalf("ProviderID = %q, want deepseek-official", req.ProviderID)
|
|
}
|
|
if req.AccountStatus != "disabled" {
|
|
t.Fatalf("AccountStatus = %q, want disabled", req.AccountStatus)
|
|
}
|
|
return []ProviderAccountInfo{{
|
|
ID: 7,
|
|
HostID: "remote43",
|
|
ProviderID: "deepseek-official",
|
|
ProviderName: "DeepSeek Official",
|
|
HostAccountID: "9",
|
|
AccountName: "deepseek-01",
|
|
AccountStatus: "disabled",
|
|
DisabledReason: "manual_disable",
|
|
}}, nil
|
|
},
|
|
})
|
|
|
|
request := httptestRequest(t, "GET", "/api/provider-accounts?provider_id=deepseek-official&account_status=disabled", nil, "secret-token")
|
|
response := httptestRecorder(handler, request)
|
|
assertStatusCode(t, response, 200)
|
|
var payload map[string][]ProviderAccountInfo
|
|
if err := json.Unmarshal(response.Body().Bytes(), &payload); err != nil {
|
|
t.Fatalf("json.Unmarshal() error = %v", err)
|
|
}
|
|
accounts := payload["provider_accounts"]
|
|
if len(accounts) != 1 || accounts[0].ID != 7 || accounts[0].AccountStatus != "disabled" {
|
|
t.Fatalf("provider_accounts = %+v, want one disabled row id=7", accounts)
|
|
}
|
|
}
|
|
|
|
func TestAPIDisableProviderAccountUsesPathID(t *testing.T) {
|
|
handler := NewAPIHandler("secret-token", ActionSet{
|
|
DisableProviderAccount: func(_ context.Context, req UpdateProviderAccountStatusRequest) (ProviderAccountInfo, error) {
|
|
if req.AccountID != 42 {
|
|
t.Fatalf("AccountID = %d, want 42", req.AccountID)
|
|
}
|
|
if req.AccountStatus != "disabled" {
|
|
t.Fatalf("AccountStatus = %q, want disabled", req.AccountStatus)
|
|
}
|
|
if req.DisabledReason != "manual_disable" {
|
|
t.Fatalf("DisabledReason = %q, want manual_disable", req.DisabledReason)
|
|
}
|
|
return ProviderAccountInfo{ID: req.AccountID, AccountStatus: req.AccountStatus, DisabledReason: req.DisabledReason}, nil
|
|
},
|
|
})
|
|
|
|
request := httptestRequest(t, "POST", "/api/provider-accounts/42/disable", map[string]any{"reason": "manual_disable"}, "secret-token")
|
|
response := httptestRecorder(handler, request)
|
|
assertStatusCode(t, response, 200)
|
|
assertJSONContains(t, response.Body().Bytes(), "provider_account.id", float64(42))
|
|
assertJSONContains(t, response.Body().Bytes(), "provider_account.account_status", "disabled")
|
|
}
|
|
|
|
func TestNewActionSetProviderAccountListAndStatusFlow(t *testing.T) {
|
|
dbPath := filepath.Join(t.TempDir(), "provider-accounts.db")
|
|
dsn := "file:" + filepath.ToSlash(dbPath) + "?_busy_timeout=5000"
|
|
actions := NewActionSet(dsn)
|
|
ctx := context.Background()
|
|
|
|
store, err := sqlite.Open(ctx, dsn)
|
|
if err != nil {
|
|
t.Fatalf("sqlite.Open() error = %v", err)
|
|
}
|
|
defer store.Close()
|
|
|
|
hostID, err := store.Hosts().Create(ctx, sqlite.Host{
|
|
HostID: "remote43",
|
|
BaseURL: "https://host.example.com",
|
|
HostVersion: "0.1.129",
|
|
CapabilityProbeJSON: `{"accounts":true}`,
|
|
AuthType: "apikey",
|
|
AuthToken: "host-key",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Hosts().Create() error = %v", err)
|
|
}
|
|
hostRow, err := store.Hosts().GetByID(ctx, hostID)
|
|
if err != nil {
|
|
t.Fatalf("Hosts().GetByID() error = %v", err)
|
|
}
|
|
packID, err := store.Packs().Create(ctx, sqlite.Pack{PackID: "openai-cn-pack", Version: "1.0.0", Checksum: "chk"})
|
|
if err != nil {
|
|
t.Fatalf("Packs().Create() error = %v", err)
|
|
}
|
|
providerRowID, err := store.Providers().Create(ctx, sqlite.Provider{
|
|
PackID: packID,
|
|
ProviderID: "deepseek-official",
|
|
DisplayName: "DeepSeek Official",
|
|
BaseURL: "https://api.deepseek.com",
|
|
Platform: "openai",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Providers().Create() error = %v", err)
|
|
}
|
|
providerAccountID, err := store.ProviderAccounts().Create(ctx, sqlite.ProviderAccount{
|
|
HostID: hostRow.ID,
|
|
ProviderID: providerRowID,
|
|
HostAccountID: "9",
|
|
KeyFingerprint: "sha256:abc",
|
|
AccountName: "deepseek-01",
|
|
AccountStatus: sqlite.ProviderAccountStatusActive,
|
|
LastProbeStatus: "passed",
|
|
LastProbeAt: "2026-05-29T00:00:00Z",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("ProviderAccounts().Create() error = %v", err)
|
|
}
|
|
|
|
listed, err := actions.ListProviderAccounts(ctx, ListProviderAccountsRequest{HostID: "remote43", ProviderID: "deepseek-official"})
|
|
if err != nil {
|
|
t.Fatalf("ListProviderAccounts() error = %v", err)
|
|
}
|
|
if len(listed) != 1 || listed[0].ID != providerAccountID {
|
|
t.Fatalf("ListProviderAccounts() = %+v, want one row for id %d", listed, providerAccountID)
|
|
}
|
|
|
|
disabled, err := actions.DisableProviderAccount(ctx, UpdateProviderAccountStatusRequest{
|
|
AccountID: providerAccountID,
|
|
DisabledReason: "manual_disable",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("DisableProviderAccount() error = %v", err)
|
|
}
|
|
if disabled.AccountStatus != sqlite.ProviderAccountStatusDisabled || disabled.DisabledReason != "manual_disable" {
|
|
t.Fatalf("DisableProviderAccount() = %+v", disabled)
|
|
}
|
|
|
|
enabled, err := actions.EnableProviderAccount(ctx, UpdateProviderAccountStatusRequest{AccountID: providerAccountID})
|
|
if err != nil {
|
|
t.Fatalf("EnableProviderAccount() error = %v", err)
|
|
}
|
|
if enabled.AccountStatus != sqlite.ProviderAccountStatusActive {
|
|
t.Fatalf("EnableProviderAccount() = %+v, want active", enabled)
|
|
}
|
|
}
|