package types_test import ( "encoding/json" "reflect" "testing" abci "github.com/cometbft/cometbft/abci/types" "github.com/stretchr/testify/suite" codectypes "github.com/cosmos/cosmos-sdk/codec/types" testdata "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" ) type eventsTestSuite struct { suite.Suite } func TestEventsTestSuite(t *testing.T) { suite.Run(t, new(eventsTestSuite)) } func (s *eventsTestSuite) TestAppendEvents() { e1 := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo")) e2 := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "bar")) a := sdk.Events{e1} b := sdk.Events{e2} c := a.AppendEvents(b) s.Require().Equal(c, sdk.Events{e1, e2}) s.Require().Equal(c, sdk.Events{e1}.AppendEvent(sdk.NewEvent("transfer", sdk.NewAttribute("sender", "bar")))) s.Require().Equal(c, sdk.Events{e1}.AppendEvents(sdk.Events{e2})) } func (s *eventsTestSuite) TestAppendAttributes() { e := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo")) e = e.AppendAttributes(sdk.NewAttribute("recipient", "bar")) s.Require().Len(e.Attributes, 2) s.Require().Equal(e, sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"), sdk.NewAttribute("recipient", "bar"))) } func (s *eventsTestSuite) TestGetAttributes() { e := sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo")) e = e.AppendAttributes(sdk.NewAttribute("recipient", "bar")) attr, found := e.GetAttribute("recipient") s.Require().True(found) s.Require().Equal(attr, sdk.NewAttribute("recipient", "bar")) _, found = e.GetAttribute("foo") s.Require().False(found) events := sdk.Events{e}.AppendEvent(sdk.NewEvent("message", sdk.NewAttribute("sender", "bar"))) attrs, found := events.GetAttributes("sender") s.Require().True(found) s.Require().Len(attrs, 2) s.Require().Equal(attrs[0], sdk.NewAttribute("sender", "foo")) s.Require().Equal(attrs[1], sdk.NewAttribute("sender", "bar")) _, found = events.GetAttributes("foo") s.Require().False(found) } func (s *eventsTestSuite) TestEmptyEvents() { s.Require().Equal(sdk.EmptyEvents(), sdk.Events{}) } func (s *eventsTestSuite) TestAttributeString() { s.Require().Equal("foo: bar", sdk.NewAttribute("foo", "bar").String()) } func (s *eventsTestSuite) TestToABCIEvents() { e := sdk.Events{sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"))} abciEvents := e.ToABCIEvents() s.Require().Len(abciEvents, 1) s.Require().Equal(abciEvents[0].Type, e[0].Type) s.Require().Equal(abciEvents[0].Attributes, e[0].Attributes) } func (s *eventsTestSuite) TestEventManager() { em := sdk.NewEventManager() event := sdk.NewEvent("reward", sdk.NewAttribute("x", "y")) events := sdk.Events{sdk.NewEvent("transfer", sdk.NewAttribute("sender", "foo"))} em.EmitEvents(events) em.EmitEvent(event) s.Require().Len(em.Events(), 2) s.Require().Equal(em.Events(), events.AppendEvent(event)) } func (s *eventsTestSuite) TestEmitTypedEvent() { s.Run("deterministic key-value order", func() { for i := 0; i < 10; i++ { em := sdk.NewEventManager() coin := sdk.NewCoin("fakedenom", sdk.NewInt(1999999)) s.Require().NoError(em.EmitTypedEvent(&coin)) s.Require().Len(em.Events(), 1) attrs := em.Events()[0].Attributes s.Require().Len(attrs, 2) s.Require().Equal(string(attrs[0].Key), "amount") s.Require().Equal(string(attrs[1].Key), "denom") } }) } func (s *eventsTestSuite) TestEventManagerTypedEvents() { em := sdk.NewEventManager() coin := sdk.NewCoin("fakedenom", sdk.NewInt(1999999)) cat := testdata.Cat{ Moniker: "Garfield", Lives: 6, } animal, err := codectypes.NewAnyWithValue(&cat) s.Require().NoError(err) hasAnimal := testdata.HasAnimal{ X: 1000, Animal: animal, } s.Require().NoError(em.EmitTypedEvents(&coin)) s.Require().NoError(em.EmitTypedEvent(&hasAnimal)) s.Require().Len(em.Events(), 2) msg1, err := sdk.ParseTypedEvent(em.Events().ToABCIEvents()[0]) s.Require().NoError(err) s.Require().Equal(coin.String(), msg1.String()) s.Require().Equal(reflect.TypeOf(&coin), reflect.TypeOf(msg1)) msg2, err := sdk.ParseTypedEvent(em.Events().ToABCIEvents()[1]) s.Require().NoError(err) s.Require().Equal(reflect.TypeOf(&hasAnimal), reflect.TypeOf(msg2)) response := msg2.(*testdata.HasAnimal) s.Require().Equal(hasAnimal.Animal.String(), response.Animal.String()) } func (s *eventsTestSuite) TestStringifyEvents() { cases := []struct { name string events sdk.Events expTxtStr string expJSONStr string }{ { name: "default", events: sdk.Events{ sdk.NewEvent("message", sdk.NewAttribute(sdk.AttributeKeySender, "foo")), sdk.NewEvent("message", sdk.NewAttribute(sdk.AttributeKeyModule, "bank")), }, expTxtStr: "\t\t- message\n\t\t\t- sender: foo\n\t\t- message\n\t\t\t- module: bank", expJSONStr: "[{\"type\":\"message\",\"attributes\":[{\"key\":\"sender\",\"value\":\"foo\"}]},{\"type\":\"message\",\"attributes\":[{\"key\":\"module\",\"value\":\"bank\"}]}]", }, { name: "multiple events with same attributes", events: sdk.Events{ sdk.NewEvent( "message", sdk.NewAttribute(sdk.AttributeKeyModule, "staking"), sdk.NewAttribute(sdk.AttributeKeySender, "cosmos1foo"), ), sdk.NewEvent("message", sdk.NewAttribute(sdk.AttributeKeySender, "foo")), }, expTxtStr: "\t\t- message\n\t\t\t- module: staking\n\t\t\t- sender: cosmos1foo\n\t\t- message\n\t\t\t- sender: foo", expJSONStr: `[{"type":"message","attributes":[{"key":"module","value":"staking"},{"key":"sender","value":"cosmos1foo"}]},{"type":"message","attributes":[{"key":"sender","value":"foo"}]}]`, }, } for _, test := range cases { se := sdk.StringifyEvents(test.events.ToABCIEvents()) s.Require().Equal(test.expTxtStr, se.String()) bz, err := json.Marshal(se) s.Require().NoError(err) s.Require().Equal(test.expJSONStr, string(bz)) } } func (s *eventsTestSuite) TestMarkEventsToIndex() { events := []abci.Event{ { Type: "message", Attributes: []abci.EventAttribute{ {Key: "sender", Value: "foo"}, {Key: "recipient", Value: "bar"}, }, }, { Type: "staking", Attributes: []abci.EventAttribute{ {Key: "deposit", Value: "5"}, {Key: "unbond", Value: "10"}, }, }, } testCases := map[string]struct { events []abci.Event indexSet map[string]struct{} expected []abci.Event }{ "empty index set": { events: events, expected: []abci.Event{ { Type: "message", Attributes: []abci.EventAttribute{ {Key: "sender", Value: "foo", Index: true}, {Key: "recipient", Value: "bar", Index: true}, }, }, { Type: "staking", Attributes: []abci.EventAttribute{ {Key: "deposit", Value: "5", Index: true}, {Key: "unbond", Value: "10", Index: true}, }, }, }, indexSet: map[string]struct{}{}, }, "index some events": { events: events, expected: []abci.Event{ { Type: "message", Attributes: []abci.EventAttribute{ {Key: "sender", Value: "foo", Index: true}, {Key: "recipient", Value: "bar"}, }, }, { Type: "staking", Attributes: []abci.EventAttribute{ {Key: "deposit", Value: "5", Index: true}, {Key: "unbond", Value: "10"}, }, }, }, indexSet: map[string]struct{}{ "message.sender": {}, "staking.deposit": {}, }, }, "index all events": { events: events, expected: []abci.Event{ { Type: "message", Attributes: []abci.EventAttribute{ {Key: "sender", Value: "foo", Index: true}, {Key: "recipient", Value: "bar", Index: true}, }, }, { Type: "staking", Attributes: []abci.EventAttribute{ {Key: "deposit", Value: "5", Index: true}, {Key: "unbond", Value: "10", Index: true}, }, }, }, indexSet: map[string]struct{}{ "message.sender": {}, "message.recipient": {}, "staking.deposit": {}, "staking.unbond": {}, }, }, } for name, tc := range testCases { tc := tc s.T().Run(name, func(_ *testing.T) { s.Require().Equal(tc.expected, sdk.MarkEventsToIndex(tc.events, tc.indexSet)) }) } }