SQL 정적 분석과 PPT·HTML 품질 기준을 강화하고 언어 fallback을 고도화한다
- SQL 전용 정적 분석 계층을 추가해 PostgreSQL/MySQL/SQL Server/SQLite/Oracle 방언 추정, statement kind 분류, object 추출, destructive DDL·broad DML·transaction boundary 위험 감지를 지원한다 - CodeLanguageCatalog의 SQL manifest/build/test/lint 힌트와 fallback summary를 SQL 분석 결과 중심으로 보강해 no-LSP 환경에서도 dialect·risk·next checks를 직접 안내한다 - DeckPlanningService가 구조화된 content 슬라이드를 kpi_dashboard/comparison/roadmap/chart로 자동 승격하고 DeckQualityReviewService·DeckRepairGuideService가 KPI 근거, verdict, owner/timeline, takeaway 부족을 별도 진단·보정한다 - HtmlSkill에 kpi_panel 섹션을 추가하고 ArtifactQualityReviewService·ArtifactRepairGuideService가 board/strategy 문서의 KPI·evidence·decision 연결 부족을 더 정확히 감지하도록 확장한다 - README.md, docs/DEVELOPMENT.md, docs/NEXT_ROADMAP.md에 2026-04-15 11:17 (KST) 기준 작업 이력과 검증 결과를 반영했다 검증 결과 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\verify_sql_doc_batch\ -p:IntermediateOutputPath=obj\verify_sql_doc_batch\ : 경고 0 / 오류 0 - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter SqlDialectDetectorTests|SqlAnalysisServiceTests|CodeLanguageCatalogTests|DeckPlanningServiceTests|DeckQualityReviewServiceTests|ArtifactQualityReviewServiceTests|ArtifactRepairGuideServiceTests|HtmlSkillConsultingSectionsTests|HtmlSkillGoldenReportTests|PptxSkillGoldenDeckTests -p:OutputPath=bin\verify_sql_doc_batch_tests\ -p:IntermediateOutputPath=obj\verify_sql_doc_batch_tests\ : 통과 47
This commit is contained in:
@@ -15,6 +15,7 @@ public class ArtifactQualityReviewServiceTests
|
||||
<h2>Current State</h2><p>Current state detail.</p><p>Evidence paragraph.</p>
|
||||
<div class="callout-info">Important message</div>
|
||||
<table><tr><th>Metric</th><th>Value</th></tr><tr><td>NPS</td><td>61</td></tr></table>
|
||||
<div class="kpi-panel"></div>
|
||||
<div class="comparison-grid"></div>
|
||||
<div class="roadmap-block"></div>
|
||||
<div class="decision-summary"></div>
|
||||
@@ -138,6 +139,27 @@ public class ArtifactQualityReviewServiceTests
|
||||
review.Issues.Should().Contain(issue => issue.Message.Contains("Strategy brief should include explicit decisions", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReviewHtml_ShouldRecommendKpiSupport_ForBoardAndStrategyPanels()
|
||||
{
|
||||
var html =
|
||||
"""
|
||||
<h2>Executive Summary</h2><p>Summary text.</p><p>Supporting paragraph.</p>
|
||||
<section class="board-report-panel"></section>
|
||||
<section class="strategy-brief-panel"></section>
|
||||
<div class="decision-summary"></div>
|
||||
<div class="comparison-grid"></div>
|
||||
<div class="roadmap-block"></div>
|
||||
<h2>Recommendation</h2><p>Recommendation text.</p><p>More detail.</p>
|
||||
<h2>Appendix</h2><p>Reference detail.</p><p>More detail.</p>
|
||||
""";
|
||||
|
||||
var review = ArtifactQualityReviewService.ReviewHtml("KPI Gap", html, hasCover: true, hasTableOfContents: true, printReady: true);
|
||||
|
||||
review.Issues.Should().Contain(issue => issue.Message.Contains("KPI panel or metric strip", StringComparison.OrdinalIgnoreCase));
|
||||
review.Issues.Should().Contain(issue => issue.Message.Contains("KPI or evidence support blocks", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ReviewStructuredDocument_ShouldRecommendEvidenceTableAndCallouts_ForLongBusinessDoc()
|
||||
{
|
||||
|
||||
@@ -36,12 +36,14 @@ public class ArtifactRepairGuideServiceTests
|
||||
["Includes print-ready CSS"],
|
||||
[
|
||||
new ArtifactReviewIssue("Board-ready report should include a decision summary block.", ArtifactReviewSeverity.Warning),
|
||||
new ArtifactReviewIssue("Board-ready report would be stronger with a KPI panel or metric strip.", ArtifactReviewSeverity.Info),
|
||||
new ArtifactReviewIssue("Strategy brief should include explicit decisions or a decision summary block.", ArtifactReviewSeverity.Warning)
|
||||
]);
|
||||
|
||||
var guide = ArtifactRepairGuideService.BuildGuide(review);
|
||||
|
||||
guide.Should().Contain("decision summary");
|
||||
guide.Should().Contain("KPI");
|
||||
guide.Should().Contain("strategy brief");
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@ public class CodeLanguageCatalogTests
|
||||
CodeLanguageCatalog.GetBuildHints("go").Should().Contain("go build ./...");
|
||||
CodeLanguageCatalog.GetTestHints("kotlin").Should().Contain("./gradlew test");
|
||||
CodeLanguageCatalog.GetLintHints("javascript").Should().Contain("eslint .");
|
||||
CodeLanguageCatalog.GetManifestHints("sql").Should().Contain("schema.sql");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -92,4 +93,15 @@ public class CodeLanguageCatalogTests
|
||||
summaries.Should().Contain(summary => summary.StartsWith("Go:"));
|
||||
summaries.Should().Contain(summary => summary.StartsWith("Rust:"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildWorkflowSummary_ShouldExposeSqlSpecificWorkflowHints()
|
||||
{
|
||||
var summary = CodeLanguageCatalog.BuildWorkflowSummary("sql");
|
||||
|
||||
summary.Should().Contain("SQL");
|
||||
summary.Should().Contain("schema.sql");
|
||||
summary.Should().Contain("disposable database");
|
||||
summary.Should().Contain("dialect");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,51 @@ public class DeckPlanningServiceTests
|
||||
result.AutoRepairCount.Should().BeGreaterThan(0);
|
||||
result.Storyline.Should().NotBeEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Prepare_ShouldPromoteStructuredContentSlides_ToBetterLayouts()
|
||||
{
|
||||
using var args = JsonDocument.Parse(
|
||||
"""
|
||||
{
|
||||
"title": "Operating Review",
|
||||
"slides": [
|
||||
{
|
||||
"layout": "content",
|
||||
"title": "KPI Snapshot",
|
||||
"kpis": [
|
||||
{ "label": "Margin", "value": "+4.2pt" },
|
||||
{ "label": "Lead Time", "value": "-18%" },
|
||||
{ "label": "Retention", "value": "92%" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "content",
|
||||
"title": "Options",
|
||||
"options": [
|
||||
{ "name": "Pilot", "pros": "Low risk", "cons": "Slow", "verdict": "Too cautious" },
|
||||
{ "name": "Phased", "pros": "Balanced", "cons": "Needs PMO", "verdict": "Recommended" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "content",
|
||||
"title": "Execution",
|
||||
"phases": [
|
||||
{ "title": "Design", "detail": "Scope and governance" },
|
||||
{ "title": "Launch", "detail": "Rollout wave" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""");
|
||||
|
||||
using var result = DeckPlanningService.Prepare(args.RootElement, args.RootElement.GetProperty("slides"), "Operating Review");
|
||||
var layouts = result.Slides.EnumerateArray()
|
||||
.Select(slide => slide.GetProperty("layout").GetString())
|
||||
.ToList();
|
||||
|
||||
layouts.Should().Contain("kpi_dashboard");
|
||||
layouts.Should().Contain("comparison");
|
||||
layouts.Should().Contain("roadmap");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,16 @@ public class HtmlSkillConsultingSectionsTests
|
||||
"rationale": "Retention and margin improved in the pilot region.",
|
||||
"actions": ["Approve budget", "Launch phase 2"]
|
||||
},
|
||||
{
|
||||
"type": "kpi_panel",
|
||||
"title": "KPI Panel",
|
||||
"headline": "Pilot economics remain above threshold",
|
||||
"items": [
|
||||
{ "label": "Margin", "value": "+4.2pt", "trend": "up", "note": "pilot" },
|
||||
{ "label": "Lead Time", "value": "-18%", "trend": "down", "note": "handoff" }
|
||||
],
|
||||
"takeaway": "The pilot shows both service and margin improvement."
|
||||
},
|
||||
{
|
||||
"type": "evidence_cards",
|
||||
"title": "Evidence",
|
||||
@@ -101,6 +111,7 @@ public class HtmlSkillConsultingSectionsTests
|
||||
html.Should().Contain("roadmap-block");
|
||||
html.Should().Contain("matrix-grid");
|
||||
html.Should().Contain("decision-summary");
|
||||
html.Should().Contain("kpi-panel");
|
||||
html.Should().Contain("evidence-cards");
|
||||
html.Should().Contain("board-report-panel");
|
||||
html.Should().Contain("strategy-brief-panel");
|
||||
|
||||
58
src/AxCopilot.Tests/Services/SqlAnalysisServiceTests.cs
Normal file
58
src/AxCopilot.Tests/Services/SqlAnalysisServiceTests.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using System.IO;
|
||||
using AxCopilot.Services;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class SqlAnalysisServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public void Analyze_ShouldSurfaceStatementKindsObjectsAndRisks()
|
||||
{
|
||||
const string sql =
|
||||
"""
|
||||
BEGIN TRANSACTION;
|
||||
CREATE TABLE orders (id SERIAL PRIMARY KEY, customer_id INT);
|
||||
UPDATE orders SET customer_id = 0;
|
||||
DROP TABLE legacy_orders;
|
||||
COMMIT;
|
||||
""";
|
||||
|
||||
var report = SqlAnalysisService.Analyze(sql, "20260415_orders_migration.sql");
|
||||
|
||||
report.Dialect.Should().Be("PostgreSQL");
|
||||
report.StatementKinds.Should().Contain("CREATE TABLE");
|
||||
report.StatementKinds.Should().Contain("UPDATE");
|
||||
report.StatementKinds.Should().Contain("DROP TABLE");
|
||||
report.Objects.Should().Contain("orders");
|
||||
report.Objects.Should().Contain("legacy_orders");
|
||||
report.Risks.Should().Contain(risk => risk.Contains("UPDATE statement without WHERE", StringComparison.OrdinalIgnoreCase));
|
||||
report.Risks.Should().Contain(risk => risk.Contains("Destructive DROP", StringComparison.OrdinalIgnoreCase));
|
||||
report.SuggestedChecks.Should().Contain(check => check.Contains("rollback", StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildFallbackSummary_ShouldReadSqlFileAndReturnSqlSpecificSummary()
|
||||
{
|
||||
var path = Path.GetTempFileName() + ".sql";
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, "CREATE TABLE users (id SERIAL PRIMARY KEY, email TEXT); DELETE FROM users;");
|
||||
|
||||
var summary = SqlAnalysisService.BuildFallbackSummary(path);
|
||||
|
||||
summary.Should().Contain("SQL static analysis");
|
||||
summary.Should().Contain("PostgreSQL");
|
||||
summary.Should().Contain("CREATE TABLE");
|
||||
summary.Should().Contain("DELETE");
|
||||
summary.Should().Contain("users");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/AxCopilot.Tests/Services/SqlDialectDetectorTests.cs
Normal file
18
src/AxCopilot.Tests/Services/SqlDialectDetectorTests.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using AxCopilot.Services;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class SqlDialectDetectorTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT);", "PostgreSQL")]
|
||||
[InlineData("CREATE TABLE [dbo].[Users] (Id INT IDENTITY(1,1), Name NVARCHAR(100)); GO", "SQL Server")]
|
||||
[InlineData("CREATE TABLE `users` (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100)) ENGINE=InnoDB;", "MySQL")]
|
||||
[InlineData("CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT); PRAGMA foreign_keys=ON;", "SQLite")]
|
||||
public void DetectDialect_ShouldRecognizeCommonDialects(string sql, string expected)
|
||||
{
|
||||
SqlDialectDetector.DetectDialect(sql).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user