1025 lines
31 KiB
Go
1025 lines
31 KiB
Go
package nosql
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/smallstep/assert"
|
|
"github.com/smallstep/certificates/acme"
|
|
"github.com/smallstep/certificates/db"
|
|
"github.com/smallstep/nosql"
|
|
"github.com/smallstep/nosql/database"
|
|
)
|
|
|
|
func TestDB_getDBOrder(t *testing.T) {
|
|
orderID := "orderID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
dbo *dbOrder
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/not-found": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return nil, database.ErrNotFound
|
|
},
|
|
},
|
|
acmeErr: acme.NewError(acme.ErrorMalformedType, "order orderID not found"),
|
|
}
|
|
},
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading order orderID: force"),
|
|
}
|
|
},
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return []byte("foo"), nil
|
|
},
|
|
},
|
|
err: errors.New("error unmarshaling order orderID into dbOrder"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbo := &dbOrder{
|
|
ID: orderID,
|
|
AccountID: "accID",
|
|
ProvisionerID: "provID",
|
|
CertificateID: "certID",
|
|
Status: acme.StatusValid,
|
|
ExpiresAt: now,
|
|
CreatedAt: now,
|
|
NotBefore: now,
|
|
NotAfter: now,
|
|
Identifiers: []acme.Identifier{
|
|
{Type: "dns", Value: "test.ca.smallstep.com"},
|
|
{Type: "dns", Value: "example.foo.com"},
|
|
},
|
|
AuthorizationIDs: []string{"foo", "bar"},
|
|
Error: acme.NewError(acme.ErrorMalformedType, "The request message was malformed"),
|
|
}
|
|
b, err := json.Marshal(dbo)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return b, nil
|
|
},
|
|
},
|
|
dbo: dbo,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if dbo, err := d.getDBOrder(context.Background(), orderID); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, dbo.ID, tc.dbo.ID)
|
|
assert.Equals(t, dbo.ProvisionerID, tc.dbo.ProvisionerID)
|
|
assert.Equals(t, dbo.CertificateID, tc.dbo.CertificateID)
|
|
assert.Equals(t, dbo.Status, tc.dbo.Status)
|
|
assert.Equals(t, dbo.CreatedAt, tc.dbo.CreatedAt)
|
|
assert.Equals(t, dbo.ExpiresAt, tc.dbo.ExpiresAt)
|
|
assert.Equals(t, dbo.NotBefore, tc.dbo.NotBefore)
|
|
assert.Equals(t, dbo.NotAfter, tc.dbo.NotAfter)
|
|
assert.Equals(t, dbo.Identifiers, tc.dbo.Identifiers)
|
|
assert.Equals(t, dbo.AuthorizationIDs, tc.dbo.AuthorizationIDs)
|
|
assert.Equals(t, dbo.Error.Error(), tc.dbo.Error.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_GetOrder(t *testing.T) {
|
|
orderID := "orderID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
dbo *dbOrder
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading order orderID: force"),
|
|
}
|
|
},
|
|
"fail/forward-acme-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return nil, database.ErrNotFound
|
|
},
|
|
},
|
|
acmeErr: acme.NewError(acme.ErrorMalformedType, "order orderID not found"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
now := clock.Now()
|
|
dbo := &dbOrder{
|
|
ID: orderID,
|
|
AccountID: "accID",
|
|
ProvisionerID: "provID",
|
|
CertificateID: "certID",
|
|
Status: acme.StatusValid,
|
|
ExpiresAt: now,
|
|
CreatedAt: now,
|
|
NotBefore: now,
|
|
NotAfter: now,
|
|
Identifiers: []acme.Identifier{
|
|
{Type: "dns", Value: "test.ca.smallstep.com"},
|
|
{Type: "dns", Value: "example.foo.com"},
|
|
},
|
|
AuthorizationIDs: []string{"foo", "bar"},
|
|
Error: acme.NewError(acme.ErrorMalformedType, "The request message was malformed"),
|
|
}
|
|
b, err := json.Marshal(dbo)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
return b, nil
|
|
},
|
|
},
|
|
dbo: dbo,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if o, err := d.GetOrder(context.Background(), orderID); err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, o.ID, tc.dbo.ID)
|
|
assert.Equals(t, o.AccountID, tc.dbo.AccountID)
|
|
assert.Equals(t, o.ProvisionerID, tc.dbo.ProvisionerID)
|
|
assert.Equals(t, o.CertificateID, tc.dbo.CertificateID)
|
|
assert.Equals(t, o.Status, tc.dbo.Status)
|
|
assert.Equals(t, o.ExpiresAt, tc.dbo.ExpiresAt)
|
|
assert.Equals(t, o.NotBefore, tc.dbo.NotBefore)
|
|
assert.Equals(t, o.NotAfter, tc.dbo.NotAfter)
|
|
assert.Equals(t, o.Identifiers, tc.dbo.Identifiers)
|
|
assert.Equals(t, o.AuthorizationIDs, tc.dbo.AuthorizationIDs)
|
|
assert.Equals(t, o.Error.Error(), tc.dbo.Error.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_UpdateOrder(t *testing.T) {
|
|
orderID := "orderID"
|
|
now := clock.Now()
|
|
dbo := &dbOrder{
|
|
ID: orderID,
|
|
AccountID: "accID",
|
|
ProvisionerID: "provID",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: now,
|
|
CreatedAt: now,
|
|
NotBefore: now,
|
|
NotAfter: now,
|
|
Identifiers: []acme.Identifier{
|
|
{Type: "dns", Value: "test.ca.smallstep.com"},
|
|
{Type: "dns", Value: "example.foo.com"},
|
|
},
|
|
AuthorizationIDs: []string{"foo", "bar"},
|
|
}
|
|
b, err := json.Marshal(dbo)
|
|
assert.FatalError(t, err)
|
|
type test struct {
|
|
db nosql.DB
|
|
o *acme.Order
|
|
err error
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
o: &acme.Order{
|
|
ID: orderID,
|
|
},
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error loading order orderID: force"),
|
|
}
|
|
},
|
|
"fail/save-error": func(t *testing.T) test {
|
|
o := &acme.Order{
|
|
ID: orderID,
|
|
Status: acme.StatusValid,
|
|
CertificateID: "certID",
|
|
Error: acme.NewError(acme.ErrorMalformedType, "The request message was malformed"),
|
|
}
|
|
return test{
|
|
o: o,
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, old, b)
|
|
|
|
dbNew := new(dbOrder)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbNew))
|
|
assert.Equals(t, dbNew.ID, dbo.ID)
|
|
assert.Equals(t, dbNew.AccountID, dbo.AccountID)
|
|
assert.Equals(t, dbNew.ProvisionerID, dbo.ProvisionerID)
|
|
assert.Equals(t, dbNew.CertificateID, o.CertificateID)
|
|
assert.Equals(t, dbNew.Status, o.Status)
|
|
assert.Equals(t, dbNew.CreatedAt, dbo.CreatedAt)
|
|
assert.Equals(t, dbNew.ExpiresAt, dbo.ExpiresAt)
|
|
assert.Equals(t, dbNew.NotBefore, dbo.NotBefore)
|
|
assert.Equals(t, dbNew.NotAfter, dbo.NotAfter)
|
|
assert.Equals(t, dbNew.AuthorizationIDs, dbo.AuthorizationIDs)
|
|
assert.Equals(t, dbNew.Identifiers, dbo.Identifiers)
|
|
assert.Equals(t, dbNew.Error.Error(), o.Error.Error())
|
|
return nil, false, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.New("error saving acme order: force"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
o := &acme.Order{
|
|
ID: orderID,
|
|
Status: acme.StatusValid,
|
|
CertificateID: "certID",
|
|
Error: acme.NewError(acme.ErrorMalformedType, "The request message was malformed"),
|
|
}
|
|
return test{
|
|
o: o,
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, string(key), orderID)
|
|
|
|
return b, nil
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, old, b)
|
|
|
|
dbNew := new(dbOrder)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbNew))
|
|
assert.Equals(t, dbNew.ID, dbo.ID)
|
|
assert.Equals(t, dbNew.AccountID, dbo.AccountID)
|
|
assert.Equals(t, dbNew.ProvisionerID, dbo.ProvisionerID)
|
|
assert.Equals(t, dbNew.CertificateID, o.CertificateID)
|
|
assert.Equals(t, dbNew.Status, o.Status)
|
|
assert.Equals(t, dbNew.CreatedAt, dbo.CreatedAt)
|
|
assert.Equals(t, dbNew.ExpiresAt, dbo.ExpiresAt)
|
|
assert.Equals(t, dbNew.NotBefore, dbo.NotBefore)
|
|
assert.Equals(t, dbNew.NotAfter, dbo.NotAfter)
|
|
assert.Equals(t, dbNew.AuthorizationIDs, dbo.AuthorizationIDs)
|
|
assert.Equals(t, dbNew.Identifiers, dbo.Identifiers)
|
|
assert.Equals(t, dbNew.Error.Error(), o.Error.Error())
|
|
return nu, true, nil
|
|
},
|
|
},
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if err := d.UpdateOrder(context.Background(), tc.o); err != nil {
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, tc.o.ID, dbo.ID)
|
|
assert.Equals(t, tc.o.CertificateID, "certID")
|
|
assert.Equals(t, tc.o.Status, acme.StatusValid)
|
|
assert.Equals(t, tc.o.Error.Error(), acme.NewError(acme.ErrorMalformedType, "The request message was malformed").Error())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_CreateOrder(t *testing.T) {
|
|
now := clock.Now()
|
|
nbf := now.Add(5 * time.Minute)
|
|
naf := now.Add(15 * time.Minute)
|
|
type test struct {
|
|
db nosql.DB
|
|
o *acme.Order
|
|
err error
|
|
_id *string
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/order-save-error": func(t *testing.T) test {
|
|
o := &acme.Order{
|
|
AccountID: "accID",
|
|
ProvisionerID: "provID",
|
|
CertificateID: "certID",
|
|
Status: acme.StatusValid,
|
|
ExpiresAt: now,
|
|
NotBefore: nbf,
|
|
NotAfter: naf,
|
|
Identifiers: []acme.Identifier{
|
|
{Type: "dns", Value: "test.ca.smallstep.com"},
|
|
{Type: "dns", Value: "example.foo.com"},
|
|
},
|
|
AuthorizationIDs: []string{"foo", "bar"},
|
|
}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, string(bucket), string(orderTable))
|
|
assert.Equals(t, string(key), o.ID)
|
|
assert.Equals(t, old, nil)
|
|
|
|
dbo := new(dbOrder)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbo))
|
|
assert.Equals(t, dbo.ID, o.ID)
|
|
assert.Equals(t, dbo.AccountID, o.AccountID)
|
|
assert.Equals(t, dbo.ProvisionerID, o.ProvisionerID)
|
|
assert.Equals(t, dbo.CertificateID, "")
|
|
assert.Equals(t, dbo.Status, o.Status)
|
|
assert.True(t, dbo.CreatedAt.Add(-time.Minute).Before(now))
|
|
assert.True(t, dbo.CreatedAt.Add(time.Minute).After(now))
|
|
assert.Equals(t, dbo.ExpiresAt, o.ExpiresAt)
|
|
assert.Equals(t, dbo.NotBefore, o.NotBefore)
|
|
assert.Equals(t, dbo.NotAfter, o.NotAfter)
|
|
assert.Equals(t, dbo.AuthorizationIDs, o.AuthorizationIDs)
|
|
assert.Equals(t, dbo.Identifiers, o.Identifiers)
|
|
assert.Equals(t, dbo.Error, nil)
|
|
return nil, false, errors.New("force")
|
|
},
|
|
},
|
|
o: o,
|
|
err: errors.New("error saving acme order: force"),
|
|
}
|
|
},
|
|
"fail/orderIDsByOrderUpdate-error": func(t *testing.T) test {
|
|
o := &acme.Order{
|
|
AccountID: "accID",
|
|
ProvisionerID: "provID",
|
|
CertificateID: "certID",
|
|
Status: acme.StatusValid,
|
|
ExpiresAt: now,
|
|
NotBefore: nbf,
|
|
NotAfter: naf,
|
|
Identifiers: []acme.Identifier{
|
|
{Type: "dns", Value: "test.ca.smallstep.com"},
|
|
{Type: "dns", Value: "example.foo.com"},
|
|
},
|
|
AuthorizationIDs: []string{"foo", "bar"},
|
|
}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(ordersByAccountIDTable))
|
|
assert.Equals(t, string(key), o.AccountID)
|
|
return nil, errors.New("force")
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, string(bucket), string(orderTable))
|
|
assert.Equals(t, string(key), o.ID)
|
|
assert.Equals(t, old, nil)
|
|
|
|
dbo := new(dbOrder)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbo))
|
|
assert.Equals(t, dbo.ID, o.ID)
|
|
assert.Equals(t, dbo.AccountID, o.AccountID)
|
|
assert.Equals(t, dbo.ProvisionerID, o.ProvisionerID)
|
|
assert.Equals(t, dbo.CertificateID, "")
|
|
assert.Equals(t, dbo.Status, o.Status)
|
|
assert.True(t, dbo.CreatedAt.Add(-time.Minute).Before(now))
|
|
assert.True(t, dbo.CreatedAt.Add(time.Minute).After(now))
|
|
assert.Equals(t, dbo.ExpiresAt, o.ExpiresAt)
|
|
assert.Equals(t, dbo.NotBefore, o.NotBefore)
|
|
assert.Equals(t, dbo.NotAfter, o.NotAfter)
|
|
assert.Equals(t, dbo.AuthorizationIDs, o.AuthorizationIDs)
|
|
assert.Equals(t, dbo.Identifiers, o.Identifiers)
|
|
assert.Equals(t, dbo.Error, nil)
|
|
return nu, true, nil
|
|
},
|
|
},
|
|
o: o,
|
|
err: errors.New("error loading orderIDs for account accID: force"),
|
|
}
|
|
},
|
|
"ok": func(t *testing.T) test {
|
|
var (
|
|
id string
|
|
idptr = &id
|
|
)
|
|
|
|
o := &acme.Order{
|
|
AccountID: "accID",
|
|
ProvisionerID: "provID",
|
|
Status: acme.StatusValid,
|
|
ExpiresAt: now,
|
|
NotBefore: nbf,
|
|
NotAfter: naf,
|
|
Identifiers: []acme.Identifier{
|
|
{Type: "dns", Value: "test.ca.smallstep.com"},
|
|
{Type: "dns", Value: "example.foo.com"},
|
|
},
|
|
AuthorizationIDs: []string{"foo", "bar"},
|
|
}
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, string(bucket), string(ordersByAccountIDTable))
|
|
assert.Equals(t, string(key), o.AccountID)
|
|
return nil, database.ErrNotFound
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
b, err := json.Marshal([]string{o.ID})
|
|
assert.FatalError(t, err)
|
|
assert.Equals(t, string(key), "accID")
|
|
assert.Equals(t, old, nil)
|
|
assert.Equals(t, nu, b)
|
|
return nu, true, nil
|
|
case string(orderTable):
|
|
*idptr = string(key)
|
|
assert.Equals(t, string(key), o.ID)
|
|
assert.Equals(t, old, nil)
|
|
|
|
dbo := new(dbOrder)
|
|
assert.FatalError(t, json.Unmarshal(nu, dbo))
|
|
assert.Equals(t, dbo.ID, o.ID)
|
|
assert.Equals(t, dbo.AccountID, o.AccountID)
|
|
assert.Equals(t, dbo.ProvisionerID, o.ProvisionerID)
|
|
assert.Equals(t, dbo.CertificateID, "")
|
|
assert.Equals(t, dbo.Status, o.Status)
|
|
assert.True(t, dbo.CreatedAt.Add(-time.Minute).Before(now))
|
|
assert.True(t, dbo.CreatedAt.Add(time.Minute).After(now))
|
|
assert.Equals(t, dbo.ExpiresAt, o.ExpiresAt)
|
|
assert.Equals(t, dbo.NotBefore, o.NotBefore)
|
|
assert.Equals(t, dbo.NotAfter, o.NotAfter)
|
|
assert.Equals(t, dbo.AuthorizationIDs, o.AuthorizationIDs)
|
|
assert.Equals(t, dbo.Identifiers, o.Identifiers)
|
|
assert.Equals(t, dbo.Error, nil)
|
|
return nu, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
o: o,
|
|
_id: idptr,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
if err := d.CreateOrder(context.Background(), tc.o); err != nil {
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
} else {
|
|
if assert.Nil(t, tc.err) {
|
|
assert.Equals(t, tc.o.ID, *tc._id)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDB_updateAddOrderIDs(t *testing.T) {
|
|
accID := "accID"
|
|
type test struct {
|
|
db nosql.DB
|
|
err error
|
|
acmeErr *acme.Error
|
|
addOids []string
|
|
res []string
|
|
}
|
|
var tests = map[string]func(t *testing.T) test{
|
|
"fail/db.Get-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, ordersByAccountIDTable)
|
|
assert.Equals(t, key, []byte(accID))
|
|
return nil, errors.New("force")
|
|
},
|
|
},
|
|
err: errors.Errorf("error loading orderIDs for account %s", accID),
|
|
}
|
|
},
|
|
"fail/unmarshal-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, ordersByAccountIDTable)
|
|
assert.Equals(t, key, []byte(accID))
|
|
return []byte("foo"), nil
|
|
},
|
|
},
|
|
err: errors.Errorf("error unmarshaling orderIDs for account %s", accID),
|
|
}
|
|
},
|
|
"fail/db.Get-order-error": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
assert.Equals(t, key, []byte(accID))
|
|
b, err := json.Marshal([]string{"foo", "bar"})
|
|
assert.FatalError(t, err)
|
|
return b, nil
|
|
case string(orderTable):
|
|
assert.Equals(t, key, []byte("foo"))
|
|
return nil, errors.New("force")
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
acmeErr: acme.NewErrorISE("error loading order foo for account accID: error loading order foo: force"),
|
|
}
|
|
},
|
|
"fail/update-order-status-error": func(t *testing.T) test {
|
|
expiry := clock.Now().Add(-5 * time.Minute)
|
|
ofoo := &dbOrder{
|
|
ID: "foo",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: expiry,
|
|
}
|
|
bfoo, err := json.Marshal(ofoo)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
assert.Equals(t, key, []byte(accID))
|
|
b, err := json.Marshal([]string{"foo", "bar"})
|
|
assert.FatalError(t, err)
|
|
return b, nil
|
|
case string(orderTable):
|
|
assert.Equals(t, key, []byte("foo"))
|
|
return bfoo, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, key, []byte("foo"))
|
|
assert.Equals(t, old, bfoo)
|
|
|
|
newdbo := new(dbOrder)
|
|
assert.FatalError(t, json.Unmarshal(nu, newdbo))
|
|
assert.Equals(t, newdbo.ID, "foo")
|
|
assert.Equals(t, newdbo.Status, acme.StatusInvalid)
|
|
assert.Equals(t, newdbo.ExpiresAt, expiry)
|
|
assert.Equals(t, newdbo.Error.Error(), acme.NewError(acme.ErrorMalformedType, "The request message was malformed").Error())
|
|
return nil, false, errors.New("force")
|
|
},
|
|
},
|
|
acmeErr: acme.NewErrorISE("error updating order foo for account accID: error updating order: error saving acme order: force"),
|
|
}
|
|
},
|
|
"fail/db.save-order-error": func(t *testing.T) test {
|
|
addOids := []string{"foo", "bar"}
|
|
b, err := json.Marshal(addOids)
|
|
assert.FatalError(t, err)
|
|
delCount := 0
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
assert.Equals(t, bucket, ordersByAccountIDTable)
|
|
assert.Equals(t, key, []byte(accID))
|
|
return nil, database.ErrNotFound
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
assert.Equals(t, bucket, ordersByAccountIDTable)
|
|
assert.Equals(t, key, []byte(accID))
|
|
assert.Equals(t, old, nil)
|
|
assert.Equals(t, nu, b)
|
|
return nil, false, errors.New("force")
|
|
},
|
|
MDel: func(bucket, key []byte) error {
|
|
delCount++
|
|
switch delCount {
|
|
case 1:
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, key, []byte("foo"))
|
|
return nil
|
|
case 2:
|
|
assert.Equals(t, bucket, orderTable)
|
|
assert.Equals(t, key, []byte("bar"))
|
|
return nil
|
|
default:
|
|
assert.FatalError(t, errors.New("delete should only be called twice"))
|
|
return errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
addOids: addOids,
|
|
err: errors.Errorf("error saving orderIDs index for account %s", accID),
|
|
}
|
|
},
|
|
"ok/no-old": func(t *testing.T) test {
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
return nil, database.ErrNotFound
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
assert.Equals(t, key, []byte(accID))
|
|
assert.Equals(t, old, nil)
|
|
assert.Equals(t, nu, nil)
|
|
return nil, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
res: []string{},
|
|
}
|
|
},
|
|
"ok/all-old-not-pending": func(t *testing.T) test {
|
|
oldOids := []string{"foo", "bar"}
|
|
bOldOids, err := json.Marshal(oldOids)
|
|
assert.FatalError(t, err)
|
|
expiry := clock.Now().Add(-5 * time.Minute)
|
|
ofoo := &dbOrder{
|
|
ID: "foo",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: expiry,
|
|
}
|
|
bfoo, err := json.Marshal(ofoo)
|
|
assert.FatalError(t, err)
|
|
obar := &dbOrder{
|
|
ID: "bar",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: expiry,
|
|
}
|
|
bbar, err := json.Marshal(obar)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
return bOldOids, nil
|
|
case string(orderTable):
|
|
switch string(key) {
|
|
case "foo":
|
|
assert.Equals(t, key, []byte("foo"))
|
|
return bfoo, nil
|
|
case "bar":
|
|
assert.Equals(t, key, []byte("bar"))
|
|
return bbar, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected key %s", string(key)))
|
|
return nil, errors.New("force")
|
|
}
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(orderTable):
|
|
return nil, true, nil
|
|
case string(ordersByAccountIDTable):
|
|
assert.Equals(t, key, []byte(accID))
|
|
assert.Equals(t, old, bOldOids)
|
|
assert.Equals(t, nu, nil)
|
|
return nil, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
res: []string{},
|
|
}
|
|
},
|
|
"ok/old-and-new": func(t *testing.T) test {
|
|
oldOids := []string{"foo", "bar"}
|
|
bOldOids, err := json.Marshal(oldOids)
|
|
assert.FatalError(t, err)
|
|
addOids := []string{"zap", "zar"}
|
|
bAddOids, err := json.Marshal(addOids)
|
|
assert.FatalError(t, err)
|
|
expiry := clock.Now().Add(-5 * time.Minute)
|
|
ofoo := &dbOrder{
|
|
ID: "foo",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: expiry,
|
|
}
|
|
bfoo, err := json.Marshal(ofoo)
|
|
assert.FatalError(t, err)
|
|
obar := &dbOrder{
|
|
ID: "bar",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: expiry,
|
|
}
|
|
bbar, err := json.Marshal(obar)
|
|
assert.FatalError(t, err)
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(ordersByAccountIDTable):
|
|
return bOldOids, nil
|
|
case string(orderTable):
|
|
switch string(key) {
|
|
case "foo":
|
|
assert.Equals(t, key, []byte("foo"))
|
|
return bfoo, nil
|
|
case "bar":
|
|
assert.Equals(t, key, []byte("bar"))
|
|
return bbar, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected key %s", string(key)))
|
|
return nil, errors.New("force")
|
|
}
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(orderTable):
|
|
return nil, true, nil
|
|
case string(ordersByAccountIDTable):
|
|
assert.Equals(t, key, []byte(accID))
|
|
assert.Equals(t, old, bOldOids)
|
|
assert.Equals(t, nu, bAddOids)
|
|
return nil, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
addOids: addOids,
|
|
res: addOids,
|
|
}
|
|
},
|
|
"ok/old-and-new-2": func(t *testing.T) test {
|
|
oldOids := []string{"foo", "bar", "baz"}
|
|
bOldOids, err := json.Marshal(oldOids)
|
|
assert.FatalError(t, err)
|
|
addOids := []string{"zap", "zar"}
|
|
now := clock.Now()
|
|
min5 := now.Add(5 * time.Minute)
|
|
expiry := now.Add(-5 * time.Minute)
|
|
|
|
o1 := &dbOrder{
|
|
ID: "foo",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: min5,
|
|
AuthorizationIDs: []string{"a"},
|
|
}
|
|
bo1, err := json.Marshal(o1)
|
|
assert.FatalError(t, err)
|
|
o2 := &dbOrder{
|
|
ID: "bar",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: expiry,
|
|
}
|
|
bo2, err := json.Marshal(o2)
|
|
assert.FatalError(t, err)
|
|
o3 := &dbOrder{
|
|
ID: "baz",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: min5,
|
|
AuthorizationIDs: []string{"b"},
|
|
}
|
|
bo3, err := json.Marshal(o3)
|
|
assert.FatalError(t, err)
|
|
|
|
az1 := &dbAuthz{
|
|
ID: "a",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: min5,
|
|
ChallengeIDs: []string{"aa"},
|
|
}
|
|
baz1, err := json.Marshal(az1)
|
|
assert.FatalError(t, err)
|
|
az2 := &dbAuthz{
|
|
ID: "b",
|
|
Status: acme.StatusPending,
|
|
ExpiresAt: min5,
|
|
ChallengeIDs: []string{"bb"},
|
|
}
|
|
baz2, err := json.Marshal(az2)
|
|
assert.FatalError(t, err)
|
|
|
|
ch1 := &dbChallenge{
|
|
ID: "aa",
|
|
Status: acme.StatusPending,
|
|
}
|
|
bch1, err := json.Marshal(ch1)
|
|
assert.FatalError(t, err)
|
|
ch2 := &dbChallenge{
|
|
ID: "bb",
|
|
Status: acme.StatusPending,
|
|
}
|
|
bch2, err := json.Marshal(ch2)
|
|
assert.FatalError(t, err)
|
|
|
|
newOids := append([]string{"foo", "baz"}, addOids...)
|
|
bNewOids, err := json.Marshal(newOids)
|
|
assert.FatalError(t, err)
|
|
|
|
return test{
|
|
db: &db.MockNoSQLDB{
|
|
MGet: func(bucket, key []byte) ([]byte, error) {
|
|
switch string(bucket) {
|
|
case string(authzTable):
|
|
switch string(key) {
|
|
case "a":
|
|
return baz1, nil
|
|
case "b":
|
|
return baz2, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected authz key %s", string(key)))
|
|
return nil, errors.New("force")
|
|
}
|
|
case string(challengeTable):
|
|
switch string(key) {
|
|
case "aa":
|
|
return bch1, nil
|
|
case "bb":
|
|
return bch2, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected challenge key %s", string(key)))
|
|
return nil, errors.New("force")
|
|
}
|
|
case string(ordersByAccountIDTable):
|
|
return bOldOids, nil
|
|
case string(orderTable):
|
|
switch string(key) {
|
|
case "foo":
|
|
return bo1, nil
|
|
case "bar":
|
|
return bo2, nil
|
|
case "baz":
|
|
return bo3, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected key %s", string(key)))
|
|
return nil, errors.New("force")
|
|
}
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, errors.New("force")
|
|
}
|
|
},
|
|
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
|
|
switch string(bucket) {
|
|
case string(orderTable):
|
|
return nil, true, nil
|
|
case string(ordersByAccountIDTable):
|
|
assert.Equals(t, key, []byte(accID))
|
|
assert.Equals(t, old, bOldOids)
|
|
assert.Equals(t, nu, bNewOids)
|
|
return nil, true, nil
|
|
default:
|
|
assert.FatalError(t, errors.Errorf("unexpected bucket %s", string(bucket)))
|
|
return nil, false, errors.New("force")
|
|
}
|
|
},
|
|
},
|
|
addOids: addOids,
|
|
res: newOids,
|
|
}
|
|
},
|
|
}
|
|
for name, run := range tests {
|
|
tc := run(t)
|
|
t.Run(name, func(t *testing.T) {
|
|
d := DB{db: tc.db}
|
|
var (
|
|
res []string
|
|
err error
|
|
)
|
|
if tc.addOids == nil {
|
|
res, err = d.updateAddOrderIDs(context.Background(), accID)
|
|
} else {
|
|
res, err = d.updateAddOrderIDs(context.Background(), accID, tc.addOids...)
|
|
}
|
|
|
|
if err != nil {
|
|
switch k := err.(type) {
|
|
case *acme.Error:
|
|
if assert.NotNil(t, tc.acmeErr) {
|
|
assert.Equals(t, k.Type, tc.acmeErr.Type)
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
assert.Equals(t, k.Status, tc.acmeErr.Status)
|
|
assert.Equals(t, k.Err.Error(), tc.acmeErr.Err.Error())
|
|
assert.Equals(t, k.Detail, tc.acmeErr.Detail)
|
|
}
|
|
default:
|
|
if assert.NotNil(t, tc.err) {
|
|
assert.HasPrefix(t, err.Error(), tc.err.Error())
|
|
}
|
|
}
|
|
} else if assert.Nil(t, tc.err) {
|
|
assert.True(t, reflect.DeepEqual(res, tc.res))
|
|
}
|
|
})
|
|
}
|
|
}
|