using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using LittleShop.Client.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; using TeleBotClient; namespace TeleBotClient { public class SimulatorProgram { public static async Task Main(string[] args) { // Configure Serilog Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}") .WriteTo.File("logs/simulator-.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); try { // Build configuration var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false) .AddEnvironmentVariables() .Build(); // Configure services var services = new ServiceCollection(); // Add logging services.AddLogging(builder => { builder.ClearProviders(); builder.AddSerilog(); }); // Add LittleShop client services.AddLittleShopClient(options => { options.BaseUrl = configuration["LittleShop:ApiUrl"] ?? "https://localhost:5001"; options.TimeoutSeconds = 30; options.MaxRetryAttempts = 3; }); // Add simulator services.AddTransient(); services.AddTransient(); var serviceProvider = services.BuildServiceProvider(); // Run tests var runner = serviceProvider.GetRequiredService(); await runner.RunAsync(); } catch (Exception ex) { Log.Fatal(ex, "Application terminated unexpectedly"); } finally { Log.CloseAndFlush(); } } } public class TestRunner { private readonly BotSimulator _simulator; private readonly ILogger _logger; private readonly List _allResults = new(); public TestRunner(BotSimulator simulator, ILogger logger) { _simulator = simulator; _logger = logger; } public async Task RunAsync() { _logger.LogInformation("==========================================="); _logger.LogInformation("šŸš€ TeleBot Client Simulator"); _logger.LogInformation("==========================================="); while (true) { Console.WriteLine("\nšŸ“‹ Select an option:"); Console.WriteLine("1. Run single simulation"); Console.WriteLine("2. Run multiple simulations"); Console.WriteLine("3. Run stress test"); Console.WriteLine("4. View statistics"); Console.WriteLine("5. Exit"); Console.Write("\nChoice: "); var choice = Console.ReadLine(); switch (choice) { case "1": await RunSingleSimulation(); break; case "2": await RunMultipleSimulations(); break; case "3": await RunStressTest(); break; case "4": DisplayStatistics(); break; case "5": _logger.LogInformation("Exiting simulator..."); return; default: Console.WriteLine("Invalid choice. Please try again."); break; } } } private async Task RunSingleSimulation() { _logger.LogInformation("\nšŸŽÆ Running single simulation...\n"); var result = await _simulator.SimulateUserSession(); _allResults.Add(result); DisplaySimulationResult(result); } private async Task RunMultipleSimulations() { Console.Write("\nHow many simulations to run? "); if (!int.TryParse(Console.ReadLine(), out var count) || count <= 0) { Console.WriteLine("Invalid number."); return; } _logger.LogInformation("\nšŸŽÆ Running {Count} simulations...\n", count); var results = new List(); var successful = 0; var failed = 0; for (int i = 1; i <= count; i++) { _logger.LogInformation("ā–¶ļø Simulation {Number}/{Total}", i, count); var result = await _simulator.SimulateUserSession(); results.Add(result); _allResults.Add(result); if (result.Success) successful++; else failed++; // Brief pause between simulations await Task.Delay(1000); } DisplayBatchSummary(results, successful, failed); } private async Task RunStressTest() { Console.Write("\nNumber of concurrent simulations: "); if (!int.TryParse(Console.ReadLine(), out var concurrent) || concurrent <= 0) { Console.WriteLine("Invalid number."); return; } Console.Write("Total simulations to run: "); if (!int.TryParse(Console.ReadLine(), out var total) || total <= 0) { Console.WriteLine("Invalid number."); return; } _logger.LogInformation("\n⚔ Starting stress test: {Concurrent} concurrent, {Total} total\n", concurrent, total); var semaphore = new SemaphoreSlim(concurrent); var tasks = new List>(); var startTime = DateTime.UtcNow; for (int i = 0; i < total; i++) { var task = Task.Run(async () => { await semaphore.WaitAsync(); try { return await _simulator.SimulateUserSession(); } finally { semaphore.Release(); } }); tasks.Add(task); } var allResults = await Task.WhenAll(tasks); var results = allResults.ToList(); _allResults.AddRange(results); var duration = DateTime.UtcNow - startTime; DisplayStressTestResults(results, duration, concurrent, total); } private void DisplayStatistics() { if (!_allResults.Any()) { Console.WriteLine("\nšŸ“Š No simulation data available yet."); return; } Console.WriteLine("\nšŸ“Š Session Statistics:"); Console.WriteLine($" Total Simulations: {_allResults.Count}"); Console.WriteLine($" Successful: {_allResults.Count(r => r.Success)}"); Console.WriteLine($" Failed: {_allResults.Count(r => !r.Success)}"); Console.WriteLine($" Success Rate: {(_allResults.Count(r => r.Success) * 100.0 / _allResults.Count):F1}%"); var successful = _allResults.Where(r => r.Success).ToList(); if (successful.Any()) { Console.WriteLine($"\nšŸ’° Order Statistics:"); Console.WriteLine($" Total Orders: {successful.Count}"); Console.WriteLine($" Total Revenue: ${successful.Sum(r => r.OrderTotal):F2}"); Console.WriteLine($" Average Order: ${successful.Average(r => r.OrderTotal):F2}"); Console.WriteLine($" Min Order: ${successful.Min(r => r.OrderTotal):F2}"); Console.WriteLine($" Max Order: ${successful.Max(r => r.OrderTotal):F2}"); // Payment distribution var payments = successful .Where(r => !string.IsNullOrEmpty(r.PaymentCurrency)) .GroupBy(r => r.PaymentCurrency) .Select(g => new { Currency = g.Key, Count = g.Count() }) .OrderByDescending(x => x.Count); Console.WriteLine($"\nšŸ’³ Payment Methods:"); foreach (var p in payments) { Console.WriteLine($" {p.Currency}: {p.Count} ({p.Count * 100.0 / successful.Count:F1}%)"); } } } private void DisplaySimulationResult(SimulationResult result) { Console.WriteLine("\n========================================"); Console.WriteLine($"šŸ“‹ Simulation Result: {result.SessionId}"); Console.WriteLine("========================================"); if (result.Success) { Console.WriteLine("āœ… Status: SUCCESS"); } else { Console.WriteLine($"āŒ Status: FAILED - {result.ErrorMessage}"); } Console.WriteLine($"ā±ļø Duration: {result.Duration.TotalSeconds:F2}s"); if (result.Steps.Any()) { Console.WriteLine("\nšŸ“ Steps Completed:"); foreach (var step in result.Steps) { Console.WriteLine($" {step}"); } } if (result.Cart != null && result.Cart.Items.Any()) { Console.WriteLine($"\nšŸ›’ Shopping Cart ({result.Cart.Items.Count} items):"); foreach (var item in result.Cart.Items) { Console.WriteLine($" - {item.Quantity}x {item.ProductName} @ ${item.UnitPrice:F2} = ${item.TotalPrice:F2}"); } Console.WriteLine($" Total: ${result.Cart.TotalAmount:F2}"); } if (result.OrderId.HasValue) { Console.WriteLine($"\nšŸ“ Order Details:"); Console.WriteLine($" Order ID: {result.OrderId}"); Console.WriteLine($" Total: ${result.OrderTotal:F2}"); } if (!string.IsNullOrEmpty(result.PaymentCurrency)) { Console.WriteLine($"\nšŸ’° Payment Details:"); Console.WriteLine($" Currency: {result.PaymentCurrency}"); Console.WriteLine($" Amount: {result.PaymentAmount}"); } Console.WriteLine("\n========================================"); } private void DisplayBatchSummary(List results, int successful, int failed) { Console.WriteLine("\nšŸ“Š Batch Summary:"); Console.WriteLine($"āœ… Successful: {successful}"); Console.WriteLine($"āŒ Failed: {failed}"); Console.WriteLine($"šŸ“ˆ Success Rate: {(successful * 100.0 / results.Count):F1}%"); if (results.Any(r => r.Success)) { var successfulResults = results.Where(r => r.Success).ToList(); Console.WriteLine($"\nšŸ’° Order Statistics:"); Console.WriteLine($" Average Order: ${successfulResults.Average(r => r.OrderTotal):F2}"); Console.WriteLine($" Total Revenue: ${successfulResults.Sum(r => r.OrderTotal):F2}"); Console.WriteLine($" Average Duration: {successfulResults.Average(r => r.Duration.TotalSeconds):F1}s"); } } private void DisplayStressTestResults(List results, TimeSpan duration, int concurrent, int total) { var successful = results.Count(r => r.Success); var failed = results.Count(r => !r.Success); Console.WriteLine("\nšŸ“Š Stress Test Results:"); Console.WriteLine($"ā±ļø Total Duration: {duration.TotalSeconds:F1}s"); Console.WriteLine($"āœ… Successful: {successful}"); Console.WriteLine($"āŒ Failed: {failed}"); Console.WriteLine($"šŸ“ˆ Success Rate: {(successful * 100.0 / total):F1}%"); Console.WriteLine($"⚔ Throughput: {(total / duration.TotalSeconds):F2} simulations/second"); Console.WriteLine($"šŸ”„ Concurrency: {concurrent} simultaneous connections"); if (failed > 0) { Console.WriteLine("\nāŒ Failure Analysis:"); var errors = results .Where(r => !r.Success && !string.IsNullOrEmpty(r.ErrorMessage)) .GroupBy(r => r.ErrorMessage) .Select(g => new { Error = g.Key, Count = g.Count() }) .OrderByDescending(x => x.Count) .Take(5); foreach (var error in errors) { Console.WriteLine($" {error.Error}: {error.Count}"); } } } } }