diff --git a/.github/workflows/dotnet-ci.yml b/.github/workflows/dotnet-ci.yml
new file mode 100644
index 0000000..a31b4b6
--- /dev/null
+++ b/.github/workflows/dotnet-ci.yml
@@ -0,0 +1,29 @@
+name: .NET CI Pipeline
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ build-and-test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '9.0.x'
+
+ - name: Restore dependencies
+ run: dotnet restore
+
+ - name: Build
+ run: dotnet build --no-restore --configuration Release
+
+ - name: Run tests
+ run: dotnet test --no-build --configuration Release --verbosity normal
\ No newline at end of file
diff --git a/FinancialTracker.API.sln b/FinancialTracker.API.sln
index 4a325e4..22a71aa 100644
--- a/FinancialTracker.API.sln
+++ b/FinancialTracker.API.sln
@@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinancialTracker", "FinancialTracker\FinancialTracker.csproj", "{BBECDC23-AC86-4437-8F43-FB9221F4724F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FinancialTracker.Tests", "FinancialTracker.Tests\FinancialTracker.Tests.csproj", "{129BAEA0-4BA6-468A-B0F1-ED748AE9D316}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +14,9 @@ Global
{BBECDC23-AC86-4437-8F43-FB9221F4724F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBECDC23-AC86-4437-8F43-FB9221F4724F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BBECDC23-AC86-4437-8F43-FB9221F4724F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {129BAEA0-4BA6-468A-B0F1-ED748AE9D316}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {129BAEA0-4BA6-468A-B0F1-ED748AE9D316}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {129BAEA0-4BA6-468A-B0F1-ED748AE9D316}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {129BAEA0-4BA6-468A-B0F1-ED748AE9D316}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/FinancialTracker.API.sln.DotSettings.user b/FinancialTracker.API.sln.DotSettings.user
new file mode 100644
index 0000000..642f398
--- /dev/null
+++ b/FinancialTracker.API.sln.DotSettings.user
@@ -0,0 +1,4 @@
+
+ <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
+ <Solution />
+</SessionState>
\ No newline at end of file
diff --git a/FinancialTracker.Tests/Controllers/CategoriesControllerTests.cs b/FinancialTracker.Tests/Controllers/CategoriesControllerTests.cs
new file mode 100644
index 0000000..ffb2b2b
--- /dev/null
+++ b/FinancialTracker.Tests/Controllers/CategoriesControllerTests.cs
@@ -0,0 +1,55 @@
+using FinancialTracker.Controllers;
+using FinancialTracker.DTOs.CategoryDtos;
+using FinancialTracker.Interfaces;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Logging;
+using Moq;
+
+namespace FinancialTracker.Tests.Controllers;
+
+public class CategoriesControllerTests
+{
+ private readonly Mock _categoryServiceMock;
+ private readonly Mock> _loggerMock;
+ private readonly CategoriesController _controller;
+
+ public CategoriesControllerTests()
+ {
+ _categoryServiceMock = new Mock();
+ _loggerMock = new Mock>();
+ _controller = new CategoriesController(_categoryServiceMock.Object, _loggerMock.Object);
+ }
+
+ [Fact]
+ public async Task Add_ValidCategory_ReturnsCreatedResult()
+ {
+ var createDto = new CategoryCreateDto { Name = "Продукты" };
+ var expectedCategory = new CategoryResponseDto { Id = 1, Name = "Продукты" };
+
+ _categoryServiceMock
+ .Setup(service => service.AddAsync(createDto))
+ .ReturnsAsync(expectedCategory);
+
+ var result = await _controller.Add(createDto);
+
+ var createdAtResult = Assert.IsType(result.Result);
+ Assert.Equal(201, createdAtResult.StatusCode);
+ Assert.Equal(expectedCategory, createdAtResult.Value);
+ }
+
+ [Fact]
+ public async Task Add_DuplicateCategory_ReturnsConflict()
+ {
+ var createDto = new CategoryCreateDto { Name = "Продукты" };
+ const string errorMessage = "Категория с именем 'Продукты' уже существует";
+
+ _categoryServiceMock
+ .Setup(service => service.AddAsync(createDto))
+ .ThrowsAsync(new InvalidOperationException(errorMessage));
+
+ var result = await _controller.Add(createDto);
+
+ var conflictResult = Assert.IsType(result.Result);
+ Assert.Equal(409, conflictResult.StatusCode);
+ }
+}
\ No newline at end of file
diff --git a/FinancialTracker.Tests/FinancialTracker.Tests.csproj b/FinancialTracker.Tests/FinancialTracker.Tests.csproj
new file mode 100644
index 0000000..1696d6a
--- /dev/null
+++ b/FinancialTracker.Tests/FinancialTracker.Tests.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net9.0
+ enable
+ enable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+