Skip to content

Commit

Permalink
#115 refactor: added builder to instance YamlConfigurationSerializer …
Browse files Browse the repository at this point in the history
…and fixed tests

+ The new structure of the yaml serializer allows for better mocking and extendability
+ Some tests used mutable gateways, which could vary depending of the computer running the test. This has been solved via mocking
+ All dependencies are now fully injected, even the configuration manager
+ Adjusted margins for Wireguard.razor
  • Loading branch information
joseantmazonsb committed Feb 25, 2022
1 parent 7d9bab5 commit 0332685
Show file tree
Hide file tree
Showing 13 changed files with 177 additions and 63 deletions.
13 changes: 13 additions & 0 deletions .idea/.idea.linguard.dir/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Linguard/Cli.Test/AddInterfaceCommandShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Net.NetworkInformation;
using System.Threading.Tasks;
using Castle.Core.Internal;
using Core.Test.Mocks;
using FluentAssertions;
using Linguard.Cli.Commands;
using Typin.Attributes;
Expand Down Expand Up @@ -60,7 +61,7 @@ public async Task CreateInterfaceWithDefinedGateway() {
var commandName = command.GetAttribute<CommandAttribute>().Name!;
var app = Utils.BuildTestApp(command);

var gateway = NetworkInterface.GetAllNetworkInterfaces().First();
var gateway = new NetworkInterfaceMock("eth0").Object;
var commandLine = $"{commandName} --gateway {gateway.Name}";

await app.App.RunAsync(commandLine);
Expand Down
8 changes: 5 additions & 3 deletions Linguard/Cli/CliStartup.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using FluentValidation;
using Linguard.Cli.Middlewares;
using Linguard.Core.Configuration;
using Linguard.Core.Configuration.Serialization;
using Linguard.Core.Managers;
using Linguard.Core.Models.Wireguard;
using Linguard.Core.Models.Wireguard.Validators;
Expand All @@ -17,9 +18,10 @@ namespace Linguard.Cli;
public class CliStartup : ICliStartup {

public void ConfigureServices(IServiceCollection services) {
var manager = new YamlConfigurationManager(new Configuration(), new WorkingDirectory());
services.AddSingleton<IConfigurationManager>(manager);

services.AddSingleton<IConfigurationManager, YamlConfigurationManager>();
services.AddTransient<IConfiguration, Configuration>();
services.AddTransient<IWorkingDirectory, WorkingDirectory>();
services.AddSingleton<IConfigurationSerializer>(DefaultYamlConfigurationSerializer.Instance);
services.AddTransient<ILogger, NLogLogger>();
services.AddTransient<ICommandRunner, CommandRunner>();
services.AddTransient<IWireguardService, WireguardService>();
Expand Down
10 changes: 10 additions & 0 deletions Linguard/Core.Test/Mocks/NetworkInterfaceMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Net.NetworkInformation;
using Moq;

namespace Core.Test.Mocks;

public class NetworkInterfaceMock : Mock<NetworkInterface> {
public NetworkInterfaceMock(string name) {
SetupGet(i => i.Name).Returns(name);
}
}
28 changes: 28 additions & 0 deletions Linguard/Core.Test/Mocks/NetworkInterfaceTypeConverterMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Net.NetworkInformation;
using Moq;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization;

namespace Core.Test.Mocks;

public class NetworkInterfaceTypeConverterMock : IYamlTypeConverter {
public bool Accepts(Type type) {
return type == typeof(NetworkInterface) || type.BaseType == typeof(NetworkInterface);
}

public object? ReadYaml(IParser parser, Type type) {
var value = (parser.Current as Scalar)?.Value;
parser.MoveNext();
if (string.IsNullOrEmpty(value)) return default;
var mock = new Mock<NetworkInterface>();
mock.SetupGet(i => i.Name).Returns(value);
return mock.Object;
}

public void WriteYaml(IEmitter emitter, object? value, Type type) {
var @interface = (value as NetworkInterface)?.Name;
emitter.Emit(new Scalar(null, @interface ?? string.Empty));
}
}
23 changes: 23 additions & 0 deletions Linguard/Core.Test/Mocks/YamlConfigurationSerializerMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Linguard.Core.Configuration;
using Linguard.Core.Configuration.Serialization;
using Linguard.Core.Drivers.TrafficStorage;
using YamlDotNet.Serialization.NamingConventions;

namespace Core.Test.Mocks;

public static class YamlConfigurationSerializerMock {

public static YamlConfigurationSerializer Instance => new YamlConfigurationSerializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.WithTypeConverter<IPAddressCidrTypeConverter>()
.WithTypeConverter<NetworkInterfaceTypeConverterMock>()
.WithTypeConverter<UriTypeConverter>()
.WithTypeConverter<StyleTypeConverter>()
.WithTagMapping<Configuration>("!Configuration")
.WithTagMapping<WireguardConfiguration>("!Wireguard")
.WithTagMapping<LoggingConfiguration>("!Logging")
.WithTagMapping<WebConfiguration>("!Web")
.WithTagMapping<TrafficConfiguration>("!Traffic")
.WithTagMapping<JsonTrafficStorageDriver>("!Json")
.Build();
}
17 changes: 12 additions & 5 deletions Linguard/Core.Test/YamlConfigurationSerializerShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.NetworkInformation;
using Core.Test.Mocks;
using FluentAssertions;
using Linguard.Core;
using Linguard.Core.Configuration;
using Linguard.Core.Configuration.Serialization;
using Linguard.Core.Drivers.TrafficStorage;
using Linguard.Core.Models;
using Linguard.Core.Models.Wireguard;
using Linguard.Log;
using Moq;
using Xunit;

namespace Core.Test;
Expand All @@ -21,7 +22,7 @@ public class YamlConfigurationSerializerShould {
const string yaml = @"!Configuration
Wireguard: !Wireguard
Interfaces:
- Gateway: Ethernet
- Gateway: eth0
Port: 0
Auto: false
Clients:
Expand Down Expand Up @@ -67,6 +68,9 @@ public class YamlConfigurationSerializerShould {
Description:
OnUp: []
OnDown: []
PrimaryDns: ''
SecondaryDns: ''
Endpoint: ''
Id: 00000000-0000-0000-0000-000000000000
PublicKey:
PrivateKey:
Expand All @@ -77,6 +81,9 @@ public class YamlConfigurationSerializerShould {
IptablesBin: iptables
WireguardBin: wg
WireguardQuickBin: wg-quick
PrimaryDns: ''
SecondaryDns: ''
Endpoint: ''
Logging: !Logging
Level: Debug
Overwrite: false
Expand All @@ -93,7 +100,7 @@ public class YamlConfigurationSerializerShould {

[Fact]
public void Deserialize() {
var serializer = new YamlConfigurationSerializer();
var serializer = YamlConfigurationSerializerMock.Instance;
var config = serializer.Deserialize(yaml);
config.Logging.Should().NotBeNull();
config.Wireguard.Should().NotBeNull();
Expand Down Expand Up @@ -123,7 +130,7 @@ public void Serialize() {
Interfaces = new HashSet<Interface> {new() {
Name = "wg1",
IPv4Address = IPAddressCidr.Parse("1.1.1.1/24"),
Gateway = NetworkInterface.GetAllNetworkInterfaces().First(),
Gateway = new NetworkInterfaceMock("eth0").Object,
Clients = new []{
new Client {
Endpoint = new Uri("vpn.example.com", UriKind.RelativeOrAbsolute),
Expand All @@ -148,7 +155,7 @@ public void Serialize() {
WireguardQuickBin = "wg-quick"
}
};
var serializer = new YamlConfigurationSerializer();
var serializer = YamlConfigurationSerializerMock.Instance;
var output = serializer.Serialize(config);
output.Trim().Should().Be(yaml.Trim());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Linguard.Core.Drivers.TrafficStorage;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

namespace Linguard.Core.Configuration.Serialization;

public static class DefaultYamlConfigurationSerializer {

public static YamlConfigurationSerializer Instance => new YamlConfigurationSerializerBuilder()
.WithNamingConvention(PascalCaseNamingConvention.Instance)
.WithTypeConverter<IPAddressCidrTypeConverter>()
.WithTypeConverter<NetworkInterfaceTypeConverter>()
.WithTypeConverter<UriTypeConverter>()
.WithTypeConverter<StyleTypeConverter>()
.WithTagMapping<Configuration>("!Configuration")
.WithTagMapping<WireguardConfiguration>("!Wireguard")
.WithTagMapping<LoggingConfiguration>("!Logging")
.WithTagMapping<WebConfiguration>("!Web")
.WithTagMapping<TrafficConfiguration>("!Traffic")
.WithTagMapping<JsonTrafficStorageDriver>("!Json")
.Build();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
using Linguard.Core.Drivers.TrafficStorage;
using YamlDotNet.Core;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization;

namespace Linguard.Core.Configuration.Serialization;

Expand All @@ -13,44 +10,9 @@ public class YamlConfigurationSerializer : IConfigurationSerializer {
private readonly IDeserializer _deserializer;
private readonly ISerializer _serializer;

public YamlConfigurationSerializer() {
var namingConvention = PascalCaseNamingConvention.Instance;
var ipAddressCidrTypeConverter = new IPAddressCidrTypeConverter();
var networkInterfaceTypeConverter = new NetworkInterfaceTypeConverter();
var styleTypeConverter = new StyleTypeConverter();
var uriTypeConverter = new UriTypeConverter();
var configurationTag = new TagName("!Configuration");
var wireguardTag = new TagName("!Wireguard");
var loggingTag = new TagName("!Logging");
var webTag = new TagName("!Web");
var trafficTag = new TagName("!Traffic");
var trafficDriverTag = new TagName("!Json");
_deserializer = new DeserializerBuilder()
.WithNamingConvention(namingConvention)
.WithTypeConverter(ipAddressCidrTypeConverter)
.WithTypeConverter(networkInterfaceTypeConverter)
.WithTypeConverter(uriTypeConverter)
.WithTypeConverter(styleTypeConverter)
.WithTagMapping(configurationTag, typeof(Configuration))
.WithTagMapping(wireguardTag, typeof(WireguardConfiguration))
.WithTagMapping(loggingTag, typeof(LoggingConfiguration))
.WithTagMapping(webTag, typeof(WebConfiguration))
.WithTagMapping(trafficTag, typeof(TrafficConfiguration))
.WithTagMapping(trafficDriverTag, typeof(JsonTrafficStorageDriver))
.Build();
_serializer = new SerializerBuilder()
.WithNamingConvention(namingConvention)
.WithTypeConverter(ipAddressCidrTypeConverter)
.WithTypeConverter(networkInterfaceTypeConverter)
.WithTypeConverter(uriTypeConverter)
.WithTypeConverter(styleTypeConverter)
.WithTagMapping(configurationTag, typeof(Configuration))
.WithTagMapping(wireguardTag, typeof(WireguardConfiguration))
.WithTagMapping(loggingTag, typeof(LoggingConfiguration))
.WithTagMapping(webTag, typeof(WebConfiguration))
.WithTagMapping(trafficTag, typeof(TrafficConfiguration))
.WithTagMapping(trafficDriverTag, typeof(JsonTrafficStorageDriver))
.Build();
public YamlConfigurationSerializer(ISerializer serializer, IDeserializer deserializer) {
_serializer = serializer;
_deserializer = deserializer;
}

public string Serialize(IConfiguration configuration) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using YamlDotNet.Core;
using YamlDotNet.Serialization;

namespace Linguard.Core.Configuration.Serialization;

public class YamlConfigurationSerializerBuilder {
private readonly DeserializerBuilder _deserializerBuilder = new();
private readonly SerializerBuilder _serializerBuilder = new();

public YamlConfigurationSerializerBuilder WithTagMapping<T>(TagName tag) {
_deserializerBuilder.WithTagMapping(tag, typeof(T));
_serializerBuilder.WithTagMapping(tag, typeof(T));
return this;
}

public YamlConfigurationSerializerBuilder WithNamingConvention(INamingConvention namingConvention) {
_deserializerBuilder.WithNamingConvention(namingConvention);
_serializerBuilder.WithNamingConvention(namingConvention);
return this;
}

public YamlConfigurationSerializerBuilder WithTypeConverter(IYamlTypeConverter converter) {
_deserializerBuilder.WithTypeConverter(converter);
_serializerBuilder.WithTypeConverter(converter);
return this;
}

public YamlConfigurationSerializerBuilder WithTypeConverter<T>() where T : IYamlTypeConverter {
return WithTypeConverter(Activator.CreateInstance<T>());
}

public YamlConfigurationSerializer Build() {
return new YamlConfigurationSerializer(_serializerBuilder.Build(), _deserializerBuilder.Build());
}
}
20 changes: 12 additions & 8 deletions Linguard/Core/Managers/YamlConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public class YamlConfigurationManager : FileConfigurationManager {
private static readonly string[] SupportedExtensions = {"yaml", "yml"};

private FileInfo? _configurationFile;
private readonly ICommandRunner _commandRunner;

protected sealed override FileInfo ConfigurationFile {
get {
if (_configurationFile != default) return _configurationFile;
Expand All @@ -28,11 +30,11 @@ protected sealed override FileInfo ConfigurationFile {
}
}

public override async void LoadDefaults() {
public override void LoadDefaults() {
LoadWebDefaults();
LoadLoggingDefaults();
LoadTrafficDefaults();
await LoadWireguardDefaults();
LoadWireguardDefaults();
}

private void LoadWebDefaults() {
Expand All @@ -48,13 +50,13 @@ private void LoadTrafficDefaults() {
Configuration.Traffic.Enabled = true;
Configuration.Traffic.StorageDriver = new JsonTrafficStorageDriver();
}
private async Task LoadWireguardDefaults() {
private void LoadWireguardDefaults() {
Configuration.Wireguard.Interfaces = new HashSet<Interface>();
Configuration.Wireguard.IptablesBin = new CommandRunner()
Configuration.Wireguard.IptablesBin = _commandRunner
.Run("whereis iptables | tr ' ' '\n' | grep bin").Stdout;
Configuration.Wireguard.WireguardBin = new CommandRunner()
Configuration.Wireguard.WireguardBin = _commandRunner
.Run("whereis wg | tr ' ' '\n' | grep bin").Stdout;
Configuration.Wireguard.WireguardQuickBin = new CommandRunner()
Configuration.Wireguard.WireguardQuickBin = _commandRunner
.Run("whereis wg-quick | tr ' ' '\n' | grep bin").Stdout;
Configuration.Wireguard.Interfaces = new();
Configuration.Wireguard.PrimaryDns = new("8.8.8.8", UriKind.RelativeOrAbsolute);
Expand All @@ -65,7 +67,9 @@ private async Task LoadWireguardDefaults() {
: new(publicIp.ToString(), UriKind.RelativeOrAbsolute);
}

public YamlConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory)
: base(configuration, workingDirectory, new YamlConfigurationSerializer()) {
public YamlConfigurationManager(IConfiguration configuration, IWorkingDirectory workingDirectory,
IConfigurationSerializer serializer, ICommandRunner commandRunner)
: base(configuration, workingDirectory, serializer) {
_commandRunner = commandRunner;
}
}
2 changes: 1 addition & 1 deletion Linguard/Web/Pages/Wireguard.razor
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</div>
}

<h2 class="mb-3">Clients</h2>
<h2 class="my-3">Clients</h2>
<RadzenSplitButton Click="AddClient" Text="Add new" Icon="add_circle_outline" class="mb-2">
<ChildContent>
@foreach (var iface in Configuration.Wireguard.Interfaces) {
Expand Down
Loading

0 comments on commit 0332685

Please sign in to comment.