using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Newtonsoft.Json; using Xunit; using CncCollector.Core; using CncCollector.Api; using CncCollector.Config; namespace CncCollector.Tests { /// /// CollectorApiServer 单元测试。 /// 使用无效DB配置创建引擎和API服务器,通过HTTP客户端验证认证和路由行为。 /// public class CollectorApiServerTests : IDisposable { private readonly CollectorEngine _engine; private readonly CollectorApiServer _server; private readonly HttpClient _client; private readonly int _port; private readonly string _apiKey = "test_api_key_123"; public CollectorApiServerTests() { // 使用随机端口避免冲突 _port = 15800 + new Random().Next(0, 1000); var config = new CollectorConfig { BusinessConnection = "Server=invalid;Database=invalid;User Id=invalid;Password=invalid;", LogConnection = "Server=invalid;Database=invalid;User Id=invalid;Password=invalid;", ApiPort = _port }; _engine = new CollectorEngine(config); _server = new CollectorApiServer(_engine, _apiKey, _port); _server.Start(); _client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) }; } public void Dispose() { _server?.Stop(); _client?.Dispose(); } // ===== 认证测试 ===== [Fact] public async Task Status_无APIKey_返回401() { var resp = await _client.GetAsync($"http://localhost:{_port}/api/collector/status"); Assert.Equal(HttpStatusCode.Unauthorized, resp.StatusCode); } [Fact] public async Task Status_错误APIKey_返回401() { var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/status"); request.Headers.Add("X-Api-Key", "wrong_key"); var resp = await _client.SendAsync(request); Assert.Equal(HttpStatusCode.Unauthorized, resp.StatusCode); } [Fact] public async Task Status_正确APIKey_返回200() { var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/status"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); Assert.Equal(HttpStatusCode.OK, resp.StatusCode); } // ===== 响应格式测试 ===== [Fact] public async Task Status_响应体为标准JSON格式() { var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/status"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); var body = await resp.Content.ReadAsStringAsync(); var json = JsonConvert.DeserializeObject>(body); Assert.NotNull(json); Assert.True(json.ContainsKey("code")); Assert.True(json.ContainsKey("message")); Assert.True(json.ContainsKey("data")); } [Fact] public async Task Status_响应data包含预期字段() { var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/status"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); var body = await resp.Content.ReadAsStringAsync(); dynamic json = JsonConvert.DeserializeObject(body); Assert.Equal(0, (int)json.code); Assert.NotNull(json.data); Assert.NotNull(json.data.isRunning); Assert.NotNull(json.data.workerCount); Assert.NotNull(json.data.workers); } // ===== 路由测试 ===== [Fact] public async Task UnknownEndpoint_返回404() { var request = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/nonexistent"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); Assert.Equal(HttpStatusCode.NotFound, resp.StatusCode); } [Fact] public async Task Refresh_正确APIKey_返回200() { var request = new HttpRequestMessage(HttpMethod.Post, $"http://localhost:{_port}/api/collector/refresh"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); Assert.Equal(HttpStatusCode.OK, resp.StatusCode); } [Fact] public async Task Stop_正确APIKey_返回200() { var request = new HttpRequestMessage(HttpMethod.Post, $"http://localhost:{_port}/api/collector/stop"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); Assert.Equal(HttpStatusCode.OK, resp.StatusCode); } [Fact] public async Task Start_正确APIKey_返回200() { var request = new HttpRequestMessage(HttpMethod.Post, $"http://localhost:{_port}/api/collector/start"); request.Headers.Add("X-Api-Key", _apiKey); var resp = await _client.SendAsync(request); Assert.Equal(HttpStatusCode.OK, resp.StatusCode); } // ===== 启停控制测试 ===== [Fact] public async Task 启停顺序_状态正确切换() { // 停止 var stopReq = new HttpRequestMessage(HttpMethod.Post, $"http://localhost:{_port}/api/collector/stop"); stopReq.Headers.Add("X-Api-Key", _apiKey); await _client.SendAsync(stopReq); // 检查状态为停止 var statusReq = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/status"); statusReq.Headers.Add("X-Api-Key", _apiKey); var statusResp = await _client.SendAsync(statusReq); var body = await statusResp.Content.ReadAsStringAsync(); dynamic json = JsonConvert.DeserializeObject(body); Assert.False((bool)json.data.isRunning); // 启动 var startReq = new HttpRequestMessage(HttpMethod.Post, $"http://localhost:{_port}/api/collector/start"); startReq.Headers.Add("X-Api-Key", _apiKey); await _client.SendAsync(startReq); // 检查状态为运行 statusReq = new HttpRequestMessage(HttpMethod.Get, $"http://localhost:{_port}/api/collector/status"); statusReq.Headers.Add("X-Api-Key", _apiKey); statusResp = await _client.SendAsync(statusReq); body = await statusResp.Content.ReadAsStringAsync(); json = JsonConvert.DeserializeObject(body); Assert.True((bool)json.data.isRunning); } } }