Skip to content

Commit

Permalink
Merge pull request #9 from Geims83/features/spectre
Browse files Browse the repository at this point in the history
Switch from System.Commandline to Spectre.Console
  • Loading branch information
gicastel authored Oct 11, 2022
2 parents 5fd3285 + 3cae9e7 commit 253d8f0
Show file tree
Hide file tree
Showing 9 changed files with 318 additions and 258 deletions.
82 changes: 40 additions & 42 deletions src/qest/Commands/GenerateCommand.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,42 @@
using System;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using qest.Models;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
using Spectre.Console;
using Spectre.Console.Cli;

namespace qest.Commands
{
internal static class Generate

internal sealed class GenerateCommand : AsyncCommand<GenerateCommand.Settings>
{
public sealed class Settings : CommandSettings
{
[Description("Folder containing test files")]
[CommandOption("-d|--folder <FOLDER>")]
[DefaultValue("templates")]
public string Folder { get; init; }

[Description("Target connection string")]
[CommandOption("-c|--tcs <TARGETCONNECTIONSTRING>")]
public string ConnectionString { get; init; }

public override ValidationResult Validate()
{
DirectoryInfo folderToLoad = new DirectoryInfo(Folder);
if (!folderToLoad.Exists)
folderToLoad.Create();

var result = Validators.ValidateConnectionString(ConnectionString);

return result;
}
}

internal const string parametersQuery = @"
SELECT
SCHEMA_NAME(SCHEMA_ID) AS [Schema],
Expand All @@ -30,12 +56,10 @@ ORDER BY
p.parameter_id
";

internal const string yamlSchema =
@"# yaml-language-server: $schema=https://raw.githubusercontent.com/Geims83/qest/0.9.2/docs/yamlSchema.json";

internal static async Task Execute(DirectoryInfo? folder, string tcs)
public override async Task<int> ExecuteAsync([NotNull] CommandContext context, [NotNull] Settings settings)
{
var sqlConnection = new SqlConnection(tcs);
var sqlConnection = new SqlConnection(settings.ConnectionString);
DirectoryInfo folder = new DirectoryInfo(settings.Folder);

try
{
Expand All @@ -58,7 +82,7 @@ internal static async Task Execute(DirectoryInfo? folder, string tcs)

if (currentSchema != rowSchema || currentSp != rowSp)
{
await SafeWriteYamlAsync(folder!, currentTest);
await Utils.SafeWriteYamlAsync(folder!, currentTest);

currentSchema = rowSchema;
currentSp = rowSp;
Expand All @@ -78,7 +102,7 @@ internal static async Task Execute(DirectoryInfo? folder, string tcs)
var outputPar = new OutputParameter();
outputPar.Name = parameterName[1..];
outputPar.Value = "?";
outputPar.Type = Test.MapType(parameterType);
outputPar.Type = Utils.MapSqlType(parameterType);

currentStep.Results.OutputParameters.Add(outputPar);
}
Expand All @@ -88,20 +112,20 @@ internal static async Task Execute(DirectoryInfo? folder, string tcs)
}
}

await SafeWriteYamlAsync(folder!, currentTest);
await Utils.SafeWriteYamlAsync(folder!, currentTest);

}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(ex.ToString());
Environment.Exit(1);
Console.ResetColor();
AnsiConsole.WriteException(ex);
return 1;
}

return 0;
}

private static Test GenerateNewTest(string schemaName, string spName)
{
{
Test currentTest = new();
currentTest.Steps = new();
TestStep currentStep = new();
Expand All @@ -118,31 +142,5 @@ private static Test GenerateNewTest(string schemaName, string spName)

return currentTest;
}

static async Task SafeWriteYamlAsync(DirectoryInfo folder, Test testTemplate)
{
FileInfo output = new(Path.Combine(folder.Name, $"{testTemplate.Name}.yml"));

var serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();

try
{
await using var stream = new StreamWriter(output.FullName, false);

string yaml = serializer.Serialize(new Test[] {testTemplate});
await stream.WriteLineAsync(yamlSchema);
await stream.WriteAsync(yaml);

Console.WriteLine($"Created template {output.Name}");
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Error creating template {output.Name}: {ex.Message}");
Console.ResetColor();
}
}
}
}
71 changes: 0 additions & 71 deletions src/qest/Commands/Options.cs

This file was deleted.

128 changes: 83 additions & 45 deletions src/qest/Commands/RunCommand.cs
Original file line number Diff line number Diff line change
@@ -1,73 +1,111 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SqlClient;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using qest.Models;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

