사내/사외 모드 회귀 보강: 전환 즉시반영 및 URL 경계 테스트 추가
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:
2026-04-04 16:25:41 +09:00
parent 3b9938e01b
commit 2e945e36d5
4 changed files with 122 additions and 1 deletions

View File

@@ -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()
{

View 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("사내모드");
}
}