tests fixes and some product advisories tunes ups

This commit is contained in:
master
2026-01-30 07:57:43 +02:00
parent 644887997c
commit 55744f6a39
345 changed files with 26290 additions and 2267 deletions

View File

@@ -0,0 +1,354 @@
// -----------------------------------------------------------------------------
// WatchlistCommandGoldenTests.cs
// Sprint: SPRINT_0129_001_ATTESTOR_identity_watchlist_alerting
// Task: WATCH-008
// Description: Golden output tests for watchlist CLI command table formatting.
// -----------------------------------------------------------------------------
using FluentAssertions;
using Xunit;
namespace StellaOps.Cli.Tests.Commands;
/// <summary>
/// Golden output tests verifying consistent table formatting for watchlist CLI commands.
/// </summary>
public sealed class WatchlistCommandGoldenTests
{
#region List Command Table Formatting
[Fact]
public void ListCommand_TableFormat_HasCorrectHeaders()
{
// Arrange: Expected table header format
var expectedHeaders = new[]
{
"Scope",
"Display Name",
"Match Mode",
"Severity",
"Status"
};
// Act: Generate mock table header
var tableHeader = GenerateListTableHeader();
// Assert: All headers should be present in order
foreach (var header in expectedHeaders)
{
tableHeader.Should().Contain(header);
}
}
[Fact]
public void ListCommand_TableFormat_HasBorders()
{
var tableHeader = GenerateListTableHeader();
tableHeader.Should().StartWith("+");
tableHeader.Should().Contain("-");
tableHeader.Should().Contain("|");
}
[Fact]
public void ListCommand_TableRow_FormatsCorrectly()
{
// Arrange: Sample entry
var entry = new MockWatchlistEntry
{
Scope = "Tenant",
DisplayName = "GitHub Actions Watcher",
MatchMode = "Glob",
Severity = "Critical",
Enabled = true
};
// Act: Format as table row
var row = FormatListTableRow(entry);
// Assert: Row contains all values with proper alignment
row.Should().Contain("Tenant");
row.Should().Contain("GitHub Actions Watcher");
row.Should().Contain("Glob");
row.Should().Contain("Critical");
row.Should().Contain("Enabled");
row.Should().StartWith("|");
row.Should().EndWith("|");
}
[Fact]
public void ListCommand_TableRow_TruncatesLongNames()
{
var entry = new MockWatchlistEntry
{
Scope = "Tenant",
DisplayName = "This is a very long display name that exceeds thirty characters",
MatchMode = "Exact",
Severity = "Warning",
Enabled = true
};
var row = FormatListTableRow(entry);
// Display name should be truncated to 30 chars max
row.Should().NotContain("exceeds thirty characters");
row.Should().Contain("...");
}
#endregion
#region Alerts Command Table Formatting
[Fact]
public void AlertsCommand_TableFormat_HasCorrectHeaders()
{
var expectedHeaders = new[]
{
"Severity",
"Entry Name",
"Matched Identity",
"Time"
};
var tableHeader = GenerateAlertsTableHeader();
foreach (var header in expectedHeaders)
{
tableHeader.Should().Contain(header);
}
}
[Fact]
public void AlertsCommand_TableRow_FormatsCorrectly()
{
var alert = new MockAlert
{
Severity = "Critical",
EntryName = "GitHub Watcher",
MatchedIssuer = "https://token.actions.githubusercontent.com",
OccurredAt = DateTimeOffset.Parse("2026-01-29T10:30:00Z")
};
var row = FormatAlertsTableRow(alert);
row.Should().Contain("Critical");
row.Should().Contain("GitHub Watcher");
row.Should().Contain("token.actions.github"); // Truncated
row.Should().Contain("2026-01-29");
}
[Fact]
public void AlertsCommand_TableRow_FormatsRelativeTime()
{
var alert = new MockAlert
{
Severity = "Warning",
EntryName = "Test Entry",
MatchedIssuer = "https://example.com",
OccurredAt = DateTimeOffset.UtcNow.AddMinutes(-5)
};
var row = FormatAlertsTableRow(alert, useRelativeTime: true);
row.Should().Contain("5m ago");
}
#endregion
#region JSON Output Formatting
[Fact]
public void JsonOutput_UsesCamelCase()
{
var entry = new MockWatchlistEntry
{
Scope = "Tenant",
DisplayName = "Test Entry",
MatchMode = "Exact",
Severity = "Warning",
Enabled = true
};
var json = FormatAsJson(entry);
json.Should().Contain("\"displayName\"");
json.Should().Contain("\"matchMode\"");
json.Should().NotContain("\"DisplayName\"");
json.Should().NotContain("\"MatchMode\"");
}
[Fact]
public void JsonOutput_IsIndented()
{
var entry = new MockWatchlistEntry
{
Scope = "Tenant",
DisplayName = "Test Entry",
MatchMode = "Exact",
Severity = "Warning",
Enabled = true
};
var json = FormatAsJson(entry);
json.Should().Contain("\n");
json.Should().Contain(" "); // Indentation
}
[Fact]
public void JsonOutput_ExcludesNullValues()
{
var entry = new MockWatchlistEntry
{
Scope = "Tenant",
DisplayName = "Test Entry",
MatchMode = "Exact",
Severity = "Warning",
Enabled = true,
Description = null
};
var json = FormatAsJson(entry);
json.Should().NotContain("\"description\": null");
}
#endregion
#region Error Message Formatting
[Fact]
public void ErrorMessage_EntryNotFound_IsActionable()
{
var id = Guid.NewGuid();
var errorMessage = FormatEntryNotFoundError(id);
errorMessage.Should().StartWith("Error:");
errorMessage.Should().Contain(id.ToString());
errorMessage.Should().Contain("not found");
}
[Fact]
public void ErrorMessage_MissingIdentityFields_ListsOptions()
{
var errorMessage = FormatMissingIdentityFieldsError();
errorMessage.Should().StartWith("Error:");
errorMessage.Should().Contain("--issuer");
errorMessage.Should().Contain("--san");
errorMessage.Should().Contain("--key-id");
}
[Fact]
public void WarningMessage_RegexMode_SuggestsAlternative()
{
var warningMessage = FormatRegexWarning();
warningMessage.Should().StartWith("Warning:");
warningMessage.Should().Contain("regex");
warningMessage.Should().Contain("performance");
warningMessage.Should().Contain("glob"); // Suggests alternative
}
#endregion
#region Helper Methods
private static string GenerateListTableHeader()
{
return @"+---------------+--------------------------------+------------+----------+---------+
| Scope | Display Name | Match Mode | Severity | Status |
+---------------+--------------------------------+------------+----------+---------+";
}
private static string GenerateAlertsTableHeader()
{
return @"+----------+----------------------+----------------------------------+------------------+
| Severity | Entry Name | Matched Identity | Time |
+----------+----------------------+----------------------------------+------------------+";
}
private static string FormatListTableRow(MockWatchlistEntry entry)
{
var displayName = entry.DisplayName.Length > 30
? entry.DisplayName.Substring(0, 27) + "..."
: entry.DisplayName;
var status = entry.Enabled ? "Enabled" : "Disabled";
return $"| {entry.Scope,-13} | {displayName,-30} | {entry.MatchMode,-10} | {entry.Severity,-8} | {status,-7} |";
}
private static string FormatAlertsTableRow(MockAlert alert, bool useRelativeTime = false)
{
var identity = alert.MatchedIssuer.Length > 32
? alert.MatchedIssuer.Substring(8, 24) // Skip https:// and truncate
: alert.MatchedIssuer;
var time = useRelativeTime
? FormatRelativeTime(alert.OccurredAt)
: alert.OccurredAt.ToString("yyyy-MM-dd HH:mm");
return $"| {alert.Severity,-8} | {alert.EntryName,-20} | {identity,-32} | {time,-16} |";
}
private static string FormatRelativeTime(DateTimeOffset time)
{
var diff = DateTimeOffset.UtcNow - time;
if (diff.TotalMinutes < 60)
return $"{(int)diff.TotalMinutes}m ago";
if (diff.TotalHours < 24)
return $"{(int)diff.TotalHours}h ago";
return $"{(int)diff.TotalDays}d ago";
}
private static string FormatAsJson(MockWatchlistEntry entry)
{
var options = new System.Text.Json.JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
return System.Text.Json.JsonSerializer.Serialize(entry, options);
}
private static string FormatEntryNotFoundError(Guid id)
{
return $"Error: Watchlist entry '{id}' not found.";
}
private static string FormatMissingIdentityFieldsError()
{
return "Error: At least one identity field is required (--issuer, --san, or --key-id)";
}
private static string FormatRegexWarning()
{
return "Warning: Regex match mode may impact performance. Consider using glob patterns instead.";
}
#endregion
#region Test Helpers
private sealed class MockWatchlistEntry
{
public string Scope { get; set; } = "Tenant";
public string DisplayName { get; set; } = "";
public string MatchMode { get; set; } = "Exact";
public string Severity { get; set; } = "Warning";
public bool Enabled { get; set; } = true;
public string? Description { get; set; }
}
private sealed class MockAlert
{
public string Severity { get; set; } = "Warning";
public string EntryName { get; set; } = "";
public string MatchedIssuer { get; set; } = "";
public DateTimeOffset OccurredAt { get; set; }
}
#endregion
}