Skip to content

Commit ec45761

Browse files
Copilotdevedse
andcommitted
Implement logrotated and compressed access.log support
Co-authored-by: devedse <2350015+devedse@users.noreply.github.com>
1 parent dcf0a51 commit ec45761

File tree

5 files changed

+499
-92
lines changed

5 files changed

+499
-92
lines changed

DeveLanCacheUI_Backend.Tests/DeveLanCacheUI_Backend.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<PrivateAssets>all</PrivateAssets>
1717
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1818
</PackageReference>
19+
<PackageReference Include="ZstdNet" Version="1.4.5" />
1920
</ItemGroup>
2021

2122
<ItemGroup>
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
using DeveLanCacheUI_Backend.LogReading;
2+
using System.IO.Compression;
3+
using System.Text;
4+
using ZstdNet;
5+
6+
namespace DeveLanCacheUI_Backend.Tests.LogReading
7+
{
8+
[TestClass]
9+
public class LogRotationTests
10+
{
11+
private string _tempDirectory = null!;
12+
13+
[TestInitialize]
14+
public void Setup()
15+
{
16+
_tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
17+
Directory.CreateDirectory(_tempDirectory);
18+
}
19+
20+
[TestCleanup]
21+
public void Cleanup()
22+
{
23+
if (Directory.Exists(_tempDirectory))
24+
{
25+
Directory.Delete(_tempDirectory, true);
26+
}
27+
}
28+
29+
[TestMethod]
30+
public void GetLogFiles_ReturnsCorrectOrderWithBasicFiles()
31+
{
32+
// Arrange
33+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
34+
35+
// Create test files
36+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log"), "current log");
37+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log.1"), "rotated 1");
38+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log.2"), "rotated 2");
39+
40+
// Act
41+
var result = InvokePrivateMethod<List<string>>(sut, "GetLogFiles", _tempDirectory);
42+
43+
// Assert
44+
Assert.AreEqual(3, result.Count);
45+
Assert.IsTrue(result[0].EndsWith("access.log"));
46+
Assert.IsTrue(result[1].EndsWith("access.log.1"));
47+
Assert.IsTrue(result[2].EndsWith("access.log.2"));
48+
}
49+
50+
[TestMethod]
51+
public void GetLogFiles_ReturnsCorrectOrderWithCompressedFiles()
52+
{
53+
// Arrange
54+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
55+
56+
// Create test files
57+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log"), "current log");
58+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log.1.gz"), "compressed 1");
59+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log.2.zst"), "compressed 2");
60+
61+
// Act
62+
var result = InvokePrivateMethod<List<string>>(sut, "GetLogFiles", _tempDirectory);
63+
64+
// Assert
65+
Assert.AreEqual(3, result.Count);
66+
Assert.IsTrue(result[0].EndsWith("access.log"));
67+
Assert.IsTrue(result[1].EndsWith("access.log.1.gz"));
68+
Assert.IsTrue(result[2].EndsWith("access.log.2.zst"));
69+
}
70+
71+
[TestMethod]
72+
public void GetLogFiles_HandlesGapInNumbers()
73+
{
74+
// Arrange
75+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
76+
77+
// Create test files with gaps
78+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log"), "current log");
79+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log.1"), "rotated 1");
80+
// Skip access.log.2
81+
File.WriteAllText(Path.Combine(_tempDirectory, "access.log.3"), "rotated 3");
82+
83+
// Act
84+
var result = InvokePrivateMethod<List<string>>(sut, "GetLogFiles", _tempDirectory);
85+
86+
// Assert - should stop at the first gap
87+
Assert.AreEqual(2, result.Count);
88+
Assert.IsTrue(result[0].EndsWith("access.log"));
89+
Assert.IsTrue(result[1].EndsWith("access.log.1"));
90+
}
91+
92+
[TestMethod]
93+
public void OpenLogFileStream_HandlesRegularFile()
94+
{
95+
// Arrange
96+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
97+
var testFile = Path.Combine(_tempDirectory, "test.log");
98+
var testContent = "test line 1\ntest line 2\n";
99+
File.WriteAllText(testFile, testContent);
100+
101+
// Act
102+
using var stream = InvokePrivateMethod<Stream>(sut, "OpenLogFileStream", testFile);
103+
using var reader = new StreamReader(stream);
104+
var content = reader.ReadToEnd();
105+
106+
// Assert
107+
Assert.AreEqual(testContent, content);
108+
}
109+
110+
[TestMethod]
111+
public void OpenLogFileStream_HandlesGzipFile()
112+
{
113+
// Arrange
114+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
115+
var testFile = Path.Combine(_tempDirectory, "test.log.gz");
116+
var testContent = "test line 1\ntest line 2\n";
117+
118+
// Create gzipped file
119+
using (var fileStream = File.Create(testFile))
120+
using (var gzipStream = new GZipStream(fileStream, CompressionMode.Compress))
121+
using (var writer = new StreamWriter(gzipStream))
122+
{
123+
writer.Write(testContent);
124+
}
125+
126+
// Act
127+
using var stream = InvokePrivateMethod<Stream>(sut, "OpenLogFileStream", testFile);
128+
using var reader = new StreamReader(stream);
129+
var content = reader.ReadToEnd();
130+
131+
// Assert
132+
Assert.AreEqual(testContent, content);
133+
}
134+
135+
[TestMethod]
136+
public void OpenLogFileStream_HandlesZstdFile()
137+
{
138+
// Arrange
139+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
140+
var testFile = Path.Combine(_tempDirectory, "test.log.zst");
141+
var testContent = "test line 1\ntest line 2\n";
142+
143+
// Create zstd compressed file
144+
using (var compressor = new Compressor())
145+
{
146+
var originalBytes = Encoding.UTF8.GetBytes(testContent);
147+
var compressedBytes = compressor.Wrap(originalBytes);
148+
File.WriteAllBytes(testFile, compressedBytes);
149+
}
150+
151+
// Act
152+
using var stream = InvokePrivateMethod<Stream>(sut, "OpenLogFileStream", testFile);
153+
using var reader = new StreamReader(stream);
154+
var content = reader.ReadToEnd();
155+
156+
// Assert
157+
Assert.AreEqual(testContent, content);
158+
}
159+
160+
[TestMethod]
161+
public void ReadAllLinesFromStream_ReadsAllLines()
162+
{
163+
// Arrange
164+
var sut = new LanCacheLogReaderHostedService(null!, null!, null!, null!);
165+
var testContent = "line1\nline2\nline3\n";
166+
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(testContent));
167+
var cts = new CancellationTokenSource();
168+
169+
// Act
170+
var result = InvokePrivateMethod<IEnumerable<string>>(sut, "ReadAllLinesFromStream", stream, cts.Token).ToList();
171+
172+
// Assert
173+
Assert.AreEqual(3, result.Count);
174+
Assert.AreEqual("line1", result[0]);
175+
Assert.AreEqual("line2", result[1]);
176+
Assert.AreEqual("line3", result[2]);
177+
}
178+
179+
/// <summary>
180+
/// Helper method to invoke private methods for testing
181+
/// </summary>
182+
private T InvokePrivateMethod<T>(object obj, string methodName, params object[] parameters)
183+
{
184+
var type = obj.GetType();
185+
var method = type.GetMethod(methodName, System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
186+
if (method == null)
187+
throw new ArgumentException($"Method {methodName} not found");
188+
189+
var result = method.Invoke(obj, parameters);
190+
return (T)result!;
191+
}
192+
}
193+
}

DeveLanCacheUI_Backend/Db/DbModels/DbSetting.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ public class DbSetting
99
public const string SettingKey_DepotVersion = nameof(SettingKey_DepotVersion);
1010
public const string SettingKey_SteamChangeNumber = nameof(SettingKey_SteamChangeNumber);
1111
public const string SettingKey_TotalBytesRead = nameof(SettingKey_TotalBytesRead);
12+
public const string SettingKey_ProcessedLogFiles = nameof(SettingKey_ProcessedLogFiles);
1213
}
1314
}

DeveLanCacheUI_Backend/DeveLanCacheUI_Backend.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
<PackageReference Include="protobuf-net" Version="3.2.52" />
3535
<PackageReference Include="SteamKit2" Version="3.2.0" />
3636
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
37+
<PackageReference Include="ZstdNet" Version="1.4.5" />
3738
</ItemGroup>
3839

3940
<ItemGroup>

0 commit comments

Comments
 (0)