using Spectre.Console;
using Spectre.Console.Cli;
namespace qest.Commands
{
internal static class Run
{

internal static void Execute(FileInfo? file, DirectoryInfo? folder, string tcs)
internal sealed class RunCommand : AsyncCommand<RunCommand.Settings>
{
public sealed class Settings : CommandSettings
{
List<Test> TestCollection = new List<Test>();
[Description("Test file")]
[CommandOption("-f|--file <FILE>")]
public string? File { get; init; }

if (file is not null)
{
TestCollection.AddRange(SafeReadYaml(file));
}
else if (folder is not null)
{
foreach (var item in folder.EnumerateFiles().Where(f => f.Extension == ".yml" || f.Extension == ".yaml"))
TestCollection.AddRange(SafeReadYaml(item));
}
else
{
Console.WriteLine("One parameter between --file or --folder is mandatory.");
Environment.Exit(1);
}
[Description("Folder containing test files")]
[CommandOption("-d|--folder <FOLDER>")]
public string? Folder { get; init; }

[Description("Target connection string")]
[CommandOption("-c|--tcs <TARGETCONNECTIONSTRING>")]
public string ConnectionString { get; init; }

if (TestCollection.Count == 0)
public override ValidationResult Validate()
{
Console.WriteLine("No test loaded");
Environment.Exit(1);
}
if (Folder is null && File is null)
{
return ValidationResult.Error("One parameter between FILE or FOLDER must be supplied.");
}

var sqlConnection = new SqlConnection(tcs);
List<Test> testsToRun = new();

foreach (var test in TestCollection)
{
test.Connection = sqlConnection;
bool pass = test.Run();
if (!pass)
Environment.Exit(1);
if (File is not null)
{
FileInfo fileToLoad = new FileInfo(File);
if (!fileToLoad.Exists)
return ValidationResult.Error("File specified does not exist.");
else
testsToRun.AddRange(Utils.SafeReadYaml(fileToLoad));
}

if (Folder is not null)
{
DirectoryInfo folderToLoad = new DirectoryInfo(Folder);
if (!folderToLoad.Exists)
return ValidationResult.Error("Folder specified does not exist.");
else
foreach (var fileToLoad in folderToLoad.EnumerateFiles().Where(f => f.Extension == ".yml" || f.Extension == ".yaml"))
testsToRun.AddRange(Utils.SafeReadYaml(fileToLoad));
}

if (!testsToRun.Any())
return ValidationResult.Error("No tests found in file or folder.");

var result = Validators.ValidateConnectionString(ConnectionString);

return result;
}
Environment.Exit(0);

}

static List<Test> SafeReadYaml(FileInfo file)
private SqlConnection? TargetSqlConnection;

public override async Task<int> ExecuteAsync([NotNull] CommandContext context, [NotNull] Settings settings)
{
List<Test> list = new List<Test>();

var deserializer = new DeserializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.Build();
List<Test> TestCollection = new();

try
if (settings.File is not null)
{
using var stream = new StreamReader(file.FullName);
string yaml = stream.ReadToEnd();
list.AddRange(deserializer.Deserialize<List<Test>>(yaml));
FileInfo fileToLoad = new FileInfo(settings.File);

TestCollection.AddRange(await Utils.SafeReadYamlAsync(fileToLoad));
}
else if (settings.Folder is not null)
{
DirectoryInfo folderToLoad = new DirectoryInfo(settings.Folder);
foreach (var item in folderToLoad.EnumerateFiles().Where(f => f.Extension == ".yml" || f.Extension == ".yaml"))
TestCollection.AddRange(await Utils.SafeReadYamlAsync(item));
}
catch (Exception ex)

AnsiConsole.MarkupLine($"[grey]{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")}[/] {TestCollection.Count} tests loaded.");

TargetSqlConnection = new SqlConnection(settings.ConnectionString);

int exitCode = 0;

foreach (var test in TestCollection)
{
Console.WriteLine($"Error deserializing {file.FullName}: {ex.Message}");

test.Connection = TargetSqlConnection;
bool pass = await test.RunAsync();

if (!pass)
{
exitCode = 1;
break;
}
}
return list;

return exitCode;
}
}
}
31 changes: 31 additions & 0 deletions src/qest/Commands/Validators.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.Data.SqlClient;
using Spectre.Console;

namespace qest.Commands
{
internal static class Validators
{
internal static ValidationResult ValidateConnectionString(string ConnectionString)
{
if (ConnectionString is null || ConnectionString.Length < Utils.ConnectionStringBarebone.Length)
return ValidationResult.Error("Connection string not supplied or too short (are you missing any keyword?)");
else
{
var sqlConnection = new SqlConnection(ConnectionString);
try
{
sqlConnection.Open();
sqlConnection.Close();
}
catch (Exception ex)
{
AnsiConsole.WriteException(ex);
return ValidationResult.Error("Connection to the targed database failed.");
}
}

return ValidationResult.Success();
}
}
}
Loading

0 comments on commit 253d8f0

Please sign in to comment.