Skip to content
Merged
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
111 changes: 111 additions & 0 deletions Rot.Tests/LoggingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using Rot.Logging;

namespace Rot.Tests;

public class LoggingTests
{
[Fact]

Check failure on line 7 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'Fact' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 7 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'FactAttribute' could not be found (are you missing a using directive or an assembly reference?)
public void ConsoleLogger_WithDebugLevel_LogsDebugMessages()
{
var logger = new ConsoleLogger(LogLevel.Debug, useColors: false);

// Should not throw
logger.Debug("Debug message");
logger.Info("Info message");
logger.Warning("Warning message");
logger.Error("Error message");
}

[Fact]

Check failure on line 19 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'Fact' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 19 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'FactAttribute' could not be found (are you missing a using directive or an assembly reference?)
public void ConsoleLogger_WithErrorLevel_OnlyLogsErrors()
{
var logger = new ConsoleLogger(LogLevel.Error, useColors: false);

// Should not throw
logger.Debug("Debug message");
logger.Info("Info message");
logger.Warning("Warning message");
logger.Error("Error message");
}

[Fact]

Check failure on line 31 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'Fact' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 31 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'FactAttribute' could not be found (are you missing a using directive or an assembly reference?)
public void NullLogger_DoesNothing()
{
var logger = NullLogger.Instance;

// Should not throw
logger.Debug("Debug message");
logger.Info("Info message");
logger.Warning("Warning message");
logger.Error("Error message");
}

[Fact]

Check failure on line 43 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'Fact' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 43 in Rot.Tests/LoggingTests.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'FactAttribute' could not be found (are you missing a using directive or an assembly reference?)
public void FileLogger_WritesToFile()
{
var tempFile = Path.GetTempFileName();
try
{
using (var logger = new FileLogger(tempFile, LogLevel.Debug))
{
logger.Info("Test message");
}

var content = File.ReadAllText(tempFile);
Assert.Contains("Test message", content);
Assert.Contains("[Info]", content);
}
finally
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
}

[Fact]
public void FileLogger_WithJsonFormat_WritesJson()
{
var tempFile = Path.GetTempFileName();
try
{
using (var logger = new FileLogger(tempFile, LogLevel.Debug, asJson: true))
{
logger.Info("Test message");
}

var content = File.ReadAllText(tempFile);
Assert.Contains("\"message\":\"Test message\"", content);
Assert.Contains("\"level\":\"info\"", content);
}
finally
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
}

[Theory]
[InlineData(LogLevel.Debug, "debug")]
[InlineData(LogLevel.Info, "info")]
[InlineData(LogLevel.Warning, "warning")]
[InlineData(LogLevel.Error, "error")]
public void FileLogger_JsonFormat_CorrectLevelString(LogLevel level, string expectedLevel)
{
var tempFile = Path.GetTempFileName();
try
{
using (var logger = new FileLogger(tempFile, LogLevel.Debug, asJson: true))
{
logger.Log(level, "Test");
}

var content = File.ReadAllText(tempFile);
Assert.Contains($"\"level\":\"{expectedLevel}\"", content);
}
finally
{
if (File.Exists(tempFile))
File.Delete(tempFile);
}
}
}
22 changes: 22 additions & 0 deletions Rot.Tests/Rot.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

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

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Rot\Rot.csproj" />
</ItemGroup>

</Project>
204 changes: 204 additions & 0 deletions Rot.Tests/TaskExecutorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
using Rot.Models;
using Rot.Services;

namespace Rot.Tests;

public class TaskExecutorTests
{
[Fact]
public void HasTask_ExistingTask_ReturnsTrue()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["build"] = new TaskDefinition { Command = "echo build" }
};
var executor = new TaskExecutor(tasks);

Assert.True(executor.HasTask("build"));
}

[Fact]
public void HasTask_NonExistingTask_ReturnsFalse()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["build"] = new TaskDefinition { Command = "echo build" }
};
var executor = new TaskExecutor(tasks);

Assert.False(executor.HasTask("test"));
}

[Fact]
public void GetTaskNames_ReturnsAllTaskNames()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["build"] = new TaskDefinition { Command = "echo build" },
["test"] = new TaskDefinition { Command = "echo test" },
["deploy"] = new TaskDefinition { Command = "echo deploy" }
};
var executor = new TaskExecutor(tasks);

var taskNames = executor.GetTaskNames().ToList();

Assert.Equal(3, taskNames.Count);
Assert.Contains("build", taskNames);
Assert.Contains("test", taskNames);
Assert.Contains("deploy", taskNames);
}

[Fact]
public async Task ExecuteTaskAsync_NonExistingTask_ReturnsOne()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["build"] = new TaskDefinition { Command = "echo build" }
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("nonexistent");

Assert.Equal(1, result);
}

[Fact]
public async Task ExecuteTaskAsync_SimpleCommand_ReturnsZero()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["echo"] = new TaskDefinition
{
Command = "echo hello",
Type = "shell"
}
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("echo");

Assert.Equal(0, result);
}

[Fact]
public async Task ExecuteTaskAsync_FailingCommand_ReturnsNonZero()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["fail"] = new TaskDefinition
{
Command = "exit 1",
Type = "shell"
}
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("fail");

Assert.NotEqual(0, result);
}

[Fact]
public async Task ExecuteTaskAsync_DryRun_DoesNotExecute()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["write-file"] = new TaskDefinition
{
Command = "touch /tmp/rot-test-file-should-not-exist",
Type = "shell"
}
};
var executor = new TaskExecutor(tasks, dryRun: true);

var result = await executor.ExecuteTaskAsync("write-file");

Assert.Equal(0, result);
Assert.False(File.Exists("/tmp/rot-test-file-should-not-exist"));
}

[Fact]
public async Task ExecuteTaskAsync_WithDependency_ExecutesDependencyFirst()
{
var executionOrder = new List<string>();
var tasks = new Dictionary<string, TaskDefinition>
{
["first"] = new TaskDefinition
{
Command = "echo first",
Type = "shell"
},
["second"] = new TaskDefinition
{
Command = "echo second",
Type = "shell",
DependsOn = new[] { "first" }
}
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("second");

Assert.Equal(0, result);
}

[Fact]
public async Task ExecuteTaskAsync_WithTimeout_TimeoutKillsProcess()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["slow"] = new TaskDefinition
{
Command = "sleep 10",
Type = "shell",
Timeout = 1 // 1 second timeout
}
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("slow");

Assert.Equal(-1, result); // -1 indicates timeout
}

[Fact]
public async Task ExecuteTaskAsync_ProcessType_ExecutesDirectly()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["echo-process"] = new TaskDefinition
{
Command = "/bin/echo",
Args = new[] { "hello", "world" },
Type = "process"
}
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("echo-process");

Assert.Equal(0, result);
}

[Fact]
public async Task ExecuteTaskAsync_WithEnvironmentVariables_SetsEnv()
{
var tasks = new Dictionary<string, TaskDefinition>
{
["env-test"] = new TaskDefinition
{
Command = "printenv MY_TEST_VAR",
Type = "shell",
Env = new Dictionary<string, string>
{
["MY_TEST_VAR"] = "test-value"
}
}
};
var executor = new TaskExecutor(tasks);

var result = await executor.ExecuteTaskAsync("env-test");

Assert.Equal(0, result);
}
}
Loading