Files

69 lines
3.0 KiB
Go
Raw Permalink Normal View History

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")
}
}