Files
taxbaik/TaxBaik.Application.Tests/BlogServiceTests.cs
kjh2064 46951d871a feat: 블로그 시즌 연동 — 홈페이지 세무 정보 섹션 시즌화
- TaxSeason / CurrentSeasonDto에 RelatedCategorySlug 추가
- TaxSeasonCalendar 각 시즌에 카테고리 슬러그 매핑
  (income-tax→income-tax, vat-1st/2nd→vat, 종부세→real-estate-tax 등)
- IBlogPostRepository.GetByCategorySlugAsync 추가
- BlogService.GetSeasonalPostsAsync: 시즌 관련 글 2개 우선 + 나머지 최신 글로 채움
- IndexModel: SeasonalPosts / RecentPosts 분리 로드
- Index.cshtml 블로그 섹션: 시즌 중 "이번 시즌 추천" 배지 + 시즌별 전체보기 버튼
- site.css: blog-card--seasonal, seasonal-blog-tag, btn-seasonal 스타일

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 23:11:45 +09:00

91 lines
3.5 KiB
C#

namespace TaxBaik.Application.Tests;
using TaxBaik.Application.DTOs;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
using Microsoft.Extensions.Caching.Memory;
using Xunit;
public class BlogServiceTests
{
[Fact]
public async Task CreateAsync_WhenPublishedWithoutSeoTitle_ThrowsValidationException()
{
var service = new BlogService(new FakeBlogPostRepository(), new MemoryCache(new MemoryCacheOptions()));
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, new MemoryCache(new MemoryCacheOptions()));
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>> GetByCategorySlugAsync(string categorySlug, int limit, CancellationToken cancellationToken = default) =>
Task.FromResult<IEnumerable<BlogPost>>(Posts.Where(x => x.IsPublished).Take(limit).ToList());
public Task<IEnumerable<BlogPost>> GetAllForAdminAsync(CancellationToken cancellationToken = default) =>
Task.FromResult<IEnumerable<BlogPost>>(Posts);
public Task<(IEnumerable<BlogPost> Items, int Total)> GetAdminPagedAsync(
int page, int pageSize, CancellationToken cancellationToken = default)
{
var items = Posts.ToList();
return Task.FromResult<(IEnumerable<BlogPost>, int)>((items, items.Count));
}
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;
}
}