diff --git a/go.mod b/go.mod index 8d17712a..1471aa6d 100644 --- a/go.mod +++ b/go.mod @@ -43,6 +43,7 @@ require ( github.com/go-resty/resty/v2 v2.2.0 github.com/golang/mock v1.4.3 github.com/google/go-cmp v0.4.0 + github.com/google/uuid v1.1.1 github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c // indirect github.com/hashicorp/go-multierror v1.0.0 github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195 diff --git a/go.sum b/go.sum index 64317ecc..e936063d 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,8 @@ github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gopherjs/gopherjs v0.0.0-20190411002643-bd77b112433e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4= github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= diff --git a/pkg/confirmer/confirmer.go b/pkg/confirmer/confirmer.go new file mode 100644 index 00000000..7b121247 --- /dev/null +++ b/pkg/confirmer/confirmer.go @@ -0,0 +1,44 @@ +package confirmer + +import ( + "errors" + "sync" + "time" +) + +type Confirmer struct { + requests map[string]*Request + locker sync.Locker +} + +func New() *Confirmer { + return &Confirmer{ + requests: make(map[string]*Request), + locker: &sync.Mutex{}, + } +} + +func (c *Confirmer) NewRequest(timeout time.Duration) *Request { + c.locker.Lock() + defer c.locker.Unlock() + + req := newRequest(timeout) + + c.requests[req.ID()] = req + + return req +} + +func (c *Confirmer) SetResponse(uuid string, value bool) error { + c.locker.Lock() + defer c.locker.Unlock() + + req, ok := c.requests[uuid] + if !ok { + return errors.New("no such request") + } + + req.value <- value + + return nil +} diff --git a/pkg/confirmer/confirmer_test.go b/pkg/confirmer/confirmer_test.go new file mode 100644 index 00000000..5e0200cf --- /dev/null +++ b/pkg/confirmer/confirmer_test.go @@ -0,0 +1,50 @@ +package confirmer + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestConfirmerYes(t *testing.T) { + c := New() + + req := c.NewRequest(1 * time.Second) + + go func() { + assert.NoError(t, c.SetResponse(req.ID(), true)) + }() + + res, err := req.Result() + assert.NoError(t, err) + assert.True(t, res) +} + +func TestConfirmerNo(t *testing.T) { + c := New() + + req := c.NewRequest(1 * time.Second) + + go func() { + assert.NoError(t, c.SetResponse(req.ID(), false)) + }() + + res, err := req.Result() + assert.NoError(t, err) + assert.False(t, res) +} + +func TestConfirmerTimeout(t *testing.T) { + c := New() + + req := c.NewRequest(1 * time.Second) + + go func() { + time.Sleep(2 * time.Second) + assert.NoError(t, c.SetResponse(req.ID(), true)) + }() + + _, err := req.Result() + assert.Error(t, err) +} diff --git a/pkg/confirmer/request.go b/pkg/confirmer/request.go new file mode 100644 index 00000000..5875e259 --- /dev/null +++ b/pkg/confirmer/request.go @@ -0,0 +1,36 @@ +package confirmer + +import ( + "errors" + "time" + + "github.com/google/uuid" +) + +type Request struct { + uuid string + value chan bool + timeout time.Duration +} + +func newRequest(timeout time.Duration) *Request { + return &Request{ + uuid: uuid.New().String(), + value: make(chan bool), + timeout: timeout, + } +} + +func (r *Request) ID() string { + return r.uuid +} + +func (r *Request) Result() (bool, error) { + select { + case res := <-r.value: + return res, nil + + case <-time.After(r.timeout): + return false, errors.New("timed out waiting for result") + } +}