Skip to content
Snippets Groups Projects
Commit ed22eb08 authored by Lyuben Penkovski's avatar Lyuben Penkovski
Browse files

Unit tests for policy evaluation endpoint

parent 4b2c985e
No related branches found
No related tags found
1 merge request!10Unit tests for policy service
Pipeline #50256 passed with stage
in 1 minute and 1 second
package regocache_test
import (
"context"
"testing"
"github.com/open-policy-agent/opa/rego"
"github.com/stretchr/testify/assert"
"code.vereign.com/gaiax/tsa/policy/internal/regocache"
"code.vereign.com/gaiax/tsa/policy/internal/service/policy"
)
const regoPolicy = `
package test
allow {
input.val == 1
}
`
func TestNew(t *testing.T) {
cache := regocache.New()
assert.Implements(t, (*policy.RegoCache)(nil), cache)
}
func TestCache_SetAndGet(t *testing.T) {
q1, err := rego.New(
rego.Module("filename.rego", regoPolicy),
rego.Query("data"),
).PrepareForEval(context.Background())
assert.NoError(t, err)
cache := regocache.New()
cache.Set("query1", &q1)
q2, ok := cache.Get("query1")
assert.True(t, ok)
assert.Equal(t, q1, *q2)
}
func TestCache_Purge(t *testing.T) {
q1, err := rego.New(
rego.Module("filename.rego", regoPolicy),
rego.Query("data"),
).PrepareForEval(context.Background())
assert.NoError(t, err)
cache := regocache.New()
cache.Set("query1", &q1)
cache.Purge()
q2, ok := cache.Get("query1")
assert.False(t, ok)
assert.Nil(t, q2)
}
func TestCache_PolicyDataChange(t *testing.T) {
q1, err := rego.New(
rego.Module("filename.rego", regoPolicy),
rego.Query("data"),
).PrepareForEval(context.Background())
assert.NoError(t, err)
cache := regocache.New()
cache.Set("query1", &q1)
cache.PolicyDataChange()
q2, ok := cache.Get("query1")
assert.False(t, ok)
assert.Nil(t, q2)
}
// Code generated by counterfeiter. DO NOT EDIT.
package policyfakes
import (
"sync"
"code.vereign.com/gaiax/tsa/policy/internal/service/policy"
"github.com/open-policy-agent/opa/rego"
)
type FakeRegoCache struct {
GetStub func(string) (*rego.PreparedEvalQuery, bool)
getMutex sync.RWMutex
getArgsForCall []struct {
arg1 string
}
getReturns struct {
result1 *rego.PreparedEvalQuery
result2 bool
}
getReturnsOnCall map[int]struct {
result1 *rego.PreparedEvalQuery
result2 bool
}
SetStub func(string, *rego.PreparedEvalQuery)
setMutex sync.RWMutex
setArgsForCall []struct {
arg1 string
arg2 *rego.PreparedEvalQuery
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeRegoCache) Get(arg1 string) (*rego.PreparedEvalQuery, bool) {
fake.getMutex.Lock()
ret, specificReturn := fake.getReturnsOnCall[len(fake.getArgsForCall)]
fake.getArgsForCall = append(fake.getArgsForCall, struct {
arg1 string
}{arg1})
stub := fake.GetStub
fakeReturns := fake.getReturns
fake.recordInvocation("Get", []interface{}{arg1})
fake.getMutex.Unlock()
if stub != nil {
return stub(arg1)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakeRegoCache) GetCallCount() int {
fake.getMutex.RLock()
defer fake.getMutex.RUnlock()
return len(fake.getArgsForCall)
}
func (fake *FakeRegoCache) GetCalls(stub func(string) (*rego.PreparedEvalQuery, bool)) {
fake.getMutex.Lock()
defer fake.getMutex.Unlock()
fake.GetStub = stub
}
func (fake *FakeRegoCache) GetArgsForCall(i int) string {
fake.getMutex.RLock()
defer fake.getMutex.RUnlock()
argsForCall := fake.getArgsForCall[i]
return argsForCall.arg1
}
func (fake *FakeRegoCache) GetReturns(result1 *rego.PreparedEvalQuery, result2 bool) {
fake.getMutex.Lock()
defer fake.getMutex.Unlock()
fake.GetStub = nil
fake.getReturns = struct {
result1 *rego.PreparedEvalQuery
result2 bool
}{result1, result2}
}
func (fake *FakeRegoCache) GetReturnsOnCall(i int, result1 *rego.PreparedEvalQuery, result2 bool) {
fake.getMutex.Lock()
defer fake.getMutex.Unlock()
fake.GetStub = nil
if fake.getReturnsOnCall == nil {
fake.getReturnsOnCall = make(map[int]struct {
result1 *rego.PreparedEvalQuery
result2 bool
})
}
fake.getReturnsOnCall[i] = struct {
result1 *rego.PreparedEvalQuery
result2 bool
}{result1, result2}
}
func (fake *FakeRegoCache) Set(arg1 string, arg2 *rego.PreparedEvalQuery) {
fake.setMutex.Lock()
fake.setArgsForCall = append(fake.setArgsForCall, struct {
arg1 string
arg2 *rego.PreparedEvalQuery
}{arg1, arg2})
stub := fake.SetStub
fake.recordInvocation("Set", []interface{}{arg1, arg2})
fake.setMutex.Unlock()
if stub != nil {
fake.SetStub(arg1, arg2)
}
}
func (fake *FakeRegoCache) SetCallCount() int {
fake.setMutex.RLock()
defer fake.setMutex.RUnlock()
return len(fake.setArgsForCall)
}
func (fake *FakeRegoCache) SetCalls(stub func(string, *rego.PreparedEvalQuery)) {
fake.setMutex.Lock()
defer fake.setMutex.Unlock()
fake.SetStub = stub
}
func (fake *FakeRegoCache) SetArgsForCall(i int) (string, *rego.PreparedEvalQuery) {
fake.setMutex.RLock()
defer fake.setMutex.RUnlock()
argsForCall := fake.setArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2
}
func (fake *FakeRegoCache) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.getMutex.RLock()
defer fake.getMutex.RUnlock()
fake.setMutex.RLock()
defer fake.setMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeRegoCache) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ policy.RegoCache = new(FakeRegoCache)
// Code generated by counterfeiter. DO NOT EDIT.
package policyfakes
import (
"context"
"sync"
"code.vereign.com/gaiax/tsa/policy/internal/service/policy"
"code.vereign.com/gaiax/tsa/policy/internal/storage"
)
type FakeStorage struct {
PolicyStub func(context.Context, string, string, string) (*storage.Policy, error)
policyMutex sync.RWMutex
policyArgsForCall []struct {
arg1 context.Context
arg2 string
arg3 string
arg4 string
}
policyReturns struct {
result1 *storage.Policy
result2 error
}
policyReturnsOnCall map[int]struct {
result1 *storage.Policy
result2 error
}
SetPolicyLockStub func(context.Context, string, string, string, bool) error
setPolicyLockMutex sync.RWMutex
setPolicyLockArgsForCall []struct {
arg1 context.Context
arg2 string
arg3 string
arg4 string
arg5 bool
}
setPolicyLockReturns struct {
result1 error
}
setPolicyLockReturnsOnCall map[int]struct {
result1 error
}
invocations map[string][][]interface{}
invocationsMutex sync.RWMutex
}
func (fake *FakeStorage) Policy(arg1 context.Context, arg2 string, arg3 string, arg4 string) (*storage.Policy, error) {
fake.policyMutex.Lock()
ret, specificReturn := fake.policyReturnsOnCall[len(fake.policyArgsForCall)]
fake.policyArgsForCall = append(fake.policyArgsForCall, struct {
arg1 context.Context
arg2 string
arg3 string
arg4 string
}{arg1, arg2, arg3, arg4})
stub := fake.PolicyStub
fakeReturns := fake.policyReturns
fake.recordInvocation("Policy", []interface{}{arg1, arg2, arg3, arg4})
fake.policyMutex.Unlock()
if stub != nil {
return stub(arg1, arg2, arg3, arg4)
}
if specificReturn {
return ret.result1, ret.result2
}
return fakeReturns.result1, fakeReturns.result2
}
func (fake *FakeStorage) PolicyCallCount() int {
fake.policyMutex.RLock()
defer fake.policyMutex.RUnlock()
return len(fake.policyArgsForCall)
}
func (fake *FakeStorage) PolicyCalls(stub func(context.Context, string, string, string) (*storage.Policy, error)) {
fake.policyMutex.Lock()
defer fake.policyMutex.Unlock()
fake.PolicyStub = stub
}
func (fake *FakeStorage) PolicyArgsForCall(i int) (context.Context, string, string, string) {
fake.policyMutex.RLock()
defer fake.policyMutex.RUnlock()
argsForCall := fake.policyArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4
}
func (fake *FakeStorage) PolicyReturns(result1 *storage.Policy, result2 error) {
fake.policyMutex.Lock()
defer fake.policyMutex.Unlock()
fake.PolicyStub = nil
fake.policyReturns = struct {
result1 *storage.Policy
result2 error
}{result1, result2}
}
func (fake *FakeStorage) PolicyReturnsOnCall(i int, result1 *storage.Policy, result2 error) {
fake.policyMutex.Lock()
defer fake.policyMutex.Unlock()
fake.PolicyStub = nil
if fake.policyReturnsOnCall == nil {
fake.policyReturnsOnCall = make(map[int]struct {
result1 *storage.Policy
result2 error
})
}
fake.policyReturnsOnCall[i] = struct {
result1 *storage.Policy
result2 error
}{result1, result2}
}
func (fake *FakeStorage) SetPolicyLock(arg1 context.Context, arg2 string, arg3 string, arg4 string, arg5 bool) error {
fake.setPolicyLockMutex.Lock()
ret, specificReturn := fake.setPolicyLockReturnsOnCall[len(fake.setPolicyLockArgsForCall)]
fake.setPolicyLockArgsForCall = append(fake.setPolicyLockArgsForCall, struct {
arg1 context.Context
arg2 string
arg3 string
arg4 string
arg5 bool
}{arg1, arg2, arg3, arg4, arg5})
stub := fake.SetPolicyLockStub
fakeReturns := fake.setPolicyLockReturns
fake.recordInvocation("SetPolicyLock", []interface{}{arg1, arg2, arg3, arg4, arg5})
fake.setPolicyLockMutex.Unlock()
if stub != nil {
return stub(arg1, arg2, arg3, arg4, arg5)
}
if specificReturn {
return ret.result1
}
return fakeReturns.result1
}
func (fake *FakeStorage) SetPolicyLockCallCount() int {
fake.setPolicyLockMutex.RLock()
defer fake.setPolicyLockMutex.RUnlock()
return len(fake.setPolicyLockArgsForCall)
}
func (fake *FakeStorage) SetPolicyLockCalls(stub func(context.Context, string, string, string, bool) error) {
fake.setPolicyLockMutex.Lock()
defer fake.setPolicyLockMutex.Unlock()
fake.SetPolicyLockStub = stub
}
func (fake *FakeStorage) SetPolicyLockArgsForCall(i int) (context.Context, string, string, string, bool) {
fake.setPolicyLockMutex.RLock()
defer fake.setPolicyLockMutex.RUnlock()
argsForCall := fake.setPolicyLockArgsForCall[i]
return argsForCall.arg1, argsForCall.arg2, argsForCall.arg3, argsForCall.arg4, argsForCall.arg5
}
func (fake *FakeStorage) SetPolicyLockReturns(result1 error) {
fake.setPolicyLockMutex.Lock()
defer fake.setPolicyLockMutex.Unlock()
fake.SetPolicyLockStub = nil
fake.setPolicyLockReturns = struct {
result1 error
}{result1}
}
func (fake *FakeStorage) SetPolicyLockReturnsOnCall(i int, result1 error) {
fake.setPolicyLockMutex.Lock()
defer fake.setPolicyLockMutex.Unlock()
fake.SetPolicyLockStub = nil
if fake.setPolicyLockReturnsOnCall == nil {
fake.setPolicyLockReturnsOnCall = make(map[int]struct {
result1 error
})
}
fake.setPolicyLockReturnsOnCall[i] = struct {
result1 error
}{result1}
}
func (fake *FakeStorage) Invocations() map[string][][]interface{} {
fake.invocationsMutex.RLock()
defer fake.invocationsMutex.RUnlock()
fake.policyMutex.RLock()
defer fake.policyMutex.RUnlock()
fake.setPolicyLockMutex.RLock()
defer fake.setPolicyLockMutex.RUnlock()
copiedInvocations := map[string][][]interface{}{}
for key, value := range fake.invocations {
copiedInvocations[key] = value
}
return copiedInvocations
}
func (fake *FakeStorage) recordInvocation(key string, args []interface{}) {
fake.invocationsMutex.Lock()
defer fake.invocationsMutex.Unlock()
if fake.invocations == nil {
fake.invocations = map[string][][]interface{}{}
}
if fake.invocations[key] == nil {
fake.invocations[key] = [][]interface{}{}
}
fake.invocations[key] = append(fake.invocations[key], args)
}
var _ policy.Storage = new(FakeStorage)
......@@ -12,6 +12,9 @@ import (
"code.vereign.com/gaiax/tsa/policy/internal/storage"
)
//go:generate counterfeiter . Storage
//go:generate counterfeiter . RegoCache
type Storage interface {
Policy(ctx context.Context, name, group, version string) (*storage.Policy, error)
SetPolicyLock(ctx context.Context, name, group, version string, lock bool) error
......
package policy_test
import (
"context"
"testing"
"time"
"github.com/open-policy-agent/opa/rego"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
"code.vereign.com/gaiax/tsa/golib/errors"
goapolicy "code.vereign.com/gaiax/tsa/policy/gen/policy"
"code.vereign.com/gaiax/tsa/policy/internal/service/policy"
"code.vereign.com/gaiax/tsa/policy/internal/service/policy/policyfakes"
"code.vereign.com/gaiax/tsa/policy/internal/storage"
)
func TestNew(t *testing.T) {
storage := &policyfakes.FakeStorage{}
regocache := &policyfakes.FakeRegoCache{}
svc := policy.New(storage, regocache, zap.NewNop())
assert.Implements(t, (*goapolicy.Service)(nil), svc)
}
func TestService_Evaluate(t *testing.T) {
// prepare test policy source code that will be evaluated
testPolicy := `package testgroup.example allow { input.msg == "yes" }`
// prepare test query that can be retrieved from rego cache
testQuery, err := rego.New(
rego.Module("example.rego", testPolicy),
rego.Query("data.testgroup.example"),
).PrepareForEval(context.Background())
assert.NoError(t, err)
// prepare test request to be used in tests
testReq := func() *goapolicy.EvaluateRequest {
return &goapolicy.EvaluateRequest{
Group: "testgroup",
PolicyName: "example",
Version: "1.0",
Input: map[string]interface{}{"msg": "yes"},
}
}
tests := []struct {
// test input
name string
req *goapolicy.EvaluateRequest
storage policy.Storage
regocache policy.RegoCache
// expected result
res *goapolicy.EvaluateResult
errkind errors.Kind
errtext string
}{
{
name: "prepared query is found in cache",
req: testReq(),
regocache: &policyfakes.FakeRegoCache{
GetStub: func(key string) (*rego.PreparedEvalQuery, bool) {
q := testQuery
return &q, true
},
},
res: &goapolicy.EvaluateResult{Result: map[string]interface{}{"allow": true}},
},
{
name: "policy is not found",
req: testReq(),
regocache: &policyfakes.FakeRegoCache{
GetStub: func(key string) (*rego.PreparedEvalQuery, bool) {
return nil, false
},
},
storage: &policyfakes.FakeStorage{
PolicyStub: func(ctx context.Context, s string, s2 string, s3 string) (*storage.Policy, error) {
return nil, errors.New(errors.NotFound)
},
},
res: nil,
errkind: errors.NotFound,
errtext: "not found",
},
{
name: "error getting policy from storage",
req: testReq(),
regocache: &policyfakes.FakeRegoCache{
GetStub: func(key string) (*rego.PreparedEvalQuery, bool) {
return nil, false
},
},
storage: &policyfakes.FakeStorage{
PolicyStub: func(ctx context.Context, s string, s2 string, s3 string) (*storage.Policy, error) {
return nil, errors.New("some error")
},
},
res: nil,
errkind: errors.Unknown,
errtext: "some error",
},
{
name: "policy is locked",
req: testReq(),
regocache: &policyfakes.FakeRegoCache{
GetStub: func(key string) (*rego.PreparedEvalQuery, bool) {
return nil, false
},
},
storage: &policyfakes.FakeStorage{
PolicyStub: func(ctx context.Context, s string, s2 string, s3 string) (*storage.Policy, error) {
return &storage.Policy{Locked: true}, nil
},
},
res: nil,
errkind: errors.Forbidden,
errtext: "policy is locked",
},
{
name: "policy is found in storage and isn't locked",
req: testReq(),
regocache: &policyfakes.FakeRegoCache{
GetStub: func(key string) (*rego.PreparedEvalQuery, bool) {
return nil, false
},
},
storage: &policyfakes.FakeStorage{
PolicyStub: func(ctx context.Context, s string, s2 string, s3 string) (*storage.Policy, error) {
return &storage.Policy{
Name: "example",
Group: "testgroup",
Version: "1.0",
Rego: testPolicy,
Locked: false,
LastUpdate: time.Now(),
}, nil
},
},
res: &goapolicy.EvaluateResult{Result: map[string]interface{}{"allow": true}},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
svc := policy.New(test.storage, test.regocache, zap.NewNop())
res, err := svc.Evaluate(context.Background(), test.req)
if err == nil {
assert.Empty(t, test.errtext)
assert.Equal(t, test.res, res)
} else {
e, ok := err.(*errors.Error)
assert.True(t, ok)
assert.Contains(t, e.Error(), test.errtext)
assert.Equal(t, test.errkind, e.Kind)
assert.Equal(t, test.res, res)
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment