Skip to content

Commit

Permalink
Merge pull request #12 from kubescape/tests
Browse files Browse the repository at this point in the history
add tests
  • Loading branch information
matthyx committed Nov 20, 2023
2 parents 95df542 + e62c3ab commit 6fb0ed4
Show file tree
Hide file tree
Showing 4 changed files with 217 additions and 17 deletions.
99 changes: 89 additions & 10 deletions adapters/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,28 @@ import (

type MockAdapter struct {
callbacks domain.Callbacks
patchStrategy bool
resources map[string][]byte
patchStrategy bool // true for client, false for server
Resources map[string][]byte
shadowObjects map[string][]byte
}

func NewMockAdapter() *MockAdapter {
func NewMockAdapter(patchStrategy bool) *MockAdapter {
return &MockAdapter{
resources: map[string][]byte{},
patchStrategy: patchStrategy,
Resources: map[string][]byte{},
shadowObjects: map[string][]byte{},
}
}

var _ Adapter = (*MockAdapter)(nil)

func (m *MockAdapter) DeleteObject(_ context.Context, id domain.KindName) error {
delete(m.resources, id.String())
delete(m.Resources, id.String())
return nil
}

func (m *MockAdapter) GetObject(ctx context.Context, id domain.KindName, baseObject []byte) error {
object, ok := m.resources[id.String()]
object, ok := m.Resources[id.String()]
if !ok {
return fmt.Errorf("object not found")
}
Expand Down Expand Up @@ -72,7 +73,7 @@ func (m *MockAdapter) PatchObject(ctx context.Context, id domain.KindName, check
}

func (m *MockAdapter) patchObject(id domain.KindName, checksum string, patch []byte) ([]byte, error) {
object, ok := m.resources[id.String()]
object, ok := m.Resources[id.String()]
if !ok {
return nil, fmt.Errorf("object not found")
}
Expand All @@ -87,13 +88,13 @@ func (m *MockAdapter) patchObject(id domain.KindName, checksum string, patch []b
if newChecksum != checksum {
return object, fmt.Errorf("checksum mismatch: %s != %s", newChecksum, checksum)
}
m.resources[id.String()] = modified
m.Resources[id.String()] = modified
m.shadowObjects[id.String()] = modified
return object, nil
}

func (m *MockAdapter) PutObject(_ context.Context, id domain.KindName, object []byte) error {
m.resources[id.String()] = object
m.Resources[id.String()] = object
return nil
}

Expand All @@ -119,7 +120,7 @@ func (m *MockAdapter) VerifyObject(ctx context.Context, id domain.KindName, newC
}

func (m *MockAdapter) verifyObject(id domain.KindName, newChecksum string) ([]byte, error) {
object, ok := m.resources[id.String()]
object, ok := m.Resources[id.String()]
if !ok {
return nil, fmt.Errorf("object not found")
}
Expand All @@ -132,3 +133,81 @@ func (m *MockAdapter) verifyObject(id domain.KindName, newChecksum string) ([]by
}
return object, nil
}

// TestCallDeleteObject is used for testing purposes only, it is similar to incluster.client response to watch.Deleted event
func (m *MockAdapter) TestCallDeleteObject(ctx context.Context, id domain.KindName) error {
ctx = utils.ContextFromGeneric(ctx, domain.Generic{})
// delete local object - this is only for testing purposes
delete(m.Resources, id.String())
// send delete
err := m.callbacks.DeleteObject(ctx, id)
if err != nil {
return fmt.Errorf("send delete: %w", err)
}
if m.patchStrategy {
// remove from known resources
delete(m.shadowObjects, id.String())
}
return nil
}

// TestCallPutOrPatch is used for testing purposes only, it is similar to incluster.client.callPutOrPatch
func (m *MockAdapter) TestCallPutOrPatch(ctx context.Context, id domain.KindName, baseObject []byte, newObject []byte) error {
ctx = utils.ContextFromGeneric(ctx, domain.Generic{})
// store object locally - this is only for testing purposes
m.Resources[id.String()] = newObject
// send put/patch
if m.patchStrategy {
if len(baseObject) > 0 {
// update reference object
m.shadowObjects[id.Name] = baseObject
}
if oldObject, ok := m.shadowObjects[id.Name]; ok {
// calculate checksum
checksum, err := utils.CanonicalHash(newObject)
if err != nil {
return fmt.Errorf("calculate checksum: %w", err)
}
// calculate patch
patch, err := jsonpatch.CreateMergePatch(oldObject, newObject)
if err != nil {
return fmt.Errorf("create merge patch: %w", err)
}
err = m.callbacks.PatchObject(ctx, id, checksum, patch)
if err != nil {
return fmt.Errorf("send patch object: %w", err)
}
} else {
err := m.callbacks.PutObject(ctx, id, newObject)
if err != nil {
return fmt.Errorf("send put object: %w", err)
}
}
// add/update known resources
m.shadowObjects[id.Name] = newObject
} else {
err := m.callbacks.PutObject(ctx, id, newObject)
if err != nil {
return fmt.Errorf("send put object: %w", err)
}
}
return nil
}

// TestCallVerifyObject is used for testing purposes only, it is similar to incluster.client.callVerifyObject
func (m *MockAdapter) TestCallVerifyObject(ctx context.Context, id domain.KindName, object []byte) error {
ctx = utils.ContextFromGeneric(ctx, domain.Generic{})
// store object locally - this is only for testing purposes
m.Resources[id.String()] = object
// calculate checksum
checksum, err := utils.CanonicalHash(object)
if err != nil {
return fmt.Errorf("calculate checksum: %w", err)
}
// send verify
err = m.callbacks.VerifyObject(ctx, id, checksum)
if err != nil {
return fmt.Errorf("send checksum: %w", err)
}
return nil
}
2 changes: 1 addition & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ func main() {
} else {
// mock adapter
logger.L().Info("initializing mock adapter")
adapter = adapters.NewMockAdapter()
adapter = adapters.NewMockAdapter(false)
}

// websocket server
Expand Down
50 changes: 50 additions & 0 deletions core/synchronizer_server_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package core

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestSynchronizer_ObjectAddedOnServer(t *testing.T) {
ctx, clientAdapter, serverAdapter := initTest(t)
// add object
err := serverAdapter.TestCallVerifyObject(ctx, kindKnownServers, object)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
// check object added
clientObj, ok := clientAdapter.Resources[kindKnownServers.String()]
assert.True(t, ok)
assert.Equal(t, object, clientObj)
}

func TestSynchronizer_ObjectDeletedOnServer(t *testing.T) {
ctx, clientAdapter, serverAdapter := initTest(t)
// pre: add object
clientAdapter.Resources[kindKnownServers.String()] = object
serverAdapter.Resources[kindKnownServers.String()] = object
// delete object
err := serverAdapter.TestCallDeleteObject(ctx, kindKnownServers)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
// check object deleted
_, ok := clientAdapter.Resources[kindKnownServers.String()]
assert.False(t, ok)
}

func TestSynchronizer_ObjectModifiedOnServer(t *testing.T) {
ctx, clientAdapter, serverAdapter := initTest(t)
// pre: add object
clientAdapter.Resources[kindKnownServers.String()] = object
serverAdapter.Resources[kindKnownServers.String()] = object
// modify object
objectV2 := []byte(`{"kind":"kind","metadata":{"name":"name","version":"2"}}`)
err := serverAdapter.TestCallPutOrPatch(ctx, kindKnownServers, nil, objectV2)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
// check object modified
clientObj, ok := clientAdapter.Resources[kindKnownServers.String()]
assert.True(t, ok)
assert.Equal(t, objectV2, clientObj)
}
83 changes: 77 additions & 6 deletions core/synchronizer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,90 @@ package core

import (
"context"
"net"
"testing"
"time"

"github.com/kubescape/go-logger"
"github.com/kubescape/go-logger/helpers"
"github.com/kubescape/synchronizer/adapters"
"github.com/kubescape/synchronizer/domain"
"github.com/stretchr/testify/assert"
)

func TestSynchronizer_HandleSyncGetObject(t *testing.T) {
synchronizer := &Synchronizer{
adapter: adapters.NewMockAdapter(),
outPool: nil,
var (
kindDeployment = domain.KindName{
Kind: domain.KindFromString("apps/v1/Deployment"),
Name: "name",
Namespace: "namespace",
}
kindKnownServers = domain.KindName{
Kind: domain.KindFromString("spdx.softwarecomposition.kubescape.io/v1beta1/KnownServers"),
Name: "name",
Namespace: "namespace",
}
object = []byte(`{"kind":"kind","metadata":{"name":"name"}}`)
)

func initTest(t *testing.T) (context.Context, *adapters.MockAdapter, *adapters.MockAdapter) {
ctx := context.WithValue(context.TODO(), domain.ContextKeyClientIdentifier, domain.ClientIdentifier{
Account: "11111111-2222-3333-4444-555555555555",
Cluster: "cluster",
})
err := logger.L().SetLevel(helpers.DebugLevel.String())
assert.NoError(t, err)
clientAdapter := adapters.NewMockAdapter(true)
serverAdapter := adapters.NewMockAdapter(false)
clientConn, serverConn := net.Pipe()
client := NewSynchronizerClient(ctx, clientAdapter, clientConn)
server := NewSynchronizerServer(ctx, serverAdapter, serverConn)
go func() {
_ = client.Start(ctx)
}()
go func() {
_ = server.Start(ctx)
}()
return ctx, clientAdapter, serverAdapter
}

func TestSynchronizer_ObjectAdded(t *testing.T) {
ctx, clientAdapter, serverAdapter := initTest(t)
// add object
err := clientAdapter.TestCallVerifyObject(ctx, kindDeployment, object)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
// check object added
serverObj, ok := serverAdapter.Resources[kindDeployment.String()]
assert.True(t, ok)
assert.Equal(t, object, serverObj)
}

func TestSynchronizer_ObjectDeleted(t *testing.T) {
ctx, clientAdapter, serverAdapter := initTest(t)
// pre: add object
clientAdapter.Resources[kindDeployment.String()] = object
serverAdapter.Resources[kindDeployment.String()] = object
// delete object
err := clientAdapter.TestCallDeleteObject(ctx, kindDeployment)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
// check object deleted
_, ok := serverAdapter.Resources[kindDeployment.String()]
assert.False(t, ok)
}

err := synchronizer.handleSyncGetObject(context.TODO(), domain.KindName{}, []byte("baseObject"))
assert.ErrorContains(t, err, "object not found")
func TestSynchronizer_ObjectModified(t *testing.T) {
ctx, clientAdapter, serverAdapter := initTest(t)
// pre: add object
clientAdapter.Resources[kindDeployment.String()] = object
serverAdapter.Resources[kindDeployment.String()] = object
// modify object
objectV2 := []byte(`{"kind":"kind","metadata":{"name":"name","version":"2"}}`)
err := clientAdapter.TestCallPutOrPatch(ctx, kindDeployment, object, objectV2)
assert.NoError(t, err)
time.Sleep(1 * time.Second)
// check object modified
serverObj, ok := serverAdapter.Resources[kindDeployment.String()]
assert.True(t, ok)
assert.Equal(t, objectV2, serverObj)
}

0 comments on commit 6fb0ed4

Please sign in to comment.