using System.Data.Common; using System.Net; using System.Text.Json; using JobTrackerApi.Data; using JobTrackerApi.Models; using JobTrackerApi.Services; using JobTrackerApi.Services.JobImport; using JobTrackerApi.Services.JobImport.Translation; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Moq; namespace IndependentAuditValidation; public static class Entry { public static async Task Main(string[] args) { if (args.Any(arg => arg.StartsWith("--print-to-pdf="))) { // Inert browser substitute. It opens no network connection and renders no HTML. var root = Environment.GetEnvironmentVariable("VALIDATION_FAKE_BROWSER_ROOT")!; await File.WriteAllTextAsync(Path.Combine(root, Environment.ProcessId + ".started"), "started"); for (var i = 0; i < 100 && !File.Exists(Path.Combine(root, "release")); i++) await Task.Delay(50); await File.WriteAllTextAsync(args.Single(arg => arg.StartsWith("--print-to-pdf="))[15..], "%PDF-synthetic-launch-probe"); return; } var output = new List(); foreach (var target in new[] { "http://10.1.2.3/", "http://[::ffff:10.1.2.3]/", "http://169.254.169.254/", "http://[::ffff:169.254.169.254]/", "http://192.168.1.1/", "http://[::ffff:192.168.1.1]/", "http://[::1]/", "http://[::ffff:127.0.0.1]/" }) { var handler = new RecordingHandler(); var clientFactory = new Mock(); clientFactory.Setup(x => x.CreateClient(It.IsAny())).Returns(new HttpClient(handler)); var resolver = new Mock(); resolver.Setup(x => x.ResolveAsync(It.IsAny(), It.IsAny())).ReturnsAsync([IPAddress.Parse("93.184.216.34")]); var service = new JobImportService(clientFactory.Object, new UniversalJobParser(), Array.Empty(), new NoOpTranslationService(), resolver.Object); var result = await service.PreviewAsync(target, default); output.Add(new { probe = "URL guard with non-network HTTP fake", target, handler.Calls, result.Parser, result.Error }); } var folder = Path.Combine(Path.GetTempPath(), "jobjakt-boundary-" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(folder); var fakeRoot = Path.Combine(folder, "fake-browser"); Directory.CreateDirectory(fakeRoot); var executable = Path.ChangeExtension(typeof(Entry).Assembly.Location, OperatingSystem.IsWindows() ? ".exe" : null); Environment.SetEnvironmentVariable("CV_PDF_BROWSER_PATH", executable); Environment.SetEnvironmentVariable("VALIDATION_FAKE_BROWSER_ROOT", fakeRoot); var pdfConfig = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Data:Root"] = folder }).Build(); var environment = new Mock(); environment.SetupGet(x => x.ContentRootPath).Returns(folder); var pdf = new PlaywrightCvPdfExporter(new AppPaths(pdfConfig, environment.Object), Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance, pdfConfig); var renders = Enumerable.Range(0, 3).Select(i => pdf.ExportAsync("synthetic-owner", new TailoredCvRenderResult("synthetic", "synthetic.pdf", "

Inert

"), default)).ToArray(); for (var i = 0; i < 100 && Directory.GetFiles(fakeRoot, "*.started").Length < 3; i++) await Task.Delay(50); var concurrentLaunches = Directory.GetFiles(fakeRoot, "*.started").Length; await File.WriteAllTextAsync(Path.Combine(fakeRoot, "release"), "release"); await Task.WhenAll(renders); output.Add(new { probe = "actual PDF exporter with three inert child processes", concurrentLaunches }); var connectionString = "Data Source=" + Path.Combine(folder, "independent.db"); var options = new DbContextOptionsBuilder().UseSqlite(connectionString).Options; using (var db = new JobTrackerContext(options, new FixedOwner())) { await db.Database.EnsureCreatedAsync(); db.Users.Add(new ApplicationUser { Id = "synthetic-owner", UserName = "owner@example.test", NormalizedUserName = "OWNER@EXAMPLE.TEST" }); await db.SaveChangesAsync(); } var career = new Mock().Object; var renderer = new Mock().Object; int id; using (var db = new JobTrackerContext(options, new FixedOwner())) id = (await new CvVariantService(db, career, renderer).CreateAsync("synthetic-owner", "Initial", null, new CvVariantSettings(), default)).Id; using (var dbA = new JobTrackerContext(options, new FixedOwner())) using (var dbB = new JobTrackerContext(options, new FixedOwner())) { var a = new CvVariantService(dbA, career, renderer); var b = new CvVariantService(dbB, career, renderer); await a.GetAsync("synthetic-owner", id, default); await b.GetAsync("synthetic-owner", id, default); await a.SaveAsync("synthetic-owner", id, "Writer A", new CvVariantSettings(), "a", default); await b.SaveAsync("synthetic-owner", id, "Writer B", new CvVariantSettings(), "b", default); } using (var db = new JobTrackerContext(options, new FixedOwner())) { var current = await db.CvVariants.SingleAsync(); var versions = await db.CvVariantVersions.OrderBy(v => v.Id).Select(v => v.Version).ToListAsync(); output.Add(new { probe = "two preloaded real SQLite contexts", current.Name, current.Version, historyVersions = versions }); } foreach (var concurrency in new[] { 1, 2 }) { var fault = new OneShotDbFailure(); var config = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary { ["Workers:AiOperationsEnabled"] = "true", ["AiQueue:WorkerConcurrency"] = concurrency.ToString(), ["AiQueue:IdleDelayMs"] = "100" }).Build(); using var host = new HostBuilder().ConfigureLogging(l => l.ClearProviders()).ConfigureServices(services => { services.AddSingleton(config); services.AddSingleton(TimeProvider.System); services.AddScoped(); services.AddDbContext(o => o.UseSqlite(connectionString).AddInterceptors(fault)); services.AddScoped(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddHostedService(p => p.GetRequiredService()); }).Build(); await host.StartAsync(); await Task.Delay(150); fault.Armed = true; await Task.Delay(700); var lifetime = host.Services.GetRequiredService(); var service = host.Services.GetRequiredService(); output.Add(new { probe = "one transient database command failure", concurrency, fault.Failures, hostStopping = lifetime.ApplicationStopping.IsCancellationRequested, hostedTaskFaulted = service.ExecuteTask?.IsFaulted, error = service.ExecuteTask?.Exception?.GetBaseException().GetType().Name }); using var shutdown = new CancellationTokenSource(TimeSpan.FromSeconds(3)); await host.StopAsync(shutdown.Token); } Console.WriteLine(JsonSerializer.Serialize(output, new JsonSerializerOptions { WriteIndented = true })); } } sealed class FixedOwner : ICurrentUserService { public string? UserId => "synthetic-owner"; } sealed class NeutralOwner : ICurrentUserService { public string? UserId => null; } sealed class RecordingHandler : HttpMessageHandler { public int Calls; protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Calls++; return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("Synthetic inert HTML; no sockets are opened.") }); } } sealed class OneShotDbFailure : DbCommandInterceptor { public volatile bool Armed; private int fired; public int Failures => fired; public override ValueTask> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult result, CancellationToken cancellationToken = default) { if (Armed && Interlocked.CompareExchange(ref fired, 1, 0) == 0) throw new TimeoutException("Synthetic one-shot database timeout"); return base.ReaderExecutingAsync(command, eventData, result, cancellationToken); } } sealed class UnusedHandler : IAiOperationHandler { public string TaskType => "synthetic.validation"; public Task ExecuteAsync(AiOperationExecutionContext context, IServiceProvider services, CancellationToken cancellationToken) => throw new InvalidOperationException("No operations were queued in this probe."); }