문서 대시보드·DOCX 스타일맵·PPT 골든 회귀를 고도화하고 검증 추가
- ExcelSkill에 summary_sheet 기반 Dashboard 시트 생성을 추가해 Summary -> Dashboard -> Detail 시트 흐름을 지원 - ArtifactQualityReviewService workbook 리뷰에 dashboard 존재 여부를 반영하고 multi-sheet workbook 보완 포인트를 강화 - DocumentAssemblerTool style_map 범위를 cover_subtitle/callout/table_header까지 확장해 템플릿 기반 DOCX 조립 품질을 개선 - Excel/DOCX/PPT 회귀 테스트를 확장하고 PptxSkillGoldenDeckTests를 추가해 strong deck 품질 기준을 고정 - README.md와 docs/DEVELOPMENT.md에 2026-04-14 23:25 (KST) 기준 작업 이력과 검증 명령을 반영 검증 결과 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify_doc_next4\\ -p:IntermediateOutputPath=obj\\verify_doc_next4\\ : 경고 0 / 오류 0 - dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -c Release -v minimal --filter ArtifactQualityReviewServiceTests|DocumentAssemblerStyleMapTests|DocumentAssemblerDocxFeaturesTests|DocumentAssemblerSemanticTests|ExcelSkillDashboardSummaryTests|ExcelSkillSummarySheetTests|ExcelSkillExecutiveSummaryLinkTests|ExcelSkillDataValidationTests|ExcelSkillConditionalFormattingTests|HtmlSkillPrintFrameTests|HtmlSkillConsultingSectionsTests|DeckQualityReviewServiceTests|PptxSkillAutoRepairTests|PptxSkillConsultingDeckTests|PptxSkillGoldenDeckTests -p:OutputPath=bin\\verify_doc_next4_tests\\ -p:IntermediateOutputPath=obj\\verify_doc_next4_tests\\ : 통과 20
This commit is contained in:
@@ -71,6 +71,7 @@ public class ArtifactQualityReviewServiceTests
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false));
|
||||
|
||||
review.Issues.Should().Contain(issue => issue.Message.Contains("summary sheet", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
@@ -28,7 +28,10 @@ public class DocumentAssemblerStyleMapTests
|
||||
stylePart.Styles = new Styles(
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckTitle", CustomStyle = true },
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckHeading1", CustomStyle = true },
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckBody", CustomStyle = true });
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckBody", CustomStyle = true },
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckSubtitle", CustomStyle = true },
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckCallout", CustomStyle = true },
|
||||
new Style { Type = StyleValues.Paragraph, StyleId = "DeckTableHeader", CustomStyle = true });
|
||||
stylePart.Styles.Save();
|
||||
mainPart.Document.Save();
|
||||
}
|
||||
@@ -48,13 +51,17 @@ public class DocumentAssemblerStyleMapTests
|
||||
"title": "Board Update",
|
||||
"format": "docx",
|
||||
"template_path": "style-template.docx",
|
||||
"cover_subtitle": "Quarterly Steering Pack",
|
||||
"style_map": {
|
||||
"title": "DeckTitle",
|
||||
"heading1": "DeckHeading1",
|
||||
"body": "DeckBody"
|
||||
"body": "DeckBody",
|
||||
"cover_subtitle": "DeckSubtitle",
|
||||
"callout": "DeckCallout",
|
||||
"table_header": "DeckTableHeader"
|
||||
},
|
||||
"sections": [
|
||||
{ "heading": "1. Executive Summary", "level": 1, "content": "<p>Margins improved across key accounts.</p>" }
|
||||
{ "heading": "1. Executive Summary", "level": 1, "content": "<p>Margins improved across key accounts.</p><div class=\"callout-info\">Focus on margin recovery</div><table><tr><th>Metric</th><th>Value</th></tr><tr><td>Margin</td><td>18%</td></tr></table>" }
|
||||
]
|
||||
}
|
||||
""").RootElement;
|
||||
@@ -69,12 +76,28 @@ public class DocumentAssemblerStyleMapTests
|
||||
paragraphs.First(paragraph => paragraph.InnerText == "Board Update")
|
||||
.ParagraphProperties?.ParagraphStyleId?.Val?.Value
|
||||
.Should().Be("DeckTitle");
|
||||
paragraphs.First(paragraph => paragraph.InnerText == "Quarterly Steering Pack")
|
||||
.ParagraphProperties?.ParagraphStyleId?.Val?.Value
|
||||
.Should().Be("DeckSubtitle");
|
||||
paragraphs.First(paragraph => paragraph.InnerText == "1. Executive Summary")
|
||||
.ParagraphProperties?.ParagraphStyleId?.Val?.Value
|
||||
.Should().Be("DeckHeading1");
|
||||
paragraphs.First(paragraph => paragraph.InnerText.Contains("Margins improved"))
|
||||
.ParagraphProperties?.ParagraphStyleId?.Val?.Value
|
||||
.Should().Be("DeckBody");
|
||||
paragraphs.First(paragraph => paragraph.InnerText.Contains("Focus on margin recovery"))
|
||||
.ParagraphProperties?.ParagraphStyleId?.Val?.Value
|
||||
.Should().Be("DeckCallout");
|
||||
|
||||
var firstHeaderParagraph = doc.MainDocumentPart.Document.Body!
|
||||
.Descendants<Table>()
|
||||
.First()
|
||||
.Descendants<TableRow>()
|
||||
.First()
|
||||
.Descendants<Paragraph>()
|
||||
.First();
|
||||
firstHeaderParagraph.ParagraphProperties?.ParagraphStyleId?.Val?.Value
|
||||
.Should().Be("DeckTableHeader");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ public class ExcelSkillDashboardSummaryTests
|
||||
"path": "dashboard-review.xlsx",
|
||||
"summary_sheet": {
|
||||
"name": "Summary",
|
||||
"dashboard_sheet_name": "Dashboard",
|
||||
"title": "Operating Review",
|
||||
"decision_summary": [
|
||||
{ "label": "Decision Ask", "value": "Approve regional rollout", "owner": "COO" }
|
||||
@@ -65,6 +66,9 @@ public class ExcelSkillDashboardSummaryTests
|
||||
|
||||
using var doc = SpreadsheetDocument.Open(Path.Combine(workDir, "dashboard-review.xlsx"), false);
|
||||
var workbookPart = doc.WorkbookPart!;
|
||||
workbookPart.Workbook.Sheets!.Elements<Sheet>().Select(sheet => sheet.Name!.Value)
|
||||
.Should().Contain(["Summary", "Dashboard", "Revenue"]);
|
||||
|
||||
var summarySheet = workbookPart.Workbook.Sheets!.Elements<Sheet>().First(sheet => sheet.Name == "Summary");
|
||||
var summaryPart = (WorksheetPart)workbookPart.GetPartById(summarySheet.Id!);
|
||||
var texts = summaryPart.Worksheet.Descendants<Cell>()
|
||||
@@ -80,6 +84,16 @@ public class ExcelSkillDashboardSummaryTests
|
||||
texts.Should().Contain("-10");
|
||||
texts.Should().Contain("Revenue");
|
||||
texts.Should().Contain("SMB margin pressure");
|
||||
|
||||
var dashboardSheet = workbookPart.Workbook.Sheets!.Elements<Sheet>().First(sheet => sheet.Name == "Dashboard");
|
||||
var dashboardPart = (WorksheetPart)workbookPart.GetPartById(dashboardSheet.Id!);
|
||||
var dashboardTexts = dashboardPart.Worksheet.Descendants<Cell>()
|
||||
.Select(cell => cell.CellValue?.Text)
|
||||
.Where(text => !string.IsNullOrWhiteSpace(text))
|
||||
.ToList();
|
||||
dashboardTexts.Should().Contain("Operating Review Dashboard");
|
||||
dashboardTexts.Should().Contain("Linked Detail Sheets");
|
||||
dashboardTexts.Should().Contain("Open: Revenue");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
124
src/AxCopilot.Tests/Services/PptxSkillGoldenDeckTests.cs
Normal file
124
src/AxCopilot.Tests/Services/PptxSkillGoldenDeckTests.cs
Normal file
@@ -0,0 +1,124 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class PptxSkillGoldenDeckTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task ExecuteAsync_WithStrongBoardDeck_ShouldReturnStableQualitySummary()
|
||||
{
|
||||
var workDir = Path.Combine(Path.GetTempPath(), "ax-pptx-golden-" + Guid.NewGuid().ToString("N"));
|
||||
Directory.CreateDirectory(workDir);
|
||||
|
||||
try
|
||||
{
|
||||
var tool = new PptxSkill();
|
||||
var context = new AgentContext
|
||||
{
|
||||
WorkFolder = workDir,
|
||||
Permission = "Auto",
|
||||
OperationMode = "external",
|
||||
};
|
||||
|
||||
var args = JsonDocument.Parse(
|
||||
"""
|
||||
{
|
||||
"path": "board-golden.pptx",
|
||||
"theme": "professional",
|
||||
"audience": "Executive committee",
|
||||
"objective": "Approve the phase-1 operating model rollout",
|
||||
"decision_ask": "Approve the phase-1 rollout and funding release",
|
||||
"slides": [
|
||||
{
|
||||
"layout": "title",
|
||||
"title": "Operating Model Refresh"
|
||||
},
|
||||
{
|
||||
"layout": "executive_summary",
|
||||
"title": "Executive Summary",
|
||||
"headline": "Phase-1 rollout can improve service consistency while protecting margin.",
|
||||
"summary_points": [
|
||||
"Three pilot functions account for most process variation and rework.",
|
||||
"Phase-1 can be delivered without slowing current-quarter commitments.",
|
||||
"The operating model change is expected to reduce handoff delay by 18%."
|
||||
],
|
||||
"recommendation": "Approve the phase-1 rollout and lock the pilot governance model.",
|
||||
"kpis": [
|
||||
{ "label": "Cycle Time", "value": "-18%", "trend": "target", "note": "handoff simplification" },
|
||||
{ "label": "Margin", "value": "+2.4pt", "trend": "run-rate", "note": "mix and rework" },
|
||||
{ "label": "On-time Delivery", "value": "96%", "trend": "pilot", "note": "stable" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "comparison",
|
||||
"title": "Options",
|
||||
"headline": "A phased rollout balances speed, control, and adoption risk.",
|
||||
"options": [
|
||||
{ "name": "Pilot only", "pros": "Lowest delivery risk", "cons": "Value capture is delayed", "verdict": "Too cautious" },
|
||||
{ "name": "Phased rollout", "pros": "Balanced speed and control", "cons": "Requires PMO discipline", "verdict": "Recommended" },
|
||||
{ "name": "Full rollout", "pros": "Fastest value capture", "cons": "High adoption risk", "verdict": "Too disruptive" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "roadmap",
|
||||
"title": "Roadmap",
|
||||
"headline": "Roll out in three waves with explicit governance checkpoints.",
|
||||
"phases": [
|
||||
{ "title": "Design", "detail": "Finalize scope, KPIs, and governance", "timeline": "Weeks 1-3", "owner": "PMO" },
|
||||
{ "title": "Pilot", "detail": "Launch pilot functions and refine playbooks", "timeline": "Weeks 4-8", "owner": "Operations" },
|
||||
{ "title": "Scale", "detail": "Expand to the remaining regions", "timeline": "Weeks 9-14", "owner": "Business" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "recommendation",
|
||||
"title": "Recommendation",
|
||||
"recommendation": "Approve phase-1 rollout, assign PMO governance, and release pilot funding.",
|
||||
"summary_points": [
|
||||
"The phased path protects execution quality while capturing measurable value in-quarter.",
|
||||
"The pilot governance model can be reused for the scale phase.",
|
||||
"The decision can be taken now because the key delivery risks are already bounded."
|
||||
],
|
||||
"next_steps": [
|
||||
"Confirm phase-1 scope this week",
|
||||
"Launch pilot governance cadence next week",
|
||||
"Review pilot performance at week 8"
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "table",
|
||||
"title": "Appendix & Evidence",
|
||||
"headers": ["Evidence", "Detail"],
|
||||
"rows": [
|
||||
["Process review", "Pilot teams show the highest rework and handoff load."],
|
||||
["Financial impact", "Run-rate margin lift is driven by lower rework and better prioritization."]
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
""").RootElement;
|
||||
|
||||
var result = await tool.ExecuteAsync(args, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeTrue();
|
||||
File.Exists(Path.Combine(workDir, "board-golden.pptx")).Should().BeTrue();
|
||||
result.Output.Should().Contain("PPT quality");
|
||||
result.Output.Should().NotContain("Slide alerts:");
|
||||
result.Output.Should().Contain("Needs work: none");
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
if (Directory.Exists(workDir))
|
||||
Directory.Delete(workDir, true);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user