diff --git a/src/FlowForge.sln b/src/FlowForge.sln new file mode 100644 index 0000000..0b76d3c --- /dev/null +++ b/src/FlowForge.sln @@ -0,0 +1,109 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{10000000-0000-0000-0000-000000000001}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Shared", "shared\FlowForge.Shared\FlowForge.Shared.csproj", "{F3A4B5C6-D7E8-9012-CDEF-123456789013}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "backend", "backend", "{10000000-0000-0000-0000-000000000002}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Api", "backend\src\FlowForge.Backend.Api\FlowForge.Backend.Api.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Application", "backend\src\FlowForge.Backend.Application\FlowForge.Backend.Application.csproj", "{D1E2F3A4-B5C6-7890-ABCD-EF1234567891}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Backend.Infrastructure", "backend\src\FlowForge.Backend.Infrastructure\FlowForge.Backend.Infrastructure.csproj", "{E2F3A4B5-C6D7-8901-BCDE-F12345678902}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build-server", "build-server", "{10000000-0000-0000-0000-000000000003}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.BuildServer", "build-server\src\FlowForge.BuildServer\FlowForge.BuildServer.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "monitor-server", "monitor-server", "{10000000-0000-0000-0000-000000000004}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.MonitorServer", "monitor-server\src\FlowForge.MonitorServer\FlowForge.MonitorServer.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{10000000-0000-0000-0000-000000000005}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.Shared.Tests", "..\test\FlowForge.Shared.Tests\FlowForge.Shared.Tests.csproj", "{20000000-0000-0000-0000-000000000001}" +EndProject +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}" +EndProject +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}" +EndProject +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}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.BuildServer.Tests", "..\test\FlowForge.BuildServer.Tests\FlowForge.BuildServer.Tests.csproj", "{20000000-0000-0000-0000-000000000005}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FlowForge.MonitorServer.Tests", "..\test\FlowForge.MonitorServer.Tests\FlowForge.MonitorServer.Tests.csproj", "{20000000-0000-0000-0000-000000000006}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {F3A4B5C6-D7E8-9012-CDEF-123456789013}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F3A4B5C6-D7E8-9012-CDEF-123456789013}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3A4B5C6-D7E8-9012-CDEF-123456789013}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F3A4B5C6-D7E8-9012-CDEF-123456789013}.Release|Any CPU.Build.0 = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU + {D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D1E2F3A4-B5C6-7890-ABCD-EF1234567891}.Release|Any CPU.Build.0 = Release|Any CPU + {E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E2F3A4B5-C6D7-8901-BCDE-F12345678902}.Release|Any CPU.Build.0 = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU + {20000000-0000-0000-0000-000000000001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20000000-0000-0000-0000-000000000001}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20000000-0000-0000-0000-000000000001}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20000000-0000-0000-0000-000000000001}.Release|Any CPU.Build.0 = Release|Any CPU + {20000000-0000-0000-0000-000000000002}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20000000-0000-0000-0000-000000000002}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20000000-0000-0000-0000-000000000002}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20000000-0000-0000-0000-000000000002}.Release|Any CPU.Build.0 = Release|Any CPU + {20000000-0000-0000-0000-000000000003}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20000000-0000-0000-0000-000000000003}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20000000-0000-0000-0000-000000000003}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20000000-0000-0000-0000-000000000003}.Release|Any CPU.Build.0 = Release|Any CPU + {20000000-0000-0000-0000-000000000004}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20000000-0000-0000-0000-000000000004}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20000000-0000-0000-0000-000000000004}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20000000-0000-0000-0000-000000000004}.Release|Any CPU.Build.0 = Release|Any CPU + {20000000-0000-0000-0000-000000000005}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20000000-0000-0000-0000-000000000005}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20000000-0000-0000-0000-000000000005}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20000000-0000-0000-0000-000000000005}.Release|Any CPU.Build.0 = Release|Any CPU + {20000000-0000-0000-0000-000000000006}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {20000000-0000-0000-0000-000000000006}.Debug|Any CPU.Build.0 = Debug|Any CPU + {20000000-0000-0000-0000-000000000006}.Release|Any CPU.ActiveCfg = Release|Any CPU + {20000000-0000-0000-0000-000000000006}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F3A4B5C6-D7E8-9012-CDEF-123456789013} = {10000000-0000-0000-0000-000000000001} + {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} = {10000000-0000-0000-0000-000000000002} + {D1E2F3A4-B5C6-7890-ABCD-EF1234567891} = {10000000-0000-0000-0000-000000000002} + {E2F3A4B5-C6D7-8901-BCDE-F12345678902} = {10000000-0000-0000-0000-000000000002} + {B2C3D4E5-F6A7-8901-BCDE-F12345678901} = {10000000-0000-0000-0000-000000000003} + {C3D4E5F6-A7B8-9012-CDEF-123456789012} = {10000000-0000-0000-0000-000000000004} + {20000000-0000-0000-0000-000000000001} = {10000000-0000-0000-0000-000000000005} + {20000000-0000-0000-0000-000000000002} = {10000000-0000-0000-0000-000000000005} + {20000000-0000-0000-0000-000000000003} = {10000000-0000-0000-0000-000000000005} + {20000000-0000-0000-0000-000000000004} = {10000000-0000-0000-0000-000000000005} + {20000000-0000-0000-0000-000000000005} = {10000000-0000-0000-0000-000000000005} + {20000000-0000-0000-0000-000000000006} = {10000000-0000-0000-0000-000000000005} + EndGlobalSection +EndGlobal diff --git a/src/build-server/src/FlowForge.BuildServer/FlowForge.BuildServer.csproj b/src/build-server/src/FlowForge.BuildServer/FlowForge.BuildServer.csproj index 51ad21a..79c4d4c 100644 --- a/src/build-server/src/FlowForge.BuildServer/FlowForge.BuildServer.csproj +++ b/src/build-server/src/FlowForge.BuildServer/FlowForge.BuildServer.csproj @@ -6,7 +6,8 @@ enable FlowForge.BuildServer Exe - x86 + + diff --git a/test/.gitkeep b/test/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/test/FlowForge.Backend.Api.Tests/FlowForge.Backend.Api.Tests.csproj b/test/FlowForge.Backend.Api.Tests/FlowForge.Backend.Api.Tests.csproj new file mode 100644 index 0000000..aeb4132 --- /dev/null +++ b/test/FlowForge.Backend.Api.Tests/FlowForge.Backend.Api.Tests.csproj @@ -0,0 +1,27 @@ + + + + net9.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + diff --git a/test/FlowForge.Backend.Api.Tests/HealthControllerTests.cs b/test/FlowForge.Backend.Api.Tests/HealthControllerTests.cs new file mode 100644 index 0000000..1bb32d3 --- /dev/null +++ b/test/FlowForge.Backend.Api.Tests/HealthControllerTests.cs @@ -0,0 +1,22 @@ +// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila) +// SPDX-License-Identifier: AGPL-3.0-or-later + +using FluentAssertions; +using FlowForge.Backend.Api.Controllers; +using Microsoft.AspNetCore.Mvc; +using Xunit; + +namespace FlowForge.Backend.Api.Tests; + +public class HealthControllerTests +{ + [Fact] + public void Get_ShouldReturnOk() + { + var controller = new HealthController(); + + var result = controller.Get(); + + result.Should().BeOfType(); + } +} diff --git a/test/FlowForge.Backend.Application.Tests/FlowDocumentValidatorTests.cs b/test/FlowForge.Backend.Application.Tests/FlowDocumentValidatorTests.cs new file mode 100644 index 0000000..f0e8cf6 --- /dev/null +++ b/test/FlowForge.Backend.Application.Tests/FlowDocumentValidatorTests.cs @@ -0,0 +1,23 @@ +// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila) +// SPDX-License-Identifier: AGPL-3.0-or-later + +using FluentAssertions; +using FlowForge.Backend.Application.Validation; +using FlowForge.Shared.Models.Flow; +using Xunit; + +namespace FlowForge.Backend.Application.Tests; + +public class FlowDocumentValidatorTests +{ + [Fact] + public void Validate_EmptyDocument_ShouldReturnNoErrors() + { + var validator = new FlowDocumentValidator(); + var doc = new FlowDocument(); + + var errors = validator.Validate(doc); + + errors.Should().BeEmpty(); + } +} diff --git a/test/FlowForge.Backend.Application.Tests/FlowForge.Backend.Application.Tests.csproj b/test/FlowForge.Backend.Application.Tests/FlowForge.Backend.Application.Tests.csproj new file mode 100644 index 0000000..0923195 --- /dev/null +++ b/test/FlowForge.Backend.Application.Tests/FlowForge.Backend.Application.Tests.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + diff --git a/test/FlowForge.Backend.Infrastructure.Tests/AppDbContextTests.cs b/test/FlowForge.Backend.Infrastructure.Tests/AppDbContextTests.cs new file mode 100644 index 0000000..d7c49d5 --- /dev/null +++ b/test/FlowForge.Backend.Infrastructure.Tests/AppDbContextTests.cs @@ -0,0 +1,25 @@ +// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila) +// SPDX-License-Identifier: AGPL-3.0-or-later + +using FluentAssertions; +using FlowForge.Backend.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; +using Xunit; + +namespace FlowForge.Backend.Infrastructure.Tests; + +public class AppDbContextTests +{ + [Fact] + public void AppDbContext_ShouldCreateSuccessfully() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: "TestDb") + .Options; + + using var context = new AppDbContext(options); + + context.Should().NotBeNull(); + context.Projects.Should().NotBeNull(); + } +} diff --git a/test/FlowForge.Backend.Infrastructure.Tests/FlowForge.Backend.Infrastructure.Tests.csproj b/test/FlowForge.Backend.Infrastructure.Tests/FlowForge.Backend.Infrastructure.Tests.csproj new file mode 100644 index 0000000..e20329e --- /dev/null +++ b/test/FlowForge.Backend.Infrastructure.Tests/FlowForge.Backend.Infrastructure.Tests.csproj @@ -0,0 +1,28 @@ + + + + net9.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + diff --git a/test/FlowForge.BuildServer.Tests/BuildPipelineTests.cs b/test/FlowForge.BuildServer.Tests/BuildPipelineTests.cs new file mode 100644 index 0000000..e5671fb --- /dev/null +++ b/test/FlowForge.BuildServer.Tests/BuildPipelineTests.cs @@ -0,0 +1,50 @@ +// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila) +// SPDX-License-Identifier: AGPL-3.0-or-later + +using FluentAssertions; +using FlowForge.BuildServer.Pipeline; +using FlowForge.Shared.Models.Build; +using Microsoft.Extensions.Logging.Abstractions; +using NSubstitute; +using NSubstitute.ExceptionExtensions; +using Xunit; + +namespace FlowForge.BuildServer.Tests; + +public class BuildPipelineTests +{ + [Fact] + public async Task ExecuteAsync_WithNoSteps_ShouldReturnTrue() + { + var pipeline = new BuildPipeline([], NullLogger.Instance); + var context = new BuildContext + { + Job = new BuildJobDto { Id = Guid.NewGuid() } + }; + + var result = await pipeline.ExecuteAsync(context, CancellationToken.None); + + result.Should().BeTrue(); + context.Errors.Should().BeEmpty(); + } + + [Fact] + public async Task ExecuteAsync_WithFailingStep_ShouldReturnFalse() + { + var failingStep = Substitute.For(); + failingStep.Name.Returns("FailStep"); + failingStep.ExecuteAsync(Arg.Any(), Arg.Any()) + .ThrowsAsync(new InvalidOperationException("test error")); + + var pipeline = new BuildPipeline([failingStep], NullLogger.Instance); + var context = new BuildContext + { + Job = new BuildJobDto { Id = Guid.NewGuid() } + }; + + var result = await pipeline.ExecuteAsync(context, CancellationToken.None); + + result.Should().BeFalse(); + context.Errors.Should().ContainSingle().Which.Should().Contain("test error"); + } +} diff --git a/test/FlowForge.BuildServer.Tests/FlowForge.BuildServer.Tests.csproj b/test/FlowForge.BuildServer.Tests/FlowForge.BuildServer.Tests.csproj new file mode 100644 index 0000000..e22d9f5 --- /dev/null +++ b/test/FlowForge.BuildServer.Tests/FlowForge.BuildServer.Tests.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + diff --git a/test/FlowForge.MonitorServer.Tests/FlowForge.MonitorServer.Tests.csproj b/test/FlowForge.MonitorServer.Tests/FlowForge.MonitorServer.Tests.csproj new file mode 100644 index 0000000..d947ace --- /dev/null +++ b/test/FlowForge.MonitorServer.Tests/FlowForge.MonitorServer.Tests.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + diff --git a/test/FlowForge.MonitorServer.Tests/SubscriptionManagerTests.cs b/test/FlowForge.MonitorServer.Tests/SubscriptionManagerTests.cs new file mode 100644 index 0000000..ead7a44 --- /dev/null +++ b/test/FlowForge.MonitorServer.Tests/SubscriptionManagerTests.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila) +// SPDX-License-Identifier: AGPL-3.0-or-later + +using FluentAssertions; +using FlowForge.MonitorServer.Services; +using Xunit; + +namespace FlowForge.MonitorServer.Tests; + +public class SubscriptionManagerTests +{ + [Fact] + public void AddSubscription_ShouldTrackVariablePath() + { + var manager = new SubscriptionManager(); + manager.AddSubscription("conn-1", "GVL_Main.bRunning"); + + var subs = manager.GetSubscriptions("conn-1"); + + subs.Should().ContainSingle().Which.Should().Be("GVL_Main.bRunning"); + } + + [Fact] + public void RemoveAllSubscriptions_ShouldClearConnection() + { + var manager = new SubscriptionManager(); + manager.AddSubscription("conn-1", "GVL_Main.bRunning"); + manager.AddSubscription("conn-1", "GVL_Main.nCounter"); + + manager.RemoveAllSubscriptions("conn-1"); + + manager.GetSubscriptions("conn-1").Should().BeEmpty(); + } +} diff --git a/test/FlowForge.Shared.Tests/FlowDocumentTests.cs b/test/FlowForge.Shared.Tests/FlowDocumentTests.cs new file mode 100644 index 0000000..cce58e6 --- /dev/null +++ b/test/FlowForge.Shared.Tests/FlowDocumentTests.cs @@ -0,0 +1,21 @@ +// Copyright (c) 2026 Qubernetic (Biró, Csaba Attila) +// SPDX-License-Identifier: AGPL-3.0-or-later + +using FluentAssertions; +using FlowForge.Shared.Models.Flow; +using Xunit; + +namespace FlowForge.Shared.Tests; + +public class FlowDocumentTests +{ + [Fact] + public void FlowDocument_DefaultValues_ShouldBeEmpty() + { + var doc = new FlowDocument(); + + doc.Name.Should().BeEmpty(); + doc.Nodes.Should().BeEmpty(); + doc.Connections.Should().BeEmpty(); + } +} diff --git a/test/FlowForge.Shared.Tests/FlowForge.Shared.Tests.csproj b/test/FlowForge.Shared.Tests/FlowForge.Shared.Tests.csproj new file mode 100644 index 0000000..fab8810 --- /dev/null +++ b/test/FlowForge.Shared.Tests/FlowForge.Shared.Tests.csproj @@ -0,0 +1,26 @@ + + + + net9.0 + enable + enable + false + true + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + +