test: AuthManager challenge triggers callback and send
This commit is contained in:
@@ -4,6 +4,8 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.wisehodl.dev/jay/go-roots-ws"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChallengeReceived struct {
|
type ChallengeReceived struct {
|
||||||
@@ -35,33 +37,70 @@ type AuthManager struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthManager(e *Envoy, respond func(string) ([]byte, error)) *AuthManager {
|
func NewAuthManager(e *Envoy, respond func(string) ([]byte, error)) *AuthManager {
|
||||||
// SubscribeInbox([]string{"AUTH"})
|
ctx, cancel := context.WithCancel(e.Context())
|
||||||
// SubscribeEvents()
|
m := &AuthManager{
|
||||||
// context.WithCancel from e.Context()
|
envoy: e,
|
||||||
// wg.Add(2); go m.routeInbox(); go m.handleEvents()
|
respond: respond,
|
||||||
return nil
|
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() {
|
func (m *AuthManager) Close() {
|
||||||
// m.cancel(); m.wg.Wait()
|
m.cancel()
|
||||||
|
m.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *AuthManager) routeInbox() {
|
func (m *AuthManager) routeInbox() {
|
||||||
defer m.wg.Done()
|
for {
|
||||||
// select ctx.Done | inbox msg
|
select {
|
||||||
// msg: envelope.FindAuthChallenge → on ErrWrongFieldType or err: continue
|
case <-m.ctx.Done():
|
||||||
// store m.challenge with lock
|
return
|
||||||
// record ChallengeReceived
|
case msg := <-m.inbox:
|
||||||
// call m.respond(challenge)
|
challenge, err := envelope.FindAuthChallenge(msg.Data)
|
||||||
// on err: record AuthResponseFailed; continue
|
if err != nil {
|
||||||
// envelope.EncloseAuthResponse(signed)
|
continue
|
||||||
// m.envoy.Send(...)
|
}
|
||||||
// on err: record AuthResponseFailed; continue
|
|
||||||
// record AuthResponseSent
|
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() {
|
func (m *AuthManager) handleEvents() {
|
||||||
defer m.wg.Done()
|
for {
|
||||||
// select ctx.Done | event
|
select {
|
||||||
// EventDisconnected: m.challenge = "" with lock
|
case <-m.ctx.Done():
|
||||||
|
return
|
||||||
|
case ev := <-m.events:
|
||||||
|
if ev.Kind == EventDisconnected {
|
||||||
|
m.mu.Lock()
|
||||||
|
m.challenge = ""
|
||||||
|
m.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-9
@@ -1,20 +1,44 @@
|
|||||||
package prism
|
package prism
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"git.wisehodl.dev/jay/go-roots-ws"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestAuthManager(t *testing.T) {
|
func TestAuthManager(t *testing.T) {
|
||||||
t.Run("challenge triggers callback and send", func(t *testing.T) {
|
t.Run("challenge triggers callback and send", func(t *testing.T) {
|
||||||
// p, envoy := newMockEnvoy(t)
|
obs := &mockObserver{}
|
||||||
// signed := []byte(`{"kind":22242,...}`)
|
p, envoy := newMockEnvoy(t, WithEmbassyObserver(obs))
|
||||||
// callback records invocation, returns signed
|
|
||||||
// m := NewAuthManager(envoy, callback)
|
signed := []byte(`{"kind":22242}`)
|
||||||
// t.Cleanup(m.Close)
|
var calledWith string
|
||||||
// p.connect(); p.receive(envelope.EncloseAuthChallenge("abc"))
|
callback := func(challenge string) ([]byte, error) {
|
||||||
// Eventually: callback called with "abc"
|
calledWith = challenge
|
||||||
// Eventually: p.sent contains AUTH response envelope wrapping signed
|
return signed, nil
|
||||||
// assert ChallengeReceived and AuthResponseSent recorded on observer
|
}
|
||||||
|
|
||||||
|
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) {
|
t.Run("callback error recorded", func(t *testing.T) {
|
||||||
|
|||||||
Reference in New Issue
Block a user