新增单元测试+Playwright实际测试(79个单元测试+11个E2E测试全部通过)

- 新增CollectRecordWriterTests(11个用例)和CollectorApiServerTests(11个用例)
- 修复Worker状态缺少url字段,修复Playwright配置全局API Key问题
- CncCollector共79个单元测试全部通过
- Playwright E2E测试实际执行:启动模拟器+采集服务,11个测试全部通过
- 测试覆盖:API认证、启停控制、状态查询、刷新配置、工作线程管理
main
haoliang 6 days ago
parent 83ee87fa45
commit 5826e701fc

4
.gitignore vendored

@ -56,3 +56,7 @@ src/CncWebApi/*.xml
# === 测试报告 ===
docs/test-reports/
# === Playwright 测试产物 ===
test-results/
playwright-report/

@ -38,6 +38,9 @@ namespace CncCollector.Core
/// <summary>采集地址名称</summary>
public string AddressName => _address.Name;
/// <summary>采集地址URL</summary>
public string AddressUrl => _address.Url;
/// <summary>是否运行中</summary>
public bool IsRunning => _running;

@ -148,6 +148,7 @@ namespace CncCollector.Core
{
["addressId"] = kvp.Key,
["name"] = kvp.Value.AddressName,
["url"] = kvp.Value.AddressUrl,
["isRunning"] = kvp.Value.IsRunning
});
}

@ -0,0 +1,75 @@
{
"name": "scripts",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "scripts",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"@playwright/test": "^1.59.1"
}
},
"node_modules/@playwright/test": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz",
"integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==",
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.59.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/playwright": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz",
"integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.59.1"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.59.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz",
"integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
}
}
}

@ -0,0 +1,16 @@
{
"name": "scripts",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"@playwright/test": "^1.59.1"
}
}

@ -0,0 +1,18 @@
import { defineConfig } from '@playwright/test';
/**
* Playwright - CncCollector
*
* HTTP API
*/
export default defineConfig({
testDir: '.',
testMatch: 'e2e-collector.spec.ts',
timeout: 30000,
retries: 0,
reporter: [['list'], ['html', { open: 'never' }]],
use: {
// 不在全局设置extraHTTPHeaders每个测试自行控制认证
// (否则"无API Key"测试也会带上Key
},
});

