Repository: ovrclk/akash (go)

Issue:
handler tests: ensure events emitted
In the handler tests, ensure that the correct events are emitted.  #662 implements this for `x/deployment/handler`, the other two modules still need to be tested.

* [ ] `x/market/handler`
* [ ] `x/provider/handler`

Pull request:
handler tests: ensure events emiited
Fixes #664 

Patch:
diff --git a/events/publish.go b/events/publish.go
index e154ce9b..2b1c4ff6 100644
--- a/events/publish.go
+++ b/events/publish.go
@@ -8,6 +8,7 @@ import (
 	"github.com/ovrclk/akash/sdkutil"
 	dtypes "github.com/ovrclk/akash/x/deployment/types"
 	mtypes "github.com/ovrclk/akash/x/market/types"
+	ptypes "github.com/ovrclk/akash/x/provider/types"
 	abci "github.com/tendermint/tendermint/abci/types"
 	tmclient "github.com/tendermint/tendermint/rpc/client"
 	ctypes "github.com/tendermint/tendermint/rpc/core/types"
@@ -99,5 +100,9 @@ func processEvent(bev abci.Event) (interface{}, bool) {
 		return mev, true
 	}
 
+	if mev, err := ptypes.ParseEvent(ev); err == nil {
+		return mev, true
+	}
+
 	return nil, false
 }
diff --git a/x/market/types/event.go b/x/market/types/event.go
index ffefb7dd..e474b56a 100644
--- a/x/market/types/event.go
+++ b/x/market/types/event.go
@@ -223,7 +223,7 @@ func parseEVPriceAttributes(attrs []sdk.Attribute) (sdk.Coin, error) {
 }
 
 // ParseEvent parses event and returns details of event and error if occurred
-func ParseEvent(ev sdkutil.Event) (interface{}, error) {
+func ParseEvent(ev sdkutil.Event) (sdkutil.ModuleEvent, error) {
 	if ev.Type != sdkutil.EventTypeMessage {
 		return nil, sdkutil.ErrUnknownType
 	}
diff --git a/x/provider/keeper/keeper.go b/x/provider/keeper/keeper.go
index b4aca0ba..04df9768 100644
--- a/x/provider/keeper/keeper.go
+++ b/x/provider/keeper/keeper.go
@@ -51,6 +51,11 @@ func (k Keeper) Create(ctx sdk.Context, provider types.Provider) error {
 	}
 
 	store.Set(key, k.cdc.MustMarshalBinaryBare(provider))
+
+	ctx.EventManager().EmitEvent(
+		types.EventProviderCreate{Owner: provider.Owner}.ToSDKEvent(),
+	)
+
 	return nil
 }
 
@@ -76,6 +81,11 @@ func (k Keeper) Update(ctx sdk.Context, provider types.Provider) error {
 		return types.ErrProviderNotFound
 	}
 	store.Set(key, k.cdc.MustMarshalBinaryBare(provider))
+
+	ctx.EventManager().EmitEvent(
+		types.EventProviderUpdate{Owner: provider.Owner}.ToSDKEvent(),
+	)
+
 	return nil
 }
 
