feat: harden auth ops and deployment baseline
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
namespace TaxBaik.Application.Tests;
|
||||
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
public class BlogServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task CreateAsync_WhenPublishedWithoutSeoTitle_ThrowsValidationException()
|
||||
{
|
||||
var service = new BlogService(new FakeBlogPostRepository());
|
||||
|
||||
await Assert.ThrowsAsync<ValidationException>(() => service.CreateAsync(new CreateBlogPostDto
|
||||
{
|
||||
Title = "테스트 포스트",
|
||||
Content = "본문",
|
||||
SeoDescription = "설명",
|
||||
IsPublished = true
|
||||
}));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_WhenTitleDuplicates_GeneratesUniqueSlug()
|
||||
{
|
||||
var repository = new FakeBlogPostRepository
|
||||
{
|
||||
Posts =
|
||||
[
|
||||
new BlogPost { Id = 1, Title = "같은 제목", Content = "본문", Slug = "같은-제목" }
|
||||
]
|
||||
};
|
||||
var service = new BlogService(repository);
|
||||
|
||||
var post = await service.CreateAsync(new CreateBlogPostDto
|
||||
{
|
||||
Title = "같은 제목",
|
||||
Content = "본문"
|
||||
});
|
||||
|
||||
Assert.Equal("같은-제목-2", post.Slug);
|
||||
}
|
||||
|
||||
private sealed class FakeBlogPostRepository : IBlogPostRepository
|
||||
{
|
||||
public List<BlogPost> Posts { get; init; } = [];
|
||||
|
||||
public Task<BlogPost?> GetByIdAsync(int id, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(Posts.FirstOrDefault(x => x.Id == id));
|
||||
|
||||
public Task<BlogPost?> GetBySlugAsync(string slug, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(Posts.FirstOrDefault(x => x.Slug == slug && x.IsPublished));
|
||||
|
||||
public Task<(IEnumerable<BlogPost> Items, int Total)> GetPublishedPagedAsync(
|
||||
int page, int pageSize, int? categoryId = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var items = Posts.Where(x => x.IsPublished).ToList();
|
||||
return Task.FromResult<(IEnumerable<BlogPost>, int)>((items, items.Count));
|
||||
}
|
||||
|
||||
public Task<IEnumerable<BlogPost>> GetAllForAdminAsync(CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IEnumerable<BlogPost>>(Posts);
|
||||
|
||||
public Task<int> CreateAsync(BlogPost post, CancellationToken cancellationToken = default)
|
||||
{
|
||||
post.Id = Posts.Count + 1;
|
||||
Posts.Add(post);
|
||||
return Task.FromResult(post.Id);
|
||||
}
|
||||
|
||||
public Task UpdateAsync(BlogPost post, CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||
|
||||
public Task DeleteAsync(int id, CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||
|
||||
public Task IncrementViewCountAsync(int id, CancellationToken cancellationToken = default) => Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
namespace TaxBaik.Application.Tests;
|
||||
|
||||
using TaxBaik.Application.Services;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
using Xunit;
|
||||
|
||||
public class InquiryServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task UpdateStatusAsync_WhenStatusIsInvalid_ThrowsValidationException()
|
||||
{
|
||||
var service = new InquiryService(new FakeInquiryRepository());
|
||||
|
||||
await Assert.ThrowsAsync<ValidationException>(() => service.UpdateStatusAsync(1, "invalid"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SubmitAsync_StoresEmailAndNewStatus()
|
||||
{
|
||||
var repository = new FakeInquiryRepository();
|
||||
var service = new InquiryService(repository);
|
||||
|
||||
await service.SubmitAsync("홍길동", "010-1234-5678", "기장", "문의합니다.", "user@example.com");
|
||||
|
||||
Assert.Equal("user@example.com", repository.Inquiries.Single().Email);
|
||||
Assert.Equal("new", repository.Inquiries.Single().Status);
|
||||
}
|
||||
|
||||
private sealed class FakeInquiryRepository : IInquiryRepository
|
||||
{
|
||||
public List<Inquiry> Inquiries { get; } = [];
|
||||
|
||||
public Task<int> CreateAsync(Inquiry inquiry, CancellationToken cancellationToken = default)
|
||||
{
|
||||
inquiry.Id = Inquiries.Count + 1;
|
||||
Inquiries.Add(inquiry);
|
||||
return Task.FromResult(inquiry.Id);
|
||||
}
|
||||
|
||||
public Task<Inquiry?> GetByIdAsync(int id, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(Inquiries.FirstOrDefault(x => x.Id == id));
|
||||
|
||||
public Task<(IEnumerable<Inquiry> Items, int Total)> GetPagedAsync(
|
||||
int page, int pageSize, string? status = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var items = status == null ? Inquiries : Inquiries.Where(x => x.Status == status).ToList();
|
||||
return Task.FromResult<(IEnumerable<Inquiry>, int)>((items, items.Count()));
|
||||
}
|
||||
|
||||
public Task UpdateStatusAsync(int id, string status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var inquiry = Inquiries.FirstOrDefault(x => x.Id == id);
|
||||
if (inquiry != null)
|
||||
inquiry.Status = status;
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.7.0" />
|
||||
<PackageReference Include="xunit" Version="2.9.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\TaxBaik.Application\TaxBaik.Application.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user