69 lines
3.0 KiB
Go
69 lines
3.0 KiB
Go
package onboarding
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
|
||
|
|
"github.com/google/uuid"
|
||
|
|
)
|
||
|
|
|
||
|
|
// A verified new phone must enter the family-creation flow without a prior
|
||
|
|
// customer account, household membership, or device binding.
|
||
|
|
func TestWeChatNewPhoneCreatesCustomerWithoutFamilyOrDevice(t *testing.T) {
|
||
|
|
f := newLoginFixture(t)
|
||
|
|
provider := &fakeWeChat{openID: uuid.NewString(), phone: f.phone}
|
||
|
|
f.s.options.WeChat = provider
|
||
|
|
f.s.options.WeChatAppID = "wx4553119e05f11660"
|
||
|
|
phoneHash := domainMAC(f.s.crypt.hashKey, "PHONE1\n"+f.phone)
|
||
|
|
start := func() string {
|
||
|
|
status, data := miniRequest(t, f.s, "POST", "/api/v1/mini/login/session", "", map[string]string{"portal": "customer"})
|
||
|
|
if status != 200 {
|
||
|
|
t.Fatalf("session status %d", status)
|
||
|
|
}
|
||
|
|
return "MiniLogin " + data["login_token"].(string)
|
||
|
|
}
|
||
|
|
auth := start()
|
||
|
|
status, data := miniRequest(t, f.s, "POST", "/api/v1/mini/login/wechat", auth, WeChatLoginInput{LoginCode: "before-phone"})
|
||
|
|
if status != 200 || data["phone_required"] != true {
|
||
|
|
t.Fatal("new identity must request phone authorization")
|
||
|
|
}
|
||
|
|
var count int
|
||
|
|
must(t, f.s.pool.QueryRow(f.ctx, `SELECT count(*) FROM customer_phone_credential WHERE phone_digest=$1`, phoneHash).Scan(&count))
|
||
|
|
if count != 0 {
|
||
|
|
t.Fatal("created phone before authorization")
|
||
|
|
}
|
||
|
|
status, data = miniRequest(t, f.s, "POST", "/api/v1/mini/login/wechat", auth, WeChatLoginInput{LoginCode: "authorized", PhoneCode: "verified-phone-code"})
|
||
|
|
if status != 200 || data["access_token"] == nil {
|
||
|
|
t.Fatalf("verified new phone was not registered: %d", status)
|
||
|
|
}
|
||
|
|
token := strings.TrimPrefix(data["access_token"].(string), "customer.")
|
||
|
|
principal, err := f.s.AccountPrincipal(f.ctx, token, "", false)
|
||
|
|
must(t, err)
|
||
|
|
var uid, kind, tenant string
|
||
|
|
must(t, f.s.pool.QueryRow(f.ctx, `SELECT id::text,account_kind,tenant_id::text FROM user_account WHERE oidc_issuer=$1 AND oidc_subject=$2 AND status='active'`, principal.Issuer, principal.Subject).Scan(&uid, &kind, &tenant))
|
||
|
|
if kind != "customer" || tenant != f.tenant {
|
||
|
|
t.Fatal("wrong signup account kind or tenant")
|
||
|
|
}
|
||
|
|
must(t, f.s.pool.QueryRow(f.ctx, `SELECT count(*) FROM household_member WHERE user_id=$1`, uid).Scan(&count))
|
||
|
|
if count != 0 {
|
||
|
|
t.Fatal("signup must not create or join a household")
|
||
|
|
}
|
||
|
|
must(t, f.s.pool.QueryRow(f.ctx, `SELECT count(*) FROM device_binding WHERE bound_by=$1`, uid).Scan(&count))
|
||
|
|
if count != 0 {
|
||
|
|
t.Fatal("signup must not require device binding")
|
||
|
|
}
|
||
|
|
status, data = miniRequest(t, f.s, "POST", "/api/v1/mini/login/wechat", start(), WeChatLoginInput{LoginCode: "repeat-login"})
|
||
|
|
if status != 200 || data["access_token"] == nil {
|
||
|
|
t.Fatal("repeat login failed")
|
||
|
|
}
|
||
|
|
again, err := f.s.AccountPrincipal(f.ctx, strings.TrimPrefix(data["access_token"].(string), "customer."), "", false)
|
||
|
|
must(t, err)
|
||
|
|
if again.Subject != principal.Subject || again.Issuer != principal.Issuer {
|
||
|
|
t.Fatal("repeat login changed account")
|
||
|
|
}
|
||
|
|
must(t, f.s.pool.QueryRow(f.ctx, `SELECT count(*) FROM user_account WHERE oidc_issuer=$1 AND oidc_subject=$2`, principal.Issuer, principal.Subject).Scan(&count))
|
||
|
|
if count != 1 {
|
||
|
|
t.Fatal("repeat login duplicated account")
|
||
|
|
}
|
||
|
|
}
|