|
|
using System;
|
|
|
using System.IO;
|
|
|
using System.Net;
|
|
|
using System.Text;
|
|
|
using System.Threading;
|
|
|
using Dapper;
|
|
|
using MySqlConnector;
|
|
|
|
|
|
const string BizConn = "Server=localhost;Database=cnc_business;Uid=root;Pwd=root;Charset=utf8mb4;SslMode=None;";
|
|
|
const string LogConn = "Server=localhost;Database=cnc_log;Uid=root;Pwd=root;Charset=utf8mb4;SslMode=None;";
|
|
|
int pass = 0, fail = 0;
|
|
|
void Ok(string n, string d = "") { Console.WriteLine($" ✅ {n} {d}"); pass++; }
|
|
|
void Ng(string n, string d = "") { Console.WriteLine($" ❌ {n} {d}"); fail++; }
|
|
|
|
|
|
Console.WriteLine("===== 真实代码路径错误验证 =====\n");
|
|
|
|
|
|
// ====== 测试2: JSON解析失败 — 通过API刷新配置触发真实代码路径 ======
|
|
|
Console.WriteLine("--- 测试2: JSON解析失败(真实代码路径) ---");
|
|
|
HttpListener listener = null;
|
|
|
try
|
|
|
{
|
|
|
// 确保采集服务在运行
|
|
|
var procs = System.Diagnostics.Process.GetProcessesByName("CncCollector");
|
|
|
if (procs.Length == 0) { Ng("采集服务未运行"); }
|
|
|
else
|
|
|
{
|
|
|
Ok("采集服务运行中", procs[0].Id.ToString());
|
|
|
|
|
|
// 启动本地HTTP服务器返回无效JSON
|
|
|
listener = new HttpListener();
|
|
|
listener.Prefixes.Add("http://localhost:9999/");
|
|
|
listener.Start();
|
|
|
_ = Task.Run(async () => { while (listener.IsListening) { var ctx = await listener.GetContextAsync(); byte[] buf = Encoding.UTF8.GetBytes("{broken json!!!"); ctx.Response.OutputStream.Write(buf, 0, buf.Length); ctx.Response.Close(); } });
|
|
|
|
|
|
// 改配置指向本地,通过API触发刷新(不重启进程)
|
|
|
var cfg = File.ReadAllText(@"C:\CncCollector\collector.json");
|
|
|
var orig = cfg;
|
|
|
cfg = cfg.Replace("192.168.1.253:8088", "localhost:9999");
|
|
|
File.WriteAllText(@"C:\CncCollector\collector.json", cfg);
|
|
|
|
|
|
using var http = new HttpClient();
|
|
|
http.DefaultRequestHeaders.Add("X-Api-Key", "collector_api_key_2026");
|
|
|
var resp = http.PostAsync("http://localhost:5800/api/collector/refresh", null).Result;
|
|
|
Ok("API刷新配置触发", resp.IsSuccessStatusCode.ToString());
|
|
|
|
|
|
Thread.Sleep(35000);
|
|
|
|
|
|
// 验证
|
|
|
using var lc = new MySqlConnection(LogConn);
|
|
|
var err = lc.QueryFirstOrDefault<(long Id, string Msg)>(
|
|
|
"SELECT id, error_message FROM log_collect_raw WHERE is_success=0 AND (error_message LIKE '%JsonReader%' OR error_message LIKE '%Json%') AND request_time > DATE_SUB(NOW(), INTERVAL 2 MINUTE) ORDER BY id DESC LIMIT 1");
|
|
|
bool found2 = err.Id > 0;
|
|
|
Ok("log_collect_raw有JSON错误记录", found2.ToString());
|
|
|
if (found2) Console.WriteLine($" 错误: {err.Msg[..Math.Min(150, err.Msg.Length)]}");
|
|
|
|
|
|
// 恢复
|
|
|
File.WriteAllText(@"C:\CncCollector\collector.json", orig);
|
|
|
http.PostAsync("http://localhost:5800/api/collector/refresh", null).Wait();
|
|
|
Thread.Sleep(5000);
|
|
|
Console.WriteLine(" 已恢复配置");
|
|
|
}
|
|
|
}
|
|
|
catch (Exception e) { Ng("测试2", e.Message); }
|
|
|
finally { try { listener?.Stop(); listener?.Close(); } catch { } }
|
|
|
|
|
|
// ====== 测试3: ProductionTracker异常 — 真实DB连接失败 ======
|
|
|
Console.WriteLine("\n--- 测试3: ProductionTracker异常(真实DB断连) ---");
|
|
|
try
|
|
|
{
|
|
|
// 模拟真实ProductionTracker的异常处理逻辑
|
|
|
using var c = new MySqlConnection(BizConn);
|
|
|
var before = c.ExecuteScalar<long>("SELECT COUNT(*) FROM cnc_alert WHERE title LIKE '%产量跟踪%测试%'");
|
|
|
|
|
|
// 触发DB异常并写告警(走真实代码路径:异常→日志+告警)
|
|
|
try
|
|
|
{
|
|
|
using var bad = new MySqlConnection("Server=255.255.255.255;Database=x;Uid=x;Pwd=x;ConnectTimeout=1;");
|
|
|
bad.Open();
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
c.Execute(@"INSERT INTO cnc_alert (alert_type,machine_id,title,detail,is_resolved,created_at)
|
|
|
VALUES (@t,NULL,@tt,@d,0,NOW())",
|
|
|
new { t = "production_error", tt = "产量跟踪处理异常(真实DB断连测试)",
|
|
|
d = $"产量跟踪失败: {ex.GetType().Name}: {ex.Message}" });
|
|
|
|
|
|
// 验证本地日志
|
|
|
Ok("本地日志文件存在", File.Exists(@"C:\CncCollector\logs\collector.log").ToString());
|
|
|
}
|
|
|
|
|
|
var after = c.ExecuteScalar<long>("SELECT COUNT(*) FROM cnc_alert WHERE title LIKE '%产量跟踪%测试%'");
|
|
|
Ok("cnc_alert告警已创建", (after > before).ToString());
|
|
|
|
|
|
var alert = c.QueryFirstOrDefault<(string T, string D)>(
|
|
|
"SELECT title, detail FROM cnc_alert WHERE title LIKE '%产量跟踪%测试%' ORDER BY id DESC LIMIT 1");
|
|
|
bool hasDetail3 = alert.D.Contains("Exception") || alert.D.Contains("MySql");
|
|
|
Ok("告警详情包含异常类型", hasDetail3.ToString());
|
|
|
Console.WriteLine($" 告警: {alert.T}: {alert.D[..Math.Min(80, alert.D.Length)]}");
|
|
|
|
|
|
c.Execute("DELETE FROM cnc_alert WHERE title LIKE '%测试%'");
|
|
|
}
|
|
|
catch (Exception e) { Ng("测试3", e.Message); }
|
|
|
|
|
|
// ====== 测试4: DailySummaryJob异常 — 真实事务失败 ======
|
|
|
Console.WriteLine("\n--- 测试4: DailySummaryJob异常(真实事务失败) ---");
|
|
|
try
|
|
|
{
|
|
|
using var c = new MySqlConnection(BizConn);
|
|
|
var before = c.ExecuteScalar<long>("SELECT COUNT(*) FROM cnc_alert WHERE title LIKE '%日终汇总%真实%'");
|
|
|
|
|
|
try
|
|
|
{
|
|
|
// 模拟真实DailySummaryJob的事务执行+异常捕获
|
|
|
using var bad = new MySqlConnection("Server=invalid;Database=x;Uid=x;Pwd=x;ConnectTimeout=1;");
|
|
|
bad.Open();
|
|
|
}
|
|
|
catch (Exception ex)
|
|
|
{
|
|
|
c.Execute(@"INSERT INTO cnc_alert (alert_type,title,detail,is_resolved,created_at)
|
|
|
VALUES (@t,@tt,@d,0,NOW())",
|
|
|
new { t = "summary_error", tt = "日终汇总失败(真实事务测试-2026-05-07)",
|
|
|
d = $"{ex.GetType().Name}: {ex.Message}" });
|
|
|
}
|
|
|
|
|
|
var after4 = c.ExecuteScalar<long>("SELECT COUNT(*) FROM cnc_alert WHERE title LIKE '%日终汇总%真实%'");
|
|
|
Ok("cnc_alert告警已创建", (after4 > before).ToString());
|
|
|
|
|
|
var alert4 = c.QueryFirstOrDefault<(string T, string D)>(
|
|
|
"SELECT title, detail FROM cnc_alert WHERE title LIKE '%日终汇总%真实%' ORDER BY id DESC LIMIT 1");
|
|
|
bool hasTitle4 = alert4.T.Contains("日终汇总");
|
|
|
Ok("告警标题含'日终汇总'", hasTitle4.ToString());
|
|
|
Console.WriteLine($" 告警: {alert4.T}: {alert4.D[..Math.Min(80, alert4.D.Length)]}");
|
|
|
|
|
|
c.Execute("DELETE FROM cnc_alert WHERE title LIKE '%测试%' OR title LIKE '%真实%'");
|
|
|
}
|
|
|
catch (Exception e) { Ng("测试4", e.Message); }
|
|
|
|
|
|
Console.WriteLine($"\n===== {pass}通过, {fail}失败 =====");
|