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
+
+
+
+
+
+
+
+
+
+