5d5f90e286
In the upcoming changes to the credential helper protocol, we'll be able to specify multiple values for the same key. In order to make this work, let's make the credential structure able to handle multiple values. Note that if there are multiple values, we'll only use the first one for most keys. That simplifies the logic and allows us to gracefully handle future extensions to the protocol.
272 lines
7.7 KiB
Go
272 lines
7.7 KiB
Go
package creds
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
type testCredHelper struct {
|
|
fillErr error
|
|
approveErr error
|
|
rejectErr error
|
|
fill []Creds
|
|
approve []Creds
|
|
reject []Creds
|
|
}
|
|
|
|
func newTestCredHelper() *testCredHelper {
|
|
return &testCredHelper{
|
|
fill: make([]Creds, 0),
|
|
approve: make([]Creds, 0),
|
|
reject: make([]Creds, 0),
|
|
}
|
|
}
|
|
|
|
func (h *testCredHelper) Fill(input Creds) (Creds, error) {
|
|
h.fill = append(h.fill, input)
|
|
return input, h.fillErr
|
|
}
|
|
|
|
func (h *testCredHelper) Approve(creds Creds) error {
|
|
h.approve = append(h.approve, creds)
|
|
return h.approveErr
|
|
}
|
|
|
|
func (h *testCredHelper) Reject(creds Creds) error {
|
|
h.reject = append(h.reject, creds)
|
|
return h.rejectErr
|
|
}
|
|
|
|
func TestCredHelperSetNoErrors(t *testing.T) {
|
|
cache := NewCredentialCacher()
|
|
helper1 := newTestCredHelper()
|
|
helper2 := newTestCredHelper()
|
|
helpers := NewCredentialHelpers([]CredentialHelper{cache, helper1, helper2})
|
|
creds := Creds{"protocol": []string{"https"}, "host": []string{"example.com"}}
|
|
|
|
out, err := helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
|
|
// calling Fill() with empty cache
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 2, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
|
|
credsWithPass := Creds{
|
|
"protocol": []string{"https"},
|
|
"host": []string{"example.com"},
|
|
"username": []string{"foo"},
|
|
"password": []string{"bar"},
|
|
}
|
|
assert.Nil(t, helpers.Approve(credsWithPass))
|
|
assert.Equal(t, 1, len(helper1.approve))
|
|
assert.Equal(t, 0, len(helper2.approve))
|
|
|
|
// calling Approve() again is cached
|
|
assert.Nil(t, helpers.Approve(credsWithPass))
|
|
assert.Equal(t, 1, len(helper1.approve))
|
|
assert.Equal(t, 0, len(helper2.approve))
|
|
|
|
// access cache
|
|
for i := 0; i < 3; i++ {
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, credsWithPass, out)
|
|
assert.Equal(t, 2, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
}
|
|
|
|
assert.Nil(t, helpers.Reject(creds))
|
|
assert.Equal(t, 1, len(helper1.reject))
|
|
assert.Equal(t, 0, len(helper2.reject))
|
|
|
|
// Reject() is never cached
|
|
assert.Nil(t, helpers.Reject(creds))
|
|
assert.Equal(t, 2, len(helper1.reject))
|
|
assert.Equal(t, 0, len(helper2.reject))
|
|
|
|
// calling Fill() with empty cache
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 3, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
}
|
|
|
|
func TestCredHelperSetFillError(t *testing.T) {
|
|
cache := NewCredentialCacher()
|
|
helper1 := newTestCredHelper()
|
|
helper2 := newTestCredHelper()
|
|
helpers := NewCredentialHelpers([]CredentialHelper{cache, helper1, helper2})
|
|
creds := Creds{"protocol": []string{"https"}, "host": []string{"example.com"}}
|
|
|
|
helper1.fillErr = errors.New("boom")
|
|
out, err := helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 1, len(helper2.fill))
|
|
|
|
assert.Nil(t, helpers.Approve(creds))
|
|
assert.Equal(t, 0, len(helper1.approve))
|
|
assert.Equal(t, 1, len(helper2.approve))
|
|
|
|
// Fill() with cache
|
|
for i := 0; i < 3; i++ {
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 1, len(helper2.fill))
|
|
}
|
|
|
|
assert.Nil(t, helpers.Reject(creds))
|
|
assert.Equal(t, 0, len(helper1.reject))
|
|
assert.Equal(t, 1, len(helper2.reject))
|
|
|
|
// Fill() with empty cache
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill)) // still skipped
|
|
assert.Equal(t, 2, len(helper2.fill))
|
|
}
|
|
|
|
func TestCredHelperSetApproveError(t *testing.T) {
|
|
cache := NewCredentialCacher()
|
|
helper1 := newTestCredHelper()
|
|
helper2 := newTestCredHelper()
|
|
helpers := NewCredentialHelpers([]CredentialHelper{cache, helper1, helper2})
|
|
creds := Creds{"protocol": []string{"https"}, "host": []string{"example.com"}}
|
|
|
|
approveErr := errors.New("boom")
|
|
helper1.approveErr = approveErr
|
|
out, err := helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
|
|
assert.Equal(t, approveErr, helpers.Approve(creds))
|
|
assert.Equal(t, 1, len(helper1.approve))
|
|
assert.Equal(t, 0, len(helper2.approve))
|
|
|
|
// cache is never set
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 2, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
|
|
assert.Nil(t, helpers.Reject(creds))
|
|
assert.Equal(t, 1, len(helper1.reject))
|
|
assert.Equal(t, 0, len(helper2.reject))
|
|
}
|
|
|
|
func TestCredHelperSetFillAndApproveError(t *testing.T) {
|
|
cache := NewCredentialCacher()
|
|
helper1 := newTestCredHelper()
|
|
helper2 := newTestCredHelper()
|
|
helpers := NewCredentialHelpers([]CredentialHelper{cache, helper1, helper2})
|
|
creds := Creds{"protocol": []string{"https"}, "host": []string{"example.com"}}
|
|
|
|
credErr := errors.New("boom")
|
|
helper1.fillErr = credErr
|
|
helper2.approveErr = credErr
|
|
|
|
out, err := helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 1, len(helper2.fill))
|
|
|
|
assert.Equal(t, credErr, helpers.Approve(creds))
|
|
assert.Equal(t, 0, len(helper1.approve)) // skipped
|
|
assert.Equal(t, 0, len(helper1.reject)) // skipped
|
|
assert.Equal(t, 1, len(helper2.approve))
|
|
|
|
// never approved, so cache is empty
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill)) // still skipped
|
|
assert.Equal(t, 2, len(helper2.fill))
|
|
}
|
|
|
|
func TestCredHelperSetRejectError(t *testing.T) {
|
|
cache := NewCredentialCacher()
|
|
helper1 := newTestCredHelper()
|
|
helper2 := newTestCredHelper()
|
|
helpers := NewCredentialHelpers([]CredentialHelper{cache, helper1, helper2})
|
|
creds := Creds{"protocol": []string{"https"}, "host": []string{"example.com"}}
|
|
|
|
rejectErr := errors.New("boom")
|
|
helper1.rejectErr = rejectErr
|
|
out, err := helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
|
|
assert.Nil(t, helpers.Approve(creds))
|
|
assert.Equal(t, 1, len(helper1.approve))
|
|
assert.Equal(t, 0, len(helper2.approve))
|
|
|
|
// Fill() with cache
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
|
|
assert.Equal(t, rejectErr, helpers.Reject(creds))
|
|
assert.Equal(t, 1, len(helper1.reject))
|
|
assert.Equal(t, 0, len(helper2.reject))
|
|
|
|
// failed Reject() still clears cache
|
|
out, err = helpers.Fill(creds)
|
|
assert.Nil(t, err)
|
|
assert.Equal(t, creds, out)
|
|
assert.Equal(t, 2, len(helper1.fill))
|
|
assert.Equal(t, 0, len(helper2.fill))
|
|
}
|
|
|
|
func TestCredHelperSetAllFillErrors(t *testing.T) {
|
|
cache := NewCredentialCacher()
|
|
helper1 := newTestCredHelper()
|
|
helper2 := newTestCredHelper()
|
|
helpers := NewCredentialHelpers([]CredentialHelper{cache, helper1, helper2})
|
|
creds := Creds{"protocol": []string{"https"}, "host": []string{"example.com"}}
|
|
|
|
helper1.fillErr = errors.New("boom 1")
|
|
helper2.fillErr = errors.New("boom 2")
|
|
out, err := helpers.Fill(creds)
|
|
if assert.NotNil(t, err) {
|
|
assert.Equal(t, "credential fill errors:\nboom 1\nboom 2", err.Error())
|
|
}
|
|
assert.Nil(t, out)
|
|
assert.Equal(t, 1, len(helper1.fill))
|
|
assert.Equal(t, 1, len(helper2.fill))
|
|
|
|
err = helpers.Approve(creds)
|
|
if assert.NotNil(t, err) {
|
|
assert.Equal(t, "no valid credential helpers to approve", err.Error())
|
|
}
|
|
assert.Equal(t, 0, len(helper1.approve))
|
|
assert.Equal(t, 0, len(helper2.approve))
|
|
|
|
err = helpers.Reject(creds)
|
|
if assert.NotNil(t, err) {
|
|
assert.Equal(t, "no valid credential helpers to reject", err.Error())
|
|
}
|
|
assert.Equal(t, 0, len(helper1.reject))
|
|
assert.Equal(t, 0, len(helper2.reject))
|
|
}
|