Skip to content

Commit

Permalink
Feat: improve test coverage and logging (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
redaLaanait committed Dec 27, 2023
1 parent 8bd96a3 commit 79b3fa8
Show file tree
Hide file tree
Showing 38 changed files with 1,420 additions and 325 deletions.
10 changes: 9 additions & 1 deletion .github/workflows/module.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ jobs:
- name: Run Unit & Functional tests
run: |
make test/ci
make ci/test
env:
DYNAMODB_ENDPOINT: http://localhost:8070

- name: Convert coverage.out to coverage.lcov
uses: jandelgado/gcov2lcov-action@v1

- name: Coveralls
uses: coverallsapp/github-action@v2.2.3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
path-to-lcov: coverage.lcov

release:
needs: [test]
Expand Down
6 changes: 3 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ start-dynamodb/persist:
--name dynamodb amazon/dynamodb-local -jar DynamoDBLocal.jar \
-sharedDb -dbPath /home/dynamodblocal

test/ci:
go test -race -cover ./...
ci/test:
go test -race -cover ./... -coverprofile coverage.out -covermode atomic

test/dynamodb: export DYNAMODB_ENDPOINT = http://localhost:$(DYNAMODB_PORT)
test/dynamodb:
Expand All @@ -28,7 +28,7 @@ test/local: test/dynamodb

.PHONY: examples
examples:
gotest -race -v -cover ./examples/...
go test -race -v -cover ./examples/...


include ./stack/elastic/Makefile
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

# Event Store
[![Coverage Status](https://coveralls.io/repos/github/ln80/event-store/badge.svg?branch=main)](https://coveralls.io/github/ln80/event-store?branch=main)
![ci status](https://github.com/ln80/event-store/actions/workflows/module.yml/badge.svg)

A **serverless-first** kit that simplifies the use of **event-sourcing** and **event-logging** patterns.

Expand Down
51 changes: 39 additions & 12 deletions avro/event.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package avro

import (
"log"
"reflect"
"time"

"github.com/ln80/event-store/event"
"github.com/ln80/event-store/internal/logger"
)

var (
noEvent = struct{}{}
)

func convertEvent(evt event.Envelope) (to *avroEvent, err error) {
Expand Down Expand Up @@ -62,15 +66,28 @@ var _ event.Envelope = &avroEvent{}

func (e *avroEvent) Event() any {
if e.fEvent != nil {
if e.fEvent == noEvent {
return nil
}
return e.fEvent
}

e.fEvent = e.FRawEvent
if e.fEvent == nil {
log.Println("[WARNING] Unmarshal event data has failed")
if e.FRawEvent != nil {
e.fEvent = e.FRawEvent
return e.fEvent
}
log := logger.Default().
WithName("avro").
WithValues(
"stmID", e.StreamID(),
"type", e.Type(),
"ver", e.Version(),
)
log.V(1).Info("Unexpected empty event data")
log.V(3).Info("Perhaps event type is no longer part of any registered Avro schemas", "embeddedSchemaID", e._schemaID)

return e.fEvent
e.fEvent = noEvent
return nil
}

func (e *avroEvent) StreamID() string {
Expand Down Expand Up @@ -143,30 +160,34 @@ func (e *avroEvent) Transform(fn func(any) any) {
}
}

func (e *avroEvent) SetSchemaID(id string) {
func (e *avroEvent) SetAVROSchemaID(id string) {
e._schemaID = id
}

func (e *avroEvent) SchemaID() string {
func (e *avroEvent) AVROSchemaID() string {
return e._schemaID
}

// checkType does change event type in the envelope based
// on the current event data struct type.
// checkType does change event type name in the envelope to match
// the current event data struct type.
func (e *avroEvent) checkType(namespace string) {
// checking global event registry might be error prone;
// make sure to require namespace; this logic might change.
// make sure to require namespace; this logic might change because
// using global event registry might be error-prone.
if namespace == "" {
return
}

// event data might be empty in the case of a removed event type
// that is not indicated as alias for another. In a such case just do skip.
// that is not indicated as alias for a new.
//
// In a such case just do skip type name upgrade.
data := e.Event()
if data == nil {
return
}

// In the case fo an empty event registry. Avro serializer use generic type i.e, a Map to represent data.
// In such as case, do skip event type name upgrade.
typ := reflect.TypeOf(data)
if typ.Kind() == reflect.Pointer {
typ = typ.Elem()
Expand All @@ -177,6 +198,12 @@ func (e *avroEvent) checkType(namespace string) {

t := event.TypeOfWithNamespace(namespace, data)
if t != e.FType {
logger.Default().WithName("avro").V(1).Info("Event type name has upgraded",
"stmID", e.StreamID(),
"old_type", e.FType,
"type", t,
"ver", e.Version())

e.FType = t
}
}
50 changes: 48 additions & 2 deletions avro/event_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,39 @@ func TestEvent(t *testing.T) {
ctx = context.WithValue(ctx, event.ContextNamespaceKey, "")
stmID := event.NewStreamID("tenantID")

defer event.NewRegister("service1").Clear()

t.Run("simple", func(t *testing.T) {
avroEvt := avroEvent{}
if want, got := event.VersionZero, avroEvt.Version(); want != got {
t.Fatalf("expect %s, %s be equals", want, got)
}
if want, got := event.VersionZero, avroEvt.GlobalVersion(); want != got {
t.Fatalf("expect %s, %s be equals", want, got)
}
avroEvt.SetGlobalVersion(event.VersionMax)
if want, got := event.VersionMax, avroEvt.GlobalVersion(); want != got {
t.Fatalf("expect %s, %s be equals", want, got)
}
if want, got := any(nil), avroEvt.Event(); want != got {
t.Fatalf("expect %s, %s be equals", want, got)
}
// check idempotency due to the existing of an internal cache
if want, got := any(nil), avroEvt.Event(); want != got {
t.Fatalf("expect %s, %s be equals", want, got)
}
})

t.Run("convert", func(t *testing.T) {
evt := event.Wrap(ctx, stmID, testutil.GenEvents(1), event.WithNameSpace("service1"))[0]
ver := event.VersionMin
gVer := event.VersionMin

evt := event.Wrap(ctx,
stmID, testutil.GenEvents(1),
event.WithNameSpace("service1"),
event.WithVersionIncr(ver, 1, event.VersionSeqDiffPart),
event.WithGlobalVersionIncr(gVer, 1, event.VersionSeqDiffPart),
)[0]

avroEvt, err := convertEvent(evt)
if err != nil {
Expand Down Expand Up @@ -43,6 +74,21 @@ func TestEvent(t *testing.T) {
if want, got := reflect.TypeOf(evts[0]).Name(), reflect.TypeOf(avroEvt.Event()).Name(); want != got {
t.Fatalf("expect %s, %s be equals", want, got)
}
})

// hack force event type
type alteredEvent struct{}
event.NewRegister("service1").Set(alteredEvent{})
avroEvt.fEvent = alteredEvent{}

if want, got := event.TypeOfWithNamespace("service1", evts[0]), avroEvt.Type(); want != got {
t.Fatalf("expect %v, %v be equals", want, got)
}

// upgrade event type based on current event registry state
avroEvt.checkType("service1")

if want, got := event.TypeOfWithNamespace("service1", alteredEvent{}), avroEvt.Type(); want != got {
t.Fatalf("expect %v, %v be equals", want, got)
}
})
}
16 changes: 16 additions & 0 deletions avro/glue/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package glue

import (
"context"

"github.com/aws/aws-sdk-go-v2/service/glue"
)

type ClientAPI interface {
CreateSchema(ctx context.Context, params *glue.CreateSchemaInput, optFns ...func(*glue.Options)) (*glue.CreateSchemaOutput, error)
RegisterSchemaVersion(ctx context.Context, params *glue.RegisterSchemaVersionInput, optFns ...func(*glue.Options)) (*glue.RegisterSchemaVersionOutput, error)
GetSchemaVersion(ctx context.Context, params *glue.GetSchemaVersionInput, optFns ...func(*glue.Options)) (*glue.GetSchemaVersionOutput, error)
GetSchemaByDefinition(ctx context.Context, params *glue.GetSchemaByDefinitionInput, optFns ...func(*glue.Options)) (*glue.GetSchemaByDefinitionOutput, error)
}

var _ ClientAPI = glue.New(glue.Options{})
30 changes: 30 additions & 0 deletions avro/glue/client_mock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package glue

import (
"context"

"github.com/aws/aws-sdk-go-v2/service/glue"
)

type MockClientAPI struct {
CreateSchemaFunc func(ctx context.Context, params *glue.CreateSchemaInput, optFns ...func(*glue.Options)) (*glue.CreateSchemaOutput, error)
RegisterSchemaVersionFunc func(ctx context.Context, params *glue.RegisterSchemaVersionInput, optFns ...func(*glue.Options)) (*glue.RegisterSchemaVersionOutput, error)
GetSchemaVersionFunc func(ctx context.Context, params *glue.GetSchemaVersionInput, optFns ...func(*glue.Options)) (*glue.GetSchemaVersionOutput, error)
GetSchemaByDefinitionFunc func(ctx context.Context, params *glue.GetSchemaByDefinitionInput, optFns ...func(*glue.Options)) (*glue.GetSchemaByDefinitionOutput, error)
}

func (m *MockClientAPI) CreateSchema(ctx context.Context, params *glue.CreateSchemaInput, optFns ...func(*glue.Options)) (*glue.CreateSchemaOutput, error) {
return m.CreateSchemaFunc(ctx, params, optFns...)
}

func (m *MockClientAPI) RegisterSchemaVersion(ctx context.Context, params *glue.RegisterSchemaVersionInput, optFns ...func(*glue.Options)) (*glue.RegisterSchemaVersionOutput, error) {
return m.RegisterSchemaVersionFunc(ctx, params, optFns...)
}

func (m *MockClientAPI) GetSchemaVersion(ctx context.Context, params *glue.GetSchemaVersionInput, optFns ...func(*glue.Options)) (*glue.GetSchemaVersionOutput, error) {
return m.GetSchemaVersionFunc(ctx, params, optFns...)
}

func (m *MockClientAPI) GetSchemaByDefinition(ctx context.Context, params *glue.GetSchemaByDefinitionInput, optFns ...func(*glue.Options)) (*glue.GetSchemaByDefinitionOutput, error) {
return m.GetSchemaByDefinitionFunc(ctx, params, optFns...)
}
Loading

0 comments on commit 79b3fa8

Please sign in to comment.