Implement tests for List validation

This commit is contained in:
Timothy Baldridge 2020-04-07 22:19:36 -06:00
parent 32d0752e3f
commit 7cf817853c
12 changed files with 163 additions and 31 deletions

View File

@ -7,6 +7,7 @@ using Microsoft.Extensions.Hosting;
using Wabbajack.Common;
using Wabbajack.Common.Http;
using Wabbajack.Common.StatusFeed;
using Wabbajack.Lib.FileUploader;
using Xunit;
using Xunit.Abstractions;
@ -22,6 +23,8 @@ namespace Wabbajack.BuildServer.Test
private bool _disposed = false;
public AbsolutePath ServerTempFolder => _severTempFolder.Dir;
public AbsolutePath ServerPublicFolder => "public".RelativeTo(AbsolutePath.EntryPoint);
public BuildServerFixture()
{
@ -42,6 +45,9 @@ namespace Wabbajack.BuildServer.Test
_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()
@ -125,8 +131,11 @@ namespace Wabbajack.BuildServer.Test
_authedClient = new Client();
Fixture = fixture.Deref();
_authedClient.Headers.Add(("x-api-key", Fixture.APIKey));
AuthorAPI.ApiKeyOverride = Fixture.APIKey;
_queue = new WorkQueue();
Queue = new WorkQueue();
Consts.ModlistSummaryURL = MakeURL("lists/status.json");
Consts.ServerWhitelistURL = MakeURL("ServerWhitelist.yaml");
}
public WorkQueue Queue { get; set; }

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.IO.Compression;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Threading.Tasks;
using Wabbajack.BuildServer.Model.Models;
using Wabbajack.BuildServer.Models.JobQueue;
using Wabbajack.BuildServer.Models.Jobs;
using Wabbajack.Common;
using Wabbajack.Lib;
using Wabbajack.Lib.Downloaders;
using Wabbajack.Lib.FileUploader;
using Wabbajack.Lib.ModListRegistry;
using Xunit;
using Xunit.Abstractions;
namespace Wabbajack.BuildServer.Test
{
public class ModListValidationTests : ABuildServerSystemTest
{
public ModListValidationTests(ITestOutputHelper output, SingletonAdaptor<BuildServerFixture> fixture) : base(output, fixture)
{
}
[Fact]
public async Task CanLoadMetadataFromTestServer()
{
var modlist = await MakeModList();
Consts.ModlistMetadataURL = modlist.ToString();
var data = await ModlistMetadata.LoadFromGithub();
Assert.Single(data);
Assert.Equal("test_list", data.First().Links.MachineURL);
}
[Fact]
public async Task CanValidateModLists()
{
var modlists = await MakeModList();
Consts.ModlistMetadataURL = modlists.ToString();
Utils.Log("Updating modlists");
var result = await AuthorAPI.UpdateServerModLists();
Assert.NotNull(result);
var sql = Fixture.GetService<SqlService>();
var settings = Fixture.GetService<AppSettings>();
var job = await sql.GetJob();
Assert.NotNull(job);
Assert.IsType<UpdateModLists>(job.Payload);
var jobResult = await job.Payload.Execute(sql, settings);
Assert.Equal(JobResultType.Success, jobResult.ResultType);
Utils.Log("Checking validated results");
var data = await ModlistMetadata.LoadFromGithub();
Assert.Single(data);
Assert.Equal(0, data.First().ValidationSummary.Failed);
Assert.Equal(1, data.First().ValidationSummary.Passed);
}
private async Task<Uri> MakeModList()
{
var archive_data = Encoding.UTF8.GetBytes("Cheese for Everyone!");
var test_archive_path = "test_archive.txt".RelativeTo(Fixture.ServerPublicFolder);
await test_archive_path.WriteAllBytesAsync(archive_data);
var modListData = new ModList
{
Archives = new List<Archive>
{
new Archive
{
Hash = await test_archive_path.FileHashAsync(),
Name = "test_archive",
Size = test_archive_path.Size,
State = new HTTPDownloader.State {Url = MakeURL("test_archive.txt")}
}
}
};
var modListPath = "test_modlist.wabbajack".RelativeTo(Fixture.ServerPublicFolder);
await using (var fs = modListPath.Create())
{
using var za = new ZipArchive(fs, ZipArchiveMode.Create);
var entry = za.CreateEntry("modlist.json");
await using var es = entry.Open();
modListData.ToJson(es);
}
var modListMetaData = new List<ModlistMetadata>
{
new ModlistMetadata
{
Official = false,
Author = "Test Suite",
Description = "A test",
DownloadMetadata = new DownloadMetadata
{
Hash = await modListPath.FileHashAsync(),
Size = modListPath.Size
},
Links = new ModlistMetadata.LinksObject
{
MachineURL = "test_list",
Download = MakeURL("test_modlist.wabbajack")
}
}
};
var metadataPath = "test_mod_list_metadata.json".RelativeTo(Fixture.ServerPublicFolder);
modListMetaData.ToJson(metadataPath);
return new Uri(MakeURL("test_mod_list_metadata.json"));
}
}
}

