diff --git a/Tests/IsapiDeleteSuccessTests.cs b/Tests/IsapiDeleteSuccessTests.cs
new file mode 100644
index 0000000..0527260
--- /dev/null
+++ b/Tests/IsapiDeleteSuccessTests.cs
@@ -0,0 +1,89 @@
+using Xunit;
+
+namespace HikvisionAttendanceService.Tests;
+
+///
+/// Covers delete-success parsing rules used by USER_DELETE confirmation
+/// (regex-based mirror so the test project does not need System.Web.Extensions).
+///
+public class IsapiDeleteSuccessTests
+{
+ [Theory]
+ [InlineData("{\"statusCode\":1,\"statusString\":\"OK\",\"subStatusCode\":\"ok\"}", 200, true)]
+ [InlineData("{\n \"statusCode\": 1,\n \"statusString\": \"OK\",\n \"subStatusCode\": \"ok\"\n}", 200, true)]
+ [InlineData("{\"statusCode\":\"1\",\"statusString\":\"OK\",\"subStatusCode\":\"ok\"}", 200, true)]
+ [InlineData("{ \"statusCode\" : 1 , \"statusString\" : \"OK\" }", 200, true)]
+ [InlineData("{\"statusCode\":1,\"subStatusCode\":\"ok\"}", 200, true)]
+ [InlineData("{\"statusCode\":1,\"statusString\":\"OK\"}", 200, true)]
+ [InlineData("{\"statusCode\":1}", 200, false)]
+ [InlineData("{\"statusCode\":4,\"statusString\":\"Invalid Operation\",\"subStatusCode\":\"employeeNoNotExist\"}", 200, false)]
+ [InlineData("{\"statusCode\":1,\"statusString\":\"OK\",\"subStatusCode\":\"ok\"}", 400, false)]
+ public void Confirmed_delete_success_rules(string body, int httpStatus, bool expected)
+ {
+ Assert.Equal(expected, IsConfirmed(body, httpStatus));
+ }
+
+ [Fact]
+ public void Failure_reason_maps_employee_not_found()
+ {
+ Assert.Equal(
+ "Employee was not found on the device",
+ Describe(200, 4, "Invalid Operation", "employeeNoNotExist"));
+ }
+
+ private static bool IsConfirmed(string? body, int httpStatus)
+ {
+ if (httpStatus < 200 || httpStatus >= 300)
+ return false;
+ if (!TryParse(body, out var statusCode, out var statusString, out var subStatusCode))
+ return false;
+ if (statusCode != 1)
+ return false;
+ return string.Equals(statusString, "OK", System.StringComparison.OrdinalIgnoreCase)
+ || string.Equals(subStatusCode, "ok", System.StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static bool TryParse(string? body, out int statusCode, out string statusString, out string subStatusCode)
+ {
+ statusCode = 0;
+ statusString = "";
+ subStatusCode = "";
+ if (string.IsNullOrWhiteSpace(body))
+ return false;
+
+ var mCode = System.Text.RegularExpressions.Regex.Match(body,
+ "\"statusCode\"\\s*:\\s*\"?(?-?\\d+)\"?",
+ System.Text.RegularExpressions.RegexOptions.IgnoreCase);
+ if (mCode.Success)
+ int.TryParse(mCode.Groups["v"].Value, out statusCode);
+
+ var mStr = System.Text.RegularExpressions.Regex.Match(body,
+ "\"statusString\"\\s*:\\s*\"(?[^\"]*)\"",
+ System.Text.RegularExpressions.RegexOptions.IgnoreCase);
+ if (mStr.Success) statusString = mStr.Groups["v"].Value;
+
+ var mSub = System.Text.RegularExpressions.Regex.Match(body,
+ "\"subStatusCode\"\\s*:\\s*\"(?[^\"]*)\"",
+ System.Text.RegularExpressions.RegexOptions.IgnoreCase);
+ if (mSub.Success) subStatusCode = mSub.Groups["v"].Value;
+
+ return statusCode != 0 || statusString.Length > 0 || subStatusCode.Length > 0;
+ }
+
+ private static string Describe(int httpStatus, int statusCode, string statusString, string subStatusCode)
+ {
+ if (httpStatus < 200 || httpStatus >= 300)
+ return "HTTP " + httpStatus;
+ var sub = (subStatusCode ?? "").Trim();
+ if (sub.IndexOf("notExist", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
+ sub.IndexOf("notFound", System.StringComparison.OrdinalIgnoreCase) >= 0 ||
+ sub.Equals("employeeNoNotExist", System.StringComparison.OrdinalIgnoreCase))
+ return "Employee was not found on the device";
+ if (!string.IsNullOrWhiteSpace(statusString) &&
+ !string.Equals(statusString, "OK", System.StringComparison.OrdinalIgnoreCase))
+ return statusString.Trim();
+ if (statusCode != 0 && statusCode != 1)
+ return "Hikvision statusCode=" + statusCode;
+ return "device delete rejected";
+ }
+}