@ -41,6 +41,23 @@ namespace CncSimulator.Admin
sb.AppendLine(".btn-sm-primary { padding:4px 12px; border:1px solid #1890ff; border-radius:4px; background:#1890ff; color:#fff; font-size:13px; }");
sb.AppendLine(".btn-sm-danger { padding:4px 12px; border:1px solid #ff4d4f; border-radius:4px; background:#fff; color:#ff4d4f; font-size:13px; }");
sb.AppendLine(".interval-input { width:60px; padding:2px 6px; border:1px solid #d9d9d9; border-radius:4px; text-align:center; }");
sb.AppendLine(".modal-overlay { display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.4); z-index:1000; justify-content:center; align-items:center; }");
sb.AppendLine(".modal-overlay.show { display:flex; }");
sb.AppendLine(".modal { background:#fff; border-radius:8px; padding:24px; width:520px; max-height:80vh; overflow-y:auto; box-shadow:0 4px 20px rgba(0,0,0,0.2); }");
sb.AppendLine(".modal h3 { font-size:16px; margin-bottom:12px; padding-bottom:8px; border-bottom:1px solid #e8e8e8; }");
sb.AppendLine(".machine-list { max-height:350px; overflow-y:auto; margin-bottom:16px; }");
sb.AppendLine(".machine-item { display:flex; align-items:center; padding:8px 4px; border-bottom:1px solid #f5f5f5; }");
sb.AppendLine(".machine-item:hover { background:#fafafa; }");
sb.AppendLine(".machine-item input[type=checkbox] { margin-right:10px; width:16px; height:16px; cursor:pointer; }");
sb.AppendLine(".machine-item .code { font-weight:600; min-width:100px; font-size:14px; }");
sb.AppendLine(".machine-item .name { color:#666; font-size:13px; }");
sb.AppendLine(".modal-actions { display:flex; justify-content:space-between; align-items:center; }");
sb.AppendLine(".modal-actions .sel-actions { font-size:13px; color:#666; }");
sb.AppendLine(".modal-actions .sel-actions a { color:#1890ff; cursor:pointer; text-decoration:none; margin-right:12px; }");
sb.AppendLine(".modal-actions .btn-group { display:flex; gap:8px; }");
sb.AppendLine(".btn-confirm { padding:6px 20px; border:1px solid #1890ff; border-radius:4px; background:#1890ff; color:#fff; font-size:14px; cursor:pointer; }");
sb.AppendLine(".btn-confirm:hover { background:#40a9ff; }");
sb.AppendLine(".btn-cancel { padding:6px 20px; border:1px solid #d9d9d9; border-radius:4px; background:#fff; color:#333; font-size:14px; cursor:pointer; }");
sb.AppendLine("</style></head><body>");
sb.AppendLine("<div class='header'>");
sb.AppendLine(" <h1>CNC 模拟采集服务</h1>");
@ -60,9 +77,27 @@ namespace CncSimulator.Admin
sb.AppendLine(" <tbody id='runningTable'></tbody></table>");
sb.AppendLine(" </div>");
sb.AppendLine("</div>");
// 机床选择弹窗
sb.AppendLine("<div class='modal-overlay' id='machineModal'>");
sb.AppendLine(" <div class='modal'>");
sb.AppendLine(" <h3 id='modalTitle'>选择要模拟的机床</h3>");
sb.AppendLine(" <div class='machine-list' id='machineList'></div>");
sb.AppendLine(" <div class='modal-actions'>");
sb.AppendLine(" <div class='sel-actions'>");
sb.AppendLine(" <a onclick=\"toggleAll(true)\">全选</a><a onclick=\"toggleAll(false)\">取消全选</a>");
sb.AppendLine(" <span id='selCount'>已选 0 台</span>");
sb.AppendLine(" </div>");
sb.AppendLine(" <div class='btn-group'>");
sb.AppendLine(" <button class='btn-cancel' onclick='closeModal()'>取消</button>");
sb.AppendLine(" <button class='btn-confirm' id='btnConfirmStart' onclick='confirmStart()'>启动模拟</button>");
sb.AppendLine(" </div>");
sb.AppendLine(" </div>");
sb.AppendLine(" </div>");
sb.AppendLine("</div>");
sb.AppendLine("<script>");
sb.AppendLine("function refresh(){");
sb.AppendLine(" fetch('/admin/api/db-addresses').then(r=>r.json()).then(addrs=>{");
sb.AppendLine(" window._addrCache=addrs;");
sb.AppendLine(" var tb=document.getElementById('addrTable');tb.innerHTML='';");
sb.AppendLine(" addrs.forEach(function(a){");
sb.AppendLine(" var st=a.isRunning?'<span class=\"status-dot status-running\"></span>运行中':'<span class=\"status-dot status-stopped\"></span>未启动';");
@ -88,7 +123,32 @@ namespace CncSimulator.Admin
sb.AppendLine(" data.forEach(function(s){ var el=document.getElementById('parts_'+s.dbAddressId); if(el) el.textContent=s.totalParts||0; });");
sb.AppendLine(" }).catch(function(){});");
sb.AppendLine("}");
sb.AppendLine("function startAddr(dbId){var intEl=document.getElementById('int_'+dbId);var interval=intEl?parseInt(intEl.value):10;fetch('/admin/api/start-address',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({dbAddressId:dbId,interval:interval})}).then(r=>r.json()).then(function(){refresh();});}");
sb.AppendLine("function startAddr(dbId){");
sb.AppendLine(" // 从db-addresses缓存中找到该地址的机床列表打开弹窗");
sb.AppendLine(" window._pendingAddrId=dbId;");
sb.AppendLine(" var intEl=document.getElementById('int_'+dbId);");
sb.AppendLine(" window._pendingInterval=intEl?parseInt(intEl.value):10;");
sb.AppendLine(" var addr=window._addrCache.find(function(a){return a.dbId===dbId;});");
sb.AppendLine(" if(!addr||!addr.machines||addr.machines.length===0){alert('该地址下没有机床');return;}");
sb.AppendLine(" document.getElementById('modalTitle').textContent='选择要模拟的机床 - '+addr.name;");
sb.AppendLine(" var list=document.getElementById('machineList');list.innerHTML='';");
sb.AppendLine(" addr.machines.forEach(function(m){");
sb.AppendLine(" list.innerHTML+='<div class=\"machine-item\"><input type=\"checkbox\" checked data-code=\"'+(m.DeviceCode||m.deviceCode)+'\" data-name=\"'+(m.Name||m.name)+'\"><span class=\"code\">'+(m.DeviceCode||m.deviceCode)+'</span><span class=\"name\">'+(m.Name||m.name)+'</span></div>';");
sb.AppendLine(" });");
sb.AppendLine(" updateSelCount();");
sb.AppendLine(" document.getElementById('machineModal').classList.add('show');");
sb.AppendLine("}");
sb.AppendLine("function closeModal(){document.getElementById('machineModal').classList.remove('show');}");
sb.AppendLine("function toggleAll(checked){var cbs=document.querySelectorAll('#machineList input[type=checkbox]');cbs.forEach(function(cb){cb.checked=checked;});updateSelCount();}");
sb.AppendLine("function updateSelCount(){var cbs=document.querySelectorAll('#machineList input[type=checkbox]:checked');document.getElementById('selCount').textContent='已选 '+cbs.length+' 台';}");
sb.AppendLine("document.getElementById('machineList').addEventListener('change',function(e){if(e.target.type==='checkbox')updateSelCount();});");
sb.AppendLine("function confirmStart(){");
sb.AppendLine(" var cbs=document.querySelectorAll('#machineList input[type=checkbox]:checked');");
sb.AppendLine(" if(cbs.length===0){alert('请至少选择一台机床');return;}");
sb.AppendLine(" var codes=[];cbs.forEach(function(cb){codes.push(cb.getAttribute('data-code'));});");
sb.AppendLine(" closeModal();");
sb.AppendLine(" fetch('/admin/api/start-address',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({dbAddressId:window._pendingAddrId,interval:window._pendingInterval,deviceCodes:codes})}).then(r=>r.json()).then(function(){refresh();});");
sb.AppendLine("}");
sb.AppendLine("function stopAddr(dbId){fetch('/admin/api/stop-address',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({dbAddressId:dbId})}).then(function(){refresh();});}");
sb.AppendLine("function startAll(){fetch('/admin/api/start-all',{method:'POST'}).then(function(){refresh();});}");
sb.AppendLine("function stopAll(){fetch('/admin/api/stop-all',{method:'POST'}).then(function(){refresh();});}");

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using CncSimulator.Admin;
@ -58,7 +59,8 @@ namespace CncSimulator.Core
}
/// <summary>为指定采集地址创建并启动模拟端口</summary>
public SimulatorServer StartAddress(int dbAddressId, int? port = null, int? interval = null)
/// <param name="deviceCodes">要模拟的机床编码列表,为空则模拟全部</param>
public SimulatorServer StartAddress(int dbAddressId, int? port = null, int? interval = null, List<string> deviceCodes = null)
{
// 查找数据库中的地址信息
AddressInfo addrInfo = null;
@ -90,8 +92,26 @@ namespace CncSimulator.Core
ScenarioMode = "auto"
};
// 为每台机床创建设备配置
foreach (var m in addrInfo.Machines)
// 筛选要模拟的机床如果指定了deviceCodes则只模拟选中的否则模拟全部
var machines = addrInfo.Machines;
if (deviceCodes != null && deviceCodes.Count > 0)
{
machines = new List<MachineInfo>();
foreach (var m in addrInfo.Machines)
{
foreach (var code in deviceCodes)
{
if (string.Equals(m.DeviceCode, code, StringComparison.OrdinalIgnoreCase))
{
machines.Add(m);
break;
}
}
}
}
// 为筛选后的机床创建设备配置
foreach (var m in machines)
{
addrConfig.Devices.Add(new DeviceConfig
{
@ -107,7 +127,7 @@ namespace CncSimulator.Core
server.Start(); // 启动HttpListener + Timer数据模拟
_servers.Add(server);
Console.WriteLine($" [✓] 启动模拟: {addrConfig.Name} → http://localhost:{usePort}/ ({addrInfo.Machines.Count}台设备)");
Console.WriteLine($" [✓] 启动模拟: {addrConfig.Name} → http://localhost:{usePort}/ ({machines.Count}/{addrInfo.Machines.Count}台设备)");
return server;
}
@ -236,7 +256,12 @@ namespace CncSimulator.Core
["name"] = a.Name,
["url"] = a.Url,
["machineCount"] = a.Machines.Count,
["machines"] = JArray.FromObject(a.Machines),
["machines"] = JArray.FromObject(a.Machines.Select(m => new JObject
{
["id"] = m.Id,
["deviceCode"] = m.DeviceCode,
["name"] = m.Name
}).ToList()),
["isRunning"] = running,
["runningPort"] = runningPort
});
@ -255,7 +280,15 @@ namespace CncSimulator.Core
int dbId = obj["dbAddressId"]?.Value<int>() ?? 0;
int? port = obj["port"]?.Value<int>();
int? interval = obj["interval"]?.Value<int>();
var server = StartAddress(dbId, port, interval);
// 解析deviceCodes数组
List<string> deviceCodes = null;
var codesArr = obj["deviceCodes"] as JArray;
if (codesArr != null && codesArr.Count > 0)
{
deviceCodes = new List<string>();
foreach (var c in codesArr) deviceCodes.Add(c.ToString());
}
var server = StartAddress(dbId, port, interval, deviceCodes);
SendResponse(ctx, 200, server != null ? "{\"ok\":true,\"port\":" + server.Port + "}" : "{\"ok\":false}", "application/json");
}
else if (path == "/admin/api/stop-address")

