feat: add audit-verified status updates with override tracking
Status updates (feature/test update and batch-update) now verify the requested status against Roslyn audit classification. Mismatches require --override "reason" to force. Overrides are logged to a new status_overrides table and reviewable via 'override list' command.
This commit is contained in:
@@ -88,6 +88,17 @@ CREATE TABLE IF NOT EXISTS library_mappings (
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS status_overrides (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
table_name TEXT NOT NULL CHECK (table_name IN ('features', 'unit_tests')),
|
||||
item_id INTEGER NOT NULL,
|
||||
audit_status TEXT NOT NULL,
|
||||
audit_reason TEXT NOT NULL,
|
||||
requested_status TEXT NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- Indexes
|
||||
CREATE INDEX IF NOT EXISTS idx_features_module ON features(module_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_features_status ON features(status);
|
||||
|
||||
BIN
porting.db
BIN
porting.db
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
# NATS .NET Porting Status Report
|
||||
|
||||
Generated: 2026-02-27 10:46:13 UTC
|
||||
Generated: 2026-02-27 10:50:16 UTC
|
||||
|
||||
## Modules (12 total)
|
||||
|
||||
@@ -12,10 +12,10 @@ Generated: 2026-02-27 10:46:13 UTC
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| deferred | 2500 |
|
||||
| deferred | 2501 |
|
||||
| n_a | 18 |
|
||||
| stub | 168 |
|
||||
| verified | 987 |
|
||||
| verified | 986 |
|
||||
|
||||
## Unit Tests (3257 total)
|
||||
|
||||
@@ -35,4 +35,4 @@ Generated: 2026-02-27 10:46:13 UTC
|
||||
|
||||
## Overall Progress
|
||||
|
||||
**1594/6942 items complete (23.0%)**
|
||||
**1593/6942 items complete (22.9%)**
|
||||
|
||||
38
reports/report_3297334.md
Normal file
38
reports/report_3297334.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# NATS .NET Porting Status Report
|
||||
|
||||
Generated: 2026-02-27 10:50:16 UTC
|
||||
|
||||
## Modules (12 total)
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| verified | 12 |
|
||||
|
||||
## Features (3673 total)
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| deferred | 2501 |
|
||||
| n_a | 18 |
|
||||
| stub | 168 |
|
||||
| verified | 986 |
|
||||
|
||||
## Unit Tests (3257 total)
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| deferred | 2662 |
|
||||
| n_a | 187 |
|
||||
| stub | 18 |
|
||||
| verified | 390 |
|
||||
|
||||
## Library Mappings (36 total)
|
||||
|
||||
| Status | Count |
|
||||
|--------|-------|
|
||||
| mapped | 36 |
|
||||
|
||||
|
||||
## Overall Progress
|
||||
|
||||
**1593/6942 items complete (22.9%)**
|
||||
136
tools/NatsNet.PortTracker/Audit/AuditVerifier.cs
Normal file
136
tools/NatsNet.PortTracker/Audit/AuditVerifier.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
namespace NatsNet.PortTracker.Audit;
|
||||
|
||||
using NatsNet.PortTracker.Data;
|
||||
|
||||
/// <summary>
|
||||
/// Verifies status updates against audit classification results.
|
||||
/// Used by feature and test update commands to ensure status accuracy.
|
||||
/// </summary>
|
||||
public static class AuditVerifier
|
||||
{
|
||||
public record VerificationResult(
|
||||
long ItemId,
|
||||
string AuditStatus,
|
||||
string AuditReason,
|
||||
bool Matches);
|
||||
|
||||
private static readonly Dictionary<string, string> DefaultSourcePaths = new()
|
||||
{
|
||||
["features"] = Path.Combine(Directory.GetCurrentDirectory(), "dotnet", "src", "ZB.MOM.NatsNet.Server"),
|
||||
["unit_tests"] = Path.Combine(Directory.GetCurrentDirectory(), "dotnet", "tests", "ZB.MOM.NatsNet.Server.Tests")
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Build a SourceIndexer for the appropriate table type.
|
||||
/// </summary>
|
||||
public static SourceIndexer BuildIndexer(string tableName)
|
||||
{
|
||||
var sourcePath = DefaultSourcePaths[tableName];
|
||||
if (!Directory.Exists(sourcePath))
|
||||
throw new DirectoryNotFoundException($"Source directory not found: {sourcePath}");
|
||||
|
||||
Console.WriteLine($"Building audit index from {sourcePath}...");
|
||||
var indexer = new SourceIndexer();
|
||||
indexer.IndexDirectory(sourcePath);
|
||||
Console.WriteLine($"Indexed {indexer.FilesIndexed} files, {indexer.MethodsIndexed} methods/properties.");
|
||||
return indexer;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verify items matching a WHERE clause against audit classification.
|
||||
/// </summary>
|
||||
public static List<VerificationResult> VerifyItems(
|
||||
Database db, SourceIndexer indexer, string tableName,
|
||||
string whereClause, List<(string, object?)> parameters, string requestedStatus)
|
||||
{
|
||||
var sql = $"SELECT id, dotnet_class, dotnet_method, go_file, go_method FROM {tableName}{whereClause} ORDER BY id";
|
||||
var rows = db.Query(sql, parameters.ToArray());
|
||||
|
||||
var classifier = new FeatureClassifier(indexer);
|
||||
var results = new List<VerificationResult>();
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var record = new FeatureClassifier.FeatureRecord(
|
||||
Id: Convert.ToInt64(row["id"]),
|
||||
DotnetClass: row["dotnet_class"]?.ToString() ?? "",
|
||||
DotnetMethod: row["dotnet_method"]?.ToString() ?? "",
|
||||
GoFile: row["go_file"]?.ToString() ?? "",
|
||||
GoMethod: row["go_method"]?.ToString() ?? "");
|
||||
|
||||
var classification = classifier.Classify(record);
|
||||
var matches = classification.Status == requestedStatus;
|
||||
results.Add(new VerificationResult(record.Id, classification.Status, classification.Reason, matches));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check verification results and print a report.
|
||||
/// Returns true if the update should proceed.
|
||||
/// </summary>
|
||||
public static bool CheckAndReport(
|
||||
List<VerificationResult> results, string requestedStatus, string? overrideComment)
|
||||
{
|
||||
var matches = results.Where(r => r.Matches).ToList();
|
||||
var mismatches = results.Where(r => !r.Matches).ToList();
|
||||
|
||||
Console.WriteLine($"\nAudit verification: {matches.Count} match, {mismatches.Count} mismatch");
|
||||
|
||||
if (mismatches.Count == 0)
|
||||
return true;
|
||||
|
||||
Console.WriteLine($"\nMismatches (requested '{requestedStatus}'):");
|
||||
foreach (var m in mismatches.Take(20))
|
||||
Console.WriteLine($" ID {m.ItemId}: audit says '{m.AuditStatus}' ({m.AuditReason})");
|
||||
if (mismatches.Count > 20)
|
||||
Console.WriteLine($" ... and {mismatches.Count - 20} more");
|
||||
|
||||
if (overrideComment is null)
|
||||
{
|
||||
Console.WriteLine($"\n{mismatches.Count} items have audit mismatches. Use --override \"reason\" to force.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Console.WriteLine($"\nOverride applied: \"{overrideComment}\"");
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log override records to the status_overrides table.
|
||||
/// </summary>
|
||||
public static void LogOverrides(
|
||||
Database db, string tableName, IEnumerable<VerificationResult> mismatches,
|
||||
string requestedStatus, string comment)
|
||||
{
|
||||
var mismatchList = mismatches.ToList();
|
||||
if (mismatchList.Count == 0) return;
|
||||
|
||||
using var transaction = db.Connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
foreach (var mismatch in mismatchList)
|
||||
{
|
||||
using var cmd = db.CreateCommand(
|
||||
"INSERT INTO status_overrides (table_name, item_id, audit_status, audit_reason, requested_status, comment) " +
|
||||
"VALUES (@table, @item, @auditStatus, @auditReason, @requestedStatus, @comment)");
|
||||
cmd.Parameters.AddWithValue("@table", tableName);
|
||||
cmd.Parameters.AddWithValue("@item", mismatch.ItemId);
|
||||
cmd.Parameters.AddWithValue("@auditStatus", mismatch.AuditStatus);
|
||||
cmd.Parameters.AddWithValue("@auditReason", mismatch.AuditReason);
|
||||
cmd.Parameters.AddWithValue("@requestedStatus", requestedStatus);
|
||||
cmd.Parameters.AddWithValue("@comment", comment);
|
||||
cmd.Transaction = transaction;
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
transaction.Commit();
|
||||
Console.WriteLine($"Logged {mismatchList.Count} override(s) to status_overrides table.");
|
||||
}
|
||||
catch
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.CommandLine;
|
||||
using NatsNet.PortTracker.Audit;
|
||||
using NatsNet.PortTracker.Data;
|
||||
|
||||
namespace NatsNet.PortTracker.Commands;
|
||||
@@ -96,31 +97,59 @@ public static class FeatureCommands
|
||||
var updateId = new Argument<int>("id") { Description = "Feature ID (use 0 with --all-in-module)" };
|
||||
var updateStatus = new Option<string>("--status") { Description = "New status", Required = true };
|
||||
var updateAllInModule = new Option<int?>("--all-in-module") { Description = "Update all features in this module ID" };
|
||||
var updateCmd = new Command("update", "Update feature status");
|
||||
var updateOverride = new Option<string?>("--override") { Description = "Override audit mismatch with this comment" };
|
||||
var updateCmd = new Command("update", "Update feature status (audit-verified)");
|
||||
updateCmd.Add(updateId);
|
||||
updateCmd.Add(updateStatus);
|
||||
updateCmd.Add(updateAllInModule);
|
||||
updateCmd.Add(updateOverride);
|
||||
updateCmd.SetAction(parseResult =>
|
||||
{
|
||||
var dbPath = parseResult.GetValue(dbOption)!;
|
||||
var id = parseResult.GetValue(updateId);
|
||||
var status = parseResult.GetValue(updateStatus)!;
|
||||
var allInModule = parseResult.GetValue(updateAllInModule);
|
||||
var overrideComment = parseResult.GetValue(updateOverride);
|
||||
using var db = new Database(dbPath);
|
||||
|
||||
var indexer = AuditVerifier.BuildIndexer("features");
|
||||
|
||||
if (allInModule is not null)
|
||||
{
|
||||
var verifications = AuditVerifier.VerifyItems(db, indexer, "features",
|
||||
" WHERE module_id = @module", [("@module", (object?)allInModule)], status);
|
||||
if (!AuditVerifier.CheckAndReport(verifications, status, overrideComment))
|
||||
return;
|
||||
|
||||
var affected = db.Execute(
|
||||
"UPDATE features SET status = @status WHERE module_id = @module",
|
||||
("@status", status), ("@module", allInModule));
|
||||
Console.WriteLine($"Updated {affected} features in module {allInModule} to '{status}'.");
|
||||
|
||||
var mismatches = verifications.Where(r => !r.Matches).ToList();
|
||||
if (mismatches.Count > 0 && overrideComment is not null)
|
||||
AuditVerifier.LogOverrides(db, "features", mismatches, status, overrideComment);
|
||||
}
|
||||
else
|
||||
{
|
||||
var verifications = AuditVerifier.VerifyItems(db, indexer, "features",
|
||||
" WHERE id = @id", [("@id", (object?)id)], status);
|
||||
if (verifications.Count == 0)
|
||||
{
|
||||
Console.WriteLine($"Feature {id} not found.");
|
||||
return;
|
||||
}
|
||||
if (!AuditVerifier.CheckAndReport(verifications, status, overrideComment))
|
||||
return;
|
||||
|
||||
var affected = db.Execute(
|
||||
"UPDATE features SET status = @status WHERE id = @id",
|
||||
("@status", status), ("@id", id));
|
||||
Console.WriteLine(affected > 0 ? $"Feature {id} updated to '{status}'." : $"Feature {id} not found.");
|
||||
|
||||
var mismatches = verifications.Where(r => !r.Matches).ToList();
|
||||
if (mismatches.Count > 0 && overrideComment is not null)
|
||||
AuditVerifier.LogOverrides(db, "features", mismatches, status, overrideComment);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -179,13 +208,14 @@ public static class FeatureCommands
|
||||
|
||||
private static Command CreateBatchUpdate(Option<string> dbOption)
|
||||
{
|
||||
var cmd = new Command("batch-update", "Bulk update feature status");
|
||||
var cmd = new Command("batch-update", "Bulk update feature status (audit-verified)");
|
||||
var idsOpt = BatchFilters.IdsOption();
|
||||
var moduleOpt = BatchFilters.ModuleOption();
|
||||
var statusOpt = BatchFilters.StatusOption();
|
||||
var executeOpt = BatchFilters.ExecuteOption();
|
||||
var setStatus = new Option<string>("--set-status") { Description = "New status to set", Required = true };
|
||||
var setNotes = new Option<string?>("--set-notes") { Description = "Notes to set" };
|
||||
var overrideOpt = new Option<string?>("--override") { Description = "Override audit mismatches with this comment" };
|
||||
|
||||
cmd.Add(idsOpt);
|
||||
cmd.Add(moduleOpt);
|
||||
@@ -193,6 +223,7 @@ public static class FeatureCommands
|
||||
cmd.Add(executeOpt);
|
||||
cmd.Add(setStatus);
|
||||
cmd.Add(setNotes);
|
||||
cmd.Add(overrideOpt);
|
||||
|
||||
cmd.SetAction(parseResult =>
|
||||
{
|
||||
@@ -203,6 +234,7 @@ public static class FeatureCommands
|
||||
var execute = parseResult.GetValue(executeOpt);
|
||||
var newStatus = parseResult.GetValue(setStatus)!;
|
||||
var notes = parseResult.GetValue(setNotes);
|
||||
var overrideComment = parseResult.GetValue(overrideOpt);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ids) && module is null && string.IsNullOrWhiteSpace(status))
|
||||
{
|
||||
@@ -213,6 +245,12 @@ public static class FeatureCommands
|
||||
using var db = new Database(dbPath);
|
||||
var (whereClause, filterParams) = BatchFilters.BuildWhereClause(ids, module, status);
|
||||
|
||||
// Audit verification
|
||||
var indexer = AuditVerifier.BuildIndexer("features");
|
||||
var verifications = AuditVerifier.VerifyItems(db, indexer, "features", whereClause, filterParams, newStatus);
|
||||
if (!AuditVerifier.CheckAndReport(verifications, newStatus, overrideComment))
|
||||
return;
|
||||
|
||||
var setClauses = new List<string> { "status = @newStatus" };
|
||||
var updateParams = new List<(string, object?)> { ("@newStatus", newStatus) };
|
||||
if (notes is not null)
|
||||
@@ -225,6 +263,14 @@ public static class FeatureCommands
|
||||
"id, name, status, module_id, notes",
|
||||
string.Join(", ", setClauses), updateParams,
|
||||
whereClause, filterParams, execute);
|
||||
|
||||
// Log overrides after successful execute
|
||||
if (execute)
|
||||
{
|
||||
var mismatches = verifications.Where(r => !r.Matches).ToList();
|
||||
if (mismatches.Count > 0 && overrideComment is not null)
|
||||
AuditVerifier.LogOverrides(db, "features", mismatches, newStatus, overrideComment);
|
||||
}
|
||||
});
|
||||
|
||||
return cmd;
|
||||
|
||||
66
tools/NatsNet.PortTracker/Commands/OverrideCommands.cs
Normal file
66
tools/NatsNet.PortTracker/Commands/OverrideCommands.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using System.CommandLine;
|
||||
using NatsNet.PortTracker.Data;
|
||||
|
||||
namespace NatsNet.PortTracker.Commands;
|
||||
|
||||
public static class OverrideCommands
|
||||
{
|
||||
public static Command Create(Option<string> dbOption)
|
||||
{
|
||||
var overrideCommand = new Command("override", "Review status override records");
|
||||
|
||||
var typeOpt = new Option<string?>("--type")
|
||||
{
|
||||
Description = "Filter by table: features or tests"
|
||||
};
|
||||
|
||||
var listCmd = new Command("list", "List all status overrides");
|
||||
listCmd.Add(typeOpt);
|
||||
listCmd.SetAction(parseResult =>
|
||||
{
|
||||
var dbPath = parseResult.GetValue(dbOption)!;
|
||||
var type = parseResult.GetValue(typeOpt);
|
||||
using var db = new Database(dbPath);
|
||||
|
||||
var sql = "SELECT id, table_name, item_id, audit_status, requested_status, comment, created_at FROM status_overrides";
|
||||
var parameters = new List<(string, object?)>();
|
||||
|
||||
if (type is not null)
|
||||
{
|
||||
var tableName = type switch
|
||||
{
|
||||
"features" => "features",
|
||||
"tests" => "unit_tests",
|
||||
_ => type
|
||||
};
|
||||
sql += " WHERE table_name = @table";
|
||||
parameters.Add(("@table", tableName));
|
||||
}
|
||||
sql += " ORDER BY created_at DESC";
|
||||
|
||||
var rows = db.Query(sql, parameters.ToArray());
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
Console.WriteLine("No overrides found.");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine($"{"ID",-5} {"Table",-12} {"Item",-6} {"Audit",-10} {"Requested",-10} {"Comment",-35} {"Date",-20}");
|
||||
Console.WriteLine(new string('-', 98));
|
||||
foreach (var row in rows)
|
||||
{
|
||||
Console.WriteLine($"{row["id"],-5} {row["table_name"],-12} {row["item_id"],-6} {row["audit_status"],-10} {row["requested_status"],-10} {Truncate(row["comment"]?.ToString(), 34),-35} {row["created_at"],-20}");
|
||||
}
|
||||
Console.WriteLine($"\nTotal: {rows.Count} overrides");
|
||||
});
|
||||
|
||||
overrideCommand.Add(listCmd);
|
||||
return overrideCommand;
|
||||
}
|
||||
|
||||
private static string Truncate(string? s, int maxLen)
|
||||
{
|
||||
if (s is null) return "";
|
||||
return s.Length <= maxLen ? s : s[..(maxLen - 2)] + "..";
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.CommandLine;
|
||||
using NatsNet.PortTracker.Audit;
|
||||
using NatsNet.PortTracker.Data;
|
||||
|
||||
namespace NatsNet.PortTracker.Commands;
|
||||
@@ -89,18 +90,37 @@ public static class TestCommands
|
||||
// update
|
||||
var updateId = new Argument<int>("id") { Description = "Test ID" };
|
||||
var updateStatus = new Option<string>("--status") { Description = "New status", Required = true };
|
||||
var updateCmd = new Command("update", "Update test status");
|
||||
var updateOverride = new Option<string?>("--override") { Description = "Override audit mismatch with this comment" };
|
||||
var updateCmd = new Command("update", "Update test status (audit-verified)");
|
||||
updateCmd.Add(updateId);
|
||||
updateCmd.Add(updateStatus);
|
||||
updateCmd.Add(updateOverride);
|
||||
updateCmd.SetAction(parseResult =>
|
||||
{
|
||||
var dbPath = parseResult.GetValue(dbOption)!;
|
||||
var id = parseResult.GetValue(updateId);
|
||||
var status = parseResult.GetValue(updateStatus)!;
|
||||
var overrideComment = parseResult.GetValue(updateOverride);
|
||||
using var db = new Database(dbPath);
|
||||
|
||||
var indexer = AuditVerifier.BuildIndexer("unit_tests");
|
||||
var verifications = AuditVerifier.VerifyItems(db, indexer, "unit_tests",
|
||||
" WHERE id = @id", [("@id", (object?)id)], status);
|
||||
if (verifications.Count == 0)
|
||||
{
|
||||
Console.WriteLine($"Test {id} not found.");
|
||||
return;
|
||||
}
|
||||
if (!AuditVerifier.CheckAndReport(verifications, status, overrideComment))
|
||||
return;
|
||||
|
||||
var affected = db.Execute("UPDATE unit_tests SET status = @status WHERE id = @id",
|
||||
("@status", status), ("@id", id));
|
||||
Console.WriteLine(affected > 0 ? $"Test {id} updated to '{status}'." : $"Test {id} not found.");
|
||||
|
||||
var mismatches = verifications.Where(r => !r.Matches).ToList();
|
||||
if (mismatches.Count > 0 && overrideComment is not null)
|
||||
AuditVerifier.LogOverrides(db, "unit_tests", mismatches, status, overrideComment);
|
||||
});
|
||||
|
||||
// map
|
||||
@@ -139,13 +159,14 @@ public static class TestCommands
|
||||
|
||||
private static Command CreateBatchUpdate(Option<string> dbOption)
|
||||
{
|
||||
var cmd = new Command("batch-update", "Bulk update test status");
|
||||
var cmd = new Command("batch-update", "Bulk update test status (audit-verified)");
|
||||
var idsOpt = BatchFilters.IdsOption();
|
||||
var moduleOpt = BatchFilters.ModuleOption();
|
||||
var statusOpt = BatchFilters.StatusOption();
|
||||
var executeOpt = BatchFilters.ExecuteOption();
|
||||
var setStatus = new Option<string>("--set-status") { Description = "New status to set", Required = true };
|
||||
var setNotes = new Option<string?>("--set-notes") { Description = "Notes to set" };
|
||||
var overrideOpt = new Option<string?>("--override") { Description = "Override audit mismatches with this comment" };
|
||||
|
||||
cmd.Add(idsOpt);
|
||||
cmd.Add(moduleOpt);
|
||||
@@ -153,6 +174,7 @@ public static class TestCommands
|
||||
cmd.Add(executeOpt);
|
||||
cmd.Add(setStatus);
|
||||
cmd.Add(setNotes);
|
||||
cmd.Add(overrideOpt);
|
||||
|
||||
cmd.SetAction(parseResult =>
|
||||
{
|
||||
@@ -163,6 +185,7 @@ public static class TestCommands
|
||||
var execute = parseResult.GetValue(executeOpt);
|
||||
var newStatus = parseResult.GetValue(setStatus)!;
|
||||
var notes = parseResult.GetValue(setNotes);
|
||||
var overrideComment = parseResult.GetValue(overrideOpt);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(ids) && module is null && string.IsNullOrWhiteSpace(status))
|
||||
{
|
||||
@@ -173,6 +196,12 @@ public static class TestCommands
|
||||
using var db = new Database(dbPath);
|
||||
var (whereClause, filterParams) = BatchFilters.BuildWhereClause(ids, module, status);
|
||||
|
||||
// Audit verification
|
||||
var indexer = AuditVerifier.BuildIndexer("unit_tests");
|
||||
var verifications = AuditVerifier.VerifyItems(db, indexer, "unit_tests", whereClause, filterParams, newStatus);
|
||||
if (!AuditVerifier.CheckAndReport(verifications, newStatus, overrideComment))
|
||||
return;
|
||||
|
||||
var setClauses = new List<string> { "status = @newStatus" };
|
||||
var updateParams = new List<(string, object?)> { ("@newStatus", newStatus) };
|
||||
if (notes is not null)
|
||||
@@ -185,6 +214,14 @@ public static class TestCommands
|
||||
"id, name, status, module_id, notes",
|
||||
string.Join(", ", setClauses), updateParams,
|
||||
whereClause, filterParams, execute);
|
||||
|
||||
// Log overrides after successful execute
|
||||
if (execute)
|
||||
{
|
||||
var mismatches = verifications.Where(r => !r.Matches).ToList();
|
||||
if (mismatches.Count > 0 && overrideComment is not null)
|
||||
AuditVerifier.LogOverrides(db, "unit_tests", mismatches, newStatus, overrideComment);
|
||||
}
|
||||
});
|
||||
|
||||
return cmd;
|
||||
|
||||
@@ -40,6 +40,7 @@ rootCommand.Add(DependencyCommands.Create(dbOption, schemaOption));
|
||||
rootCommand.Add(ReportCommands.Create(dbOption, schemaOption));
|
||||
rootCommand.Add(PhaseCommands.Create(dbOption, schemaOption));
|
||||
rootCommand.Add(AuditCommand.Create(dbOption));
|
||||
rootCommand.Add(OverrideCommands.Create(dbOption));
|
||||
|
||||
var parseResult = rootCommand.Parse(args);
|
||||
return await parseResult.InvokeAsync();
|
||||
|
||||
Reference in New Issue
Block a user