boulder/cmd/akamai-purger/main_test.go

191 lines
5.4 KiB
Go

package notmain
import (
"context"
"errors"
"fmt"
"testing"
"time"
akamaipb "github.com/letsencrypt/boulder/akamai/proto"
"github.com/letsencrypt/boulder/config"
blog "github.com/letsencrypt/boulder/log"
"github.com/letsencrypt/boulder/test"
)
func TestThroughput_optimizeAndValidate(t *testing.T) {
dur := func(in time.Duration) config.Duration { return config.Duration{Duration: in} }
tests := []struct {
name string
input Throughput
want Throughput
wantErr string
}{
{
"negative instances",
Throughput{defaultEntriesPerBatch, dur(defaultPurgeBatchInterval), -1},
Throughput{},
"must be positive",
},
{
"negative batch interval",
Throughput{defaultEntriesPerBatch, config.Duration{Duration: -1}, -1},
Throughput{},
"must be positive",
},
{
"negative entries per batch",
Throughput{-1, dur(defaultPurgeBatchInterval), 1},
Throughput{},
"must be positive",
},
{
"empty input computes sane defaults",
Throughput{},
Throughput{defaultEntriesPerBatch, dur(defaultPurgeBatchInterval), 1},
"",
},
{
"strict configuration is honored",
Throughput{2, dur(1 * time.Second), 1},
Throughput{2, dur(1 * time.Second), 1},
"",
},
{
"slightly looser configuration still within limits",
Throughput{defaultEntriesPerBatch, dur(defaultPurgeBatchInterval - time.Millisecond), 1},
Throughput{defaultEntriesPerBatch, dur(defaultPurgeBatchInterval - time.Millisecond), 1},
"",
},
{
"too many requests per second",
Throughput{QueueEntriesPerBatch: 1, PurgeBatchInterval: dur(19999 * time.Microsecond)},
Throughput{},
"requests per second limit",
},
{
"too many URLs per second",
Throughput{PurgeBatchInterval: dur(29 * time.Millisecond)},
Throughput{},
"URLs per second limit",
},
{
"too many bytes per request",
Throughput{QueueEntriesPerBatch: 125, PurgeBatchInterval: dur(1 * time.Second)},
Throughput{},
"bytes per request limit",
},
{
"two instances computes sane defaults",
Throughput{TotalInstances: 2},
Throughput{defaultEntriesPerBatch, dur(defaultPurgeBatchInterval * 2), 2},
"",
},
{
"too many requests per second across multiple instances",
Throughput{PurgeBatchInterval: dur(defaultPurgeBatchInterval), TotalInstances: 2},
Throughput{},
"requests per second limit",
},
{
"too many entries per second across multiple instances",
Throughput{PurgeBatchInterval: dur(59 * time.Millisecond), TotalInstances: 2},
Throughput{},
"URLs per second limit",
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
err := tc.input.optimizeAndValidate()
if tc.wantErr != "" {
test.AssertError(t, err, "")
test.AssertContains(t, err.Error(), tc.wantErr)
} else {
test.AssertNotError(t, err, "")
test.AssertEquals(t, tc.input, tc.want)
}
})
}
}
type mockCCU struct {
akamaipb.AkamaiPurgerClient
}
func (m *mockCCU) Purge(urls []string) error {
return errors.New("Lol, I'm a mock")
}
func TestAkamaiPurgerQueue(t *testing.T) {
ap := &akamaiPurger{
maxStackSize: 250,
entriesPerBatch: 2,
client: &mockCCU{},
log: blog.NewMock(),
}
// Add 250 entries to fill the stack.
for i := range 250 {
req := akamaipb.PurgeRequest{Urls: []string{fmt.Sprintf("http://test.com/%d", i)}}
_, err := ap.Purge(context.Background(), &req)
test.AssertNotError(t, err, fmt.Sprintf("Purge failed for entry %d.", i))
}
// Add another entry to the stack and using the Purge method.
req := akamaipb.PurgeRequest{Urls: []string{"http://test.com/250"}}
_, err := ap.Purge(context.Background(), &req)
test.AssertNotError(t, err, "Purge failed.")
// Verify that the stack is still full.
test.AssertEquals(t, len(ap.toPurge), 250)
// Verify that the first entry in the stack is the entry we just added.
test.AssertEquals(t, ap.toPurge[len(ap.toPurge)-1][0], "http://test.com/250")
// Verify that the last entry in the stack is the second entry we added.
test.AssertEquals(t, ap.toPurge[0][0], "http://test.com/1")
expectedTopEntryAfterFailure := ap.toPurge[len(ap.toPurge)-(ap.entriesPerBatch+1)][0]
// Fail to purge a batch of entries from the stack.
batch := ap.takeBatch()
test.AssertNotNil(t, batch, "Batch should not be nil.")
err = ap.purgeBatch(batch)
test.AssertError(t, err, "Mock should have failed to purge.")
// Verify that the stack is no longer full.
test.AssertEquals(t, len(ap.toPurge), 248)
// The first entry of the next batch should be on the top after the failed
// purge.
test.AssertEquals(t, ap.toPurge[len(ap.toPurge)-1][0], expectedTopEntryAfterFailure)
}
func TestAkamaiPurgerQueueWithOneEntry(t *testing.T) {
ap := &akamaiPurger{
maxStackSize: 250,
entriesPerBatch: 2,
client: &mockCCU{},
log: blog.NewMock(),
}
// Add one entry to the stack and using the Purge method.
req := akamaipb.PurgeRequest{Urls: []string{"http://test.com/0"}}
_, err := ap.Purge(context.Background(), &req)
test.AssertNotError(t, err, "Purge failed.")
test.AssertEquals(t, len(ap.toPurge), 1)
test.AssertEquals(t, ap.toPurge[len(ap.toPurge)-1][0], "http://test.com/0")
// Fail to purge a batch of entries from the stack.
batch := ap.takeBatch()
test.AssertNotNil(t, batch, "Batch should not be nil.")
err = ap.purgeBatch(batch)
test.AssertError(t, err, "Mock should have failed to purge.")
// Verify that the stack no longer contains our entry.
test.AssertEquals(t, len(ap.toPurge), 0)
}