## Summary Many tests proved a specific change when it landed but don't need to run on every CI build forever. This removes **202 test methods and 9 files: 313 test cases, 2,247 → 1,934**. UOContent.Tests drops from about 14s to about 5s locally. Removed: - **Tautologies.** Asserting a constant, a constructor readback or a trivial getter, or testing BCL behavior. - **Exact duplicates** of a surviving test on the same code path. - **Obsolete regressions** whose failure can no longer be expressed. - **Game-content values** shard owners customize (era drop rates, loot and skill tables, single-creature tuning). Each customization would otherwise force a test fix. ## How it was decided 1. Every test was classified, with a reason, by a per-shard sweep over all 287 test files. 2. Each proposed deletion was then checked by a second, adversarial reviewer against the surviving tests and the production code. 3. Anything that was the only coverage of a separate code path was **kept**, even where it looked like a duplicate. Examples: the unsigned `SpanReader`/`SpanWriter` methods, the at-location versus bounds enumerators, null-map enumerables, the security filters, archive-journal state transitions. About 130 proposed deletions were rejected this way. Whole files removed: - `FishingTests`, apart from 3 tests: the map-edge scan, serpent cleanup, and SOS chest consumption. The SOS test now stops at the first chest instead of measuring a catch rate. The multi-million-cast rate simulations are gone. - `LordOaksTests` and `MeerEnragedTests` (single-creature tests). - `AutoArchiveHelperTests`, `JsonDiscoverableTypeAttributeTests`, `DecimalSerializationTests` and `EnumConversionTests` (tautologies, or tests of their own local helpers). - `NetworkStatsTests`. - `MigratedDataLoadTests` (a subset of `AllSpawnFilesLoadTests`). - `SpawnerFixtureCapture`: a capture tool, not a test, and its `version == 12` assert can no longer pass. The `.bin` fixtures it produced stay. ## Tests - Server.Tests: 811 pass. - UOContent.Tests: 1,123 pass. A follow-up PR, stacked on this one, fixes how the remaining tests behave: shared state, CI-vacuous tests, harness duplication.
218 lines
6.6 KiB
C#
218 lines
6.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Server.Saves;
|
|
using Xunit;
|
|
|
|
namespace Server.Tests;
|
|
|
|
[Collection("Sequential UOContent Tests")]
|
|
public class ArchiveJournalTests : IDisposable
|
|
{
|
|
private readonly string _testDir;
|
|
|
|
public ArchiveJournalTests()
|
|
{
|
|
_testDir = Path.Combine(Path.GetTempPath(), $"modernuo-journal-test-{Guid.NewGuid():N}");
|
|
Directory.CreateDirectory(_testDir);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (Directory.Exists(_testDir))
|
|
{
|
|
Directory.Delete(_testDir, true);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void BeginOperation_CreatesStartedEntry()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
|
|
// Act
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Hourly,
|
|
new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/archive.tar.zst.tmp",
|
|
"/archives/hourly/2026-03-22-14.tar.zst",
|
|
["backup1", "backup2"]
|
|
);
|
|
|
|
// Assert
|
|
Assert.Equal(ArchiveOperationState.Started, entry.State);
|
|
Assert.Equal(ArchivePeriod.Hourly, entry.Period);
|
|
Assert.Equal("/tmp/archive.tar.zst.tmp", entry.TempFile);
|
|
Assert.Equal(2, entry.SourceDirectories.Count);
|
|
Assert.Contains(entry, ArchiveJournal.Operations);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordArchived_TransitionsState()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Daily,
|
|
new DateTime(2026, 3, 22, 0, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/test.tmp",
|
|
"/archives/daily/2026-03-22.tar.zst",
|
|
["backup1"]
|
|
);
|
|
|
|
// Act
|
|
ArchiveJournal.RecordArchived(entry, 42, 1024000);
|
|
|
|
// Assert
|
|
Assert.Equal(ArchiveOperationState.Archived, entry.State);
|
|
Assert.Equal(42, entry.EntryCount);
|
|
Assert.Equal(1024000, entry.ArchiveBytes);
|
|
Assert.Null(entry.TempFile); // Temp file cleared after rename
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordDistributed_StoresResults()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Monthly,
|
|
new DateTime(2026, 3, 1, 0, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/test.tmp",
|
|
"/archives/monthly/2026-03.tar.zst",
|
|
["backup1"]
|
|
);
|
|
ArchiveJournal.RecordArchived(entry, 100, 5000000);
|
|
|
|
var results = new Dictionary<string, bool>
|
|
{
|
|
{ "Local Filesystem", true },
|
|
{ "S3 us-east-1", false }
|
|
};
|
|
|
|
// Act
|
|
ArchiveJournal.RecordDistributed(entry, results);
|
|
|
|
// Assert
|
|
Assert.Equal(ArchiveOperationState.Distributed, entry.State);
|
|
Assert.True(entry.DestinationResults["Local Filesystem"]);
|
|
Assert.False(entry.DestinationResults["S3 us-east-1"]);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordCompleted_IsTerminal()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Hourly,
|
|
new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/test.tmp",
|
|
"/archives/hourly/test.tar.zst",
|
|
["backup1"]
|
|
);
|
|
ArchiveJournal.RecordArchived(entry, 10, 500);
|
|
ArchiveJournal.RecordDistributed(entry, new Dictionary<string, bool>());
|
|
|
|
// Act
|
|
ArchiveJournal.RecordCompleted(entry);
|
|
|
|
// Assert
|
|
Assert.Equal(ArchiveOperationState.Completed, entry.State);
|
|
Assert.NotEqual(DateTime.MaxValue, entry.CompletedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecordFailure_RecordsReason()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Hourly,
|
|
new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/test.tmp",
|
|
"/archives/hourly/test.tar.zst",
|
|
["backup1"]
|
|
);
|
|
|
|
// Act
|
|
ArchiveJournal.RecordFailure(entry, "Disk full");
|
|
|
|
// Assert
|
|
Assert.Equal(ArchiveOperationState.Failed, entry.State);
|
|
Assert.Equal("Disk full", entry.FailureReason);
|
|
Assert.NotEqual(DateTime.MaxValue, entry.CompletedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public void Journal_PersistsToJsonFile()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Hourly,
|
|
new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/test.tmp",
|
|
"/archives/hourly/test.tar.zst",
|
|
["backup1"]
|
|
);
|
|
|
|
// Assert - journal file should exist
|
|
var journalPath = Path.Combine(_testDir, ".archive-journal.json");
|
|
Assert.True(File.Exists(journalPath));
|
|
|
|
var content = File.ReadAllText(journalPath);
|
|
Assert.Contains("Hourly", content);
|
|
Assert.Contains("Started", content);
|
|
}
|
|
|
|
[Fact]
|
|
public void RecoverInterrupted_CleansUpStartedOperations()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
|
|
// Create a temp file that simulates an interrupted archive
|
|
var tempFile = Path.Combine(_testDir, "interrupted.tar.zst.tmp");
|
|
File.WriteAllText(tempFile, "partial archive data");
|
|
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Hourly,
|
|
new DateTime(2026, 3, 22, 14, 0, 0, DateTimeKind.Utc),
|
|
tempFile,
|
|
Path.Combine(_testDir, "final.tar.zst"),
|
|
["backup1"]
|
|
);
|
|
|
|
// Act
|
|
ArchiveJournal.RecoverInterrupted();
|
|
|
|
// Assert
|
|
Assert.Equal(ArchiveOperationState.Failed, entry.State);
|
|
Assert.Equal("Interrupted during archive creation", entry.FailureReason);
|
|
Assert.False(File.Exists(tempFile)); // Temp file should be cleaned up
|
|
}
|
|
|
|
[Fact]
|
|
public void RecoverInterrupted_CompletesDistributedOperations()
|
|
{
|
|
// Arrange
|
|
ArchiveJournal.Configure(_testDir);
|
|
var entry = ArchiveJournal.BeginOperation(
|
|
ArchivePeriod.Daily,
|
|
new DateTime(2026, 3, 22, 0, 0, 0, DateTimeKind.Utc),
|
|
"/tmp/test.tmp",
|
|
"/archives/daily/test.tar.zst",
|
|
["backup1"]
|
|
);
|
|
ArchiveJournal.RecordArchived(entry, 50, 2000);
|
|
ArchiveJournal.RecordDistributed(entry, new Dictionary<string, bool> { { "Local", true } });
|
|
|
|
// Act - simulate restart
|
|
ArchiveJournal.RecoverInterrupted();
|
|
|
|
// Assert - should be marked completed
|
|
Assert.Equal(ArchiveOperationState.Completed, entry.State);
|
|
}
|
|
}
|