19 Commits

Author SHA1 Message Date
argoyle 60795d413d Merge branch 'update-goamqp' into 'main'
chore: update to latest version of goamqp

See merge request unboundsoftware/shiny/authz_client!3
2021-05-15 12:44:05 +00:00
argoyle a2164c4beb chore: update to latest version of goamqp 2021-05-15 14:42:19 +02:00
argoyle 90e028b173 Merge branch 'group-imports' into 'main'
chore: group imports

See merge request unboundsoftware/shiny/authz_client!2
2021-05-03 18:54:24 +00:00
argoyle 6ce176b927 chore: group imports 2021-05-03 20:52:15 +02:00
argoyle 48a063eba2 chore: rename master -> main 2021-04-07 09:49:09 +02:00
argoyle 5634c26039 Merge branch 'ci-workflows' into 'master'
chore: add CI workflows

See merge request unboundsoftware/shiny/authz_client!1
2020-11-23 16:43:28 +00:00
argoyle 429f45935a chore: add CI workflows 2020-11-23 17:20:09 +01:00
argoyle 3c36225665 chore: change coverage badge to codecov.io 2020-07-03 21:31:34 +02:00
argoyle f6417140cb chore: simplify pipeline 2020-06-26 20:44:52 +02:00
argoyle fb0273fe3c chore: add codecov upload 2020-04-27 19:27:48 +02:00
argoyle eb147039b6 fix: sort companies before comparing since map-iteration is not stable 2020-04-12 20:46:18 +02:00
argoyle c36ff4fa98 fix: use go mod download 2020-04-12 20:40:08 +02:00
argoyle 965d317a11 fix: update to Go 1.14 to fix test errors 2020-04-12 20:37:16 +02:00
argoyle 376278e2be chore: modify event structure 2020-04-12 20:33:35 +02:00
argoyle e0632f1895 fix: path to repo 2019-12-31 13:00:45 +01:00
argoyle afc14717e3 chore: add tests 2019-12-31 12:58:20 +01:00
argoyle db43357ce0 feat: add handling of removed privilege 2019-12-31 11:53:49 +01:00
argoyle 1476170f88 feat: add name and registration number to event 2019-12-08 15:46:52 +01:00
argoyle d1898339b1 fix: print unexpected messages 2019-11-22 08:12:48 +01:00
8 changed files with 451 additions and 100 deletions
+12 -21
View File
@@ -1,18 +1,10 @@
include:
- template: 'Workflows/MergeRequest-Pipelines.gitlab-ci.yml'
image: golang:1.15
variables:
GOCACHE: "${CI_PROJECT_DIR}/_go/cache"
before_script:
- mkdir -p ${CI_PROJECT_DIR}/_go/{pkg,bin,cache}
- rm -rf /go/pkg || true
- mkdir -p /go
- ln -s ${CI_PROJECT_DIR}/_go/pkg /go/pkg
- ln -s ${CI_PROJECT_DIR}/_go/bin /go/bin
cache:
key: "$CI_COMMIT_REF_NAME"
paths:
- _go
untracked: true
GOFLAGS: -mod=readonly
stages:
- deps
@@ -20,22 +12,21 @@ stages:
deps:
stage: deps
image: golang:1.13
script:
- go get -mod=readonly
- go mod download
test:
stage: test
dependencies:
- deps
image: golang:1.13
script:
- go fmt $(go list ./...)
- go vet $(go list ./...)
- unset "${!CI@}"
- CGO_ENABLED=1 go test -p 1 -mod=readonly -race -coverprofile=.testCoverage.txt -covermode=atomic -coverpkg=$(go list ./... | tr '\n' , | sed 's/,$//') ./...
- go tool cover -html=.testCoverage.txt -o coverage.html
- go tool cover -func=.testCoverage.txt
- CGO_ENABLED=1 go test -mod=readonly -race -coverprofile=coverage.txt -covermode=atomic -coverpkg=$(go list ./... | tr '\n' , | sed 's/,$//') ./...
- go tool cover -html=coverage.txt -o coverage.html
- go tool cover -func=coverage.txt
- bash <(curl -s https://codecov.io/bash)
artifacts:
paths:
- coverage.html
- coverage.txt
+2 -1
View File
@@ -1,3 +1,4 @@
# Shiny authz-client
[![Build Status](https://gitlab.com/unboundsoftware/shiny/authz-client/badges/master/pipeline.svg)](https://gitlab.com/unboundsoftware/shiny/authz-client/commits/master)[![coverage report](https://gitlab.com/unboundsoftware/shiny/authz-client/badges/master/coverage.svg)](https://gitlab.com/unboundsoftware/shiny/authz-client/commits/master)
[![Build Status](https://gitlab.com/unboundsoftware/shiny/authz_client/badges/main/pipeline.svg)](https://gitlab.com/unboundsoftware/shiny/authz_client/commits/main)
[![codecov](https://codecov.io/gl/unboundsoftware:shiny/authz_client/branch/main/graph/badge.svg?token=AQS7QVLCEQ)](https://codecov.io/gl/unboundsoftware:shiny/authz_client)
+66 -35
View File
@@ -5,6 +5,9 @@ import (
"fmt"
"io/ioutil"
"net/http"
"reflect"
"gitlab.com/sparetimecoders/goamqp"
)
// CompanyPrivileges contains the privileges for a combination of email address and company id
@@ -18,24 +21,11 @@ type CompanyPrivileges struct {
Supplier bool `json:"supplier"`
}
// PrivilegeAdded is the event sent when a new privilege is added
type PrivilegeAdded struct {
Email string `json:"email"`
CompanyID string `json:"companyId"`
Admin bool `json:"admin"`
Company bool `json:"company"`
Consumer bool `json:"consumer"`
Time bool `json:"time"`
Invoicing bool `json:"invoicing"`
Accounting bool `json:"accounting"`
Supplier bool `json:"supplier"`
}
// PrivilegeHandler processes PrivilegeAdded-events and fetches the initial set of privileges from an authz-service
type PrivilegeHandler struct {
client *http.Client
baseURL string
privileges map[string]map[string]CompanyPrivileges
privileges map[string]map[string]*CompanyPrivileges
}
// OptsFunc is used to configure the PrivilegeHandler
@@ -53,7 +43,7 @@ func New(opts ...OptsFunc) *PrivilegeHandler {
handler := &PrivilegeHandler{
client: &http.Client{},
baseURL: "http://authz-service",
privileges: map[string]map[string]CompanyPrivileges{},
privileges: map[string]map[string]*CompanyPrivileges{},
}
for _, opt := range opts {
opt(handler)
@@ -81,30 +71,60 @@ func (h *PrivilegeHandler) Fetch() error {
}
// Process privilege-related events and update the internal state
func (h *PrivilegeHandler) Process(msg interface{}) bool {
if ev, ok := msg.(*PrivilegeAdded); ok {
h.setPrivileges(ev)
return true
func (h *PrivilegeHandler) Process(msg interface{}, _ goamqp.Headers) (interface{}, error) {
switch ev := msg.(type) {
case *UserAdded:
if priv, exists := h.privileges[ev.Email]; exists {
priv[ev.CompanyID] = &CompanyPrivileges{}
} else {
h.privileges[ev.Email] = map[string]*CompanyPrivileges{
ev.CompanyID: {},
}
}
return nil, nil
case *UserRemoved:
if priv, exists := h.privileges[ev.Email]; exists {
delete(priv, ev.CompanyID)
}
return nil, nil
case *PrivilegeAdded:
h.setPrivileges(ev.Email, ev.CompanyID, ev.Privilege, true)
return nil, nil
case *PrivilegeRemoved:
h.setPrivileges(ev.Email, ev.CompanyID, ev.Privilege, false)
return nil, nil
default:
fmt.Printf("Got unexpected message type (%s): '%+v'\n", reflect.TypeOf(msg).String(), msg)
return nil, fmt.Errorf("unexpected event type: '%s'", reflect.TypeOf(msg))
}
return false
}
func (h *PrivilegeHandler) setPrivileges(ev *PrivilegeAdded) {
if priv, exists := h.privileges[ev.Email]; exists {
priv[ev.CompanyID] = CompanyPrivileges{
Admin: ev.Admin,
Company: ev.Company,
Consumer: ev.Consumer,
Time: ev.Time,
Invoicing: ev.Invoicing,
Accounting: ev.Accounting,
Supplier: ev.Supplier,
func (h *PrivilegeHandler) setPrivileges(email, companyId string, privilege Privilege, set bool) {
if priv, exists := h.privileges[email]; exists {
if c, exists := priv[companyId]; exists {
switch privilege {
case PrivilegeAdmin:
c.Admin = set
case PrivilegeCompany:
c.Company = set
case PrivilegeConsumer:
c.Consumer = set
case PrivilegeTime:
c.Time = set
case PrivilegeInvoicing:
c.Invoicing = set
case PrivilegeAccounting:
c.Accounting = set
case PrivilegeSupplier:
c.Supplier = set
}
} else {
priv[companyId] = &CompanyPrivileges{}
h.setPrivileges(email, companyId, privilege, set)
}
} else {
h.privileges[ev.Email] = map[string]CompanyPrivileges{
ev.CompanyID: {},
}
h.setPrivileges(ev)
h.privileges[email] = map[string]*CompanyPrivileges{}
h.setPrivileges(email, companyId, privilege, set)
}
}
@@ -113,10 +133,21 @@ func (h *PrivilegeHandler) CompaniesByUser(email string, predicate func(privileg
var result []string
if p, exists := h.privileges[email]; exists {
for k, v := range p {
if predicate(v) {
if predicate(*v) {
result = append(result, k)
}
}
}
return result
}
// IsAllowed return true if the provided predicate return true for the privileges matching the provided email and companyID, return false otherwise
func (h *PrivilegeHandler) IsAllowed(email, companyID string, predicate func(privileges CompanyPrivileges) bool) bool {
if p, exists := h.privileges[email]; exists {
if v, exists := p[companyID]; exists {
return predicate(*v)
}
}
return false
}
+185 -42
View File
@@ -2,18 +2,97 @@ package client
import (
"fmt"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"sort"
"testing"
"github.com/stretchr/testify/assert"
"gitlab.com/sparetimecoders/goamqp"
)
func TestPrivilegeHandler_Process_InvalidType(t *testing.T) {
handler := New(WithBaseURL("base"))
result := handler.Process("abc")
result, err := handler.Process("abc", goamqp.Headers{})
assert.False(t, result)
assert.Nil(t, result)
assert.EqualError(t, err, "unexpected event type: 'string'")
}
func TestPrivilegeHandler_Process_PrivilegeRemoved(t *testing.T) {
handler := New(WithBaseURL("base"))
result, err := handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeAdmin,
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
assert.Equal(t, []string{"abc-123"}, companies)
result, err = handler.Process(&PrivilegeRemoved{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeAdmin,
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
assert.Empty(t, companies)
}
func TestPrivilegeHandler_Process_UserAdded_And_UserRemoved(t *testing.T) {
handler := New(WithBaseURL("base"))
result, err := handler.Process(&UserAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
result, err = handler.Process(&UserAdded{
Email: "jim@example.org",
CompanyID: "abc-456",
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return true
})
sort.Strings(companies)
assert.Equal(t, []string{"abc-123", "abc-456"}, companies)
result, err = handler.Process(&UserRemoved{
Email: "jim@example.org",
CompanyID: "abc-123",
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
result, err = handler.Process(&UserRemoved{
Email: "jim@example.org",
CompanyID: "abc-456",
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return true
})
assert.Empty(t, companies)
}
func TestPrivilegeHandler_GetCompanies_Email_Not_Found(t *testing.T) {
@@ -29,41 +108,48 @@ func TestPrivilegeHandler_GetCompanies_Email_Not_Found(t *testing.T) {
func TestPrivilegeHandler_GetCompanies_No_Companies_Found(t *testing.T) {
handler := New(WithBaseURL("base"))
result := handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Admin: false,
Company: false,
Consumer: false,
Time: false,
Invoicing: false,
Accounting: false,
Supplier: false,
})
assert.True(t, result)
result, err := handler.Process(&UserAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
})
assert.Empty(t, companies)
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return true
})
assert.Equal(t, []string{"abc-123"}, companies)
result, err = handler.Process(&UserRemoved{
Email: "jim@example.org",
CompanyID: "abc-123",
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies = handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return true
})
assert.Empty(t, companies)
}
func TestPrivilegeHandler_GetCompanies_Company_With_Company_Access_Found(t *testing.T) {
handler := New(WithBaseURL("base"))
result := handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Admin: false,
Company: true,
Consumer: false,
Time: false,
Invoicing: false,
Accounting: false,
Supplier: false,
})
assert.True(t, result)
result, err := handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeCompany,
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Company
@@ -75,26 +161,83 @@ func TestPrivilegeHandler_GetCompanies_Company_With_Company_Access_Found(t *test
func TestPrivilegeHandler_GetCompanies_Company_With_Admin_Access_Found(t *testing.T) {
handler := New(WithBaseURL("base"))
result := handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Admin: true,
Company: false,
Consumer: false,
Time: false,
Invoicing: false,
Accounting: false,
Supplier: false,
})
assert.True(t, result)
result, err := handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeConsumer,
}, goamqp.Headers{})
assert.Nil(t, result)
assert.NoError(t, err)
companies := handler.CompaniesByUser("jim@example.org", func(privileges CompanyPrivileges) bool {
return privileges.Admin
return privileges.Consumer
})
assert.Equal(t, []string{"abc-123"}, companies)
}
func TestPrivilegeHandler_IsAllowed_Return_False_If_No_Privileges(t *testing.T) {
handler := New(WithBaseURL("base"))
result := handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Company
})
assert.False(t, result)
}
func TestPrivilegeHandler_IsAllowed_Return_True_If_Privilege_Exists(t *testing.T) {
handler := New(WithBaseURL("base"))
_, _ = handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeTime,
}, goamqp.Headers{})
result := handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Time
})
assert.True(t, result)
_, _ = handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeInvoicing,
}, goamqp.Headers{})
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Invoicing
})
assert.True(t, result)
_, _ = handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeAccounting,
}, goamqp.Headers{})
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Accounting
})
assert.True(t, result)
_, _ = handler.Process(&PrivilegeAdded{
Email: "jim@example.org",
CompanyID: "abc-123",
Privilege: PrivilegeSupplier,
}, goamqp.Headers{})
result = handler.IsAllowed("jim@example.org", "abc-123", func(privileges CompanyPrivileges) bool {
return privileges.Supplier
})
assert.True(t, result)
}
func TestPrivilegeHandler_Fetch_Error_Response(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(500)
@@ -106,7 +249,7 @@ func TestPrivilegeHandler_Fetch_Error_Response(t *testing.T) {
server.Close()
err := handler.Fetch()
assert.EqualError(t, err, fmt.Sprintf("Get http://%s/authz: dial tcp %s: connect: connection refused", baseURL, baseURL))
assert.EqualError(t, err, fmt.Sprintf("Get \"http://%s/authz\": dial tcp %s: connect: connection refused", baseURL, baseURL))
}
func TestPrivilegeHandler_Fetch_Error_Unreadable_Body(t *testing.T) {
@@ -160,7 +303,7 @@ func TestPrivilegeHandler_Fetch_Valid(t *testing.T) {
err := handler.Fetch()
assert.NoError(t, err)
expectedPrivileges := map[string]map[string]CompanyPrivileges{
expectedPrivileges := map[string]map[string]*CompanyPrivileges{
"jim@example.org": {
"00010203-0405-4607-8809-0a0b0c0d0e0f": {
Admin: false,
+62
View File
@@ -0,0 +1,62 @@
package client
// UserAdded is the event sent when a new user is added to a company
type UserAdded struct {
Email string `json:"email"`
CompanyID string `json:"companyId"`
}
// UserRemoved is the event sent when a user is removed from a company
type UserRemoved struct {
Email string `json:"email"`
CompanyID string `json:"companyId"`
}
// Privilege is an enumeration of all available privileges
type Privilege string
const (
PrivilegeAdmin = "ADMIN"
PrivilegeCompany = "COMPANY"
PrivilegeConsumer = "CONSUMER"
PrivilegeTime = "TIME"
PrivilegeInvoicing = "INVOICING"
PrivilegeAccounting = "ACCOUNTING"
PrivilegeSupplier = "SUPPLIER"
)
var AllPrivilege = []Privilege{
PrivilegeAdmin,
PrivilegeCompany,
PrivilegeConsumer,
PrivilegeTime,
PrivilegeInvoicing,
PrivilegeAccounting,
PrivilegeSupplier,
}
func (e Privilege) IsValid() bool {
switch e {
case PrivilegeAdmin, PrivilegeCompany, PrivilegeConsumer, PrivilegeTime, PrivilegeInvoicing, PrivilegeAccounting, PrivilegeSupplier:
return true
}
return false
}
func (e Privilege) String() string {
return string(e)
}
// PrivilegeAdded is the event sent when a new privilege is added
type PrivilegeAdded struct {
Email string `json:"email"`
CompanyID string `json:"companyId"`
Privilege Privilege `json:"privilege"`
}
// PrivilegeRemoved is the event sent when a privilege is removed
type PrivilegeRemoved struct {
Email string `json:"email"`
CompanyID string `json:"companyId"`
Privilege Privilege `json:"privilege"`
}
+110
View File
@@ -0,0 +1,110 @@
package client
import "testing"
func TestPrivilege_IsValid(t *testing.T) {
tests := []struct {
name string
e Privilege
want bool
}{
{
name: "Admin",
e: "ADMIN",
want: true,
},
{
name: "Company",
e: "COMPANY",
want: true,
},
{
name: "Consumer",
e: "CONSUMER",
want: true,
},
{
name: "Time",
e: "TIME",
want: true,
},
{
name: "Invoicing",
e: "INVOICING",
want: true,
},
{
name: "Accounting",
e: "ACCOUNTING",
want: true,
},
{
name: "Supplier",
e: "SUPPLIER",
want: true,
},
{
name: "Invalid",
e: "BLUTTI",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.e.IsValid(); got != tt.want {
t.Errorf("IsValid() = %v, want %v", got, tt.want)
}
})
}
}
func TestPrivilege_String(t *testing.T) {
tests := []struct {
name string
e Privilege
want string
}{
{
name: "Admin",
e: "ADMIN",
want: "ADMIN",
},
{
name: "Company",
e: "COMPANY",
want: "COMPANY",
},
{
name: "Consumer",
e: "CONSUMER",
want: "CONSUMER",
},
{
name: "Time",
e: "TIME",
want: "TIME",
},
{
name: "Invoicing",
e: "INVOICING",
want: "INVOICING",
},
{
name: "Accounting",
e: "ACCOUNTING",
want: "ACCOUNTING",
},
{
name: "Supplier",
e: "SUPPLIER",
want: "SUPPLIER",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.e.String(); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}
})
}
}
+4 -1
View File
@@ -2,4 +2,7 @@ module gitlab.com/unboundsoftware/shiny/authz_client
go 1.13
require github.com/stretchr/testify v1.4.0
require (
github.com/stretchr/testify v1.4.0
gitlab.com/sparetimecoders/goamqp v0.3.1
)
+10
View File
@@ -1,10 +1,20 @@
github.com/caarlos0/env v3.5.0+incompatible/go.mod h1:tdCsowwCzMLdkqRYDlHpZCp2UooDD3MspDBjZ2AD02Y=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271 h1:WhxRHzgeVGETMlmVfqhRn8RIeeNoPr2Czh33I4Zdccw=
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
gitlab.com/sparetimecoders/goamqp v0.3.1 h1:Pf5nANlKzCP6h5MVrKgfKknf3Vkz3psGh97Mh8Coxqs=
gitlab.com/sparetimecoders/goamqp v0.3.1/go.mod h1:Xci7oyW/j9kJXr0QRgLRGv8e0R91ZSksPhYCei5FK5U=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=