From 51148a8a9148ae9d9f11184a14effb7eff866844 Mon Sep 17 00:00:00 2001 From: Jay Date: Wed, 3 Jun 2026 15:25:52 -0400 Subject: [PATCH] test: AuthManager challenge triggers callback and send --- auth.go | 79 +++++++++++++++++++++++++++++++++++++++------------- auth_test.go | 42 ++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 29 deletions(-) diff --git a/auth.go b/auth.go index bf72f7d..52354e9 100644 --- a/auth.go +++ b/auth.go @@ -4,6 +4,8 @@ import ( "context" "sync" "time" + + "git.wisehodl.dev/jay/go-roots-ws" ) type ChallengeReceived struct { @@ -35,33 +37,70 @@ type AuthManager struct { } func NewAuthManager(e *Envoy, respond func(string) ([]byte, error)) *AuthManager { - // SubscribeInbox([]string{"AUTH"}) - // SubscribeEvents() - // context.WithCancel from e.Context() - // wg.Add(2); go m.routeInbox(); go m.handleEvents() - return nil + ctx, cancel := context.WithCancel(e.Context()) + m := &AuthManager{ + envoy: e, + respond: respond, + inbox: e.SubscribeInbox([]string{"AUTH"}), + events: e.SubscribeEvents(), + ctx: ctx, + cancel: cancel, + } + m.wg.Go(m.routeInbox) + m.wg.Go(m.handleEvents) + return m } func (m *AuthManager) Close() { - // m.cancel(); m.wg.Wait() + m.cancel() + m.wg.Wait() } func (m *AuthManager) routeInbox() { - defer m.wg.Done() - // select ctx.Done | inbox msg - // msg: envelope.FindAuthChallenge → on ErrWrongFieldType or err: continue - // store m.challenge with lock - // record ChallengeReceived - // call m.respond(challenge) - // on err: record AuthResponseFailed; continue - // envelope.EncloseAuthResponse(signed) - // m.envoy.Send(...) - // on err: record AuthResponseFailed; continue - // record AuthResponseSent + for { + select { + case <-m.ctx.Done(): + return + case msg := <-m.inbox: + challenge, err := envelope.FindAuthChallenge(msg.Data) + if err != nil { + continue + } + + m.mu.Lock() + m.challenge = challenge + m.mu.Unlock() + + now := time.Now() + m.envoy.Observer().Record(msg.ID, ChallengeReceived{Challenge: challenge, At: now}) + + signed, err := m.respond(challenge) + if err != nil { + m.envoy.Observer().Record(msg.ID, AuthResponseFailed{Err: err, At: time.Now()}) + continue + } + + if err := m.envoy.Send([]byte(envelope.EncloseAuthResponse(signed))); err != nil { + m.envoy.Observer().Record(msg.ID, AuthResponseFailed{Err: err, At: time.Now()}) + continue + } + + m.envoy.Observer().Record(msg.ID, AuthResponseSent{At: time.Now()}) + } + } } func (m *AuthManager) handleEvents() { - defer m.wg.Done() - // select ctx.Done | event - // EventDisconnected: m.challenge = "" with lock + for { + select { + case <-m.ctx.Done(): + return + case ev := <-m.events: + if ev.Kind == EventDisconnected { + m.mu.Lock() + m.challenge = "" + m.mu.Unlock() + } + } + } } diff --git a/auth_test.go b/auth_test.go index fa11de8..6d6fda0 100644 --- a/auth_test.go +++ b/auth_test.go @@ -1,20 +1,44 @@ package prism import ( + "bytes" "testing" + + "git.wisehodl.dev/jay/go-roots-ws" + "github.com/stretchr/testify/assert" ) func TestAuthManager(t *testing.T) { t.Run("challenge triggers callback and send", func(t *testing.T) { - // p, envoy := newMockEnvoy(t) - // signed := []byte(`{"kind":22242,...}`) - // callback records invocation, returns signed - // m := NewAuthManager(envoy, callback) - // t.Cleanup(m.Close) - // p.connect(); p.receive(envelope.EncloseAuthChallenge("abc")) - // Eventually: callback called with "abc" - // Eventually: p.sent contains AUTH response envelope wrapping signed - // assert ChallengeReceived and AuthResponseSent recorded on observer + obs := &mockObserver{} + p, envoy := newMockEnvoy(t, WithEmbassyObserver(obs)) + + signed := []byte(`{"kind":22242}`) + var calledWith string + callback := func(challenge string) ([]byte, error) { + calledWith = challenge + return signed, nil + } + + m := NewAuthManager(envoy, callback) + t.Cleanup(m.Close) + + p.connect() + p.receive([]byte(envelope.EncloseAuthChallenge("abc"))) + + Eventually(t, func() bool { return calledWith == "abc" }, "callback not called with challenge") + + Eventually(t, func() bool { + select { + case got := <-p.sent: + return bytes.Contains(got, signed) + default: + return false + } + }, "AUTH response envelope not sent") + + assert.Len(t, EventsOf[ChallengeReceived](obs), 1) + assert.Len(t, EventsOf[AuthResponseSent](obs), 1) }) t.Run("callback error recorded", func(t *testing.T) {