테스트 회귀망 강화: 권한 모드/슬래시 카탈로그 L4 통합 검증 추가
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- PermissionModeCatalogTests 추가: 글로벌/도구 정규화, 승인 필요 정책, 한국어 표시 라벨 검증 - PermissionModePresentationCatalogTests 추가: 권한 표면 순서와 unknown fallback(Default) 검증 - SlashCommandCatalogTests 추가: dev 전용 명령 필터링과 /compact,/permissions,/mcp 핵심 명령 등록 검증 - OperationModePolicyTests 보강: deny 패턴이 allow 패턴보다 우선되는 충돌 케이스 추가 - README.md, docs/DEVELOPMENT.md에 2026-04-04 13:40(KST) 기준 이력 반영
This commit is contained in:
@@ -222,7 +222,7 @@ public class MyHandler : IActionHandler
|
||||
|
||||
### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리
|
||||
|
||||
업데이트: 2026-04-04 13:32 (KST)
|
||||
업데이트: 2026-04-04 13:40 (KST)
|
||||
|
||||
| 분류 | 내용 |
|
||||
|------|------|
|
||||
@@ -279,6 +279,7 @@ public class MyHandler : IActionHandler
|
||||
| slash 조회 API 전환 | 내장 slash 매칭/조회 경로를 `SlashCommandCatalog.MatchBuiltinCommands`/`TryGetEntry`로 통일 |
|
||||
| 권한 표시 카탈로그 분리 | 권한 모드 라벨/설명/아이콘/색을 `PermissionModePresentationCatalog`로 분리해 팝업 표면 기준을 단일화 |
|
||||
| 탭별 설정 해석기 도입 | `AgentTabSettingsResolver`를 추가해 Cowork/Code 분기(검증 활성/Code 전용 도구 비활성)를 단일 경로로 정리 |
|
||||
| L4 통합 회귀 보강 | `PermissionModeCatalogTests`/`PermissionModePresentationCatalogTests`/`SlashCommandCatalogTests`를 추가하고 deny 우선 규칙을 `OperationModePolicyTests`에 반영해 권한·슬래시 회귀망을 강화 |
|
||||
| Slash palette 상태 분리 시작 | `ChatWindow`에 몰려 있던 slash 상태를 `SlashPaletteState`로 분리해 이후 Codex/Claude형 composer 개편 기반 마련 |
|
||||
| 런처 이미지 미리보기 추가 | `#` 클립보드 이미지 항목에서 `Shift+Enter`로 전용 미리보기 창을 열고, 줌·원본 해상도 확인·PNG/JPEG/BMP 저장·클립보드 복사를 지원 |
|
||||
| 검증 | `dotnet build` 경고 0 / 오류 0, `dotnet test` 436 passed / 0 failed |
|
||||
|
||||
@@ -3367,3 +3367,29 @@ else:
|
||||
### 4) 품질 게이트
|
||||
- dotnet build src/AxCopilot/AxCopilot.csproj -p:UseSharedCompilation=false -nodeReuse:false 통과 (경고 0, 오류 0).
|
||||
- dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj --filter "FullyQualifiedName~AgentTabSettingsResolverTests|FullyQualifiedName~ChatWindowSlashPolicyTests|FullyQualifiedName~OperationModeReadinessTests" 통과 (49 passed, 0 failed).
|
||||
|
||||
## 2026-04-04 추가 진행 기록 (연속 실행 29차: 권한/슬래시 L4 통합 회귀 보강)
|
||||
|
||||
업데이트: 2026-04-04 13:40 (KST)
|
||||
|
||||
### 1) 권한 모드 카탈로그 테스트 추가
|
||||
- 신규 `PermissionModeCatalogTests`:
|
||||
- 글로벌/도구 권한 정규화 매핑 검증
|
||||
- 사용자 승인 필요 여부 정책 검증
|
||||
- 권한 모드 표시 라벨(활용하지 않음/소극 활용/적극 활용/계획 중심/완전 자동/질문 없이 진행) 검증
|
||||
- 신규 `PermissionModePresentationCatalogTests`:
|
||||
- 권한 표면 순서(Deny→Default→AcceptEdits→Plan→BypassPermissions→DontAsk) 검증
|
||||
- 미정의 모드 fallback이 `Default`로 수렴하는지 검증
|
||||
|
||||
### 2) 권한 규칙 우선순위 회귀 보강
|
||||
- `OperationModePolicyTests`에 deny/allow 패턴 충돌 케이스 추가:
|
||||
- `process@git push * = deny`가 `process@git * = acceptedits`보다 우선 적용되는지 검증
|
||||
|
||||
### 3) slash 카탈로그 회귀 보강
|
||||
- 신규 `SlashCommandCatalogTests`:
|
||||
- Chat 탭에서 dev 전용 명령(`/review`)이 숨겨지는지 검증
|
||||
- 핵심 parity 명령(`/compact`, `/permissions`, `/mcp`)의 카탈로그 등록 검증
|
||||
|
||||
### 4) 품질 게이트
|
||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Debug -p:UseSharedCompilation=false -nodeReuse:false` 통과 (경고 0, 오류 0).
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj --filter "FullyQualifiedName~PermissionModeCatalogTests|FullyQualifiedName~PermissionModePresentationCatalogTests|FullyQualifiedName~SlashCommandCatalogTests|FullyQualifiedName~OperationModePolicyTests|FullyQualifiedName~OperationModeReadinessTests|FullyQualifiedName~ChatWindowSlashPolicyTests"` 통과 (88 passed, 0 failed).
|
||||
|
||||
@@ -103,6 +103,23 @@ public class OperationModePolicyTests
|
||||
askCalled.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AgentContext_GetEffectiveToolPermission_DenyPatternPrecedesAllowPattern()
|
||||
{
|
||||
var context = new AgentContext
|
||||
{
|
||||
Permission = "AcceptEdits",
|
||||
ToolPermissions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
["process@git *"] = "acceptedits",
|
||||
["process@git push *"] = "deny",
|
||||
}
|
||||
};
|
||||
|
||||
context.GetEffectiveToolPermission("process", "git status").Should().Be("Default");
|
||||
context.GetEffectiveToolPermission("process", "git push origin main").Should().Be("Deny");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AgentContext_GetEffectiveToolPermission_AcceptEditsAllowsWriteButKeepsProcessPrompted()
|
||||
{
|
||||
|
||||
61
src/AxCopilot.Tests/Services/PermissionModeCatalogTests.cs
Normal file
61
src/AxCopilot.Tests/Services/PermissionModeCatalogTests.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class PermissionModeCatalogTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(null, PermissionModeCatalog.Default)]
|
||||
[InlineData("", PermissionModeCatalog.Default)]
|
||||
[InlineData("ask", PermissionModeCatalog.Default)]
|
||||
[InlineData("auto", PermissionModeCatalog.AcceptEdits)]
|
||||
[InlineData("accept", PermissionModeCatalog.AcceptEdits)]
|
||||
[InlineData("plan", PermissionModeCatalog.Plan)]
|
||||
[InlineData("bypass", PermissionModeCatalog.BypassPermissions)]
|
||||
[InlineData("dontask", PermissionModeCatalog.DontAsk)]
|
||||
[InlineData("deny", PermissionModeCatalog.Deny)]
|
||||
[InlineData("unknown", PermissionModeCatalog.Default)]
|
||||
public void NormalizeGlobalMode_ShouldMapExpectedModes(string? input, string expected)
|
||||
{
|
||||
PermissionModeCatalog.NormalizeGlobalMode(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ask", PermissionModeCatalog.Default)]
|
||||
[InlineData("auto", PermissionModeCatalog.AcceptEdits)]
|
||||
[InlineData("plan", PermissionModeCatalog.Plan)]
|
||||
[InlineData("bypass", PermissionModeCatalog.BypassPermissions)]
|
||||
[InlineData("dontask", PermissionModeCatalog.DontAsk)]
|
||||
[InlineData("deny", PermissionModeCatalog.Deny)]
|
||||
[InlineData("unknown", PermissionModeCatalog.Default)]
|
||||
public void NormalizeToolOverride_ShouldMapExpectedModes(string? input, string expected)
|
||||
{
|
||||
PermissionModeCatalog.NormalizeToolOverride(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("ask", true)]
|
||||
[InlineData("auto", false)]
|
||||
[InlineData("acceptedits", false)]
|
||||
[InlineData("bypassPermissions", false)]
|
||||
[InlineData("dontAsk", false)]
|
||||
[InlineData("deny", false)]
|
||||
public void RequiresUserApproval_ShouldMatchPolicy(string? input, bool expected)
|
||||
{
|
||||
PermissionModeCatalog.RequiresUserApproval(input).Should().Be(expected);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(PermissionModeCatalog.Deny, "활용하지 않음")]
|
||||
[InlineData(PermissionModeCatalog.Default, "소극 활용")]
|
||||
[InlineData(PermissionModeCatalog.AcceptEdits, "적극 활용")]
|
||||
[InlineData(PermissionModeCatalog.Plan, "계획 중심")]
|
||||
[InlineData(PermissionModeCatalog.BypassPermissions, "완전 자동")]
|
||||
[InlineData(PermissionModeCatalog.DontAsk, "질문 없이 진행")]
|
||||
public void ToDisplayLabel_ShouldReturnKoreanLabel(string mode, string expected)
|
||||
{
|
||||
PermissionModeCatalog.ToDisplayLabel(mode).Should().Be(expected);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class PermissionModePresentationCatalogTests
|
||||
{
|
||||
[Fact]
|
||||
public void Ordered_ShouldMatchExpectedModeOrder()
|
||||
{
|
||||
PermissionModePresentationCatalog.Ordered.Select(x => x.Mode).Should().ContainInOrder(
|
||||
[
|
||||
PermissionModeCatalog.Deny,
|
||||
PermissionModeCatalog.Default,
|
||||
PermissionModeCatalog.AcceptEdits,
|
||||
PermissionModeCatalog.Plan,
|
||||
PermissionModeCatalog.BypassPermissions,
|
||||
PermissionModeCatalog.DontAsk,
|
||||
]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Resolve_ShouldFallbackToDefaultPresentation_OnUnknownMode()
|
||||
{
|
||||
var resolved = PermissionModePresentationCatalog.Resolve("unknown-mode");
|
||||
resolved.Mode.Should().Be(PermissionModeCatalog.Default);
|
||||
}
|
||||
}
|
||||
31
src/AxCopilot.Tests/Views/SlashCommandCatalogTests.cs
Normal file
31
src/AxCopilot.Tests/Views/SlashCommandCatalogTests.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using AxCopilot.Views;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Views;
|
||||
|
||||
public class SlashCommandCatalogTests
|
||||
{
|
||||
[Fact]
|
||||
public void MatchBuiltinCommands_ShouldFilterDevCommandsInChatTab()
|
||||
{
|
||||
var chatMatches = SlashCommandCatalog.MatchBuiltinCommands("/rev", isDevTab: false);
|
||||
var devMatches = SlashCommandCatalog.MatchBuiltinCommands("/rev", isDevTab: true);
|
||||
|
||||
chatMatches.Should().BeEmpty();
|
||||
devMatches.Should().ContainSingle(x => x.Cmd == "/review");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Catalog_ShouldContainCoreParityCommands()
|
||||
{
|
||||
SlashCommandCatalog.TryGetEntry("/compact", out var compactEntry).Should().BeTrue();
|
||||
compactEntry.SystemPrompt.Should().Be("__COMPACT__");
|
||||
|
||||
SlashCommandCatalog.TryGetEntry("/permissions", out var permissionEntry).Should().BeTrue();
|
||||
permissionEntry.SystemPrompt.Should().Be("__PERMISSIONS__");
|
||||
|
||||
SlashCommandCatalog.TryGetEntry("/mcp", out var mcpEntry).Should().BeTrue();
|
||||
mcpEntry.SystemPrompt.Should().Be("__MCP__");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user