Skip to content

Commit fde2919

Browse files
q-soriartyclaude
andcommitted
test: add test projects and root solution for all modules
Create 6 xUnit test projects with NSubstitute + FluentAssertions covering Shared, Backend.Api, Backend.Application, Backend.Infrastructure, BuildServer, and MonitorServer. Add root FlowForge.sln with solution folders. Defer x86 platform target on BuildServer until COM references are added. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent a49ecc1 commit fde2919

15 files changed

Lines changed: 445 additions & 1 deletion

File tree

src/FlowForge.sln

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.31903.59
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{10000000-0000-0000-0000-000000000001}"
7+
EndProject
8+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Shared", "shared\FlowForge.Shared\FlowForge.Shared.csproj", "{F3A4B5C6-D7E8-9012-CDEF-123456789013}"
9+
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "backend", "backend", "{10000000-0000-0000-0000-000000000002}"
11+
EndProject
12+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Api", "backend\src\FlowForge.Backend.Api\FlowForge.Backend.Api.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
13+
EndProject
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Application", "backend\src\FlowForge.Backend.Application\FlowForge.Backend.Application.csproj", "{D1E2F3A4-B5C6-7890-ABCD-EF1234567891}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Infrastructure", "backend\src\FlowForge.Backend.Infrastructure\FlowForge.Backend.Infrastructure.csproj", "{E2F3A4B5-C6D7-8901-BCDE-F12345678902}"
17+
EndProject
18+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-server", "build-server", "{10000000-0000-0000-0000-000000000003}"
19+
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.BuildServer", "build-server\src\FlowForge.BuildServer\FlowForge.BuildServer.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
21+
EndProject
22+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "monitor-server", "monitor-server", "{10000000-0000-0000-0000-000000000004}"
23+
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.MonitorServer", "monitor-server\src\FlowForge.MonitorServer\FlowForge.MonitorServer.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}"
25+
EndProject
26+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{10000000-0000-0000-0000-000000000005}"
27+
EndProject
28+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Shared.Tests", "..\test\FlowForge.Shared.Tests\FlowForge.Shared.Tests.csproj", "{20000000-0000-0000-0000-000000000001}"
29+
EndProject
30+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Api.Tests", "..\test\FlowForge.Backend.Api.Tests\FlowForge.Backend.Api.Tests.csproj", "{20000000-0000-0000-0000-000000000002}"
31+
EndProject
32+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Application.Tests", "..\test\FlowForge.Backend.Application.Tests\FlowForge.Backend.Application.Tests.csproj", "{20000000-0000-0000-0000-000000000003}"
33+
EndProject
34+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Infrastructure.Tests", "..\test\FlowForge.Backend.Infrastructure.Tests\FlowForge.Backend.Infrastructure.Tests.csproj", "{20000000-0000-0000-0000-000000000004}"
35+
EndProject
36+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.BuildServer.Tests", "..\test\FlowForge.BuildServer.Tests\FlowForge.BuildServer.Tests.csproj", "{20000000-0000-0000-0000-000000000005}"
37+
EndProject
38+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.MonitorServer.Tests", "..\test\FlowForge.MonitorServer.Tests\FlowForge.MonitorServer.Tests.csproj", "{20000000-0000-0000-0000-000000000006}"
39+
EndProject
40+
Global
41+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
42+
Debug|Any CPU = Debug|Any CPU
43+
Release|Any CPU = Release|Any CPU
44+
EndGlobalSection
45+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
46+
{F3A4B5C6-D7E8-9012-CDEF-123456789013}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
47+
{F3A4B5C6-D7E8-9012-CDEF-123456789013}.Debug|Any CPU.Build.0 = Debug|Any CPU
48+
{F3A4B5C6-D7E8-9012-CDEF-123456789013}.Release|Any CPU.ActiveCfg = Release|Any CPU
49+
{F3A4B5C6-D7E8-9012-CDEF-123456789013}.Release|Any CPU.Build.0 = Release|Any CPU
50+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
51+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
52+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
53+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
55+
{D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Debug|Any CPU.Build.0 = Debug|Any CPU
56+
{D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Release|Any CPU.ActiveCfg = Release|Any CPU
57+
{D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Release|Any CPU.Build.0 = Release|Any CPU
58+
{E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
59+
{E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Debug|Any CPU.Build.0 = Debug|Any CPU
60+
{E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Release|Any CPU.ActiveCfg = Release|Any CPU
61+
{E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Release|Any CPU.Build.0 = Release|Any CPU
62+
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
63+
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU
64+
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU
65+
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU
66+
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
67+
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
68+
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
69+
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
70+
{20000000-0000-0000-0000-000000000001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
71+
{20000000-0000-0000-0000-000000000001}.Debug|Any CPU.Build.0 = Debug|Any CPU
72+
{20000000-0000-0000-0000-000000000001}.Release|Any CPU.ActiveCfg = Release|Any CPU
73+
{20000000-0000-0000-0000-000000000001}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{20000000-0000-0000-0000-000000000002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75+
{20000000-0000-0000-0000-000000000002}.Debug|Any CPU.Build.0 = Debug|Any CPU
76+
{20000000-0000-0000-0000-000000000002}.Release|Any CPU.ActiveCfg = Release|Any CPU
77+
{20000000-0000-0000-0000-000000000002}.Release|Any CPU.Build.0 = Release|Any CPU
78+
{20000000-0000-0000-0000-000000000003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
79+
{20000000-0000-0000-0000-000000000003}.Debug|Any CPU.Build.0 = Debug|Any CPU
80+
{20000000-0000-0000-0000-000000000003}.Release|Any CPU.ActiveCfg = Release|Any CPU
81+
{20000000-0000-0000-0000-000000000003}.Release|Any CPU.Build.0 = Release|Any CPU
82+
{20000000-0000-0000-0000-000000000004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
83+
{20000000-0000-0000-0000-000000000004}.Debug|Any CPU.Build.0 = Debug|Any CPU
84+
{20000000-0000-0000-0000-000000000004}.Release|Any CPU.ActiveCfg = Release|Any CPU
85+
{20000000-0000-0000-0000-000000000004}.Release|Any CPU.Build.0 = Release|Any CPU
86+
{20000000-0000-0000-0000-000000000005}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
87+
{20000000-0000-0000-0000-000000000005}.Debug|Any CPU.Build.0 = Debug|Any CPU
88+
{20000000-0000-0000-0000-000000000005}.Release|Any CPU.ActiveCfg = Release|Any CPU
89+
{20000000-0000-0000-0000-000000000005}.Release|Any CPU.Build.0 = Release|Any CPU
90+
{20000000-0000-0000-0000-000000000006}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
91+
{20000000-0000-0000-0000-000000000006}.Debug|Any CPU.Build.0 = Debug|Any CPU
92+
{20000000-0000-0000-0000-000000000006}.Release|Any CPU.ActiveCfg = Release|Any CPU
93+
{20000000-0000-0000-0000-000000000006}.Release|Any CPU.Build.0 = Release|Any CPU
94+
EndGlobalSection
95+
GlobalSection(NestedProjects) = preSolution
96+
{F3A4B5C6-D7E8-9012-CDEF-123456789013} = {10000000-0000-0000-0000-000000000001}
97+
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {10000000-0000-0000-0000-000000000002}
98+
{D1E2F3A4-B5C6-7890-ABCD-EF1234567891} = {10000000-0000-0000-0000-000000000002}
99+
{E2F3A4B5-C6D7-8901-BCDE-F12345678902} = {10000000-0000-0000-0000-000000000002}
100+
{B2C3D4E5-F6A7-8901-BCDE-F12345678901} = {10000000-0000-0000-0000-000000000003}
101+
{C3D4E5F6-A7B8-9012-CDEF-123456789012} = {10000000-0000-0000-0000-000000000004}
102+
{20000000-0000-0000-0000-000000000001} = {10000000-0000-0000-0000-000000000005}
103+
{20000000-0000-0000-0000-000000000002} = {10000000-0000-0000-0000-000000000005}
104+
{20000000-0000-0000-0000-000000000003} = {10000000-0000-0000-0000-000000000005}
105+
{20000000-0000-0000-0000-000000000004} = {10000000-0000-0000-0000-000000000005}
106+
{20000000-0000-0000-0000-000000000005} = {10000000-0000-0000-0000-000000000005}
107+
{20000000-0000-0000-0000-000000000006} = {10000000-0000-0000-0000-000000000005}
108+
EndGlobalSection
109+
EndGlobal

src/build-server/src/FlowForge.BuildServer/FlowForge.BuildServer.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
<ImplicitUsings>enable</ImplicitUsings>
77
<RootNamespace>FlowForge.BuildServer</RootNamespace>
88
<OutputType>Exe</OutputType>
9-
<PlatformTarget>x86</PlatformTarget>
9+
<!-- TODO: Set PlatformTarget to x86 when COM references (EnvDTE, TCatSysManagerLib) are added -->
10+
<!-- <PlatformTarget>x86</PlatformTarget> -->
1011
</PropertyGroup>
1112

1213
<ItemGroup>

test/.gitkeep

Whitespace-only changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<IsPackable>false</IsPackable>
8+
<IsTestProject>true</IsTestProject>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
13+
<PackageReference Include="xunit" Version="2.*" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="2.*">
15+
<PrivateAssets>all</PrivateAssets>
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
17+
</PackageReference>
18+
<PackageReference Include="NSubstitute" Version="5.*" />
19+
<PackageReference Include="FluentAssertions" Version="7.*" />
20+
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.*" />
21+
</ItemGroup>
22+
23+
<ItemGroup>
24+
<ProjectReference Include="..\..\src\backend\src\FlowForge.Backend.Api\FlowForge.Backend.Api.csproj" />
25+
</ItemGroup>
26+
27+
</Project>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila)
2+
// SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
using FluentAssertions;
5+
using FlowForge.Backend.Api.Controllers;
6+
using Microsoft.AspNetCore.Mvc;
7+
using Xunit;
8+
9+
namespace FlowForge.Backend.Api.Tests;
10+
11+
public class HealthControllerTests
12+
{
13+
[Fact]
14+
public void Get_ShouldReturnOk()
15+
{
16+
var controller = new HealthController();
17+
18+
var result = controller.Get();
19+
20+
result.Should().BeOfType<OkObjectResult>();
21+
}
22+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila)
2+
// SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
using FluentAssertions;
5+
using FlowForge.Backend.Application.Validation;
6+
using FlowForge.Shared.Models.Flow;
7+
using Xunit;
8+
9+
namespace FlowForge.Backend.Application.Tests;
10+
11+
public class FlowDocumentValidatorTests
12+
{
13+
[Fact]
14+
public void Validate_EmptyDocument_ShouldReturnNoErrors()
15+
{
16+
var validator = new FlowDocumentValidator();
17+
var doc = new FlowDocument();
18+
19+
var errors = validator.Validate(doc);
20+
21+
errors.Should().BeEmpty();
22+
}
23+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<IsPackable>false</IsPackable>
8+
<IsTestProject>true</IsTestProject>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
13+
<PackageReference Include="xunit" Version="2.*" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="2.*">
15+
<PrivateAssets>all</PrivateAssets>
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
17+
</PackageReference>
18+
<PackageReference Include="NSubstitute" Version="5.*" />
19+
<PackageReference Include="FluentAssertions" Version="7.*" />
20+
</ItemGroup>
21+
22+
<ItemGroup>
23+
<ProjectReference Include="..\..\src\backend\src\FlowForge.Backend.Application\FlowForge.Backend.Application.csproj" />
24+
</ItemGroup>
25+
26+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila)
2+
// SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
using FluentAssertions;
5+
using FlowForge.Backend.Infrastructure.Persistence;
6+
using Microsoft.EntityFrameworkCore;
7+
using Xunit;
8+
9+
namespace FlowForge.Backend.Infrastructure.Tests;
10+
11+
public class AppDbContextTests
12+
{
13+
[Fact]
14+
public void AppDbContext_ShouldCreateSuccessfully()
15+
{
16+
var options = new DbContextOptionsBuilder<AppDbContext>()
17+
.UseInMemoryDatabase(databaseName: "TestDb")
18+
.Options;
19+
20+
using var context = new AppDbContext(options);
21+
22+
context.Should().NotBeNull();
23+
context.Projects.Should().NotBeNull();
24+
}
25+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<IsPackable>false</IsPackable>
8+
<IsTestProject>true</IsTestProject>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.*" />
13+
<PackageReference Include="xunit" Version="2.*" />
14+
<PackageReference Include="xunit.runner.visualstudio" Version="2.*">
15+
<PrivateAssets>all</PrivateAssets>
16+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
17+
</PackageReference>
18+
<PackageReference Include="NSubstitute" Version="5.*" />
19+
<PackageReference Include="FluentAssertions" Version="7.*" />
20+
<PackageReference Include="Testcontainers.PostgreSql" Version="4.*" />
21+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="9.0.*" />
22+
</ItemGroup>
23+
24+
<ItemGroup>
25+
<ProjectReference Include="..\..\src\backend\src\FlowForge.Backend.Infrastructure\FlowForge.Backend.Infrastructure.csproj" />
26+
</ItemGroup>
27+
28+
</Project>
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila)
2+
// SPDX-License-Identifier: AGPL-3.0-or-later
3+
4+
using FluentAssertions;
5+
using FlowForge.BuildServer.Pipeline;
6+
using FlowForge.Shared.Models.Build;
7+
using Microsoft.Extensions.Logging.Abstractions;
8+
using NSubstitute;
9+
using NSubstitute.ExceptionExtensions;
10+
using Xunit;
11+
12+
namespace FlowForge.BuildServer.Tests;
13+
14+
public class BuildPipelineTests
15+
{
16+
[Fact]
17+
public async Task ExecuteAsync_WithNoSteps_ShouldReturnTrue()
18+
{
19+
var pipeline = new BuildPipeline([], NullLogger<BuildPipeline>.Instance);
20+
var context = new BuildContext
21+
{
22+
Job = new BuildJobDto { Id = Guid.NewGuid() }
23+
};
24+
25+
var result = await pipeline.ExecuteAsync(context, CancellationToken.None);
26+
27+
result.Should().BeTrue();
28+
context.Errors.Should().BeEmpty();
29+
}
30+
31+
[Fact]
32+
public async Task ExecuteAsync_WithFailingStep_ShouldReturnFalse()
33+
{
34+
var failingStep = Substitute.For<IBuildStep>();
35+
failingStep.Name.Returns("FailStep");
36+
failingStep.ExecuteAsync(Arg.Any<BuildContext>(), Arg.Any<CancellationToken>())
37+
.ThrowsAsync(new InvalidOperationException("test error"));
38+
39+
var pipeline = new BuildPipeline([failingStep], NullLogger<BuildPipeline>.Instance);
40+
var context = new BuildContext
41+
{
42+
Job = new BuildJobDto { Id = Guid.NewGuid() }
43+
};
44+
45+
var result = await pipeline.ExecuteAsync(context, CancellationToken.None);
46+
47+
result.Should().BeFalse();
48+
context.Errors.Should().ContainSingle().Which.Should().Contain("test error");
49+
}
50+
}

0 commit comments

Comments
 (0)