diff --git a/x/provider/types/event.go b/x/provider/types/event.go
new file mode 100644
index 00000000..573f49b8
--- /dev/null
+++ b/x/provider/types/event.go
@@ -0,0 +1,108 @@
+package types
+
+import (
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/ovrclk/akash/sdkutil"
+)
+
+const (
+	evActionProviderCreate = "provider-create"
+	evActionProviderUpdate = "provider-update"
+	evActionProviderDelete = "provider-delete"
+	evOwnerKey             = "owner"
+)
+
+// EventProviderCreate struct
+type EventProviderCreate struct {
+	Owner sdk.AccAddress
+}
+
+// ToSDKEvent method creates new sdk event for EventProviderCreate struct
+func (ev EventProviderCreate) ToSDKEvent() sdk.Event {
+	return sdk.NewEvent(sdkutil.EventTypeMessage,
+		append([]sdk.Attribute{
+			sdk.NewAttribute(sdk.AttributeKeyModule, ModuleName),
+			sdk.NewAttribute(sdk.AttributeKeyAction, evActionProviderCreate),
+		}, ProviderEVAttributes(ev.Owner)...)...,
+	)
+}
+
+// EventProviderUpdate struct
+type EventProviderUpdate struct {
+	Owner sdk.AccAddress
+}
+
+// ToSDKEvent method creates new sdk event for EventProviderUpdate struct
+func (ev EventProviderUpdate) ToSDKEvent() sdk.Event {
+	return sdk.NewEvent(sdkutil.EventTypeMessage,
+		append([]sdk.Attribute{
+			sdk.NewAttribute(sdk.AttributeKeyModule, ModuleName),
+			sdk.NewAttribute(sdk.AttributeKeyAction, evActionProviderUpdate),
+		}, ProviderEVAttributes(ev.Owner)...)...,
+	)
+}
+
+// EventProviderDelete struct
+type EventProviderDelete struct {
+	Owner sdk.AccAddress
+}
+
+// ToSDKEvent method creates new sdk event for EventProviderDelete struct
+func (ev EventProviderDelete) ToSDKEvent() sdk.Event {
+	return sdk.NewEvent(sdkutil.EventTypeMessage,
+		append([]sdk.Attribute{
+			sdk.NewAttribute(sdk.AttributeKeyModule, ModuleName),
+			sdk.NewAttribute(sdk.AttributeKeyAction, evActionProviderDelete),
+		}, ProviderEVAttributes(ev.Owner)...)...,
+	)
+}
+
+// ProviderEVAttributes returns event attribues for given Provider
+func ProviderEVAttributes(owner sdk.AccAddress) []sdk.Attribute {
+	return []sdk.Attribute{
+		sdk.NewAttribute(evOwnerKey, owner.String()),
+	}
+}
+
+// ParseEVProvider returns provider details for given event attributes
+func ParseEVProvider(attrs []sdk.Attribute) (sdk.AccAddress, error) {
+	owner, err := sdkutil.GetAccAddress(attrs, evOwnerKey)
+	if err != nil {
+		return sdk.AccAddress{}, err
+	}
+
+	return owner, nil
+}
+
+// ParseEvent parses event and returns details of event and error if occurred
+// TODO: Enable returning actual events.
+func ParseEvent(ev sdkutil.Event) (sdkutil.ModuleEvent, error) {
+	if ev.Type != sdkutil.EventTypeMessage {
+		return nil, sdkutil.ErrUnknownType
+	}
+	if ev.Module != ModuleName {
+		return nil, sdkutil.ErrUnknownModule
+	}
+	switch ev.Action {
+	case evActionProviderCreate:
+		owner, err := ParseEVProvider(ev.Attributes)
+		if err != nil {
+			return nil, err
+		}
+		return EventProviderCreate{Owner: owner}, nil
+	case evActionProviderUpdate:
+		owner, err := ParseEVProvider(ev.Attributes)
+		if err != nil {
+			return nil, err
+		}
+		return EventProviderUpdate{Owner: owner}, nil
+	case evActionProviderDelete:
+		owner, err := ParseEVProvider(ev.Attributes)
+		if err != nil {
+			return nil, err
+		}
+		return EventProviderDelete{Owner: owner}, nil
+	default:
+		return nil, sdkutil.ErrUnknownAction
+	}
+}


Test patch:
diff --git a/events/publish_test.go b/events/publish_test.go
index 39e09190..a10d25e6 100644
--- a/events/publish_test.go
+++ b/events/publish_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/ovrclk/akash/testutil"
 	dtypes "github.com/ovrclk/akash/x/deployment/types"
 	mtypes "github.com/ovrclk/akash/x/market/types"
