Skip to content

Commit

Permalink
beatlabs#128 refine requirements
Browse files Browse the repository at this point in the history
Signed-off-by: Vangelis Katikaridis <[email protected]>
  • Loading branch information
drakos74 committed Mar 25, 2020
1 parent 306b5e7 commit 44cd4cd
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 14 deletions.
19 changes: 17 additions & 2 deletions sync/http/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"github.com/beatlabs/patron/sync"
)

// TODO : add comments where applicable

type CacheHeader int

const (
Expand All @@ -21,11 +23,13 @@ const (
no_store
no_transform
only_if_cached
public
private

CacheControlHeader = "CACHE-CONTROL"
)

var cacheHeaders = map[string]CacheHeader{"max-age": max_age, "max-stale": max_stale, "min-fresh": min_fresh, "no-cache": no_cache, "no-store": no_store, "no-transform": no_transform, "only-if-cached": only_if_cached}
var cacheHeaders = map[string]CacheHeader{"public": public, "private": private, "max-age": max_age, "max-stale": max_stale, "min-fresh": min_fresh, "no-cache": no_cache, "no-store": no_store, "no-transform": no_transform, "only-if-cached": only_if_cached}

type TimeInstant func() int64

Expand All @@ -47,6 +51,8 @@ func cacheHandler(hnd sync.ProcessorFunc, cache cache.Cache, instant TimeInstant
// TODO : cache also errors ???
if r, ok := resp.(cachedResponse); ok && notExpired(now, r.lastValid, ttl) {
println(fmt.Sprintf("cache = %v", cache))
// TODO : set the headers
// ETag , Last-Modified
return r.response, r.err
} else {
log.Errorf("could not parse cached response from %v", resp)
Expand Down Expand Up @@ -121,14 +127,22 @@ func extractCacheHeaders(request *sync.Request) (bool, bool, int64) {
}
case no_cache:
/**
retrieve from the store
return response if entity has changed
e.g. (304 response if nothing has changed : 304 Not Modified)
it SHOULD NOT include min-fresh, max-stale, or max-age.
reqeust should be accompanied by an ETag token
*/
fallthrough
case no_store:
/**
no storage whatsoever
*/
fallthrough
case private:
/**
server does not support private caching,
this should be the responsibility of the client
*/
noCache = true
case no_transform:
/**
Expand Down Expand Up @@ -159,6 +173,7 @@ type cachedResponse struct {
}

func createRequestKey(request *sync.Request) string {
// TODO : define the key requirements in more detail
return fmt.Sprintf("%s:%s", request.Headers, request.Fields)
}

Expand Down
94 changes: 82 additions & 12 deletions sync/http/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,17 +41,20 @@ func TestExtractCacheHeaders(t *testing.T) {

}

func TestCacheHandler(t *testing.T) {
type testArgs struct {
header map[string]string
fields map[string]string
response *sync.Response
timeInstance int64
err error
}

type args struct {
header map[string]string
fields map[string]string
response *sync.Response
timeInstance int64
err error
}
// TestCacheMaxAgeHeader tests the cache implementation
// for the same request,
// for header max-age
func TestCacheMaxAgeHeader(t *testing.T) {

params := [][]args{
args := [][]testArgs{
// cache expiration with max-age header
{
// initial request
Expand Down Expand Up @@ -105,20 +108,88 @@ func TestCacheHandler(t *testing.T) {
},
}

run(t, args)

}

// TestCacheMaxAgeHeader tests the cache implementation
// for the same request,
// for header max-age
func TestCacheMinFreshHeader(t *testing.T) {

args := [][]testArgs{
// cache expiration with max-age header
{
// initial request
{
fields: map[string]string{"VALUE": "1"},
header: map[string]string{CacheControlHeader: "min-fresh=10"},
response: sync.NewResponse(10),
timeInstance: 1,
err: nil,
},
// cache response
{
fields: map[string]string{"VALUE": "1"},
header: map[string]string{CacheControlHeader: "max-age=10"},
response: sync.NewResponse(10),
timeInstance: 9,
err: nil,
},
// still cached response because we are at the edge of the expiry e.g. 11 - 1 = 10
{
fields: map[string]string{"VALUE": "1"},
header: map[string]string{CacheControlHeader: "max-age=10"},
response: sync.NewResponse(10),
timeInstance: 11,
err: nil,
},
// new response because cache has expired
{
fields: map[string]string{"VALUE": "1"},
header: map[string]string{CacheControlHeader: "max-age=10"},
response: sync.NewResponse(120),
timeInstance: 12,
err: nil,
},
// make an extra request with the new cache value
{
fields: map[string]string{"VALUE": "1"},
header: map[string]string{CacheControlHeader: "max-age=10"},
response: sync.NewResponse(120),
timeInstance: 15,
err: nil,
},
// and another when the previous has expired 12 + 10 = 22
{
fields: map[string]string{"VALUE": "1"},
header: map[string]string{CacheControlHeader: "max-age=10"},
response: sync.NewResponse(230),
timeInstance: 23,
err: nil,
},
},
}

run(t, args)

}
func run(t *testing.T, args [][]testArgs) {
handler := func(timeInstance int64) func(ctx context.Context, request *sync.Request) (*sync.Response, error) {
return func(ctx context.Context, request *sync.Request) (*sync.Response, error) {
i, err := strconv.Atoi(request.Fields["VALUE"])
if err != nil {
return nil, err
}
// return the specified parameter multiplied by the time instant
return sync.NewResponse(i * 10 * int(timeInstance)), nil
}
}

cache := &testingCache{cache: make(map[string]interface{})}

for _, param := range params {
for _, arg := range param {
for _, testArg := range args {
for _, arg := range testArg {
request := sync.NewRequest(arg.fields, nil, arg.header, nil)
// initial request
response, err := cacheHandler(handler(arg.timeInstance), cache, func() int64 {
Expand All @@ -135,7 +206,6 @@ func TestCacheHandler(t *testing.T) {
}
}
}

}

type testingCache struct {
Expand Down

0 comments on commit 44cd4cd

Please sign in to comment.