122 lines
4.4 KiB
Go
122 lines
4.4 KiB
Go
package creator
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"git.ipao.vip/rogee/creator-hub/internal/account"
|
|
)
|
|
|
|
func TestCreatorFirstLoginBindsUIDAndNeverReplacesIt(t *testing.T) {
|
|
store, accounts, ctx := openCreatorIntegrationStore(t)
|
|
create := func(id string) {
|
|
t.Helper()
|
|
err := accounts.CreateAccount(ctx, account.Account{ID: id, Name: id, Platform: "douyin", CredentialReference: account.CredentialReference{ID: id, Provider: "secret_manager"}, CredentialKey: "creatorhub/" + id}, integrationCredentialBridge{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.EnsureAccountProfile(ctx, id); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
create("uid-first")
|
|
create("uid-second")
|
|
before, err := store.GetAccountProfile(ctx, "uid-first")
|
|
if err != nil || before.PlatformAccountKey != "" {
|
|
t.Fatalf("new account must be unbound: %#v err=%v", before, err)
|
|
}
|
|
for i := 0; i < 2; i++ {
|
|
result, err := store.RecordVerifiedLoginResult(ctx, "uid-first", "99491952055")
|
|
if err != nil || result.Status != "logged_in" || result.ActualKey != "99491952055" {
|
|
t.Fatalf("verify %d: %#v err=%v", i, result, err)
|
|
}
|
|
}
|
|
bound, err := accounts.GetAccount(ctx, "uid-first")
|
|
if err != nil || bound.PlatformAccountKey != "99491952055" {
|
|
t.Fatalf("verified UID was not persisted: %#v err=%v", bound, err)
|
|
}
|
|
if _, err := store.RecordVerifiedLoginResult(ctx, "uid-first", "123456789"); !errors.Is(err, ErrConflict) || !strings.Contains(err.Error(), "99491952055") || !strings.Contains(err.Error(), "123456789") {
|
|
t.Fatalf("mismatch must explain both UIDs: %v", err)
|
|
}
|
|
if _, err := store.RecordVerifiedLoginResult(ctx, "uid-second", "99491952055"); !errors.Is(err, ErrConflict) || !strings.Contains(err.Error(), "已绑定") {
|
|
t.Fatalf("duplicate binding should fail visibly: %v", err)
|
|
}
|
|
second, err := store.GetAccountProfile(ctx, "uid-second")
|
|
if err != nil || second.PlatformAccountKey != "" || second.LoginStatus == "logged_in" {
|
|
t.Fatalf("failed bind changed account: %#v err=%v", second, err)
|
|
}
|
|
}
|
|
|
|
func TestCreatorConcurrentFirstLoginBindsExactlyOneUID(t *testing.T) {
|
|
store, accounts, ctx := openCreatorIntegrationStore(t)
|
|
id := "uid-concurrent"
|
|
if err := accounts.CreateAccount(ctx, account.Account{ID: id, Name: id, Platform: "douyin", CredentialReference: account.CredentialReference{ID: id, Provider: "secret_manager"}, CredentialKey: "creatorhub/" + id}, integrationCredentialBridge{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.EnsureAccountProfile(ctx, id); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var wg sync.WaitGroup
|
|
outcomes := make(chan error, 2)
|
|
for _, uid := range []string{"111111111", "222222222"} {
|
|
wg.Add(1)
|
|
go func(uid string) {
|
|
defer wg.Done()
|
|
_, err := store.RecordVerifiedLoginResult(ctx, id, uid)
|
|
outcomes <- err
|
|
}(uid)
|
|
}
|
|
wg.Wait()
|
|
close(outcomes)
|
|
succeeded, conflicts := 0, 0
|
|
for err := range outcomes {
|
|
if err == nil {
|
|
succeeded++
|
|
} else if errors.Is(err, ErrConflict) {
|
|
conflicts++
|
|
} else {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if succeeded != 1 || conflicts != 1 {
|
|
t.Fatalf("competing logins changed UID more than once: success=%d conflict=%d", succeeded, conflicts)
|
|
}
|
|
}
|
|
|
|
func TestUIDMigrationClearsUnverifiedKeysAndPreservesVerifiedUID(t *testing.T) {
|
|
store, accounts, ctx := openCreatorIntegrationStore(t)
|
|
for i, status := range []string{"logged_in", "needs_login", "manual_required"} {
|
|
id := fmt.Sprintf("uid-migration-%d", i)
|
|
if err := accounts.CreateAccount(ctx, account.Account{ID: id, Name: id, Platform: "douyin", PlatformAccountKey: fmt.Sprintf("1000000%d", i), CredentialReference: account.CredentialReference{ID: id, Provider: "secret_manager"}, CredentialKey: "creatorhub/" + id}, integrationCredentialBridge{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.EnsureAccountProfile(ctx, id); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.db.ExecContext(ctx, `UPDATE creator_account_profile SET login_status=$2 WHERE account_id=(SELECT id FROM social_account WHERE account_id=$1)`, id, status); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
migration, err := os.ReadFile("../environment/migrations/1047_account_uid_first_login.sql")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := store.db.ExecContext(ctx, string(migration)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
profile, err := store.GetAccountProfile(ctx, fmt.Sprintf("uid-migration-%d", i))
|
|
want := ""
|
|
if i == 0 {
|
|
want = "10000000"
|
|
}
|
|
if err != nil || profile.PlatformAccountKey != want {
|
|
t.Fatalf("migration %d: %#v err=%v", i, profile, err)
|
|
}
|
|
}
|
|
}
|