ModernUO/Projects/UOContent.Tests/Tests/Network/Bans/CrowdSecConfigurationTests.cs
Kamron Batman 6249a20f15
feat(bans): contribute-first ban channel with CrowdSec reporter
Add a pluggable ban seam: BanChannel fans locally-decided bans out to
IBanReporter sinks (Report/Retract) and the accept path enforces locally.
CrowdSec is a write-only reporter living in UOContent (registered into the
Core seam via BanChannel.Register) — it batches decisions onto a bounded,
coalescing, drop-on-overflow queue and POSTs /v1/alerts with watcher creds,
retry/backoff, and a bounded flush-on-stop. CrowdSec never enforces in-app.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:37:42 -07:00

82 lines
3.7 KiB
C#

/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: CrowdSecConfigurationTests.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
*************************************************************************/
using System;
using System.Text.Json;
using Server.Json;
using Server.Network.Bans.CrowdSec;
using Xunit;
namespace Server.Tests.Network.Bans;
public class CrowdSecConfigurationTests
{
// Locks the JsonConfig casing/converter contract: JsonConfig's options are case-SENSITIVE, so
// every settings member must carry an explicit [JsonPropertyName("camelCase")] or it silently
// binds nothing. These tests round-trip through the exact options the loader uses.
[Fact]
public void CrowdSecSettings_RoundTripsThroughJsonConfig()
{
var original = new CrowdSecSettings
{
LapiUrl = "http://10.0.0.5:9090",
MachineId = "shard",
Password = "secret",
Origin = "modernuo",
ManualBanDuration = TimeSpan.FromHours(168),
FlushInterval = TimeSpan.FromSeconds(1),
MaxQueue = 10000
};
var json = JsonConfig.Serialize(original);
// camelCase property names must be present (not PascalCase) or the case-sensitive reader binds nothing.
Assert.Contains("\"lapiUrl\"", json);
Assert.Contains("\"machineId\"", json);
Assert.Contains("\"password\"", json);
Assert.Contains("\"origin\"", json);
Assert.Contains("\"manualBanDuration\"", json);
Assert.Contains("\"flushInterval\"", json);
Assert.Contains("\"maxQueue\"", json);
var restored = JsonSerializer.Deserialize<CrowdSecSettings>(json, JsonConfig.DefaultOptions);
Assert.NotNull(restored);
Assert.Equal(original.LapiUrl, restored.LapiUrl);
Assert.Equal(original.MachineId, restored.MachineId);
Assert.Equal(original.Password, restored.Password);
Assert.Equal(original.Origin, restored.Origin);
Assert.Equal(original.ManualBanDuration, restored.ManualBanDuration); // TimeSpan survives
Assert.Equal(original.FlushInterval, restored.FlushInterval); // TimeSpan survives
Assert.Equal(original.MaxQueue, restored.MaxQueue);
Assert.True(restored.ReportingEnabled);
}
[Fact]
public void CrowdSecSettings_Defaults_AreReportingDisabledLocalLoopback()
{
var settings = new CrowdSecSettings();
Assert.Equal("http://127.0.0.1:8080", settings.LapiUrl);
Assert.Equal("", settings.MachineId);
Assert.Equal("", settings.Password);
Assert.Equal("modernuo", settings.Origin);
Assert.Equal(TimeSpan.FromHours(168), settings.ManualBanDuration);
Assert.Equal(TimeSpan.FromSeconds(1), settings.FlushInterval);
Assert.Equal(10000, settings.MaxQueue);
Assert.False(settings.ReportingEnabled); // empty machineId/password => inert
}
}