Browse Source

Add tests for tye init (#77)

pull/82/head
Justin Kotalik 6 years ago
committed by GitHub
parent
commit
90e665481b
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      src/Tye.Core/Tye.Core.csproj
  2. 7
      src/tye/ConfigModel/ConfigFactory.cs
  3. 112
      src/tye/InitHost.cs
  4. 86
      src/tye/Program.InitCommand.cs
  5. 9
      test/E2ETest/E2ETest.csproj
  6. 33
      test/E2ETest/TestHelpers.cs
  7. 84
      test/E2ETest/TyeInitTests.cs
  8. 31
      test/E2ETest/TyeRunTests.cs
  9. 8
      test/E2ETest/testassets/init/frontend-backend.yaml
  10. 10
      test/E2ETest/testassets/init/multi-project.yaml
  11. 6
      test/E2ETest/testassets/init/single-project.yaml

6
src/Tye.Core/Tye.Core.csproj

@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<RootNamespace>Tye</RootNamespace>
<LangVersion>8.0</LangVersion>
@ -33,7 +33,7 @@
<Compile Include="..\shared\TempDirectory.cs" Link="TempDirectory.cs" />
<Compile Include="..\shared\TempFile.cs" Link="TempFile.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Imports.targets" />
</ItemGroup>

7
src/tye/ConfigModel/ConfigFactory.cs

@ -65,8 +65,7 @@ namespace Tye.ConfigModel
continue;
}
var projectFilePath = project.AbsolutePath.Replace('\\', Path.DirectorySeparatorChar);
var extension = Path.GetExtension(projectFilePath).ToLower();
var extension = Path.GetExtension(project.AbsolutePath).ToLower();
switch (extension)
{
case ".csproj":
@ -76,7 +75,7 @@ namespace Tye.ConfigModel
continue;
}
var description = CreateService(new FileInfo(projectFilePath));
var description = CreateService(new FileInfo(project.AbsolutePath.Replace('\\', '/')));
if (description != null)
{
application.Services.Add(description);
@ -140,7 +139,7 @@ namespace Tye.ConfigModel
var service = new ConfigService()
{
Name = Path.GetFileNameWithoutExtension(file.Name).ToLowerInvariant(),
Project = file.FullName,
Project = file.FullName.Replace('\\', '/'),
};
PopulateFromLaunchProfile(service, launchProfile);

112
src/tye/InitHost.cs

@ -0,0 +1,112 @@
using System;
using System.CommandLine;
using System.CommandLine.IO;
using System.IO;
using Tye.ConfigModel;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace Tye
{
public class InitHost
{
public static string CreateTyeFile(FileInfo? path, bool force)
{
var (content, outputFilePath) = CreateTyeFileContent(path, force);
File.WriteAllText(outputFilePath, content);
return outputFilePath;
}
public static (string, string) CreateTyeFileContent(FileInfo? path, bool force)
{
if (path is FileInfo && path.Exists && !force)
{
ThrowIfTyeFilePresent(path, "tye.yml");
ThrowIfTyeFilePresent(path, "tye.yaml");
}
var template = @"
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
#
# define global settings here
# name: exampleapp # application name
# registry: exampleuser # dockerhub username or container registry hostname
# define multiple services here
services:
- name: myservice
# project: app.csproj # msbuild project path (relative to this file)
# executable: app.exe # path to an executable (relative to this file)
# args: --arg1=3 # arguments to pass to the process
# replicas: 5 # number of times to launch the application
# env: # array of environment variables
# - name: key
# value: value
# bindings: # optional array of bindings (ports, connection strings)
# - port: 8080 # number port of the binding
".TrimStart();
// Output in the current directory unless an input file was provided, then
// output next to the input file.
var outputFilePath = "tye.yaml";
if (path is FileInfo && path.Exists)
{
var application = ConfigFactory.FromFile(path);
var serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
.Build();
var extension = path.Extension.ToLowerInvariant();
var directory = path.Directory;
// Clear all bindings if any for solutions and project files
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj")
{
// If the input file is a project or solution then use that as the name
application.Name = Path.GetFileNameWithoutExtension(path.Name).ToLowerInvariant();
foreach (var service in application.Services)
{
service.Bindings = null!;
service.Configuration = null!;
service.Project = service.Project!.Substring(directory.FullName.Length).TrimStart('/');
}
// If the input file is a sln/project then place the config next to it
outputFilePath = Path.Combine(directory.FullName, "tye.yaml");
}
else
{
// If the input file is a yaml, then use the directory name.
application.Name = path.Directory.Name.ToLowerInvariant();
// If the input file is a yaml, then replace it.
outputFilePath = path.FullName;
}
template = @"
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
".TrimStart() + serializer.Serialize(application);
}
return (template, outputFilePath);
}
private static void ThrowIfTyeFilePresent(FileInfo? path, string yml)
{
var tyeYaml = Path.Combine(path!.DirectoryName, yml);
if (File.Exists(tyeYaml))
{
throw new CommandException($"File '{tyeYaml}' already exists. Use --force to override the {yml} file if desired.");
}
}
}
}

86
src/tye/Program.InitCommand.cs

@ -29,95 +29,11 @@ namespace Tye
command.Handler = CommandHandler.Create<IConsole, FileInfo?, bool>((console, path, force) =>
{
var output = new OutputContext(console, Verbosity.Info);
if (path is FileInfo && path.Exists && !force)
{
ThrowIfTyeFilePresent(path, "tye.yml");
ThrowIfTyeFilePresent(path, "tye.yaml");
}
var template = @"
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
#
# define global settings here
# name: exampleapp # application name
# registry: exampleuser # dockerhub username or container registry hostname
# define multiple services here
services:
- name: myservice
# project: app.csproj # msbuild project path (relative to this file)
# executable: app.exe # path to an executable (relative to this file)
# args: --arg1=3 # arguments to pass to the process
# replicas: 5 # number of times to launch the application
# env: # array of environment variables
# - name: key
# value: value
# bindings: # optional array of bindings (ports, connection strings)
# - port: 8080 # number port of the binding
".TrimStart();
// Output in the current directory unless an input file was provided, then
// output next to the input file.
var outputFilePath = "tye.yaml";
if (path is FileInfo && path.Exists)
{
var application = ConfigFactory.FromFile(path);
var serializer = new SerializerBuilder()
.WithNamingConvention(CamelCaseNamingConvention.Instance)
.ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitDefaults)
.Build();
var extension = path.Extension.ToLowerInvariant();
var directory = path.Directory;
// Clear all bindings if any for solutions and project files
if (extension == ".sln" || extension == ".csproj" || extension == ".fsproj")
{
// If the input file is a project or solution then use that as the name
application.Name = Path.GetFileNameWithoutExtension(path.Name).ToLowerInvariant();
foreach (var service in application.Services)
{
service.Bindings = null!;
service.Configuration = null!;
service.Project = service.Project!.Substring(directory.FullName.Length).TrimStart(Path.DirectorySeparatorChar);
}
// If the input file is a sln/project then place the config next to it
outputFilePath = Path.Combine(directory.FullName, "tye.yaml");
}
else
{
// If the input file is a yaml, then use the directory name.
application.Name = path.Directory.Name.ToLowerInvariant();
// If the input file is a yaml, then replace it.
outputFilePath = path.FullName;
}
template = @"
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
".TrimStart() + serializer.Serialize(application);
}
File.WriteAllText(outputFilePath, template);
var outputFilePath = InitHost.CreateTyeFile(path, force);
console.Out.WriteLine($"Created '{outputFilePath}'.");
});
return command;
}
private static void ThrowIfTyeFilePresent(FileInfo? path, string yml)
{
var tyeYaml = Path.Combine(path!.DirectoryName, yml);
if (File.Exists(tyeYaml))
{
throw new CommandException($"File '{tyeYaml}' already exists. Use --force to override the {yml} file if desired.");
}
}
}
}

