From 2b76bce2f1c997dea55b4a0b0a2c76d07ebd59c1 Mon Sep 17 00:00:00 2001 From: Jamie Rodriguez Date: Tue, 22 Aug 2023 16:00:50 +0200 Subject: [PATCH] Add tests and rework implementation --- .../discovery/policies/sap_system_policy.ex | 4 +- .../projectors/database_projector.ex | 50 --- .../projectors/sap_system_projector.ex | 50 --- .../read_models/host_read_model.ex | 1 - .../mark_application_instance_absent.ex | 4 +- .../commands/mark_database_instance_absent.ex | 4 +- .../application_instance_marked_absent.ex | 2 +- .../application_instance_marked_present.ex | 13 + .../events/database_instance_marked_absent.ex | 2 +- .../database_instance_marked_present.ex | 13 + lib/trento/domain/sap_system/instance.ex | 2 +- lib/trento/domain/sap_system/sap_system.ex | 186 +++++++-- lib/trento_web/views/v1/sap_system_view.ex | 15 - test/support/factory.ex | 20 + .../policies/sap_system_policy_test.exs | 2 - .../domain/sap_system/sap_system_test.exs | 361 ++++++++++++++++++ 16 files changed, 571 insertions(+), 158 deletions(-) create mode 100644 lib/trento/domain/sap_system/events/application_instance_marked_present.ex create mode 100644 lib/trento/domain/sap_system/events/database_instance_marked_present.ex diff --git a/lib/trento/application/integration/discovery/policies/sap_system_policy.ex b/lib/trento/application/integration/discovery/policies/sap_system_policy.ex index b9222028bc..537ee8bb96 100644 --- a/lib/trento/application/integration/discovery/policies/sap_system_policy.ex +++ b/lib/trento/application/integration/discovery/policies/sap_system_policy.ex @@ -152,7 +152,7 @@ defmodule Trento.Integration.Discovery.SapSystemPolicy do host_id: instance.host_id, instance_number: instance.instance_number, sap_system_id: instance.sap_system_id, - absent: DateTime.utc_now() + absent_at: DateTime.utc_now() }) %DatabaseInstanceReadModel{} = instance -> @@ -160,7 +160,7 @@ defmodule Trento.Integration.Discovery.SapSystemPolicy do host_id: instance.host_id, instance_number: instance.instance_number, sap_system_id: instance.sap_system_id, - absent: DateTime.utc_now() + absent_at: DateTime.utc_now() }) end) end diff --git a/lib/trento/application/projectors/database_projector.ex b/lib/trento/application/projectors/database_projector.ex index 7a66dc9b8e..09b7a21095 100644 --- a/lib/trento/application/projectors/database_projector.ex +++ b/lib/trento/application/projectors/database_projector.ex @@ -19,7 +19,6 @@ defmodule Trento.DatabaseProjector do DatabaseDeregistered, DatabaseHealthChanged, DatabaseInstanceDeregistered, - DatabaseInstanceMarkedAbsent, DatabaseInstanceHealthChanged, DatabaseInstanceRegistered, DatabaseInstanceSystemReplicationChanged, @@ -174,29 +173,6 @@ defmodule Trento.DatabaseProjector do end ) - project( - %DatabaseInstanceMarkedAbsent{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - absent: absent - }, - fn multi -> - changeset = - DatabaseInstanceReadModel - |> Repo.get_by( - sap_system_id: sap_system_id, - instance_number: instance_number, - host_id: host_id - ) - |> DatabaseInstanceReadModel.changeset(%{ - absent: absent - }) - - Ecto.Multi.update(multi, :database_instance, changeset) - end - ) - project( %DatabaseInstanceDeregistered{ instance_number: instance_number, @@ -345,32 +321,6 @@ defmodule Trento.DatabaseProjector do ) end - @impl true - def after_update( - %DatabaseInstanceMarkedAbsent{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - absent: absent - }, - _, - %{ - database_instance: %DatabaseInstanceReadModel{sid: sid} - } - ) do - TrentoWeb.Endpoint.broadcast( - @databases_topic, - "database_instance_marked_absent", - SapSystemView.render("instance_marked_absent.json", - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - sid: sid, - absent: absent - ) - ) - end - @impl true def after_update( %DatabaseDeregistered{ diff --git a/lib/trento/application/projectors/sap_system_projector.ex b/lib/trento/application/projectors/sap_system_projector.ex index 69cc8343e0..d8c59392ff 100644 --- a/lib/trento/application/projectors/sap_system_projector.ex +++ b/lib/trento/application/projectors/sap_system_projector.ex @@ -11,7 +11,6 @@ defmodule Trento.SapSystemProjector do alias Trento.Domain.Events.{ ApplicationInstanceDeregistered, ApplicationInstanceHealthChanged, - ApplicationInstanceMarkedAbsent, ApplicationInstanceMoved, ApplicationInstanceRegistered, SapSystemDeregistered, @@ -180,29 +179,6 @@ defmodule Trento.SapSystemProjector do end ) - project( - %ApplicationInstanceMarkedAbsent{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - absent: absent - }, - fn multi -> - changeset = - ApplicationInstanceReadModel - |> Repo.get_by( - sap_system_id: sap_system_id, - instance_number: instance_number, - host_id: host_id - ) - |> ApplicationInstanceReadModel.changeset(%{ - absent: absent - }) - - Ecto.Multi.update(multi, :application_instance, changeset) - end - ) - project( %ApplicationInstanceDeregistered{ instance_number: instance_number, @@ -372,32 +348,6 @@ defmodule Trento.SapSystemProjector do ) end - @impl true - def after_update( - %ApplicationInstanceMarkedAbsent{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - absent: absent - }, - _, - %{ - application_instance: %ApplicationInstanceReadModel{sid: sid} - } - ) do - TrentoWeb.Endpoint.broadcast( - @sap_systems_topic, - "application_instance_marked_absent", - SapSystemView.render("instance_marked_absent.json", - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - sid: sid, - absent: absent - ) - ) - end - @impl true def after_update( %ApplicationInstanceDeregistered{ diff --git a/lib/trento/application/read_models/host_read_model.ex b/lib/trento/application/read_models/host_read_model.ex index 76f849e1da..f596daaa89 100644 --- a/lib/trento/application/read_models/host_read_model.ex +++ b/lib/trento/application/read_models/host_read_model.ex @@ -35,7 +35,6 @@ defmodule Trento.HostReadModel do field :last_heartbeat_timestamp, :utc_datetime_usec, virtual: true field :deregistered_at, :utc_datetime_usec - field :absent, :utc_datetime_usec end @spec changeset(t() | Ecto.Changeset.t(), map) :: Ecto.Changeset.t() diff --git a/lib/trento/domain/sap_system/commands/mark_application_instance_absent.ex b/lib/trento/domain/sap_system/commands/mark_application_instance_absent.ex index ec6e3766ac..5db77fb94c 100644 --- a/lib/trento/domain/sap_system/commands/mark_application_instance_absent.ex +++ b/lib/trento/domain/sap_system/commands/mark_application_instance_absent.ex @@ -1,6 +1,6 @@ defmodule Trento.Domain.Commands.MarkApplicationInstanceAbsent do @moduledoc """ - Mark an application instance as absent from the monitoring system. + Mark an application instance as absent """ @required_fields :all @@ -10,6 +10,6 @@ defmodule Trento.Domain.Commands.MarkApplicationInstanceAbsent do field :instance_number, :string field :host_id, Ecto.UUID field :sap_system_id, Ecto.UUID - field :absent, :utc_datetime_usec + field :absent_at, :utc_datetime_usec end end diff --git a/lib/trento/domain/sap_system/commands/mark_database_instance_absent.ex b/lib/trento/domain/sap_system/commands/mark_database_instance_absent.ex index 51fc51d437..54750d0efb 100644 --- a/lib/trento/domain/sap_system/commands/mark_database_instance_absent.ex +++ b/lib/trento/domain/sap_system/commands/mark_database_instance_absent.ex @@ -1,6 +1,6 @@ defmodule Trento.Domain.Commands.MarkDatabaseInstanceAbsent do @moduledoc """ - Mark a database instance as absent from the monitoring system. + Mark a database instance as absent """ @required_fields :all @@ -10,6 +10,6 @@ defmodule Trento.Domain.Commands.MarkDatabaseInstanceAbsent do field :instance_number, :string field :host_id, Ecto.UUID field :sap_system_id, Ecto.UUID - field :absent, :utc_datetime_usec + field :absent_at, :utc_datetime_usec end end diff --git a/lib/trento/domain/sap_system/events/application_instance_marked_absent.ex b/lib/trento/domain/sap_system/events/application_instance_marked_absent.ex index 017a710081..7a85e3baa1 100644 --- a/lib/trento/domain/sap_system/events/application_instance_marked_absent.ex +++ b/lib/trento/domain/sap_system/events/application_instance_marked_absent.ex @@ -9,6 +9,6 @@ defmodule Trento.Domain.Events.ApplicationInstanceMarkedAbsent do field :instance_number, :string field :host_id, Ecto.UUID field :sap_system_id, Ecto.UUID - field :absent, :utc_datetime_usec + field :absent_at, :utc_datetime_usec end end diff --git a/lib/trento/domain/sap_system/events/application_instance_marked_present.ex b/lib/trento/domain/sap_system/events/application_instance_marked_present.ex new file mode 100644 index 0000000000..8650818a33 --- /dev/null +++ b/lib/trento/domain/sap_system/events/application_instance_marked_present.ex @@ -0,0 +1,13 @@ +defmodule Trento.Domain.Events.ApplicationInstanceMarkedPresent do + @moduledoc """ + This event is emitted when an application instance is marked as present in the SAP system. + """ + + use Trento.Event + + defevent do + field :instance_number, :string + field :host_id, Ecto.UUID + field :sap_system_id, Ecto.UUID + end +end diff --git a/lib/trento/domain/sap_system/events/database_instance_marked_absent.ex b/lib/trento/domain/sap_system/events/database_instance_marked_absent.ex index 0d81a686b3..a4665107e4 100644 --- a/lib/trento/domain/sap_system/events/database_instance_marked_absent.ex +++ b/lib/trento/domain/sap_system/events/database_instance_marked_absent.ex @@ -9,6 +9,6 @@ defmodule Trento.Domain.Events.DatabaseInstanceMarkedAbsent do field :instance_number, :string field :host_id, Ecto.UUID field :sap_system_id, Ecto.UUID - field :absent, :utc_datetime_usec + field :absent_at, :utc_datetime_usec end end diff --git a/lib/trento/domain/sap_system/events/database_instance_marked_present.ex b/lib/trento/domain/sap_system/events/database_instance_marked_present.ex new file mode 100644 index 0000000000..283e5c2477 --- /dev/null +++ b/lib/trento/domain/sap_system/events/database_instance_marked_present.ex @@ -0,0 +1,13 @@ +defmodule Trento.Domain.Events.DatabaseInstanceMarkedPresent do + @moduledoc """ + This event is emitted when a database instance is marked as present in the SAP system. + """ + + use Trento.Event + + defevent do + field :instance_number, :string + field :host_id, Ecto.UUID + field :sap_system_id, Ecto.UUID + end +end diff --git a/lib/trento/domain/sap_system/instance.ex b/lib/trento/domain/sap_system/instance.ex index dd72699f54..f45c0b94a4 100644 --- a/lib/trento/domain/sap_system/instance.ex +++ b/lib/trento/domain/sap_system/instance.ex @@ -17,6 +17,6 @@ defmodule Trento.Domain.SapSystem.Instance do field :health, Ecto.Enum, values: Health.values() field :system_replication, :string field :system_replication_status, :string - field :absent, :utc_datetime_usec + field :absent_at, :utc_datetime_usec end end diff --git a/lib/trento/domain/sap_system/sap_system.ex b/lib/trento/domain/sap_system/sap_system.ex index 830bac3c14..44aae132de 100644 --- a/lib/trento/domain/sap_system/sap_system.ex +++ b/lib/trento/domain/sap_system/sap_system.ex @@ -71,6 +71,7 @@ defmodule Trento.Domain.SapSystem do ApplicationInstanceDeregistered, ApplicationInstanceHealthChanged, ApplicationInstanceMarkedAbsent, + ApplicationInstanceMarkedPresent, ApplicationInstanceMoved, ApplicationInstanceRegistered, DatabaseDeregistered, @@ -78,6 +79,7 @@ defmodule Trento.Domain.SapSystem do DatabaseInstanceDeregistered, DatabaseInstanceHealthChanged, DatabaseInstanceMarkedAbsent, + DatabaseInstanceMarkedPresent, DatabaseInstanceRegistered, DatabaseInstanceSystemReplicationChanged, DatabaseRegistered, @@ -241,6 +243,9 @@ defmodule Trento.Domain.SapSystem do |> Multi.execute(fn _ -> maybe_emit_database_instance_registered_event(instance, command) end) + |> Multi.execute(fn _ -> + maybe_emit_database_instance_marked_present_event(instance, command) + end) |> Multi.execute(&maybe_emit_database_health_changed_event/1) |> Multi.execute(&maybe_emit_sap_system_health_changed_event/1) end @@ -260,6 +265,9 @@ defmodule Trento.Domain.SapSystem do instance ) end) + |> Multi.execute(fn sap_system -> + maybe_emit_application_instance_marked_present_event(sap_system, instance) + end) |> Multi.execute(fn sap_system -> maybe_emit_sap_system_restored_event(sap_system, instance) end) @@ -285,6 +293,9 @@ defmodule Trento.Domain.SapSystem do instance ) end) + |> Multi.execute(fn _ -> + maybe_emit_application_instance_marked_present_event(sap_system, instance) + end) |> Multi.execute(fn sap_system -> maybe_emit_application_instance_health_changed_event( sap_system, @@ -305,43 +316,47 @@ defmodule Trento.Domain.SapSystem do end def execute( - %SapSystem{sap_system_id: sap_system_id} = sap_system, + %SapSystem{sap_system_id: sap_system_id, database: %Database{instances: instances}}, %MarkDatabaseInstanceAbsent{ instance_number: instance_number, host_id: host_id, - absent: absent + absent_at: absent_at } ) do - sap_system - |> Multi.new() - |> Multi.execute(fn _ -> - %DatabaseInstanceMarkedAbsent{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - absent: absent - } - end) + case get_instance(instances, host_id, instance_number) do + %Instance{absent_at: nil} -> + %DatabaseInstanceMarkedAbsent{ + instance_number: instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + } + + _ -> + nil + end end def execute( - %SapSystem{sap_system_id: sap_system_id} = sap_system, + %SapSystem{sap_system_id: sap_system_id, application: %Application{instances: instances}}, %MarkApplicationInstanceAbsent{ instance_number: instance_number, host_id: host_id, - absent: absent + absent_at: absent_at } ) do - sap_system - |> Multi.new() - |> Multi.execute(fn _ -> - %ApplicationInstanceMarkedAbsent{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - absent: absent - } - end) + case get_instance(instances, host_id, instance_number) do + %Instance{absent_at: nil} -> + %ApplicationInstanceMarkedAbsent{ + instance_number: instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + } + + _ -> + nil + end end # Deregister a database instance and emit a DatabaseInstanceDeregistered @@ -447,7 +462,8 @@ defmodule Trento.Domain.SapSystem do instance_number: instance_number, features: features, host_id: host_id, - health: health + health: health, + absent_at: nil } | instances ] @@ -536,7 +552,8 @@ defmodule Trento.Domain.SapSystem do instance_number: instance_number, features: features, host_id: host_id, - health: health + health: health, + absent_at: nil } ] } @@ -563,7 +580,8 @@ defmodule Trento.Domain.SapSystem do instance_number: instance_number, features: features, host_id: host_id, - health: health + health: health, + absent_at: nil } | instances ] @@ -664,12 +682,68 @@ defmodule Trento.Domain.SapSystem do snapshot end + def apply( + %SapSystem{database: %Database{instances: instances} = database} = sap_system, + %DatabaseInstanceMarkedPresent{ + instance_number: instance_number, + host_id: host_id + } + ) do + instances = + Enum.map( + instances, + fn instance -> + if instance.instance_number == instance_number and instance.host_id == host_id do + %Instance{instance | absent_at: nil} + else + instance + end + end + ) + + %SapSystem{ + sap_system + | database: %Database{ + database + | instances: instances + } + } + end + + def apply( + %SapSystem{application: %Application{instances: instances} = application} = sap_system, + %ApplicationInstanceMarkedPresent{ + instance_number: instance_number, + host_id: host_id + } + ) do + instances = + Enum.map( + instances, + fn instance -> + if instance.instance_number == instance_number and instance.host_id == host_id do + %Instance{instance | absent_at: nil} + else + instance + end + end + ) + + %SapSystem{ + sap_system + | application: %Application{ + application + | instances: instances + } + } + end + def apply( %SapSystem{database: %Database{instances: instances} = database} = sap_system, %DatabaseInstanceMarkedAbsent{ instance_number: instance_number, host_id: host_id, - absent: absent + absent_at: absent_at } ) do instances = @@ -677,7 +751,7 @@ defmodule Trento.Domain.SapSystem do instances, fn instance -> if instance.instance_number == instance_number and instance.host_id == host_id do - %Instance{instance | absent: absent} + %Instance{instance | absent_at: absent_at} else instance end @@ -698,7 +772,7 @@ defmodule Trento.Domain.SapSystem do %ApplicationInstanceMarkedAbsent{ instance_number: instance_number, host_id: host_id, - absent: absent + absent_at: absent_at } ) do instances = @@ -706,7 +780,7 @@ defmodule Trento.Domain.SapSystem do instances, fn instance -> if instance.instance_number == instance_number and instance.host_id == host_id do - %Instance{instance | absent: absent} + %Instance{instance | absent_at: absent_at} else instance end @@ -856,6 +930,28 @@ defmodule Trento.Domain.SapSystem do defp maybe_emit_database_instance_registered_event(_, _), do: nil + defp maybe_emit_database_instance_marked_present_event( + %Instance{absent_at: nil}, + %RegisterDatabaseInstance{} + ), + do: nil + + defp maybe_emit_database_instance_marked_present_event( + %Instance{ + instance_number: instance_number, + host_id: host_id + }, + %RegisterDatabaseInstance{sap_system_id: sap_system_id} + ) do + %DatabaseInstanceMarkedPresent{ + instance_number: instance_number, + host_id: host_id, + sap_system_id: sap_system_id + } + end + + defp maybe_emit_database_instance_marked_present_event(_, _), do: nil + defp maybe_emit_database_instance_system_replication_changed_event( %Instance{ system_replication: system_replication, @@ -998,6 +1094,34 @@ defmodule Trento.Domain.SapSystem do end end + defp maybe_emit_application_instance_marked_present_event( + %SapSystem{ + sap_system_id: sap_system_id, + application: %Application{instances: instances} + }, + %RegisterApplicationInstance{ + instance_number: instance_number, + host_id: host_id + } + ) do + case get_instance(instances, host_id, instance_number) do + %Instance{absent_at: nil} -> + nil + + %Instance{} -> + %ApplicationInstanceMarkedPresent{ + instance_number: instance_number, + host_id: host_id, + sap_system_id: sap_system_id + } + + _ -> + nil + end + end + + defp maybe_emit_application_instance_marked_present_event(_, _), do: nil + defp maybe_emit_application_instance_health_changed_event( %SapSystem{application: %Application{instances: instances}}, %RegisterApplicationInstance{ diff --git a/lib/trento_web/views/v1/sap_system_view.ex b/lib/trento_web/views/v1/sap_system_view.ex index d03e954d35..6d1f20230f 100644 --- a/lib/trento_web/views/v1/sap_system_view.ex +++ b/lib/trento_web/views/v1/sap_system_view.ex @@ -150,21 +150,6 @@ defmodule TrentoWeb.V1.SapSystemView do def render("database_deregistered.json", %{id: id, sid: sid}), do: %{id: id, sid: sid} - def render("instance_marked_absent.json", %{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - sid: sid, - absent: absent - }), - do: %{ - instance_number: instance_number, - host_id: host_id, - sap_system_id: sap_system_id, - sid: sid, - absent: absent - } - def render("instance_deregistered.json", %{ sap_system_id: id, instance_number: instance_number, diff --git a/test/support/factory.ex b/test/support/factory.ex index 49bf60b590..431d5b3b5c 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -22,12 +22,14 @@ defmodule Trento.Factory do alias Trento.Domain.Events.{ ApplicationInstanceDeregistered, + ApplicationInstanceMarkedAbsent, ApplicationInstanceRegistered, ClusterDeregistered, ClusterRegistered, ClusterTombstoned, DatabaseDeregistered, DatabaseInstanceDeregistered, + DatabaseInstanceMarkedAbsent, DatabaseInstanceRegistered, DatabaseRegistered, DatabaseRestored, @@ -260,6 +262,15 @@ defmodule Trento.Factory do } end + def database_instance_marked_absent_event_factory do + DatabaseInstanceMarkedAbsent.new!(%{ + instance_number: "00", + host_id: Faker.UUID.v4(), + sap_system_id: Faker.UUID.v4(), + absent_at: DateTime.utc_now() + }) + end + def database_instance_deregistered_event_factory do DatabaseInstanceDeregistered.new!(%{ instance_number: "00", @@ -307,6 +318,15 @@ defmodule Trento.Factory do } end + def application_instance_marked_absent_event_factory do + ApplicationInstanceMarkedAbsent.new!(%{ + instance_number: "00", + host_id: Faker.UUID.v4(), + sap_system_id: Faker.UUID.v4(), + absent_at: DateTime.utc_now() + }) + end + def application_instance_deregistered_event_factory do ApplicationInstanceDeregistered.new!(%{ sap_system_id: Faker.UUID.v4(), diff --git a/test/trento/application/integration/discovery/policies/sap_system_policy_test.exs b/test/trento/application/integration/discovery/policies/sap_system_policy_test.exs index 3007dc45ab..231524e9e3 100644 --- a/test/trento/application/integration/discovery/policies/sap_system_policy_test.exs +++ b/test/trento/application/integration/discovery/policies/sap_system_policy_test.exs @@ -11,8 +11,6 @@ defmodule Trento.Integration.Discovery.SapSystemPolicyTest do alias Trento.Integration.Discovery.SapSystemPolicy alias Trento.Domain.Commands.{ - DeregisterApplicationInstance, - DeregisterDatabaseInstance, MarkApplicationInstanceAbsent, MarkDatabaseInstanceAbsent, RegisterApplicationInstance, diff --git a/test/trento/domain/sap_system/sap_system_test.exs b/test/trento/domain/sap_system/sap_system_test.exs index c69bf2a1a3..3a97745366 100644 --- a/test/trento/domain/sap_system/sap_system_test.exs +++ b/test/trento/domain/sap_system/sap_system_test.exs @@ -8,6 +8,8 @@ defmodule Trento.SapSystemTest do alias Trento.Domain.Commands.{ DeregisterApplicationInstance, DeregisterDatabaseInstance, + MarkApplicationInstanceAbsent, + MarkDatabaseInstanceAbsent, RegisterApplicationInstance, RegisterDatabaseInstance, RollUpSapSystem @@ -16,12 +18,16 @@ defmodule Trento.SapSystemTest do alias Trento.Domain.Events.{ ApplicationInstanceDeregistered, ApplicationInstanceHealthChanged, + ApplicationInstanceMarkedAbsent, + ApplicationInstanceMarkedPresent, ApplicationInstanceMoved, ApplicationInstanceRegistered, DatabaseDeregistered, DatabaseHealthChanged, DatabaseInstanceDeregistered, DatabaseInstanceHealthChanged, + DatabaseInstanceMarkedAbsent, + DatabaseInstanceMarkedPresent, DatabaseInstanceRegistered, DatabaseInstanceSystemReplicationChanged, DatabaseRegistered, @@ -4202,6 +4208,361 @@ defmodule Trento.SapSystemTest do end end + describe "delta deregistration" do + test "newly registered instances should be marked as present" do + sap_system_id = Faker.UUID.v4() + sid = fake_sid() + ensa_version = EnsaVersion.ensa1() + host_id = Faker.UUID.v4() + + initial_events = [ + build( + :database_registered_event, + sap_system_id: sap_system_id, + sid: sid + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid + ), + build(:application_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + features: "MESSAGESERVER", + host_id: host_id + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id, + sid: sid, + ensa_version: ensa_version + ) + ] + + assert_events_and_state( + initial_events, + [], + [], + fn state -> + assert %SapSystem{ + sid: ^sid, + ensa_version: ^ensa_version, + application: %SapSystem.Application{ + sid: ^sid, + instances: [ + %SapSystem.Instance{ + absent_at: nil + } + ] + }, + database: %SapSystem.Database{ + sid: ^sid, + instances: [ + %SapSystem.Instance{ + absent_at: nil + } + ] + } + } = state + end + ) + end + + test "receiving an 'register instance' command for an already-registered instance sets instance as present if absent" do + sap_system_id = Faker.UUID.v4() + sid = fake_sid() + ensa_version = EnsaVersion.ensa1() + host_id = Faker.UUID.v4() + absent_db_instance_number = "01" + present_db_instance_number = "02" + absent_app_instance_number = "03" + present_app_instance_number = "04" + + initial_events = [ + build( + :database_registered_event, + sap_system_id: sap_system_id, + sid: sid + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: absent_db_instance_number, + system_replication: nil, + system_replication_status: nil + ), + build( + :database_instance_marked_absent_event, + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: absent_db_instance_number, + absent_at: DateTime.utc_now() + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: present_db_instance_number, + system_replication: nil, + system_replication_status: nil + ), + build(:application_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: absent_app_instance_number, + features: "ABAP" + ), + build( + :application_instance_marked_absent_event, + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: absent_app_instance_number, + absent_at: DateTime.utc_now() + ), + build(:application_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: present_app_instance_number, + features: "MESSAGESERVER" + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id, + sid: sid, + ensa_version: ensa_version + ) + ] + + assert_events_and_state( + initial_events, + [ + %RegisterDatabaseInstance{ + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: absent_db_instance_number, + health: :passing + }, + %RegisterDatabaseInstance{ + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: present_db_instance_number, + health: :passing + }, + %RegisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: absent_app_instance_number, + health: :passing, + ensa_version: ensa_version + }, + %RegisterApplicationInstance{ + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: present_app_instance_number, + health: :passing, + ensa_version: ensa_version + } + ], + [ + %DatabaseInstanceMarkedPresent{ + instance_number: absent_db_instance_number, + host_id: host_id, + sap_system_id: sap_system_id + }, + %ApplicationInstanceMarkedPresent{ + instance_number: absent_app_instance_number, + host_id: host_id, + sap_system_id: sap_system_id + } + ], + fn state -> + assert %SapSystem{ + sid: ^sid, + ensa_version: ^ensa_version, + application: %SapSystem.Application{ + sid: ^sid, + instances: [ + %SapSystem.Instance{ + absent_at: nil + }, + %SapSystem.Instance{ + absent_at: nil + } + ] + }, + database: %SapSystem.Database{ + sid: ^sid, + instances: [ + %SapSystem.Instance{ + absent_at: nil + }, + %SapSystem.Instance{ + absent_at: nil + } + ] + } + } = state + end + ) + end + + test "absent instances receiving 'mark as absent' command should mark as absent" do + sap_system_id = Faker.UUID.v4() + sid = fake_sid() + ensa_version = EnsaVersion.ensa1() + host_id = Faker.UUID.v4() + absent_db_instance_number = "01" + present_db_instance_number = "02" + absent_app_instance_number = "03" + present_app_instance_number = "04" + absent_db_absent_at = DateTime.utc_now() + absent_app_absent_at = DateTime.utc_now() + + initial_events = [ + build( + :database_registered_event, + sap_system_id: sap_system_id, + sid: sid + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: absent_db_instance_number, + system_replication: nil, + system_replication_status: nil + ), + build( + :database_instance_marked_absent_event, + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: absent_db_instance_number, + absent_at: absent_db_absent_at + ), + build( + :database_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: present_db_instance_number, + system_replication: nil, + system_replication_status: nil + ), + build(:application_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: absent_app_instance_number, + features: "MESSAGESERVER" + ), + build( + :application_instance_marked_absent_event, + sap_system_id: sap_system_id, + host_id: host_id, + instance_number: absent_app_instance_number, + absent_at: absent_app_absent_at + ), + build(:application_instance_registered_event, + sap_system_id: sap_system_id, + sid: sid, + host_id: host_id, + instance_number: present_app_instance_number, + features: "ABAP" + ), + build( + :sap_system_registered_event, + sap_system_id: sap_system_id, + sid: sid, + ensa_version: ensa_version + ) + ] + + absent_at = DateTime.utc_now() + + assert_events_and_state( + initial_events, + [ + %MarkDatabaseInstanceAbsent{ + instance_number: absent_db_instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + }, + %MarkDatabaseInstanceAbsent{ + instance_number: present_db_instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + }, + %MarkApplicationInstanceAbsent{ + instance_number: absent_app_instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + }, + %MarkApplicationInstanceAbsent{ + instance_number: present_app_instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + } + ], + [ + %DatabaseInstanceMarkedAbsent{ + instance_number: present_db_instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + }, + %ApplicationInstanceMarkedAbsent{ + instance_number: present_app_instance_number, + host_id: host_id, + sap_system_id: sap_system_id, + absent_at: absent_at + } + ], + fn state -> + assert %SapSystem{ + sid: ^sid, + database: %SapSystem.Database{ + sid: ^sid, + instances: [ + %SapSystem.Instance{ + instance_number: ^present_db_instance_number, + absent_at: ^absent_at + }, + %SapSystem.Instance{ + instance_number: ^absent_db_instance_number, + absent_at: ^absent_db_absent_at + } + ] + }, + application: %SapSystem.Application{ + sid: ^sid, + instances: [ + %SapSystem.Instance{ + instance_number: ^present_app_instance_number, + absent_at: ^absent_at + }, + %SapSystem.Instance{ + instance_number: ^absent_app_instance_number, + absent_at: ^absent_app_absent_at + } + ] + } + } = state + end + ) + end + end + defp fake_sid, do: Enum.join([Faker.Util.letter(), Faker.Util.letter(), Faker.Util.letter()]) end