gethttp01 validate unit tests working

This commit is contained in:
max furman 2021-03-23 22:12:25 -07:00
parent 7f9ffbd514
commit 3612a0b990
7 changed files with 1487 additions and 900 deletions

File diff suppressed because it is too large Load diff

View file

@ -19,7 +19,7 @@ type dbAuthz struct {
Identifier acme.Identifier `json:"identifier"`
Status acme.Status `json:"status"`
ExpiresAt time.Time `json:"expiresAt"`
Challenges []string `json:"challenges"`
ChallengeIDs []string `json:"challengeIDs"`
Wildcard bool `json:"wildcard"`
CreatedAt time.Time `json:"createdAt"`
Error *acme.Error `json:"error"`
@ -55,8 +55,8 @@ func (db *DB) GetAuthorization(ctx context.Context, id string) (*acme.Authorizat
if err != nil {
return nil, err
}
var chs = make([]*acme.Challenge, len(dbaz.Challenges))
for i, chID := range dbaz.Challenges {
var chs = make([]*acme.Challenge, len(dbaz.ChallengeIDs))
for i, chID := range dbaz.ChallengeIDs {
chs[i], err = db.GetChallenge(ctx, chID, id)
if err != nil {
return nil, err
@ -97,7 +97,7 @@ func (db *DB) CreateAuthorization(ctx context.Context, az *acme.Authorization) e
CreatedAt: now,
ExpiresAt: az.ExpiresAt,
Identifier: az.Identifier,
Challenges: chIDs,
ChallengeIDs: chIDs,
Token: az.Token,
Wildcard: az.Wildcard,
}

View file

@ -76,7 +76,7 @@ func TestDB_getDBAuthz(t *testing.T) {
CreatedAt: now,
ExpiresAt: now.Add(5 * time.Minute),
Error: acme.NewErrorISE("force"),
Challenges: []string{"foo", "bar"},
ChallengeIDs: []string{"foo", "bar"},
Wildcard: true,
}
b, err := json.Marshal(dbaz)
@ -179,7 +179,7 @@ func TestDB_GetAuthorization(t *testing.T) {
CreatedAt: now,
ExpiresAt: now.Add(5 * time.Minute),
Error: acme.NewErrorISE("force"),
Challenges: []string{"foo", "bar"},
ChallengeIDs: []string{"foo", "bar"},
Wildcard: true,
}
b, err := json.Marshal(dbaz)
@ -217,7 +217,7 @@ func TestDB_GetAuthorization(t *testing.T) {
CreatedAt: now,
ExpiresAt: now.Add(5 * time.Minute),
Error: acme.NewErrorISE("force"),
Challenges: []string{"foo", "bar"},
ChallengeIDs: []string{"foo", "bar"},
Wildcard: true,
}
b, err := json.Marshal(dbaz)
@ -255,7 +255,7 @@ func TestDB_GetAuthorization(t *testing.T) {
CreatedAt: now,
ExpiresAt: now.Add(5 * time.Minute),
Error: acme.NewErrorISE("force"),
Challenges: []string{"foo", "bar"},
ChallengeIDs: []string{"foo", "bar"},
Wildcard: true,
}
b, err := json.Marshal(dbaz)
@ -374,7 +374,7 @@ func TestDB_CreateAuthorization(t *testing.T) {
})
assert.Equals(t, dbaz.Status, az.Status)
assert.Equals(t, dbaz.Token, az.Token)
assert.Equals(t, dbaz.Challenges, []string{"foo", "bar"})
assert.Equals(t, dbaz.ChallengeIDs, []string{"foo", "bar"})
assert.Equals(t, dbaz.Wildcard, az.Wildcard)
assert.Equals(t, dbaz.ExpiresAt, az.ExpiresAt)
assert.Nil(t, dbaz.Error)
@ -428,7 +428,7 @@ func TestDB_CreateAuthorization(t *testing.T) {
})
assert.Equals(t, dbaz.Status, az.Status)
assert.Equals(t, dbaz.Token, az.Token)
assert.Equals(t, dbaz.Challenges, []string{"foo", "bar"})
assert.Equals(t, dbaz.ChallengeIDs, []string{"foo", "bar"})
assert.Equals(t, dbaz.Wildcard, az.Wildcard)
assert.Equals(t, dbaz.ExpiresAt, az.ExpiresAt)
assert.Nil(t, dbaz.Error)
@ -473,7 +473,7 @@ func TestDB_UpdateAuthorization(t *testing.T) {
Token: "token",
CreatedAt: now,
ExpiresAt: now.Add(5 * time.Minute),
Challenges: []string{"foo", "bar"},
ChallengeIDs: []string{"foo", "bar"},
Wildcard: true,
}
b, err := json.Marshal(dbaz)
@ -530,7 +530,7 @@ func TestDB_UpdateAuthorization(t *testing.T) {
assert.Equals(t, dbNew.Identifier, dbaz.Identifier)
assert.Equals(t, dbNew.Status, acme.StatusValid)
assert.Equals(t, dbNew.Token, dbaz.Token)
assert.Equals(t, dbNew.Challenges, dbaz.Challenges)
assert.Equals(t, dbNew.ChallengeIDs, dbaz.ChallengeIDs)
assert.Equals(t, dbNew.Wildcard, dbaz.Wildcard)
assert.Equals(t, dbNew.CreatedAt, dbaz.CreatedAt)
assert.Equals(t, dbNew.ExpiresAt, dbaz.ExpiresAt)
@ -580,7 +580,7 @@ func TestDB_UpdateAuthorization(t *testing.T) {
assert.Equals(t, dbNew.Identifier, dbaz.Identifier)
assert.Equals(t, dbNew.Status, acme.StatusValid)
assert.Equals(t, dbNew.Token, dbaz.Token)
assert.Equals(t, dbNew.Challenges, dbaz.Challenges)
assert.Equals(t, dbNew.ChallengeIDs, dbaz.ChallengeIDs)
assert.Equals(t, dbNew.Wildcard, dbaz.Wildcard)
assert.Equals(t, dbNew.CreatedAt, dbaz.CreatedAt)
assert.Equals(t, dbNew.ExpiresAt, dbaz.ExpiresAt)

View file

@ -42,10 +42,18 @@ func New(db nosqlDB.DB) (*DB, error) {
// save writes the new data to the database, overwriting the old data if it
// existed.
func (db *DB) save(ctx context.Context, id string, nu interface{}, old interface{}, typ string, table []byte) error {
newB, err := json.Marshal(nu)
var (
err error
newB []byte
)
if nu == nil {
newB = nil
} else {
newB, err = json.Marshal(nu)
if err != nil {
return errors.Wrapf(err, "error marshaling acme type: %s, value: %v", typ, nu)
}
}
var oldB []byte
if old == nil {
oldB = nil

View file

@ -110,6 +110,19 @@ func TestDB_save(t *testing.T) {
},
},
},
"ok/nils": test{
nu: nil,
old: nil,
db: &db.MockNoSQLDB{
MCmpAndSwap: func(bucket, key, old, nu []byte) ([]byte, bool, error) {
assert.Equals(t, bucket, challengeTable)
assert.Equals(t, string(key), "id")
assert.Equals(t, old, nil)
assert.Equals(t, nu, nil)
return nu, true, nil
},
},
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {

View file

@ -127,16 +127,18 @@ func (db *DB) updateAddOrderIDs(ctx context.Context, accID string, addOids ...st
defer ordersByAccountMux.Unlock()
b, err := db.db.Get(ordersByAccountIDTable, []byte(accID))
var (
oldOids []string
)
if err != nil {
if nosql.IsErrNotFound(err) {
return []string{}, nil
}
if !nosql.IsErrNotFound(err) {
return nil, errors.Wrapf(err, "error loading orderIDs for account %s", accID)
}
var oids []string
if err := json.Unmarshal(b, &oids); err != nil {
} else {
if err := json.Unmarshal(b, &oldOids); err != nil {
return nil, errors.Wrapf(err, "error unmarshaling orderIDs for account %s", accID)
}
}
// Remove any order that is not in PENDING state and update the stored list
// before returning.
@ -145,7 +147,7 @@ func (db *DB) updateAddOrderIDs(ctx context.Context, accID string, addOids ...st
// The server SHOULD include pending orders and SHOULD NOT include orders
// that are invalid in the array of URLs.
pendOids := []string{}
for _, oid := range oids {
for _, oid := range oldOids {
o, err := db.GetOrder(ctx, oid)
if err != nil {
return nil, acme.WrapErrorISE(err, "error loading order %s for account %s", oid, accID)
@ -158,15 +160,27 @@ func (db *DB) updateAddOrderIDs(ctx context.Context, accID string, addOids ...st
}
}
pendOids = append(pendOids, addOids...)
if len(oids) == 0 {
oids = nil
var (
_old interface{} = oldOids
_new interface{} = pendOids
)
switch {
case len(oldOids) == 0 && len(pendOids) == 0:
// If list has not changed from empty, then no need to write the DB.
return []string{}, nil
case len(oldOids) == 0:
_old = nil
case len(pendOids) == 0:
_new = nil
}
if err = db.save(ctx, accID, pendOids, oids, "orderIDsByAccountID", ordersByAccountIDTable); err != nil {
if err = db.save(ctx, accID, _new, _old, "orderIDsByAccountID", ordersByAccountIDTable); err != nil {
// Delete all orders that may have been previously stored if orderIDsByAccountID update fails.
for _, oid := range addOids {
// Ignore error from delete -- we tried our best.
// TODO when we have logging w/ request ID tracking, logging this error.
db.db.Del(orderTable, []byte(oid))
}
return nil, errors.Wrap(err, "error saving OrderIDsByAccountID index")
return nil, errors.Wrapf(err, "error saving orderIDs index for account %s", accID)
}
return pendOids, nil
}

View file

@ -3,6 +3,7 @@ package nosql
import (
"context"
"encoding/json"
"reflect"
"testing"
"time"
@ -511,8 +512,16 @@ func TestDB_CreateOrder(t *testing.T) {
return nil, nosqldb.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(bucket), string(orderTable))
assert.Equals(t, string(key), o.ID)
assert.Equals(t, old, nil)
@ -532,6 +541,10 @@ func TestDB_CreateOrder(t *testing.T) {
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,
@ -555,3 +568,434 @@ func TestDB_CreateOrder(t *testing.T) {
})
}
}
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, "order has expired").Error())
return nil, false, errors.New("force")
},
},
acmeErr: acme.NewErrorISE("error updating order foo for account accID: 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, nosqldb.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/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) {
db := DB{db: tc.db}
var (
res []string
err error
)
if tc.addOids == nil {
res, err = db.updateAddOrderIDs(context.Background(), accID)
} else {
res, err = db.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))
}
}
})
}
}