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\n test 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\n test 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\n test 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\n line2\n line3\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+ }
0 commit comments