View File

@ -22,7 +22,7 @@ namespace Wabbajack.BuildServer.Controllers
[HttpGet]
[Route("status.json")]
public async Task<IEnumerable<ModlistSummary>> HandleGetLists()
public async Task<IEnumerable<ModListSummary>> HandleGetLists()
{
return await SQL.GetModListSummaries();
}

View File

@ -92,7 +92,7 @@ namespace Wabbajack.BuildServer.Models.Jobs
var dto = new ModListStatus
{
Id = list.Links.MachineURL,
Summary = new ModlistSummary
Summary = new ModListSummary
{
Name = status.Name,
MachineURL = list.Links?.MachineURL ?? status.Name,

View File

@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Wabbajack.Common.Serialization.Json;
using Wabbajack.Lib;
using Wabbajack.Lib.ModListRegistry;
@ -10,7 +11,7 @@ namespace Wabbajack.BuildServer.Models
public class ModListStatus
{
public string Id { get; set; }
public ModlistSummary Summary { get; set; }
public ModListSummary Summary { get; set; }
public ModlistMetadata Metadata { get; set; }
public DetailedStatus DetailedStatus { get; set; }
@ -24,6 +25,7 @@ namespace Wabbajack.BuildServer.Models
}
}
[JsonName("DetailedStatus")]
public class DetailedStatus
{
public string Name { get; set; }
@ -34,6 +36,7 @@ namespace Wabbajack.BuildServer.Models
public string MachineName { get; set; }
}
[JsonName("DetailedStatusItem")]
public class DetailedStatusItem
{
public bool IsFailing { get; set; }

View File

@ -447,11 +447,11 @@ namespace Wabbajack.BuildServer.Model.Models
}
#region ModLists
public async Task<IEnumerable<ModlistSummary>> GetModListSummaries()
public async Task<IEnumerable<ModListSummary>> GetModListSummaries()
{
await using var conn = await Open();
var results = await conn.QueryAsync<string>("SELECT Summary from dbo.ModLists");
return results.Select(s => s.FromJsonString<ModlistSummary>()).ToList();
return results.Select(s => s.FromJsonString<ModListSummary>()).ToList();
}
public async Task<DetailedStatus> GetDetailedModlistStatus(string machineUrl)
@ -599,8 +599,8 @@ namespace Wabbajack.BuildServer.Model.Models
await conn.ExecuteAsync(@"MERGE dbo.ModLists AS Target
USING (SELECT @MachineUrl MachineUrl, @Metadata Metadata, @Summary Summary, @DetailedStatus DetailedStatus) AS Source
ON Target.MachineUrl = Source.MachineUrl
WHEN MATCHED THEN UPDATE SET Target.Summary = Source.Summary, Target.Metadata = Source.Metadata, Target.DetailedStatus = Source.DetailedStats
WHEN NOT MATCHED THEN INSERT (MachineUrl, Summary, Metadata, DetailedStatus) VALUES (@MachineUrl, @Summary, @Metadata, @DetailedStatus)",
WHEN MATCHED THEN UPDATE SET Target.Summary = Source.Summary, Target.Metadata = Source.Metadata, Target.DetailedStatus = Source.DetailedStatus
WHEN NOT MATCHED THEN INSERT (MachineUrl, Summary, Metadata, DetailedStatus) VALUES (@MachineUrl, @Summary, @Metadata, @DetailedStatus);",
new
{
MachineUrl = dto.Metadata.Links.MachineURL,

View File

@ -81,7 +81,6 @@ namespace Wabbajack.Common
}.Select(s => (RelativePath)s).ToHashSet();
public static string ModPermissionsURL = "https://raw.githubusercontent.com/wabbajack-tools/opt-out-lists/master/NexusModPermissions.yml";
public static string ServerWhitelistURL = "https://raw.githubusercontent.com/wabbajack-tools/opt-out-lists/master/ServerWhitelist.yml";
public static string ModlistMetadataURL = "https://raw.githubusercontent.com/wabbajack-tools/mod-lists/master/modlists.json";
public static string ModlistSummaryURL = "http://build.wabbajack.org/lists/status.json";

View File

@ -47,6 +47,12 @@ namespace Wabbajack.Common
var ser = JsonSerializer.Create(JsonSettings);
ser.Serialize(writer, obj);
}
public static void ToJson<T>(this T obj, AbsolutePath path)
{
using var fs = path.Create();
obj.ToJson(fs);
}
public static string ToJson<T>(this T obj)
{

View File

@ -21,8 +21,11 @@ namespace Wabbajack.Lib.FileUploader
{
public static IObservable<bool> HaveAuthorAPIKey => Utils.HaveEncryptedJsonObservable(Consts.AuthorAPIKeyFile);
public static string ApiKeyOverride = null;
public static async Task<string> GetAPIKey(string apiKey = null)
{
if (ApiKeyOverride != null) return ApiKeyOverride;
return apiKey ?? (await Consts.LocalAppDataPath.Combine(Consts.AuthorAPIKeyFile).ReadAllTextAsync()).Trim();
}
@ -130,7 +133,7 @@ namespace Wabbajack.Lib.FileUploader
public static async Task<string> RunJob(string jobtype)
{
var client = await GetAuthorizedClient();
return await client.GetStringAsync($"https://{Consts.WabbajackCacheHostname}/jobs/enqueue_job/{jobtype}");
return await client.GetStringAsync($"{Consts.WabbajackBuildServerUri}jobs/enqueue_job/{jobtype}");
}

View File

@ -9,6 +9,7 @@ using Game = Wabbajack.Common.Game;
namespace Wabbajack.Lib.ModListRegistry
{
[JsonName("ModListMetadata")]
public class ModlistMetadata
{
[JsonProperty("title")]
@ -35,8 +36,9 @@ namespace Wabbajack.Lib.ModListRegistry
public DownloadMetadata DownloadMetadata { get; set; }
[JsonIgnore]
public ModlistSummary ValidationSummary { get; set; } = new ModlistSummary();
public ModListSummary ValidationSummary { get; set; } = new ModListSummary();
[JsonName("Links")]
public class LinksObject
{
[JsonProperty("image")]
@ -66,10 +68,10 @@ namespace Wabbajack.Lib.ModListRegistry
var metadata = (await metadataResult).FromJsonString<List<ModlistMetadata>>();
try
{
var summaries = (await summaryResult).FromJsonString<List<ModlistSummary>>().ToDictionary(d => d.Name);
var summaries = (await summaryResult).FromJsonString<List<ModListSummary>>().ToDictionary(d => d.MachineURL);
foreach (var data in metadata)
if (summaries.TryGetValue(data.Title, out var summary))
if (summaries.TryGetValue(data.Links.MachineURL, out var summary))
data.ValidationSummary = summary;
}
catch (Exception)
@ -104,7 +106,8 @@ namespace Wabbajack.Lib.ModListRegistry
}
public class ModlistSummary
[JsonName("ModListSummary")]
public class ModListSummary
{
[JsonProperty("name")]
public string Name { get; set; }

View File

@ -31,7 +31,7 @@ namespace Wabbajack.Lib.Validation
using (var result = await response.Content.ReadAsStreamAsync())
{
ServerWhitelist = result.FromYaml<ServerWhitelist>();
Utils.Log($"Loaded permissions for {ServerWhitelist.AllowedPrefixes.Count} servers and {ServerWhitelist.GoogleIDs.Count} Google Drive files");
Utils.Log($"Loaded permissions for {ServerWhitelist.AllowedPrefixes?.Count ?? 0} servers and {ServerWhitelist.GoogleIDs?.Count ?? 0} Google Drive files");
}
}

View File

@ -44,23 +44,6 @@ steps:
command: 'test'
projects: '**/*.Test.csproj'
arguments: '/p:Platform=x64 /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:CoverletOutput=$(Build.SourcesDirectory)/CodeCoverage'
# Generate the report using ReportGenerator (https://github.com/danielpalme/ReportGenerator)
# First install the tool on the machine, then run it
- script: |
dotnet tool install -g dotnet-reportgenerator-globaltool
reportgenerator -reports:$(Build.SourcesDirectory)/tests/**/coverage.cobertura.xml -targetdir:$(Build.SourcesDirectory)/CodeCoverage -reporttypes:HtmlInline_AzurePipelines;Cobertura
displayName: Create Code coverage report
# Publish the code coverage result (summary and web site)
# The summary allows to view the coverage percentage in the summary tab
# The web site allows to view which lines are covered directly in Azure Pipeline
- task: PublishCodeCoverageResults@1
displayName: 'Publish code coverage'
inputs:
codeCoverageTool: Cobertura
summaryFileLocation: '$(Build.SourcesDirectory)/CodeCoverage/Cobertura.xml'
reportDirectory: '$(Build.SourcesDirectory)/CodeCoverage'
- task: DotNetCoreCLI@2
inputs: