사내/사외 모드 회귀 보강: 전환 즉시반영 및 URL 경계 테스트 추가
Some checks failed
Release Gate / gate (push) Has been cancelled
Some checks failed
Release Gate / gate (push) Has been cancelled
- OperationModePolicyTests에 IsExternalUrl 경계 케이스(http/https vs file/mailto) 추가 - OperationModeReadinessTests 정비 및 internal→external 전환 즉시반영 테스트 추가 - README/DEVELOPMENT 이력(2026-04-04 16:24 KST) 동기화
This commit is contained in:
@@ -222,7 +222,7 @@ public class MyHandler : IActionHandler
|
||||
|
||||
### v0.7.3 — AX Agent 권한 코어 재구성 + 입력 계층 정리
|
||||
|
||||
업데이트: 2026-04-04 16:18 (KST)
|
||||
업데이트: 2026-04-04 16:24 (KST)
|
||||
|
||||
| 분류 | 내용 |
|
||||
|------|------|
|
||||
@@ -293,6 +293,7 @@ public class MyHandler : IActionHandler
|
||||
| 권한 팝업 즉시반영 정렬 | 권한 팝업에 `활용하지 않음`을 핵심 영역 맨 위에 배치하고, 대화 권한이 없을 때도 탭 기본값(Deny/DefaultAgentPermission)을 즉시 반영하도록 로딩 경로를 보강 |
|
||||
| 권한 색상 체계 통일 | 권한 요약 카드/상단 배너에서 모드별 색상(Deny=녹색, Passive=파랑, Active=녹색, Plan=보라, FullAuto=주황, Silent=빨강)을 팝업 체계와 일치시킴 |
|
||||
| 슬래시 네비게이션 입력 보강 | InputBox 포커스 상태에서도 방향키/Page/Home/End/Tab이 슬래시 목록 탐색에 즉시 반영되도록 키 처리 경로를 통합하고, 모든 그룹 접힘 상태에서 휠 스크롤 fallback을 추가 |
|
||||
| 사내/사외 모드 회귀 보강 | operationMode 전환 직후 WebSearch 동작 반영과 URL 판별 경계(HTTP/파일/mailto) 테스트를 추가해 내부 차단 정책의 즉시성/정확성을 강화 |
|
||||
| Slash palette 상태 분리 시작 | `ChatWindow`에 몰려 있던 slash 상태를 `SlashPaletteState`로 분리해 이후 Codex/Claude형 composer 개편 기반 마련 |
|
||||
| 런처 이미지 미리보기 추가 | `#` 클립보드 이미지 항목에서 `Shift+Enter`로 전용 미리보기 창을 열고, 줌·원본 해상도 확인·PNG/JPEG/BMP 저장·클립보드 복사를 지원 |
|
||||
| 검증 | `dotnet build` 경고 0 / 오류 0, `dotnet test` 436 passed / 0 failed |
|
||||
|
||||
@@ -3671,3 +3671,23 @@ else:
|
||||
### 4) 품질 게이트
|
||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Debug -p:UseSharedCompilation=false -nodeReuse:false` 통과 (경고 0, 오류 0).
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -p:UseSharedCompilation=false -nodeReuse:false --filter "FullyQualifiedName~ChatWindowSlashPolicyTests|FullyQualifiedName~PermissionModeCatalogTests|FullyQualifiedName~ChatSessionStateServiceTests"` 통과 (109 passed, 0 failed).
|
||||
|
||||
## 2026-04-04 추가 진행 기록 (연속 실행 43차: 사내/사외 모드 회귀 보강)
|
||||
|
||||
업데이트: 2026-04-04 16:24 (KST)
|
||||
|
||||
### 1) 정책 경계 테스트 보강
|
||||
- `OperationModePolicyTests`에 `IsExternalUrl_DetectsOnlyHttpSchemes` 추가.
|
||||
- 검증 범위:
|
||||
- `http/https`는 외부 URL로 판정
|
||||
- 로컬 파일 경로, `mailto`, 일반 문자열은 외부 URL이 아님
|
||||
|
||||
### 2) 모드 전환 즉시 반영 테스트 추가
|
||||
- `OperationModeReadinessTests`를 정리/보강:
|
||||
- `WebSearch_ModeSwitch_InternalToExternal_ShouldReflectImmediately` 추가
|
||||
- 같은 핸들러 인스턴스에서 설정값 전환 후 결과가 즉시 `차단`→`검색`으로 반영되는지 검증
|
||||
- 기존 readiness 테스트의 문자열 비교도 정상 한글 기준으로 정비.
|
||||
|
||||
### 3) 품질 게이트
|
||||
- `dotnet build src/AxCopilot/AxCopilot.csproj -c Debug -p:UseSharedCompilation=false -nodeReuse:false` 통과 (경고 0, 오류 0).
|
||||
- `dotnet test src/AxCopilot.Tests/AxCopilot.Tests.csproj -p:UseSharedCompilation=false -nodeReuse:false --filter "FullyQualifiedName~OperationModePolicyTests|FullyQualifiedName~OperationModeReadinessTests"` 통과 (20 passed, 0 failed).
|
||||
|
||||
@@ -31,6 +31,16 @@ public class OperationModePolicyTests
|
||||
OperationModePolicy.IsBlockedAgentToolInInternalMode("open_external", @"E:\work\report.html").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void IsExternalUrl_DetectsOnlyHttpSchemes()
|
||||
{
|
||||
OperationModePolicy.IsExternalUrl("https://example.com").Should().BeTrue();
|
||||
OperationModePolicy.IsExternalUrl("http://intranet.local").Should().BeTrue();
|
||||
OperationModePolicy.IsExternalUrl(@"E:\work\report.html").Should().BeFalse();
|
||||
OperationModePolicy.IsExternalUrl("mailto:admin@example.com").Should().BeFalse();
|
||||
OperationModePolicy.IsExternalUrl("not-a-url").Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AgentContext_CheckToolPermissionAsync_BlocksRestrictedToolsInInternalMode()
|
||||
{
|
||||
|
||||
90
src/AxCopilot.Tests/Services/OperationModeReadinessTests.cs
Normal file
90
src/AxCopilot.Tests/Services/OperationModeReadinessTests.cs
Normal file
@@ -0,0 +1,90 @@
|
||||
using System.Text.Json;
|
||||
using AxCopilot.Handlers;
|
||||
using AxCopilot.Services;
|
||||
using AxCopilot.Services.Agent;
|
||||
using FluentAssertions;
|
||||
using Xunit;
|
||||
|
||||
namespace AxCopilot.Tests.Services;
|
||||
|
||||
public class OperationModeReadinessTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task WebSearch_InternalMode_ShowsBlockedMessage()
|
||||
{
|
||||
var settings = new SettingsService();
|
||||
settings.Settings.OperationMode = OperationModePolicy.InternalMode;
|
||||
var handler = new WebSearchHandler(settings);
|
||||
|
||||
var items = (await handler.GetItemsAsync("ax copilot", CancellationToken.None)).ToList();
|
||||
|
||||
items.Should().NotBeEmpty();
|
||||
items[0].Title.Should().Contain("차단");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WebSearch_ExternalMode_ReturnsSearchItems()
|
||||
{
|
||||
var settings = new SettingsService();
|
||||
settings.Settings.OperationMode = OperationModePolicy.ExternalMode;
|
||||
var handler = new WebSearchHandler(settings);
|
||||
|
||||
var items = (await handler.GetItemsAsync("ax copilot", CancellationToken.None)).ToList();
|
||||
|
||||
items.Should().NotBeEmpty();
|
||||
items.Any(i => i.Title.Contains("검색")).Should().BeTrue();
|
||||
items.Any(i => i.Title.Contains("차단")).Should().BeFalse();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WebSearch_ModeSwitch_InternalToExternal_ShouldReflectImmediately()
|
||||
{
|
||||
var settings = new SettingsService();
|
||||
var handler = new WebSearchHandler(settings);
|
||||
|
||||
settings.Settings.OperationMode = OperationModePolicy.InternalMode;
|
||||
var internalItems = (await handler.GetItemsAsync("ax copilot", CancellationToken.None)).ToList();
|
||||
|
||||
settings.Settings.OperationMode = OperationModePolicy.ExternalMode;
|
||||
var externalItems = (await handler.GetItemsAsync("ax copilot", CancellationToken.None)).ToList();
|
||||
|
||||
internalItems.Should().NotBeEmpty();
|
||||
externalItems.Should().NotBeEmpty();
|
||||
internalItems[0].Title.Should().Contain("차단");
|
||||
externalItems.Any(i => i.Title.Contains("검색")).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HttpTool_InternalMode_IsBlockedEvenWhenCalledDirectly()
|
||||
{
|
||||
var tool = new HttpTool();
|
||||
using var doc = JsonDocument.Parse("""{"method":"GET","url":"http://127.0.0.1:8080/health"}""");
|
||||
var context = new AgentContext
|
||||
{
|
||||
OperationMode = OperationModePolicy.InternalMode,
|
||||
Permission = "Auto"
|
||||
};
|
||||
|
||||
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeFalse();
|
||||
result.Output.Should().Contain("사내모드");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OpenExternal_InternalMode_BlocksExternalUrl()
|
||||
{
|
||||
var tool = new OpenExternalTool();
|
||||
using var doc = JsonDocument.Parse("""{"path":"https://example.com"}""");
|
||||
var context = new AgentContext
|
||||
{
|
||||
OperationMode = OperationModePolicy.InternalMode,
|
||||
Permission = "Auto"
|
||||
};
|
||||
|
||||
var result = await tool.ExecuteAsync(doc.RootElement, context, CancellationToken.None);
|
||||
|
||||
result.Success.Should().BeFalse();
|
||||
result.Output.Should().Contain("사내모드");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user