Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .github/workflows/dotnet-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: .NET tests

on:
pull_request:
push:
branches:
- master

jobs:
seed-tests:
name: Seed tests
runs-on: ubuntu-latest

steps:
- name: Check out repository
uses: actions/checkout@v4

- name: Set up .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Run tests
run: dotnet test OneGateApp.Tests/OneGateApp.Tests.csproj --configuration Release --verbosity normal
21 changes: 21 additions & 0 deletions OneGateApp.Tests/OneGateApp.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.4" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
69 changes: 69 additions & 0 deletions OneGateApp.Tests/RawResourceTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Text.Json;
using System.Text.RegularExpressions;

namespace OneGateApp.Tests;

public sealed partial class RawResourceTests
{
[GeneratedRegex("^0x[0-9a-fA-F]{40}$", RegexOptions.CultureInvariant)]
private static partial Regex UInt160HashPattern();

[Theory]
[InlineData("protocol.json")]
[InlineData("tokens.json")]
[InlineData("nft.json")]
public void RawJsonResourcesAreValidJson(string fileName)
{
using JsonDocument document = LoadRawJson(fileName);

Assert.NotEqual(JsonValueKind.Undefined, document.RootElement.ValueKind);
}

[Fact]
public void ProtocolResourceContainsExpectedNeoMainnetConfiguration()
{
using JsonDocument document = LoadRawJson("protocol.json");
JsonElement configuration = document.RootElement.GetProperty("ProtocolConfiguration");

Assert.Equal(860833102, configuration.GetProperty("Network").GetInt32());
Assert.Equal(53, configuration.GetProperty("AddressVersion").GetInt32());
Assert.True(configuration.GetProperty("MillisecondsPerBlock").GetInt32() > 0);
Assert.True(configuration.GetProperty("StandbyCommittee").GetArrayLength() >= 7);
Assert.True(configuration.GetProperty("SeedList").GetArrayLength() > 0);
}

[Theory]
[InlineData("tokens.json", true)]
[InlineData("nft.json", false)]
public void TokenCatalogEntriesHaveRequiredFields(string fileName, bool requiresDecimals)
{
using JsonDocument document = LoadRawJson(fileName);
JsonElement catalog = document.RootElement;

Assert.Equal(JsonValueKind.Array, catalog.ValueKind);
Assert.True(catalog.GetArrayLength() > 0);

foreach (JsonElement token in catalog.EnumerateArray())
{
string hash = token.GetProperty("hash").GetString()!;
string name = token.GetProperty("name").GetString()!;
string symbol = token.GetProperty("symbol").GetString()!;

Assert.Matches(UInt160HashPattern(), hash);
Assert.False(string.IsNullOrWhiteSpace(name));
Assert.False(string.IsNullOrWhiteSpace(symbol));

if (requiresDecimals)
{
int decimals = token.GetProperty("decimals").GetInt32();
Assert.InRange(decimals, 0, 18);
}
}
}

static JsonDocument LoadRawJson(string fileName)
{
string path = Path.Combine(TestPaths.RepositoryRoot, "OneGateApp", "Resources", "Raw", fileName);
return JsonDocument.Parse(File.ReadAllText(path));
}
}
37 changes: 37 additions & 0 deletions OneGateApp.Tests/ResourceParityTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Xml.Linq;

namespace OneGateApp.Tests;

public sealed class ResourceParityTests
{
[Fact]
public void LocalizedStringResourcesExposeSameKeysAsNeutralResource()
{
string resourceDirectory = Path.Combine(TestPaths.RepositoryRoot, "OneGateApp", "Properties");
string neutralPath = Path.Combine(resourceDirectory, "Strings.resx");
string[] localizedPaths = Directory.GetFiles(resourceDirectory, "Strings.*.resx");

SortedSet<string> neutralKeys = LoadResourceKeys(neutralPath);
Assert.NotEmpty(localizedPaths);

foreach (string localizedPath in localizedPaths)
{
SortedSet<string> localizedKeys = LoadResourceKeys(localizedPath);
string[] missing = neutralKeys.Except(localizedKeys).ToArray();
string[] extra = localizedKeys.Except(neutralKeys).ToArray();

Assert.True(missing.Length == 0 && extra.Length == 0,
$"{Path.GetFileName(localizedPath)} resource keys differ. Missing: {string.Join(", ", missing)}. Extra: {string.Join(", ", extra)}.");
}
}

static SortedSet<string> LoadResourceKeys(string path)
{
XDocument document = XDocument.Load(path);
IEnumerable<string> keys = document.Root!
.Elements("data")
.Select(element => element.Attribute("name")?.Value)
.Where(name => !string.IsNullOrWhiteSpace(name))!;
return new SortedSet<string>(keys, StringComparer.Ordinal);
}
}
21 changes: 21 additions & 0 deletions OneGateApp.Tests/TestPaths.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace OneGateApp.Tests;

static class TestPaths
{
public static string RepositoryRoot { get; } = FindRepositoryRoot();

static string FindRepositoryRoot()
{
DirectoryInfo? directory = new(AppContext.BaseDirectory);
while (directory is not null)
{
string projectPath = Path.Combine(directory.FullName, "OneGateApp", "OneGateApp.csproj");
if (File.Exists(projectPath))
return directory.FullName;

directory = directory.Parent;
}

throw new DirectoryNotFoundException("Could not locate the OneGateApp repository root.");
}
}