using System; using System.Net.Http; using System.Reactive.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Wabbajack.BuildServer.Model.Models; using Wabbajack.BuildServer.Models.JobQueue; using Wabbajack.Common; using Wabbajack.Common.Http; using Wabbajack.Common.StatusFeed; using Wabbajack.Lib.FileUploader; using Xunit; using Xunit.Abstractions; namespace Wabbajack.BuildServer.Test { public class BuildServerFixture : ADBTest, IDisposable { private IHost _host; private CancellationTokenSource _token; private Task _task; public readonly TempFolder _severTempFolder = new TempFolder(); private bool _disposed = false; public AbsolutePath ServerTempFolder => _severTempFolder.Dir; public AbsolutePath ServerPublicFolder => "public".RelativeTo(AbsolutePath.EntryPoint); public AbsolutePath ServerArchivesFolder => "archives".RelativeTo(AbsolutePath.EntryPoint); public AbsolutePath ServerUpdatesFolder => "updates".RelativeTo(AbsolutePath.EntryPoint); public BuildServerFixture() { var builder = Program.CreateHostBuilder( new[] { $"WabbajackSettings:DownloadDir={"tmp".RelativeTo(AbsolutePath.EntryPoint)}", $"WabbajackSettings:ArchiveDir={"archives".RelativeTo(AbsolutePath.EntryPoint)}", $"WabbajackSettings:TempFolder={ServerTempFolder}", $"WabbajackSettings:SQLConnection={PublicConnStr}", $"WabbajackSettings:BunnyCDN_User=TEST", $"WabbajackSettings:BunnyCDN_Password=TEST", "WabbajackSettings:JobScheduler=false", "WabbajackSettings:JobRunner=false", "WabbajackSettinss:DisableNexusForwarding=true" }, true); _host = builder.Build(); _token = new CancellationTokenSource(); _task = _host.RunAsync(_token.Token); Consts.WabbajackBuildServerUri = new Uri("http://localhost:8080"); "ServerWhitelist.yaml".RelativeTo(ServerPublicFolder).WriteAllText( "GoogleIDs:\nAllowedPrefixes:\n - http://localhost"); } ~BuildServerFixture() { Dispose(); } public T GetService() { return (T)_host.Services.GetService(typeof(T)); } public void Dispose() { if (_disposed) return; _disposed = true; if (!_token.IsCancellationRequested) _token.Cancel(); try { _task.Wait(); } catch (Exception) { // } _severTempFolder.DisposeAsync().AsTask().Wait(); } } /// /// Bit of a hack to get around that we don't want the system starting and stopping our /// HTTP server for each class its testing. /// /// public class SingletonAdaptor where T : new() { private static T _singleton = default; private static object _lock = new object(); public SingletonAdaptor() { } public T Deref() { lock (this) { if (_singleton == null) { _singleton = new T(); if (_singleton is IAsyncLifetime d) { d.InitializeAsync().Wait(); } } return _singleton; } } } [Collection("ServerTests")] public class ABuildServerSystemTest : XunitContextBase, IClassFixture> { protected readonly Client _client; private readonly IDisposable _unsubMsgs; private readonly IDisposable _unsubErr; protected Client _authedClient; protected WorkQueue _queue; private Random _random; public ABuildServerSystemTest(ITestOutputHelper output, SingletonAdaptor fixture) : base(output) { Filters.Clear(); _unsubMsgs = Utils.LogMessages.OfType().Subscribe(onNext: msg => XunitContext.WriteLine(msg.ShortDescription)); _unsubErr = Utils.LogMessages.OfType().Subscribe(msg => XunitContext.WriteLine("ERROR: User intervention required: " + msg.ShortDescription)); _client = new Client(); _authedClient = new Client(); Fixture = fixture.Deref(); _authedClient.Headers.Add(("x-api-key", Fixture.APIKey)); AuthorAPI.ApiKeyOverride = Fixture.APIKey; _queue = new WorkQueue(); Queue = new WorkQueue(); _random = new Random(); Consts.ModlistSummaryURL = MakeURL("lists/status.json"); Consts.ServerWhitelistURL = MakeURL("ServerWhitelist.yaml"); } public WorkQueue Queue { get; set; } public BuildServerFixture Fixture { get; set; } protected string MakeURL(string path) { return "http://localhost:8080/" + path; } protected byte[] RandomData() { var arr = new byte[_random.Next(1024)]; _random.NextBytes(arr); return arr; } protected async Task ClearJobQueue() { var sql = Fixture.GetService(); while (true) { var job = await sql.GetJob(); if (job == null) break; job.Result = JobResult.Success(); await sql.FinishJob(job); } } protected async Task RunAllJobs() { var sql = Fixture.GetService(); var settings = Fixture.GetService(); while (true) { var job = await sql.GetJob(); if (job == null) break; job.Result = await job.Payload.Execute(sql, settings); await sql.FinishJob(job); } } public override void Dispose() { Queue.Dispose(); base.Dispose(); _unsubMsgs.Dispose(); _unsubErr.Dispose(); } } }