fix(ci): align auth and payment verification tests
This commit is contained in:
@@ -2,7 +2,6 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -20,8 +19,6 @@ import (
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/logger"
|
||||
"github.com/Wei-Shaw/sub2api/internal/pkg/pagination"
|
||||
"github.com/Wei-Shaw/sub2api/internal/util/httputil"
|
||||
|
||||
entsql "entgo.io/ent/dialect/sql"
|
||||
)
|
||||
|
||||
// AdminService interface defines admin management operations
|
||||
@@ -999,17 +996,6 @@ func (s *adminServiceImpl) BindUserAuthIdentity(ctx context.Context, userID int6
|
||||
return buildAdminBoundAuthIdentity(identity, channel), nil
|
||||
}
|
||||
|
||||
func (s *adminServiceImpl) adminSQLDB() (*sql.DB, error) {
|
||||
if s == nil || s.entClient == nil {
|
||||
return nil, infraerrors.ServiceUnavailable("ADMIN_SQL_NOT_READY", "admin sql access is not ready")
|
||||
}
|
||||
driver, ok := s.entClient.Driver().(*entsql.Driver)
|
||||
if !ok || driver.DB() == nil {
|
||||
return nil, infraerrors.ServiceUnavailable("ADMIN_SQL_NOT_READY", "admin sql access is not ready")
|
||||
}
|
||||
return driver.DB(), nil
|
||||
}
|
||||
|
||||
func normalizeAdminBindChannelInput(input *AdminBindAuthIdentityChannelInput) *AdminBindAuthIdentityChannelInput {
|
||||
if input == nil {
|
||||
return nil
|
||||
|
||||
@@ -11,8 +11,8 @@ import (
|
||||
|
||||
dbent "github.com/Wei-Shaw/sub2api/ent"
|
||||
"github.com/Wei-Shaw/sub2api/ent/identityadoptiondecision"
|
||||
dbpredicate "github.com/Wei-Shaw/sub2api/ent/predicate"
|
||||
"github.com/Wei-Shaw/sub2api/ent/pendingauthsession"
|
||||
dbpredicate "github.com/Wei-Shaw/sub2api/ent/predicate"
|
||||
infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
|
||||
|
||||
entsql "entgo.io/ent/dialect/sql"
|
||||
|
||||
@@ -679,13 +679,6 @@ func (s *AuthService) LoginOrRegisterOAuthWithTokenPair(ctx context.Context, ema
|
||||
return tokenPair, user, nil
|
||||
}
|
||||
|
||||
func (s *AuthService) assignDefaultSubscriptions(ctx context.Context, userID int64) {
|
||||
if s.settingService == nil {
|
||||
return
|
||||
}
|
||||
s.assignSubscriptions(ctx, userID, s.settingService.GetDefaultSubscriptions(ctx), "auto assigned by default user subscriptions setting")
|
||||
}
|
||||
|
||||
func (s *AuthService) assignSubscriptions(ctx context.Context, userID int64, items []DefaultSubscriptionSetting, notes string) {
|
||||
if s.settingService == nil || s.defaultSubAssigner == nil || userID <= 0 {
|
||||
return
|
||||
@@ -863,7 +856,7 @@ func (s *AuthService) hasProviderGrantRecord(
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer rows.Close()
|
||||
defer func() { _ = rows.Close() }()
|
||||
return rows.Next(), rows.Err()
|
||||
}
|
||||
|
||||
@@ -917,7 +910,7 @@ func (s *AuthService) ensureEmailAuthIdentity(ctx context.Context, user *User, s
|
||||
DoNothing().
|
||||
Exec(ctx); err != nil {
|
||||
if isSQLNoRowsError(err) {
|
||||
err = nil
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
|
||||
@@ -53,40 +53,6 @@ func pcApplyEnabledVisibleMethodInstances(typeInstances map[string][]*dbent.Paym
|
||||
return filtered
|
||||
}
|
||||
|
||||
func pcApplyVisibleMethodRouting(typeInstances map[string][]*dbent.PaymentProviderInstance, vals map[string]string, available map[string]bool) map[string][]*dbent.PaymentProviderInstance {
|
||||
if len(typeInstances) == 0 {
|
||||
return typeInstances
|
||||
}
|
||||
|
||||
filtered := make(map[string][]*dbent.PaymentProviderInstance, len(typeInstances))
|
||||
for paymentType, instances := range typeInstances {
|
||||
visibleMethod := NormalizeVisibleMethod(paymentType)
|
||||
switch visibleMethod {
|
||||
case payment.TypeAlipay, payment.TypeWxpay:
|
||||
if !visibleMethodShouldBeExposed(visibleMethod, vals, available) {
|
||||
continue
|
||||
}
|
||||
targetProviderKey, ok := VisibleMethodProviderKeyForSource(visibleMethod, vals[visibleMethodSourceSettingKey(visibleMethod)])
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
matching := make([]*dbent.PaymentProviderInstance, 0, len(instances))
|
||||
for _, inst := range instances {
|
||||
if inst.ProviderKey == targetProviderKey {
|
||||
matching = append(matching, inst)
|
||||
}
|
||||
}
|
||||
if len(matching) == 0 {
|
||||
continue
|
||||
}
|
||||
filtered[paymentType] = matching
|
||||
default:
|
||||
filtered[paymentType] = instances
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// GetMethodLimits returns per-payment-type limits from enabled provider instances.
|
||||
func (s *PaymentConfigService) GetMethodLimits(ctx context.Context, types []string) ([]MethodLimits, error) {
|
||||
instances, err := s.entClient.PaymentProviderInstance.Query().
|
||||
|
||||
@@ -210,9 +210,15 @@ func TestCreateProviderInstanceRejectsConflictingVisibleMethodEnablement(t *test
|
||||
}
|
||||
|
||||
_, err := svc.CreateProviderInstance(ctx, CreateProviderInstanceRequest{
|
||||
ProviderKey: "easypay",
|
||||
Name: "EasyPay Alipay",
|
||||
Config: map[string]string{"pid": "1001"},
|
||||
ProviderKey: "easypay",
|
||||
Name: "EasyPay Alipay",
|
||||
Config: map[string]string{
|
||||
"pid": "1001",
|
||||
"pkey": "pkey-1001",
|
||||
"apiBase": "https://pay.example.com",
|
||||
"notifyUrl": "https://merchant.example.com/notify",
|
||||
"returnUrl": "https://merchant.example.com/return",
|
||||
},
|
||||
SupportedTypes: []string{"alipay"},
|
||||
Enabled: true,
|
||||
})
|
||||
@@ -240,9 +246,15 @@ func TestUpdateProviderInstanceRejectsEnablingConflictingVisibleMethodProvider(t
|
||||
}
|
||||
|
||||
existing, err := svc.CreateProviderInstance(ctx, CreateProviderInstanceRequest{
|
||||
ProviderKey: "easypay",
|
||||
Name: "EasyPay WeChat",
|
||||
Config: map[string]string{"pid": "2001"},
|
||||
ProviderKey: "easypay",
|
||||
Name: "EasyPay WeChat",
|
||||
Config: map[string]string{
|
||||
"pid": "2001",
|
||||
"pkey": "pkey-2001",
|
||||
"apiBase": "https://pay.example.com",
|
||||
"notifyUrl": "https://merchant.example.com/notify",
|
||||
"returnUrl": "https://merchant.example.com/return",
|
||||
},
|
||||
SupportedTypes: []string{"wxpay"},
|
||||
Enabled: true,
|
||||
})
|
||||
@@ -276,9 +288,15 @@ func TestUpdateProviderInstancePersistsEnabledAndSupportedTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
instance, err := svc.CreateProviderInstance(ctx, CreateProviderInstanceRequest{
|
||||
ProviderKey: "easypay",
|
||||
Name: "EasyPay",
|
||||
Config: map[string]string{"pid": "3001"},
|
||||
ProviderKey: "easypay",
|
||||
Name: "EasyPay",
|
||||
Config: map[string]string{
|
||||
"pid": "3001",
|
||||
"pkey": "pkey-3001",
|
||||
"apiBase": "https://pay.example.com",
|
||||
"notifyUrl": "https://merchant.example.com/notify",
|
||||
"returnUrl": "https://merchant.example.com/return",
|
||||
},
|
||||
SupportedTypes: []string{"alipay"},
|
||||
Enabled: false,
|
||||
})
|
||||
|
||||
@@ -23,8 +23,6 @@ const (
|
||||
PaymentSourceHostedRedirect = "hosted_redirect"
|
||||
PaymentSourceWechatInAppResume = "wechat_in_app_resume"
|
||||
|
||||
paymentResumeFallbackSigningKey = "sub2api-payment-resume"
|
||||
|
||||
SettingPaymentVisibleMethodAlipaySource = "payment_visible_method_alipay_source"
|
||||
SettingPaymentVisibleMethodWxpaySource = "payment_visible_method_wxpay_source"
|
||||
SettingPaymentVisibleMethodAlipayEnabled = "payment_visible_method_alipay_enabled"
|
||||
|
||||
@@ -413,7 +413,7 @@ func mustCreateFallbackSignedToken(t *testing.T, claims any) string {
|
||||
t.Fatalf("marshal claims: %v", err)
|
||||
}
|
||||
encodedPayload := base64.RawURLEncoding.EncodeToString(payload)
|
||||
mac := hmac.New(sha256.New, []byte(paymentResumeFallbackSigningKey))
|
||||
mac := hmac.New(sha256.New, []byte("sub2api-payment-resume"))
|
||||
_, _ = mac.Write([]byte(encodedPayload))
|
||||
signature := base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
|
||||
return encodedPayload + "." + signature
|
||||
|
||||
@@ -4,7 +4,11 @@ package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -52,6 +56,28 @@ func newWebhookProviderTestLoadBalancer(client *dbent.Client) payment.LoadBalanc
|
||||
return payment.NewDefaultLoadBalancer(client, []byte(webhookProviderTestEncryptionKey))
|
||||
}
|
||||
|
||||
func encryptValidWebhookWxpayConfig(t *testing.T, suffix string) string {
|
||||
t.Helper()
|
||||
|
||||
key, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
require.NoError(t, err)
|
||||
|
||||
privDER, err := x509.MarshalPKCS8PrivateKey(key)
|
||||
require.NoError(t, err)
|
||||
pubDER, err := x509.MarshalPKIXPublicKey(&key.PublicKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
return encryptWebhookProviderConfig(t, map[string]string{
|
||||
"appId": "wx-app-" + suffix,
|
||||
"mchId": "mch-" + suffix,
|
||||
"privateKey": string(pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privDER})),
|
||||
"apiV3Key": webhookProviderTestEncryptionKey,
|
||||
"publicKey": string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: pubDER})),
|
||||
"publicKeyId": "public-key-id-" + suffix,
|
||||
"certSerial": "cert-serial-" + suffix,
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetOrderProviderInstanceResolvesUniqueLegacyProviderKey(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := newPaymentConfigServiceTestClient(t)
|
||||
@@ -275,24 +301,8 @@ func TestGetOrderProviderInstanceRejectsMissingSnapshotInstanceWithoutLegacyFall
|
||||
func TestGetWebhookProviderRejectsAmbiguousRegistryFallback(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
client := newPaymentConfigServiceTestClient(t)
|
||||
wxpayConfigA := encryptWebhookProviderConfig(t, map[string]string{
|
||||
"appId": "wx-app-a",
|
||||
"mchId": "mch-a",
|
||||
"privateKey": "private-key-a",
|
||||
"apiV3Key": webhookProviderTestEncryptionKey,
|
||||
"publicKey": "public-key-a",
|
||||
"publicKeyId": "public-key-id-a",
|
||||
"certSerial": "cert-serial-a",
|
||||
})
|
||||
wxpayConfigB := encryptWebhookProviderConfig(t, map[string]string{
|
||||
"appId": "wx-app-b",
|
||||
"mchId": "mch-b",
|
||||
"privateKey": "private-key-b",
|
||||
"apiV3Key": webhookProviderTestEncryptionKey,
|
||||
"publicKey": "public-key-b",
|
||||
"publicKeyId": "public-key-id-b",
|
||||
"certSerial": "cert-serial-b",
|
||||
})
|
||||
wxpayConfigA := encryptValidWebhookWxpayConfig(t, "a")
|
||||
wxpayConfigB := encryptValidWebhookWxpayConfig(t, "b")
|
||||
_, err := client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeWxpay).
|
||||
SetName("wxpay-a").
|
||||
@@ -442,24 +452,8 @@ func TestGetWebhookProviderUsesProviderSnapshotBeforeWxpayFallback(t *testing.T)
|
||||
Save(ctx)
|
||||
require.NoError(t, err)
|
||||
|
||||
wxpayConfigA := encryptWebhookProviderConfig(t, map[string]string{
|
||||
"appId": "wx-app-snapshot-a",
|
||||
"mchId": "mch-snapshot-a",
|
||||
"privateKey": "private-key-snapshot-a",
|
||||
"apiV3Key": webhookProviderTestEncryptionKey,
|
||||
"publicKey": "public-key-snapshot-a",
|
||||
"publicKeyId": "public-key-id-snapshot-a",
|
||||
"certSerial": "cert-serial-snapshot-a",
|
||||
})
|
||||
wxpayConfigB := encryptWebhookProviderConfig(t, map[string]string{
|
||||
"appId": "wx-app-snapshot-b",
|
||||
"mchId": "mch-snapshot-b",
|
||||
"privateKey": "private-key-snapshot-b",
|
||||
"apiV3Key": webhookProviderTestEncryptionKey,
|
||||
"publicKey": "public-key-snapshot-b",
|
||||
"publicKeyId": "public-key-id-snapshot-b",
|
||||
"certSerial": "cert-serial-snapshot-b",
|
||||
})
|
||||
wxpayConfigA := encryptValidWebhookWxpayConfig(t, "snapshot-a")
|
||||
wxpayConfigB := encryptValidWebhookWxpayConfig(t, "snapshot-b")
|
||||
instA, err := client.PaymentProviderInstance.Create().
|
||||
SetProviderKey(payment.TypeWxpay).
|
||||
SetName("wxpay-snapshot-a").
|
||||
|
||||
@@ -183,10 +183,6 @@ type UpsertUserAvatarInput struct {
|
||||
SHA256 string
|
||||
}
|
||||
|
||||
type userAuthIdentityReader interface {
|
||||
ListUserAuthIdentities(ctx context.Context, userID int64) ([]UserAuthIdentityRecord, error)
|
||||
}
|
||||
|
||||
type userProfileIdentityTxRunner interface {
|
||||
WithUserProfileIdentityTx(ctx context.Context, fn func(txCtx context.Context) error) error
|
||||
}
|
||||
@@ -812,17 +808,6 @@ func maskOpaqueIdentity(value string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func cloneAnyMap(values map[string]any) map[string]any {
|
||||
if len(values) == 0 {
|
||||
return map[string]any{}
|
||||
}
|
||||
cloned := make(map[string]any, len(values))
|
||||
for key, value := range values {
|
||||
cloned[key] = value
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
// Security: Increments TokenVersion to invalidate all existing JWT tokens
|
||||
func (s *UserService) ChangePassword(ctx context.Context, userID int64, req ChangePasswordRequest) error {
|
||||
|
||||
Reference in New Issue
Block a user