9
test/E2ETest/E2ETest.csproj

@ -18,4 +18,13 @@
<ProjectReference Include="..\..\src\tye\tye.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="testassets\**\*" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<None Remove="testassets\init\frontend-backend.yaml" />
<None Remove="testassets\init\multi-project.yaml" />
</ItemGroup>
</Project>

33
test/E2ETest/TestHelpers.cs

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace E2ETest
{
public static class TestHelpers
{
// https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/Testing/src/TestPathUtilities.cs
// This can get into a bad pattern for having crazy paths in places. Eventually, especially if we use helix,
// we may want to avoid relying on sln position.
public static string GetSolutionRootDirectory(string solution)
{
var applicationBasePath = AppContext.BaseDirectory;
var directoryInfo = new DirectoryInfo(applicationBasePath);
do
{
var projectFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{solution}.sln"));
if (projectFileInfo.Exists)
{
return projectFileInfo.DirectoryName;
}
directoryInfo = directoryInfo.Parent;
}
while (directoryInfo.Parent != null);
throw new Exception($"Solution file {solution}.sln could not be found in {applicationBasePath} or its parent directories.");
}
}
}

84
test/E2ETest/TyeInitTests.cs

@ -0,0 +1,84 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using System.Text.Json;
using Microsoft.Build.Construction;
using Tye;
using Tye.ConfigModel;
using Xunit;
using Xunit.Abstractions;
namespace E2ETest
{
public class TyeInitTests
{
private readonly ITestOutputHelper output;
private readonly TestOutputLogEventSink sink;
public TyeInitTests(ITestOutputHelper output)
{
this.output = output;
sink = new TestOutputLogEventSink(output);
}
[Fact]
public void SingleProjectInitTest()
{
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project"));
using var tempDirectory = TempDirectory.Create();
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath);
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "test-project.csproj"));
var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false);
var expectedContent = File.ReadAllText("testassets/init/single-project.yaml");
output.WriteLine(content);
Assert.Equal(expectedContent, content);
}
[Fact]
public void MultiProjectInitTest()
{
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "multi-project"));
using var tempDirectory = TempDirectory.Create();
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath);
// delete already present yaml
File.Delete(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml"));
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "multi-project.sln"));
var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false);
var expectedContent = File.ReadAllText("testassets/init/multi-project.yaml");
output.WriteLine(content);
Assert.Equal(expectedContent, content);
}
[Fact]
public void FrontendBackendTest()
{
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "frontend-backend"));
using var tempDirectory = TempDirectory.Create();
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath);
// delete already present yaml
File.Delete(Path.Combine(tempDirectory.DirectoryPath, "tye.yaml"));
var projectFile = new FileInfo(Path.Combine(tempDirectory.DirectoryPath, "frontend-backend.sln"));
var (content, _) = InitHost.CreateTyeFileContent(projectFile, force: false);
var expectedContent = File.ReadAllText("testassets/init/frontend-backend.yaml");
output.WriteLine(content);
Assert.Equal(expectedContent, content);
}
}
}

