littleshop/TeleBot/TeleBotClient/SimulatorProgram.cs
2025-08-27 18:02:39 +01:00

367 lines
14 KiB
C#

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<BotSimulator>();
services.AddTransient<TestRunner>();
var serviceProvider = services.BuildServiceProvider();
// Run tests
var runner = serviceProvider.GetRequiredService<TestRunner>();
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<TestRunner> _logger;
private readonly List<SimulationResult> _allResults = new();
public TestRunner(BotSimulator simulator, ILogger<TestRunner> 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<SimulationResult>();
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<Task<SimulationResult>>();
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<SimulationResult> 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<SimulationResult> 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}");
}
}
}
}
}