refactor: move buildable .NET source into src/, update CI/doc paths
TaxBaik CI/CD / build-and-deploy (push) Successful in 2m7s

Groups the repo root into src (buildable source), docs (already existed),
and everything else (db/, scripts/, tests/, deploy/ - deployment/ops/test
assets that aren't compiled, already organized as their own folders). CI
now only needs src/ to build: dotnet restore/build/test/publish all point
at src/TaxBaik.sln, src/TaxBaik.Web/, src/TaxBaik.Proxy/.

- git mv every project (Domain, Infrastructure, Application,
  Application.Tests, Web, Web.Client, Proxy) and TaxBaik.sln into src/ as a
  unit, so relative ProjectReference/.sln paths stay valid unchanged.
- .gitea/workflows/deploy.yml: 6 dotnet restore/clean/build/test/publish
  invocations now point at src/. db/migrations and scripts/ stay at root
  (deploy_gb.sh and browser-e2e.yml only touch published output and the
  deployed URL, not source paths - verified, no changes needed there).
- scripts/validate_admin_render.sh: admin render-mode file paths now
  src/TaxBaik.Web.Client/...
- scripts/validate_kst_timestamps.sh: dropped deploy.sh from its target
  list - that script was removed in the prior cleanup commit (dead, no
  CI workflow referenced it) but this validator still expected it to exist.
- CLAUDE.md, docs/ENGINEERING_HARNESS.md, docs/ADMIN_PATTERN_CRITIQUE_WBS.md:
  updated project-structure diagram, dotnet run/build commands, and grep
  targets to the new src/ paths (also fixed a pre-existing stale path in
  ADMIN_PATTERN_CRITIQUE_WBS.md that still said TaxBaik.Web/Components/Admin
  from before that ever moved to TaxBaik.Web.Client).
- Added a Repo Root harness rule + Architecture Guardrail entries: new files
  belong under src/docs/tests/scripts/db/deploy, not loose at root; temp
  work stays outside the repo (or under a gitignored .scratch/) and is
  never committed.

Verified locally: dotnet build/test src/TaxBaik.sln (26/26 tests), and all
three scripts/validate_*.sh pass against the new layout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 10:37:37 +09:00
parent c00d002972
commit ea447495d3
277 changed files with 36 additions and 29 deletions
@@ -1,124 +0,0 @@
namespace TaxBaik.Application.Tests;
using TaxBaik.Application.Services;
using TaxBaik.Domain.Entities;
using TaxBaik.Domain.Interfaces;
using Microsoft.Extensions.Caching.Memory;
using Xunit;
public class InquiryServiceTests
{
[Fact]
public async Task UpdateStatusAsync_WhenStatusIsInvalid_ThrowsValidationException()
{
var service = new InquiryService(new FakeInquiryRepository(), new FakeInquiryNotificationService(), new MemoryCache(new MemoryCacheOptions()));
await Assert.ThrowsAsync<ValidationException>(() => service.UpdateStatusAsync(1, "invalid"));
}
[Fact]
public async Task SubmitAsync_StoresEmailAndNewStatus()
{
var repository = new FakeInquiryRepository();
var service = new InquiryService(repository, new FakeInquiryNotificationService(), new MemoryCache(new MemoryCacheOptions()));
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<int> CountAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(Inquiries.Count);
public Task<int> CountThisMonthAsync(CancellationToken cancellationToken = default)
=> Task.FromResult(Inquiries.Count);
public Task<int> CountByStatusAsync(string status, CancellationToken cancellationToken = default)
=> Task.FromResult(Inquiries.Count(x => x.Status == status));
public Task<int> CountByDateRangeAsync(DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
=> Task.FromResult(Inquiries.Count(x => x.CreatedAt >= startDate && x.CreatedAt <= endDate));
public Task<int> CountByStatusAndDateAsync(string status, DateTime startDate, DateTime endDate, CancellationToken cancellationToken = default)
=> Task.FromResult(Inquiries.Count(x => x.Status == status && x.CreatedAt >= startDate && x.CreatedAt <= endDate));
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;
}
public Task UpdateAdminMemoAsync(int id, string? adminMemo, CancellationToken cancellationToken = default)
{
var inquiry = Inquiries.FirstOrDefault(x => x.Id == id);
if (inquiry != null)
inquiry.AdminMemo = adminMemo;
return Task.CompletedTask;
}
public Task UpdateAsync(Inquiry inquiry, CancellationToken cancellationToken = default)
{
var existing = Inquiries.FirstOrDefault(x => x.Id == inquiry.Id);
if (existing != null)
{
existing.Name = inquiry.Name;
existing.Phone = inquiry.Phone;
existing.Email = inquiry.Email;
existing.ServiceType = inquiry.ServiceType;
existing.Message = inquiry.Message;
existing.Status = inquiry.Status;
existing.AdminMemo = inquiry.AdminMemo;
}
return Task.CompletedTask;
}
public Task LinkClientAsync(int inquiryId, int clientId, CancellationToken cancellationToken = default)
{
var inquiry = Inquiries.FirstOrDefault(x => x.Id == inquiryId);
if (inquiry != null)
inquiry.ClientId = clientId;
return Task.CompletedTask;
}
public Task DeleteAsync(int id, CancellationToken cancellationToken = default)
{
var inquiry = Inquiries.FirstOrDefault(x => x.Id == id);
if (inquiry != null)
Inquiries.Remove(inquiry);
return Task.CompletedTask;
}
}
private sealed class FakeInquiryNotificationService : IInquiryNotificationService
{
public Task NotifyCreatedAsync(int inquiryId, string name, string phone, string serviceType, string message, string? ipAddress, DateTime createdAtUtc, CancellationToken ct = default)
=> Task.CompletedTask;
public Task NotifyStatusChangedAsync(int inquiryId, string name, string phone, string serviceType, string previousStatus, string newStatus, string? changedBy = null, CancellationToken ct = default)
=> Task.CompletedTask;
}
}