31
test/E2ETest/TyeRunTest.cs → test/E2ETest/TyeRunTests.cs

@ -16,22 +16,21 @@ using Xunit.Abstractions;
namespace E2ETest
{
public class TyeRunTest
public class TyeRunTests
{
private readonly ITestOutputHelper output;
private readonly TestOutputLogEventSink sink;
public TyeRunTest(ITestOutputHelper output)
public TyeRunTests(ITestOutputHelper output)
{
this.output = output;
sink = new TestOutputLogEventSink(output);
}
[Fact]
public async Task SingleProjectTest()
{
var projectDirectory = new DirectoryInfo(Path.Combine(GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project"));
var projectDirectory = new DirectoryInfo(Path.Combine(TestHelpers.GetSolutionRootDirectory("tye"), "samples", "single-project", "test-project"));
using var tempDirectory = TempDirectory.Create();
DirectoryCopy.Copy(projectDirectory.FullName, tempDirectory.DirectoryPath);
@ -97,29 +96,5 @@ namespace E2ETest
await host.StopAsync();
}
}
// https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/Testing/src/TestPathUtilities.cs
// This can get into a bad pattern for having crazy paths in places. Eventually, especially if we use helix,
// we may want to avoid relying on sln position.
public static string GetSolutionRootDirectory(string solution)
{
var applicationBasePath = AppContext.BaseDirectory;
var directoryInfo = new DirectoryInfo(applicationBasePath);
do
{
var projectFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, $"{solution}.sln"));
if (projectFileInfo.Exists)
{
return projectFileInfo.DirectoryName;
}
directoryInfo = directoryInfo.Parent;
}
while (directoryInfo.Parent != null);
throw new Exception($"Solution file {solution}.sln could not be found in {applicationBasePath} or its parent directories.");
}
}
}

8
test/E2ETest/testassets/init/frontend-backend.yaml

@ -0,0 +1,8 @@
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
name: frontend-backend
services:
- name: backend
project: backend/backend.csproj
- name: frontend
project: frontend/frontend.csproj

10
test/E2ETest/testassets/init/multi-project.yaml

@ -0,0 +1,10 @@
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
name: multi-project
services:
- name: backend
project: backend/backend.csproj
- name: frontend
project: frontend/frontend.csproj
- name: worker
project: worker/worker.csproj

6
test/E2ETest/testassets/init/single-project.yaml

@ -0,0 +1,6 @@
# tye application configuration file
# read all about it at https://github.com/dotnet/tye
name: test-project
services:
- name: test-project
project: test-project.csproj
Loading…
Cancel
Save