#!/usr/bin/env elixir # Enhanced Application Startup Test # Run with: elixir test_enhanced_startup.exs Mix.install([]) defmodule EnhancedStartupTest do @moduledoc """ Test script to validate the enhanced application startup configuration. This script simulates the application startup process and validates: 1. Configuration loading 2. Supervisor hierarchy 3. Listener setup 4. System status """ require Logger def run_startup_validation do IO.puts("=== Enhanced Application Startup Validation ===\n") # Test 1: Configuration Validation test_configuration_loading() # Test 2: Supervisor Structure Validation test_supervisor_structure() # Test 3: Listener Configuration Validation test_listener_configurations() # Test 4: Migration Path Validation test_migration_path() IO.puts("\n=== Startup Validation Complete ===") end defp test_configuration_loading do IO.puts("๐Ÿงช Testing Configuration Loading...") # Simulate loading device listeners config mock_device_listeners = [ %{ id: :legacy_mercury_listener, port: 5000, protocol: DaProductApp.Switch.Protocol, max_connections: 100, name: "Legacy Mercury Device Listener" }, %{ id: :enhanced_primary_listener, port: 8583, protocol: DaProductApp.Switch.EnhancedProtocol, max_connections: 100, name: "Enhanced Primary ISO8583 Listener" } ] # Validate required fields required_fields = [:id, :port, :protocol, :max_connections, :name] Enum.each(mock_device_listeners, fn listener -> missing_fields = Enum.filter(required_fields, fn field -> not Map.has_key?(listener, field) end) if Enum.empty?(missing_fields) do IO.puts(" โœ… Listener configuration valid: #{listener.name}") else IO.puts(" โŒ Missing fields in #{listener.name}: #{inspect(missing_fields)}") end end) IO.puts("") end defp test_supervisor_structure do IO.puts("๐Ÿงช Testing Supervisor Structure...") # Simulate supervisor hierarchy application_children = [ "DaProductAppWeb.Telemetry", "DaProductApp.Repo", "DNSCluster", "Phoenix.PubSub", "Finch", "DaProductApp.MercuryISO8583.NetworkSupervisor", "DaProductApp.Switch.EnhancedDeviceListenerSupervisor", # New enhanced supervisor "DaProductAppWeb.Endpoint" ] IO.puts(" โœ… Application supervision tree:") Enum.each(application_children, fn child -> IO.puts(" - #{child}") end) IO.puts(" โœ… Enhanced supervisor replaces legacy DeviceListenerSupervisor") IO.puts(" โœ… Maintains existing NetworkSupervisor for upstream connections") IO.puts("") end defp test_listener_configurations do IO.puts("๐Ÿงช Testing Listener Configurations...") # Test different configuration scenarios test_scenarios = [ %{ name: "Legacy Only (Pre-Migration)", listeners: [%{port: 5000, protocol: "Protocol"}], expected_ports: [5000] }, %{ name: "Migration Phase (Both Running)", listeners: [ %{port: 5000, protocol: "Protocol"}, %{port: 8583, protocol: "EnhancedProtocol"}, %{port: 8584, protocol: "EnhancedProtocol"} ], expected_ports: [5000, 8583, 8584] }, %{ name: "Enhanced Only (Post-Migration)", listeners: [ %{port: 8583, protocol: "EnhancedProtocol"}, %{port: 8584, protocol: "EnhancedProtocol"} ], expected_ports: [8583, 8584] } ] Enum.each(test_scenarios, fn scenario -> IO.puts(" ๐Ÿ“‹ Scenario: #{scenario.name}") actual_ports = Enum.map(scenario.listeners, &Map.get(&1, :port)) if actual_ports == scenario.expected_ports do IO.puts(" โœ… Port configuration matches expected") else IO.puts(" โš ๏ธ Port mismatch - Expected: #{inspect(scenario.expected_ports)}, Got: #{inspect(actual_ports)}") end # Check protocol distribution protocol_distribution = scenario.listeners |> Enum.group_by(&Map.get(&1, :protocol)) |> Enum.map(fn {protocol, listeners} -> {protocol, length(listeners)} end) IO.puts(" ๐Ÿ“Š Protocol distribution: #{inspect(protocol_distribution)}") end) IO.puts("") end defp test_migration_path do IO.puts("๐Ÿงช Testing Migration Path...") migration_phases = [ %{ phase: "Phase 1: Setup Enhanced Listeners", actions: [ "Deploy enhanced supervisor code", "Start enhanced listeners on ports 8583, 8584, 8585", "Keep legacy listener on port 5000", "Validate all listeners are running" ], validation: "All ports accepting connections" }, %{ phase: "Phase 2: Side-by-Side Testing", actions: [ "Run migration_test_client.exs", "Compare responses between legacy and enhanced", "Monitor performance metrics", "Validate routing rules work correctly" ], validation: "Responses identical between protocols" }, %{ phase: "Phase 3: Traffic Migration", actions: [ "Configure load balancer for new ports", "Gradually shift traffic to enhanced ports", "Monitor connection counts", "Watch for any errors or performance issues" ], validation: "Enhanced ports handling production traffic" }, %{ phase: "Phase 4: Legacy Removal", actions: [ "Wait for legacy connections to close naturally", "Remove legacy listener when connections = 0", "Update configuration to remove legacy", "Cleanup old Protocol.ex if not needed" ], validation: "Only enhanced protocol running" } ] Enum.with_index(migration_phases, 1) |> Enum.each(fn {phase, index} -> IO.puts(" #{index}. #{phase.phase}") Enum.each(phase.actions, fn action -> IO.puts(" - #{action}") end) IO.puts(" โœ… Success criteria: #{phase.validation}") IO.puts("") end) end end # Configuration Validation Functions defmodule ConfigValidator do def validate_device_listeners_config(config) do required_keys = [:id, :port, :protocol, :max_connections, :name] Enum.map(config, fn listener -> missing_keys = Enum.filter(required_keys, fn key -> not Map.has_key?(listener, key) end) if Enum.empty?(missing_keys) do {:ok, listener} else {:error, "Missing keys: #{inspect(missing_keys)}"} end end) end def validate_channels_config(config) do Enum.map(config, fn {port, channel_config} -> required_keys = [:name, :packager, :protocol] missing_keys = Enum.filter(required_keys, fn key -> not Map.has_key?(channel_config, key) end) if Enum.empty?(missing_keys) do {:ok, {port, channel_config}} else {:error, "Port #{port} missing keys: #{inspect(missing_keys)}"} end end) end def validate_port_conflicts(device_listeners, channels) do listener_ports = Enum.map(device_listeners, &Map.get(&1, :port)) channel_ports = Map.keys(channels) conflicts = Enum.filter(listener_ports, fn port -> port in channel_ports end) if Enum.empty?(conflicts) do {:ok, "No port conflicts detected"} else {:warning, "Port conflicts detected: #{inspect(conflicts)}. Enhanced supervisor will use channels config for these ports."} end end end # Run the validation IO.puts("Starting enhanced application startup validation...") EnhancedStartupTest.run_startup_validation() # Additional config validation IO.puts("\n๐Ÿ” Running Configuration Validation...") # Mock configurations for testing mock_device_listeners = [ %{id: :legacy, port: 5000, protocol: DaProductApp.Switch.Protocol, max_connections: 100, name: "Legacy"}, %{id: :enhanced1, port: 8583, protocol: DaProductApp.Switch.EnhancedProtocol, max_connections: 100, name: "Enhanced 1"} ] mock_channels = %{ 8583 => %{name: "primary", packager: :iso87b, protocol: DaProductApp.Switch.EnhancedProtocol}, 8584 => %{name: "secondary", packager: :iso87b, protocol: DaProductApp.Switch.EnhancedProtocol} } case ConfigValidator.validate_device_listeners_config(mock_device_listeners) do results when is_list(results) -> valid_count = Enum.count(results, &match?({:ok, _}, &1)) IO.puts("โœ… Device listeners validation: #{valid_count}/#{length(results)} valid") error -> IO.puts("โŒ Device listeners validation failed: #{inspect(error)}") end case ConfigValidator.validate_channels_config(mock_channels) do results when is_list(results) -> valid_count = Enum.count(results, &match?({:ok, _}, &1)) IO.puts("โœ… Channels validation: #{valid_count}/#{length(results)} valid") error -> IO.puts("โŒ Channels validation failed: #{inspect(error)}") end case ConfigValidator.validate_port_conflicts(mock_device_listeners, mock_channels) do {:ok, message} -> IO.puts("โœ… Port conflict check: #{message}") {:warning, message} -> IO.puts("โš ๏ธ Port conflict check: #{message}") {:error, message} -> IO.puts("โŒ Port conflict check: #{message}") end IO.puts("\n๐ŸŽฏ Ready for enhanced application startup!")