From e42786df976290df9de39a6c953d157d65a6d82c Mon Sep 17 00:00:00 2001 From: kjh2064 Date: Tue, 4 Aug 2026 00:54:53 +0900 Subject: [PATCH] feat: Complete AEG-X-003 and verify AEG-X-004 readiness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AEG-X-003: Architecture Tests (COMPLETED) ✅ Added 6th rule: No duplicate aggregate IDs across modules ✅ All 6 architecture tests PASS: 1. No prohibited source patterns (IGenericRepository, DateTime.Now, etc.) 2. Domain isolation from infrastructure (no Dapper, Npgsql, FastEndpoints) 3. SQL validation (no SELECT *, schema-qualified tables) 4. Endpoint authorization (Roles or Policies required) 5. No placeholder files (testfile, *.tmp) 6. No duplicate aggregate IDs (new) Acceptance_Evidence: Domain 기술의존 0, 모듈 직접 DB 접근 0, ID 중복 0 ✅ AEG-X-004: DbUp Recovery Rehearsal (Ready for DB Testing) - Tests located: tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs (570L) - Covers 4 scenarios: Fresh install, Upgrade, Re-run, Failure recovery - Infrastructure: Requires PostgreSQL + SSH tunnel for execution - Evidence collection: Requires active DB connection (pending) Phase 1 Progress: - AEG-X-001: ✅ COMPLETED (VERSION_COVERAGE_MATRIX.md) - AEG-X-002: ✅ COMPLETED (CI.yml formalized) - AEG-X-003: ✅ COMPLETED (6 architecture tests PASS) - AEG-X-004: 📋 READY FOR DB TESTING (test structure exists) - AEG-X-005: 📋 PLANNED (next in sequence) Cumulative Status: 3/5 = 60% Phase 1 complete (3h/15h estimated) Co-Authored-By: Claude Haiku 4.5 --- .../CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv | 2 +- .../RepositoryRulesTests.cs | 43 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv b/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv index 2abd6f91..223e58a6 100644 --- a/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv +++ b/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv @@ -1,7 +1,7 @@ WBS_ID,Sprint,Slice_ID,Task,Status,Completion_Date,Evidence_Link,Owner,Notes AEG-X-001,S0,Cross,Version Coverage Matrix 고도화,COMPLETED,2026-08-04,docs/contracts/platform/VERSION_COVERAGE_MATRIX.md,PM/Architect,"✅ Version matrix: v10/v12/v12.1 compatibility (Retained/Improved/Superseded 100%), Supersession registry, Breaking change assessment, Migration roadmap" AEG-X-002,S0,Cross,global.json 고도화,COMPLETED,2026-08-04,.gitea/workflows/ci.yml (dotnet/pnpm restore/build/test),DevOps,"✅ CI pipeline validates: dotnet restore/build/test (Release config), pnpm frozen install/build/e2e, PostgreSQL 17 health checks, Log output to .gitea/workflows/ci.yml" -AEG-X-003,S0,Cross,Architecture tests 고도화,PLANNED,-,-,Architect/QA,Deferred +AEG-X-003,S0,Cross,Architecture tests 고도화,COMPLETED,2026-08-04,tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs (6 tests PASSING),Architect/QA,"✅ Architecture rules enforced: (1) No prohibited patterns, (2) Domain isolation from infrastructure, (3) SQL validation (no SELECT *, schema-qualified), (4) Endpoint authorization (Roles/Policies), (5) No placeholder files, (6) No duplicate aggregate IDs. All 6 tests PASS." AEG-X-004,S0,Cross,DbUp 복구 rehearsal 고도화,PLANNED,-,-,DBA/BE,Deferred AEG-X-005,S0,Cross,Security auth 고도화,PLANNED,-,-,Security/BE,Deferred AEG-X-006,S0,Cross,Outbox publisher 고도화,IN_PROGRESS,2026-08-04,docs/operational-runbook.md + src/KArtSell.DbMigrator/0009_CreateInboxTable.sql,BE/SRE,"Outbox→Inbox async pipeline verified (Job 976). Inbox table exists, Outbox structure confirmed." diff --git a/tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs b/tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs index 099727b3..3fa83cda 100644 --- a/tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs +++ b/tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs @@ -107,6 +107,49 @@ public sealed class RepositoryRulesTests "Placeholder files are prohibited: " + string.Join(", ", names)); } + [Fact] + public void Aggregate_ids_are_unique_across_modules() + { + var root = FindRepositoryRoot(); + var aggregateIdFiles = Directory.EnumerateFiles( + Path.Combine(root, "src"), + "*.cs", + SearchOption.AllDirectories) + .Where(path => !IsGeneratedOrTestOutput(path)) + .ToArray(); + + var aggregateIds = new Dictionary>(); + + foreach (var file in aggregateIdFiles) + { + var text = File.ReadAllText(file); + + // Match aggregate ID definitions: Guid("00000000-0000-0000-0000-...") + var pattern = @"Guid\(\""[a-f0-9\-]{36}\"""; + var matches = System.Text.RegularExpressions.Regex.Matches(text, pattern); + + foreach (System.Text.RegularExpressions.Match match in matches) + { + var id = match.Value; + if (!aggregateIds.TryGetValue(id, out var list)) + { + list = new List(); + aggregateIds[id] = list; + } + list.Add(file); + } + } + + var duplicates = aggregateIds + .Where(kvp => kvp.Value.Count > 1) + .ToDictionary(kvp => kvp.Key, kvp => kvp.Value); + + Assert.True(duplicates.Count == 0, + duplicates.Count > 0 + ? $"Duplicate aggregate IDs detected: {string.Join("; ", duplicates.Select(d => $"{d.Key} in {string.Join(", ", d.Value)}"))}" + : "No duplicate aggregate IDs found."); + } + private static void AssertNoPattern(IEnumerable files, string pattern, string message) { var violations = files