Эндпоинты

This commit is contained in:
Халимов Рустам
2026-03-30 23:41:01 +03:00
parent ce212c11c1
commit d3f1e3f361
50 changed files with 1890 additions and 5 deletions
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Shared\IntegrationTests.Shared\Knot.IntegrationTests.Shared.csproj" />
<ProjectReference Include="..\..\..\src\Modules\Stories\Knot.Modules.Stories.csproj" />
<ProjectReference Include="..\..\..\src\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
</ItemGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
<ProjectReference Include="..\..\..\src\Modules\Settings\Knot.Modules.Settings.csproj" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
using Knot.IntegrationTests;
using FluentAssertions;
using Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
using Knot.Shared.Kernel;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using NSubstitute;
using System.Net;
using System.Net.Http.Json;
using Xunit;
using System;
using System.Threading.Tasks;
namespace Knot.IntegrationTests.Stories;
public class StoriesTests : BaseIntegrationTest
{
[Fact]
public async Task CreateStory_ShouldReturnOk_WhenValidRequest()
{
// Arrange
var request = new CreateStoryRequest("Text", null, "Hello Integration Test", null);
// Act
var response = await _client.PostAsJsonAsync("api/stories", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var content = await response.Content.ReadFromJsonAsync<dynamic>();
}
}
public record CreateStoryRequest(string Type, string? MediaUrl, string? Content, string? BgColor);
@@ -0,0 +1,76 @@
using FluentAssertions;
using Knot.Modules.Stories.Application.Stories.Commands.CreateStory;
using Knot.Modules.Stories.Domain;
using Knot.Shared.Kernel;
using Knot.Modules.Settings.Application.Settings.Abstractions;
using Knot.Modules.Settings.Application.Settings.DTOs;
using Knot.Modules.Stories.Application.Abstractions;
using NSubstitute;
using Xunit;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Stories.UnitTests;
public class CreateStoryCommandHandlerTests
{
private readonly IStoryRepository _storyRepository;
private readonly ISettingsService _settingsService;
private readonly CreateStoryCommandHandler _handler;
public CreateStoryCommandHandlerTests()
{
_storyRepository = Substitute.For<IStoryRepository>();
_settingsService = Substitute.For<ISettingsService>();
_handler = new CreateStoryCommandHandler(_storyRepository, _settingsService);
}
[Fact]
public async Task Handle_ShouldSucceed_WhenValidRequest()
{
// Arrange
var command = new CreateStoryCommand(Guid.NewGuid(), "Text", null, "Hello Story", null);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
await _storyRepository.Received(1).AddAsync(Arg.Any<Story>(), Arg.Any<CancellationToken>());
}
[Fact]
public async Task Handle_ShouldFail_WhenKlipyDisabledAndKlipyType()
{
// Arrange
var command = new CreateStoryCommand(Guid.NewGuid(), "Klipy", "http://klipy.com/gif", null, null);
var settings = new SystemSettingsDto { Klipy = new KlipyConfig { Enabled = false } };
_settingsService.GetSettingsAsync(Arg.Any<CancellationToken>()).Returns(settings);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Code.Should().Be("Stories.KlipyDisabled");
await _storyRepository.DidNotReceive().AddAsync(Arg.Any<Story>(), Arg.Any<CancellationToken>());
}
[Fact]
public async Task Handle_ShouldSucceed_WhenKlipyEnabledAndKlipyType()
{
// Arrange
var command = new CreateStoryCommand(Guid.NewGuid(), "Klipy", "http://klipy.com/gif", null, null);
var settings = new SystemSettingsDto { Klipy = new KlipyConfig { Enabled = true } };
_settingsService.GetSettingsAsync(Arg.Any<CancellationToken>()).Returns(settings);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
await _storyRepository.Received(1).AddAsync(Arg.Any<Story>(), Arg.Any<CancellationToken>());
}
}
@@ -0,0 +1,76 @@
using FluentAssertions;
using Knot.Modules.Stories.Application.Stories.Commands.DeleteStory;
using Knot.Modules.Stories.Domain;
using Knot.Shared.Kernel;
using Knot.Modules.Stories.Application.Abstractions;
using NSubstitute;
using Xunit;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Knot.Modules.Stories.UnitTests;
public class DeleteStoryCommandHandlerTests
{
private readonly IStoryRepository _storyRepository;
private readonly DeleteStoryCommandHandler _handler;
public DeleteStoryCommandHandlerTests()
{
_storyRepository = Substitute.For<IStoryRepository>();
_handler = new DeleteStoryCommandHandler(_storyRepository);
}
[Fact]
public async Task Handle_ShouldReturnError_WhenStoryNotFound()
{
// Arrange
var command = new DeleteStoryCommand(Guid.NewGuid(), Guid.NewGuid());
_storyRepository.GetByIdAsync(command.StoryId, Arg.Any<CancellationToken>()).Returns((Story?)null);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Be(StoryErrors.StoryNotFound);
}
[Fact]
public async Task Handle_ShouldReturnError_WhenUserIsNotOwner()
{
// Arrange
var ownerId = Guid.NewGuid();
var viewerId = Guid.NewGuid();
var story = Story.Create(ownerId, StoryType.Text, null, "Content", null);
var command = new DeleteStoryCommand(viewerId, story.Id);
_storyRepository.GetByIdAsync(command.StoryId, Arg.Any<CancellationToken>()).Returns(story);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.IsFailure.Should().BeTrue();
result.Error.Should().Be(StoryErrors.Unauthorized);
}
[Fact]
public async Task Handle_ShouldSucceed_WhenOwnerDeletes()
{
// Arrange
var ownerId = Guid.NewGuid();
var story = Story.Create(ownerId, StoryType.Text, null, "Content", null);
var command = new DeleteStoryCommand(ownerId, story.Id);
_storyRepository.GetByIdAsync(command.StoryId, Arg.Any<CancellationToken>()).Returns(story);
// Act
var result = await _handler.Handle(command, CancellationToken.None);
// Assert
result.IsSuccess.Should().BeTrue();
await _storyRepository.Received(1).DeleteAsync(story, Arg.Any<CancellationToken>());
}
}
@@ -0,0 +1,30 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.4" />
<PackageReference Include="FluentAssertions" Version="8.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.0" />
<PackageReference Include="NSubstitute" Version="5.3.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Modules\Stories\Knot.Modules.Stories.csproj" />
<ProjectReference Include="..\..\..\src\Shared\Knot.Shared.Kernel\Knot.Shared.Kernel.csproj" />
<ProjectReference Include="..\..\..\src\Modules\Settings\Knot.Modules.Settings.csproj" />
</ItemGroup>
</Project>