Files
Codex Local bf1233d5df
ci / verify (push) Canceled after 0s
feat: expand companion platform, device controls, and memoir workflows
Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
2026-10-08 10:06:44 +08:00

145 lines
4.6 KiB
Go

package onboarding
import (
"context"
"crypto/sha256"
"encoding/json"
"github.com/google/uuid"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestIntegrationClaimReplayCannotReturnReplacement(t *testing.T) {
f := newFixture(t)
in := ClaimInput{ElderID: f.elder, Generation: f.d.Generation}
id := uuid.NewString()
first, err := f.s.Claim(f.ctx, f.web, id, requestJSON(in), in)
must(t, err)
cancel := CancelInput{ClaimID: first["claim_id"].(string)}
_, err = f.s.Cancel(f.ctx, f.web, uuid.NewString(), requestJSON(cancel), cancel)
must(t, err)
_, err = f.s.Claim(f.ctx, f.web, uuid.NewString(), requestJSON(in), in)
must(t, err)
_, err = f.s.Claim(f.ctx, f.web, id, requestJSON(in), in)
if err == nil {
t.Fatal("returned cancelled claim from cache")
}
}
func TestIntegrationConsumedHandoffCannotReplayReplacement(t *testing.T) {
f := newFixture(t)
var local string
must(t, f.s.pool.QueryRow(f.ctx, `SELECT local_setup_id::text FROM device_setup_session WHERE id=$1::uuid`, f.setup).Scan(&local))
verifier := randomToken(32)
sum := sha256.Sum256([]byte(verifier))
in := HandoffInput{SetupID: f.setup, LocalID: local, Challenge: encode64(sum[:])}
id := uuid.NewString()
first, err := f.s.Handoff(f.ctx, f.d, id, requestJSON(in), in)
must(t, err)
_, err = f.s.Exchange(f.ctx, ExchangeInput{Token: first["handoff_token"].(string), Verifier: verifier})
must(t, err)
_, err = f.s.Handoff(f.ctx, f.d, uuid.NewString(), requestJSON(in), in)
must(t, err)
_, err = f.s.Handoff(f.ctx, f.d, id, requestJSON(in), in)
if err == nil {
t.Fatal("returned consumed handoff")
}
}
type captureSMS struct {
code string
calls int
purpose string
}
func (p *captureSMS) SendForPurpose(ctx context.Context, phone, code, id, purpose string) error {
p.purpose = purpose
return p.Send(ctx, phone, code, id)
}
func (p *captureSMS) Send(_ context.Context, _ string, code string, _ string) error {
p.code = code
p.calls++
return nil
}
func TestIntegrationSMSLoginRateAndOneTimeCode(t *testing.T) {
f := newFixture(t)
sender := &captureSMS{}
f.s.options.SMS = sender
f.s.options.SMSLoginEnabled = true
// Start a fresh anonymous browser session, as a phone opened from the device.
_, err := f.s.pool.Exec(f.ctx, `UPDATE onboarding_web_session SET user_id=NULL,issuer=NULL,subject=NULL WHERE token_hash=$1`, f.web.Hash)
must(t, err)
f.web.UserID = ""
f.web.Issuer = ""
f.web.Subject = ""
phone := "+8613" + strings.ReplaceAll(uuid.NewString(), "-", "")[:9]
// Use decimal digits while keeping independent fixtures unique.
for i, c := range phone {
if c >= 'a' && c <= 'f' {
phone = phone[:i] + string('0'+(c-'a')) + phone[i+1:]
}
}
in := SMSSendInput{Phone: phone, Purpose: "LOGIN", CaptchaID: f.knownCaptchaFor(f.web.Hash), CaptchaCode: "654321"}
id := uuid.NewString()
sent, err := f.s.SendSMS(f.ctx, f.web, id, in)
must(t, err)
_, err = f.s.SendSMS(f.ctx, f.web, id, in)
must(t, err)
if sender.calls != 1 {
t.Fatal("SMS retry sent twice")
}
in.CaptchaID = f.knownCaptchaFor(f.web.Hash)
_, err = f.s.SendSMS(f.ctx, f.web, uuid.NewString(), in)
if err == nil {
t.Fatal("resend cooldown ignored")
}
verify := SMSVerifyInput{ChallengeID: sent["sms_challenge_id"].(string), Code: sender.code}
bad := verify
bad.Code = "000000"
if bad.Code == sender.code {
bad.Code = "999999"
}
_, err = f.s.VerifySMS(f.ctx, f.web, bad)
if err == nil {
t.Fatal("incorrect SMS accepted")
}
credentials, err := f.s.VerifySMS(f.ctx, f.web, verify)
must(t, err)
principal, err := f.s.AccountPrincipal(f.ctx, credentials.AccountToken, credentials.CSRF, true)
must(t, err)
if principal.Issuer != "urn:platform:onboarding:sms" {
t.Fatal("incorrect SMS identity namespace")
}
_, err = f.s.VerifySMS(f.ctx, f.web, verify)
if err == nil {
t.Fatal("SMS consumed twice")
}
}
func TestHTTPSMSGatewayContract(t *testing.T) {
calls := 0
server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
calls++
if r.Header.Get("Authorization") != "Bearer test-token" || r.Header.Get("Idempotency-Key") != "test-request" {
t.Error("gateway authentication missing")
}
var body map[string]any
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
t.Error(err)
}
if body["code"] != "123456" || body["phone_e164"] != "+8613800000000" {
t.Error("gateway payload incorrect")
}
w.WriteHeader(204)
}))
defer server.Close()
provider, err := NewHTTPSMSProvider(server.URL, "test-token")
must(t, err)
provider.(*HTTPSMSProvider).client = server.Client()
must(t, provider.Send(context.Background(), "+8613800000000", "123456", "test-request"))
if calls != 1 {
t.Fatal("unexpected request count")
}
}