+	ptypes "github.com/ovrclk/akash/x/provider/types"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -25,6 +26,11 @@ func Test_processEvent(t *testing.T) {
 		mtypes.EventBidClosed{ID: testutil.BidID(t), Price: testutil.Coin(t)},
 		mtypes.EventLeaseCreated{ID: testutil.LeaseID(t), Price: testutil.Coin(t)},
 		mtypes.EventLeaseClosed{ID: testutil.LeaseID(t), Price: testutil.Coin(t)},
+
+		// x/provider events
+		ptypes.EventProviderCreate{Owner: testutil.AccAddress(t)},
+		ptypes.EventProviderUpdate{Owner: testutil.AccAddress(t)},
+		ptypes.EventProviderDelete{Owner: testutil.AccAddress(t)},
 	}
 
 	for _, test := range tests {
diff --git a/testutil/event.go b/testutil/event.go
index 0ca7704a..3196c859 100644
--- a/testutil/event.go
+++ b/testutil/event.go
@@ -6,6 +6,8 @@ import (
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/ovrclk/akash/sdkutil"
 	dtypes "github.com/ovrclk/akash/x/deployment/types"
+	mtypes "github.com/ovrclk/akash/x/market/types"
+	ptypes "github.com/ovrclk/akash/x/provider/types"
 	"github.com/stretchr/testify/require"
 )
 
@@ -32,3 +34,25 @@ func ParseDeploymentEvent(t testing.TB, events sdk.Events) sdkutil.ModuleEvent {
 
 	return iev
 }
+
+func ParseMarketEvent(t testing.TB, events sdk.Events) sdkutil.ModuleEvent {
+	t.Helper()
+
+	uev := ParseEvent(t, events)
+
+	iev, err := mtypes.ParseEvent(uev)
+	require.NoError(t, err)
+
+	return iev
+}
+
+func ParseProviderEvent(t testing.TB, events sdk.Events) sdkutil.ModuleEvent {
+	t.Helper()
+
+	uev := ParseEvent(t, events)
+
+	iev, err := ptypes.ParseEvent(uev)
+	require.NoError(t, err)
+
+	return iev
+}
diff --git a/x/deployment/types/events_test.go b/x/deployment/types/events_test.go
index 0fa401fc..475b2cb0 100644
--- a/x/deployment/types/events_test.go
+++ b/x/deployment/types/events_test.go
@@ -6,6 +6,7 @@ import (
 
 	sdk "github.com/cosmos/cosmos-sdk/types"
 	"github.com/pkg/errors"
+	"github.com/stretchr/testify/require"
 
 	"github.com/ovrclk/akash/sdkutil"
 )
@@ -25,15 +26,12 @@ type testEventParsing struct {
 func (tep testEventParsing) testMessageType() func(t *testing.T) {
 	_, err := ParseEvent(tep.msg)
 	return func(t *testing.T) {
-		t.Logf("ERR: %v", err)
-		// expected error doesn't match returned    || error returned but not expected
-		if (tep.expErr != nil && errors.Is(err, tep.expErr)) || (err != nil && tep.expErr == nil) {
-			// if the error expected is errWildcard to catch untyped errors, don't fail the test, the error was expected.
-			if errors.Is(tep.expErr, errWildcard) {
-				t.Errorf("unexpected error: %v exp: %v", err, tep.expErr)
-				t.Logf("%T %v", errors.Cause(err), err)
-				t.Logf("%+v", tep)
-			}
+		t.Logf("%+v", tep)
+		// if the error expected is errWildcard to catch untyped errors, don't fail the test, the error was expected.
+		if errors.Is(tep.expErr, errWildcard) {
+			require.Error(t, err)
+		} else {
+			require.Equal(t, tep.expErr, err)
 		}
 	}
 }
diff --git a/x/market/handler/handler_test.go b/x/market/handler/handler_test.go
index 02754bf0..fb170fa7 100644
--- a/x/market/handler/handler_test.go
+++ b/x/market/handler/handler_test.go
@@ -97,7 +97,18 @@ func TestCreateBidValid(t *testing.T) {
 	require.NotNil(t, res)
 	require.NoError(t, err)
 
-	_, found := suite.mkeeper.GetBid(suite.ctx, types.MakeBidID(order.ID(), provider))
+	bid := types.MakeBidID(order.ID(), provider)
+
+	t.Run("ensure event created", func(t *testing.T) {
+		iev := testutil.ParseMarketEvent(t, res.Events[2:])
+		require.IsType(t, types.EventBidCreated{}, iev)
+
+		dev := iev.(types.EventBidCreated)
+
+		require.Equal(t, bid, dev.ID)
+	})
+
+	_, found := suite.mkeeper.GetBid(suite.ctx, bid)
 	require.True(t, found)
 }
 
@@ -269,6 +280,15 @@ func TestCloseOrderValid(t *testing.T) {
 	res, err := suite.handler(suite.ctx, msg)
 	require.NotNil(t, res)
 	require.NoError(t, err)
+
+	t.Run("ensure event created", func(t *testing.T) {
+		iev := testutil.ParseMarketEvent(t, res.Events[3:4])
+		require.IsType(t, types.EventOrderClosed{}, iev)
+
+		dev := iev.(types.EventOrderClosed)
+
+		require.Equal(t, msg.OrderID, dev.ID)
+	})
 }
 
 func TestCloseBidNonExisting(t *testing.T) {
@@ -313,6 +333,15 @@ func TestCloseBidValid(t *testing.T) {
 	res, err := suite.handler(suite.ctx, msg)
 	require.NotNil(t, res)
 	require.NoError(t, err)
+
+	t.Run("ensure event created", func(t *testing.T) {
+		iev := testutil.ParseMarketEvent(t, res.Events[3:4])
+		require.IsType(t, types.EventBidClosed{}, iev)
+
+		dev := iev.(types.EventBidClosed)
+
+		require.Equal(t, msg.BidID, dev.ID)
+	})
 }
 
 func TestCloseBidNotActiveLease(t *testing.T) {
diff --git a/x/market/types/events_test.go b/x/market/types/events_test.go
new file mode 100644
index 00000000..7b0cc643
--- /dev/null
+++ b/x/market/types/events_test.go
@@ -0,0 +1,463 @@
+package types
+
+import (
+	"fmt"
+	"testing"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+
+	"github.com/pkg/errors"
+
+	"github.com/ovrclk/akash/sdkutil"
+	"github.com/stretchr/testify/require"
+)
+
+var (
+	keyAcc, _ = sdk.AccAddressFromBech32("akash1qtqpdszzakz7ugkey7ka2cmss95z26ygar2mgr")
+	//keyParams = sdk.NewKVStoreKey(params.StoreKey)
+
+	errWildcard = errors.New("wildcard string error can't be matched")
+	evOwnerKey  = "owner"
+	evDSeqKey   = "dseq"
+	evGSeqKey   = "gseq"
+)
+
+type testEventParsing struct {
+	msg    sdkutil.Event
+	expErr error
+}
+
+func (tep testEventParsing) testMessageType() func(t *testing.T) {
+	_, err := ParseEvent(tep.msg)
+	return func(t *testing.T) {
+		t.Logf("%+v", tep)
+		// if the error expected is errWildcard to catch untyped errors, don't fail the test, the error was expected.
+		if errors.Is(tep.expErr, errWildcard) {
+			require.Error(t, err)
+		} else {
+			require.Equal(t, tep.expErr, err)
+		}
+	}
+}
+
+var TEPS = []testEventParsing{
+	{
+		msg: sdkutil.Event{
+			Type: "nil",
+		},
+		expErr: sdkutil.ErrUnknownType,
+	},
+	{
+		msg: sdkutil.Event{
+			Type: sdkutil.EventTypeMessage,
+		},
+		expErr: sdkutil.ErrUnknownModule,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+		},
+		expErr: sdkutil.ErrUnknownAction,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: "nil",
+		},
+		expErr: sdkutil.ErrUnknownModule,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: "nil",
+		},
+		expErr: sdkutil.ErrUnknownAction,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionOrderCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+			},
+		},
+		expErr: nil,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionOrderCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: "nooo",
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionOrderCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "abc",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionOrderClosed,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+			},
+		},
+		expErr: nil,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionBidCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "23",
+				},
+			},
+		},
+		expErr: nil,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionBidCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: "yesss",
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "23",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionBidCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "hello",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionBidClosed,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "23",
+				},
+			},
+		},
+		expErr: nil,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionLeaseCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "23",
+				},
+			},
+		},
+		expErr: nil,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionLeaseCreated,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: "hello",
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "23",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionLeaseClosed,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evDSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evGSeqKey,
+					Value: "2",
+				},
+				{
+					Key:   evOSeqKey,
+					Value: "5",
+				},
+				{
+					Key:   evProviderKey,
+					Value: keyAcc.String(),
+				},
+				{
+					Key:   evPriceDenomKey,
+					Value: "akt",
+				},
+				{
+					Key:   evPriceAmountKey,
+					Value: "23",
+				},
+			},
+		},
+		expErr: nil,
+	},
+}
+
+func TestEventParsing(t *testing.T) {
+	for i, test := range TEPS {
+		t.Run(fmt.Sprintf("%d", i),
+			test.testMessageType())
+	}
+}
diff --git a/x/provider/handler/handler_test.go b/x/provider/handler/handler_test.go
index 52511c4d..9f83e992 100644
--- a/x/provider/handler/handler_test.go
+++ b/x/provider/handler/handler_test.go
@@ -76,6 +76,16 @@ func TestProviderCreate(t *testing.T) {
 	require.NotNil(t, res)
 	require.NoError(t, err)
 
+	t.Run("ensure event created", func(t *testing.T) {
+
+		iev := testutil.ParseProviderEvent(t, res.Events)
+		require.IsType(t, types.EventProviderCreate{}, iev)
+
+		dev := iev.(types.EventProviderCreate)
+
+		require.Equal(t, msg.Owner, dev.Owner)
+	})
+
 	res, err = suite.handler(suite.ctx, msg)
 	require.Nil(t, res)
 	require.Error(t, err)
@@ -103,6 +113,17 @@ func TestProviderUpdateExisting(t *testing.T) {
 	require.NoError(t, err)
 
 	res, err := suite.handler(suite.ctx, updateMsg)
+
+	t.Run("ensure event created", func(t *testing.T) {
+
+		iev := testutil.ParseProviderEvent(t, res.Events[1:])
+		require.IsType(t, types.EventProviderUpdate{}, iev)
+
+		dev := iev.(types.EventProviderUpdate)
+
+		require.Equal(t, updateMsg.Owner, dev.Owner)
+	})
+
 	require.NoError(t, err)
 	require.NotNil(t, res)
 }
@@ -159,6 +180,16 @@ func TestProviderUpdateAttributes(t *testing.T) {
 	require.NoError(t, err)
 	require.NotNil(t, res)
 
+	t.Run("ensure event created", func(t *testing.T) {
+
+		iev := testutil.ParseProviderEvent(t, res.Events[4:])
+		require.IsType(t, types.EventProviderUpdate{}, iev)
+
+		dev := iev.(types.EventProviderUpdate)
+
+		require.Equal(t, updateMsg.Owner, dev.Owner)
+	})
+
 	updateMsg.Attributes = nil
 	res, err = suite.handler(suite.ctx, updateMsg)
 	require.Error(t, err, types.ErrIncompatibleAttributes.Error())
@@ -186,6 +217,10 @@ func TestProviderDeleteExisting(t *testing.T) {
 	require.Nil(t, res)
 	require.EqualError(t, err, handler.ErrInternal.Error()+": NOTIMPLEMENTED")
 	require.True(t, errors.Is(err, handler.ErrInternal))
+
+	t.Run("ensure event created", func(t *testing.T) {
+		// TODO: this should emit a ProviderDelete
+	})
 }
 
 func TestProviderDeleteNonExisting(t *testing.T) {
diff --git a/x/provider/types/events_test.go b/x/provider/types/events_test.go
new file mode 100644
index 00000000..e96ce511
--- /dev/null
+++ b/x/provider/types/events_test.go
@@ -0,0 +1,197 @@
+package types
+
+import (
+	"fmt"
+	"testing"
+
+	sdk "github.com/cosmos/cosmos-sdk/types"
+	"github.com/pkg/errors"
+	"github.com/stretchr/testify/require"
+
+	"github.com/ovrclk/akash/sdkutil"
+)
+
+var (
+	keyAcc, _ = sdk.AccAddressFromBech32("akash1qtqpdszzakz7ugkey7ka2cmss95z26ygar2mgr")
+	//keyParams = sdk.NewKVStoreKey(params.StoreKey)
+
+	errWildcard = errors.New("wildcard string error can't be matched")
+)
+
+type testEventParsing struct {
+	msg    sdkutil.Event
+	expErr error
+}
+
+func (tep testEventParsing) testMessageType() func(t *testing.T) {
+	_, err := ParseEvent(tep.msg)
+	return func(t *testing.T) {
+		t.Logf("%+v", tep)
+		// if the error expected is errWildcard to catch untyped errors, don't fail the test, the error was expected.
+		if errors.Is(tep.expErr, errWildcard) {
+			require.Error(t, err)
+		} else {
+			require.Equal(t, tep.expErr, err)
+		}
+	}
+}
+
+var TEPS = []testEventParsing{
+	{
+		msg: sdkutil.Event{
+			Type: "nil",
+		},
+		expErr: sdkutil.ErrUnknownType,
+	},
+	{
+		msg: sdkutil.Event{
+			Type: sdkutil.EventTypeMessage,
+		},
+		expErr: sdkutil.ErrUnknownModule,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+		},
+		expErr: sdkutil.ErrUnknownAction,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: "nil",
+		},
+		expErr: sdkutil.ErrUnknownModule,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: "nil",
+		},
+		expErr: sdkutil.ErrUnknownAction,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionProviderCreate,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+			},
+		},
+		expErr: nil,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionProviderCreate,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: "hello",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:       sdkutil.EventTypeMessage,
+			Module:     ModuleName,
+			Action:     evActionProviderCreate,
+			Attributes: []sdk.Attribute{},
+		},
+		expErr: errWildcard,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionProviderUpdate,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+			},
+		},
+		expErr: nil,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionProviderUpdate,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: "hello",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:       sdkutil.EventTypeMessage,
+			Module:     ModuleName,
+			Action:     evActionProviderUpdate,
+			Attributes: []sdk.Attribute{},
+		},
+		expErr: errWildcard,
+	},
+
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionProviderDelete,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: keyAcc.String(),
+				},
+			},
+		},
+		expErr: nil,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:   sdkutil.EventTypeMessage,
+			Module: ModuleName,
+			Action: evActionProviderDelete,
+			Attributes: []sdk.Attribute{
+				{
+					Key:   evOwnerKey,
+					Value: "hello",
+				},
+			},
+		},
+		expErr: errWildcard,
+	},
+	{
+		msg: sdkutil.Event{
+			Type:       sdkutil.EventTypeMessage,
+			Module:     ModuleName,
+			Action:     evActionProviderDelete,
+			Attributes: []sdk.Attribute{},
+		},
+		expErr: errWildcard,
+	},
+}
+
+func TestEventParsing(t *testing.T) {
+	for i, test := range TEPS {
+		t.Run(fmt.Sprintf("%d", i),
+			test.testMessageType())
+	}
+}