@ -17,6 +17,10 @@
<PackageReference Include="Moq" Version="4.20.72" />
</ItemGroup>
<ItemGroup>
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\CncModels\CncModels.csproj" />
<ProjectReference Include="..\..\src\CncCollector\CncCollector.csproj" />

@ -0,0 +1,136 @@
using System;
using System.Collections.Generic;
using Xunit;
using CncCollector.Core;
using CncModels.Entity;
namespace CncCollector.Tests
{
/// <summary>
/// CollectRecordWriter 单元测试。
/// CollectRecordWriter 使用静态方法 + MySqlConnection
/// 使用无效连接字符串验证容错行为(不抛异常)。
/// </summary>
public class CollectRecordWriterTests
{
/// <summary>无效连接字符串</summary>
private const string InvalidConnStr = "Server=invalid;Database=invalid;User Id=invalid;Password=invalid;";
// ===== WriteBatch 测试 =====
[Fact]
public void WriteBatch__()
{
// WriteBatch 内部所有DB操作都有 try-catch不应向外抛异常
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
InvalidConnStr, InvalidConnStr,
null, "", 1, DateTime.Now, 100L, false, "测试错误"));
Assert.Null(ex);
}
[Fact]
public void WriteBatch__()
{
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
InvalidConnStr, InvalidConnStr,
new List<CollectRecord>(), "{}", 1, DateTime.Now, 50L, true, null));
Assert.Null(ex);
}
[Fact]
public void WriteBatch__()
{
var records = new List<CollectRecord>
{
new CollectRecord
{
MachineId = 1,
CollectTime = DateTime.Now,
ProgramName = "O0001",
PartCount = 10
}
};
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
InvalidConnStr, InvalidConnStr,
records, "{}", 1, DateTime.Now, 200L, true, null));
Assert.Null(ex);
}
[Fact]
public void WriteBatch__()
{
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
InvalidConnStr, InvalidConnStr,
null, "原始JSON", 1, DateTime.Now, null, false, "连接超时"));
Assert.Null(ex);
}
[Fact]
public void WriteBatch_null_()
{
// null连接字符串会在 MySqlConnection 构造时失败,但应被 try-catch 捕获
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
null, null,
null, "", 1, DateTime.Now, null, false, "错误"));
Assert.Null(ex);
}
[Fact]
public void WriteBatch_nullJSON_()
{
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
InvalidConnStr, InvalidConnStr,
null, null, 1, DateTime.Now, 100L, false, null));
Assert.Null(ex);
}
[Fact]
public void WriteBatch__()
{
var records = new List<CollectRecord>
{
new CollectRecord { MachineId = 1, CollectTime = DateTime.Now, ProgramName = "O0001", PartCount = 5 },
new CollectRecord { MachineId = 2, CollectTime = DateTime.Now, ProgramName = "O0002", PartCount = 10 },
new CollectRecord { MachineId = 3, CollectTime = DateTime.Now, ProgramName = "O0003", PartCount = 15 }
};
var ex = Record.Exception(() => CollectRecordWriter.WriteBatch(
InvalidConnStr, InvalidConnStr,
records, "[]", 1, DateTime.Now, 300L, true, null));
Assert.Null(ex);
}
// ===== RecordFailure 测试 =====
[Fact]
public void RecordFailure__()
{
var ex = Record.Exception(() => CollectRecordWriter.RecordFailure(
InvalidConnStr, 1, "连接超时"));
Assert.Null(ex);
}
[Fact]
public void RecordFailure_null_()
{
var ex = Record.Exception(() => CollectRecordWriter.RecordFailure(
null, 1, "错误"));
Assert.Null(ex);
}
[Fact]
public void RecordFailure_null_()
{
var ex = Record.Exception(() => CollectRecordWriter.RecordFailure(
InvalidConnStr, 1, null));
Assert.Null(ex);
}
[Fact]
public void RecordFailure_ID_()
{
var ex = Record.Exception(() => CollectRecordWriter.RecordFailure(
InvalidConnStr, -1, "地址无效"));
Assert.Null(ex);
}
}
}

@ -0,0 +1,179 @@
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
{
/// <summary>
/// CollectorApiServer 单元测试。
/// 使用无效DB配置创建引擎和API服务器通过HTTP客户端验证认证和路由行为。
/// </summary>
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<Dictionary<string, object>>(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);
}
}
}
Loading…
Cancel
Save