From ea9c730cc93dcc23d9c91e189a5edb4323443919 Mon Sep 17 00:00:00 2001 From: Donald Hutchison Date: Wed, 23 Mar 2022 16:03:50 +0100 Subject: [PATCH] Add test to fail suite if event map doesn't cover all the events in the application (#594) * Generate event map + add test. * Move event map template to external file. * Regenerate map. --- .../celo/events/contract_events/event_map.ex | 21 +---- apps/explorer/lib/mix/tasks/event_map.ex | 83 ++----------------- .../lib/mix/tasks/event_map_template.eex | 68 +++++++++++++++ .../explorer/celo/events/event_map_test.exs | 14 ++++ 4 files changed, 90 insertions(+), 96 deletions(-) create mode 100644 apps/explorer/lib/mix/tasks/event_map_template.eex diff --git a/apps/explorer/lib/explorer/celo/events/contract_events/event_map.ex b/apps/explorer/lib/explorer/celo/events/contract_events/event_map.ex index d9b2cbc2905d..2c2462ba8f45 100644 --- a/apps/explorer/lib/explorer/celo/events/contract_events/event_map.ex +++ b/apps/explorer/lib/explorer/celo/events/contract_events/event_map.ex @@ -70,8 +70,6 @@ defmodule Explorer.Celo.ContractEvents.EventMap do Elixir.Explorer.Celo.ContractEvents.Election.EpochRewardsDistributedToVotersEvent, "0xe5d4e30fb8364e57bc4d662a07d0cf36f4c34552004c4c3624620a2c1d1c03dc" => Elixir.Explorer.Celo.ContractEvents.Goldtoken.TransferCommentEvent, - "0xe5d4e30fb8364e57bc4d662a07d0cf36f4c34552004c4c3624620a2c1d1c03dc" => - Elixir.Explorer.Celo.ContractEvents.Stabletoken.TransferCommentEvent, "0xae7458f8697a680da6be36406ea0b8f40164915ac9cc40c0dad05a2ff6e8c6a8" => Elixir.Explorer.Celo.ContractEvents.Election.ValidatorGroupActiveVoteRevokedEvent, "0xd3532f70444893db82221041edb4dc26c94593aeb364b0b14dfc77d5ee905152" => @@ -82,23 +80,6 @@ defmodule Explorer.Celo.ContractEvents.EventMap do Elixir.Explorer.Celo.ContractEvents.Validators.ValidatorEcdsaPublicKeyUpdatedEvent } - @name_to_event %{ - "ValidatorGroupVoteActivated" => Elixir.Explorer.Celo.ContractEvents.Election.ValidatorGroupVoteActivatedEvent, - "ConstitutionSet" => Elixir.Explorer.Celo.ContractEvents.Governance.ConstitutionSetEvent, - "AssetAllocationSet" => Elixir.Explorer.Celo.ContractEvents.Reserve.AssetAllocationSetEvent, - "EpochRewardsDistributedToVoters" => - Elixir.Explorer.Celo.ContractEvents.Election.EpochRewardsDistributedToVotersEvent, - "TransferComment" => Elixir.Explorer.Celo.ContractEvents.Goldtoken.TransferCommentEvent, - "TransferComment" => Elixir.Explorer.Celo.ContractEvents.Stabletoken.TransferCommentEvent, - "ValidatorGroupActiveVoteRevoked" => - Elixir.Explorer.Celo.ContractEvents.Election.ValidatorGroupActiveVoteRevokedEvent, - "ValidatorGroupVoteCast" => Elixir.Explorer.Celo.ContractEvents.Election.ValidatorGroupVoteCastEvent, - "ValidatorEpochPaymentDistributed" => - Elixir.Explorer.Celo.ContractEvents.Validators.ValidatorEpochPaymentDistributedEvent, - "ValidatorEcdsaPublicKeyUpdated" => - Elixir.Explorer.Celo.ContractEvents.Validators.ValidatorEcdsaPublicKeyUpdatedEvent - } - def event_for_topic(topic), do: Map.get(@topic_to_event, topic) - def maps, do: {@name_to_event, @topic_to_event} + def map, do: @topic_to_event end diff --git a/apps/explorer/lib/mix/tasks/event_map.ex b/apps/explorer/lib/mix/tasks/event_map.ex index 1a7093145ba8..10b4612f5f1a 100644 --- a/apps/explorer/lib/mix/tasks/event_map.ex +++ b/apps/explorer/lib/mix/tasks/event_map.ex @@ -4,89 +4,18 @@ defmodule Mix.Tasks.EventMap do use Mix.Task alias Explorer.Celo.ContractEvents.EventTransformer - - @template """ - # This file is auto generated, changes will be lost upon regeneration - - defmodule Explorer.Celo.ContractEvents.EventMap do - @moduledoc "Map event names and event topics to concrete contract event structs" - - alias Explorer.Celo.ContractEvents.EventTransformer - alias Explorer.Repo - - @doc "Convert ethrpc log parameters to CeloContractEvent insertion parameters" - def rpc_to_event_params(logs) when is_list(logs) do - logs - |> Enum.map(fn params = %{first_topic: event_topic} -> - case event_for_topic(event_topic) do - nil -> - nil - - event -> - event - |> struct!() - |> EventTransformer.from_params(params) - |> EventTransformer.to_celo_contract_event_params() - end - end) - |> Enum.reject(&is_nil/1) - end - - @doc "Convert CeloContractEvent instance to their concrete types" - def celo_contract_event_to_concrete_event(events) when is_list(events) do - events - |> Enum.map(&celo_contract_event_to_concrete_event/1) - |> Enum.reject(&is_nil/1) - end - - def celo_contract_event_to_concrete_event(%{topic: topic} = params) do - case event_for_topic(topic) do - nil -> - nil - - event -> - event - |> struct!() - |> EventTransformer.from_celo_contract_event(params) - end - end - - @doc "Run ecto query and convert all CeloContractEvents into their concrete types" - def query_all(query) do - query - |> Repo.all() - |> celo_contract_event_to_concrete_event() - end - - @doc "Convert concrete event to CeloContractEvent insertion parameters" - def event_to_contract_event_params(events) when is_list(events) do - events |> Enum.map(&event_to_contract_event_params/1) - end - - def event_to_contract_event_params(event) do - event |> EventTransformer.to_celo_contract_event_params() - end - - @topic_to_event %{ - <%= for module <- @modules do %> "<%= module.topic %>" => - <%= module %>, - <% end %>} - - def event_for_topic(topic), do: Map.get(@topic_to_event, topic) - def map, do: @topic_to_event - - end - - """ + require Logger @path "lib/explorer/celo/events/contract_events/event_map.ex" - + @template "lib/explorer/mix.tasks/event_map_template.eex" @shortdoc "Creates a module mapping topics to event names and vice versa" def run(args) do {options, _, _} = OptionParser.parse(args, strict: [verbose: :boolean]) modules = get_events() - event_map = EEx.eval_string(@template, assigns: [modules: modules]) + + Logger.info("Found #{length(modules)} Celo contract events defined in the Explorer application") + event_map = EEx.eval_file(@template, assigns: [modules: modules]) if Keyword.get(options, :verbose) do IO.puts(event_map) @@ -94,6 +23,8 @@ defmodule Mix.Tasks.EventMap do _ = File.rm(@path) File.write(@path, event_map) + + Logger.info("Wrote event map to #{@path}") end @dialyzer {:nowarn_function, get_events: 0} diff --git a/apps/explorer/lib/mix/tasks/event_map_template.eex b/apps/explorer/lib/mix/tasks/event_map_template.eex new file mode 100644 index 000000000000..8fd65a43c965 --- /dev/null +++ b/apps/explorer/lib/mix/tasks/event_map_template.eex @@ -0,0 +1,68 @@ +# This file is auto generated, changes will be lost upon regeneration +defmodule Explorer.Celo.ContractEvents.EventMap do + @moduledoc "Map event names and event topics to concrete contract event structs" + + alias Explorer.Celo.ContractEvents.EventTransformer + alias Explorer.Repo + + @doc "Convert ethrpc log parameters to CeloContractEvent insertion parameters" + def rpc_to_event_params(logs) when is_list(logs) do + logs + |> Enum.map(fn params = %{first_topic: event_topic} -> + case event_for_topic(event_topic) do + nil -> + nil + + event -> + event + |> struct!() + |> EventTransformer.from_params(params) + |> EventTransformer.to_celo_contract_event_params() + end + end) + |> Enum.reject(&is_nil/1) + end + + @doc "Convert CeloContractEvent instance to their concrete types" + def celo_contract_event_to_concrete_event(events) when is_list(events) do + events + |> Enum.map(&celo_contract_event_to_concrete_event/1) + |> Enum.reject(&is_nil/1) + end + + def celo_contract_event_to_concrete_event(%{topic: topic} = params) do + case event_for_topic(topic) do + nil -> + nil + + event -> + event + |> struct!() + |> EventTransformer.from_celo_contract_event(params) + end + end + + @doc "Run ecto query and convert all CeloContractEvents into their concrete types" + def query_all(query) do + query + |> Repo.all() + |> celo_contract_event_to_concrete_event() + end + + @doc "Convert concrete event to CeloContractEvent insertion parameters" + def event_to_contract_event_params(events) when is_list(events) do + events |> Enum.map(&event_to_contract_event_params/1) + end + + def event_to_contract_event_params(event) do + event |> EventTransformer.to_celo_contract_event_params() + end + + @topic_to_event %{ + <%= for module <- @modules do %> "<%= module.topic %>" => + <%= module %>, + <% end %>} + + def event_for_topic(topic), do: Map.get(@topic_to_event, topic) + def map, do: @topic_to_event +end \ No newline at end of file diff --git a/apps/explorer/test/explorer/celo/events/event_map_test.exs b/apps/explorer/test/explorer/celo/events/event_map_test.exs index 7161628b5dfe..de5346f86db7 100644 --- a/apps/explorer/test/explorer/celo/events/event_map_test.exs +++ b/apps/explorer/test/explorer/celo/events/event_map_test.exs @@ -3,6 +3,7 @@ defmodule Explorer.Celo.ContractEvents.EventMapTest do alias Explorer.Celo.ContractEvents.Election.ValidatorGroupActiveVoteRevokedEvent alias Explorer.Celo.ContractEvents.EventMap + alias Explorer.Celo.ContractEvents.EventTransformer alias Explorer.Repo describe "event map" do @@ -78,4 +79,17 @@ defmodule Explorer.Celo.ContractEvents.EventMapTest do assert result.value == 420_420_420 end end + + describe "General functionality" do + test "event map should have definitions for all events" do + event_map_topic_set = EventMap.map() |> Map.keys() |> MapSet.new() + + contract_events_topic_set = + EventTransformer.__protocol__(:impls) + |> then(fn {:consolidated, modules} -> Enum.map(modules, & &1.topic()) end) + |> MapSet.new() + + assert MapSet.subset?(contract_events_topic_set, event_map_topic_set) + end + end end