1+ using System . Reflection ;
12using System . Threading ;
3+ using Jellyfin . Plugin . Themerr . Configuration ;
24using MediaBrowser . Common . Configuration ;
35using MediaBrowser . Controller . Library ;
6+ using MediaBrowser . Model . Plugins ;
7+ using MediaBrowser . Model . Serialization ;
8+ using MediaBrowser . Model . Tasks ;
49using Microsoft . Extensions . Logging ;
510using Moq ;
611
712namespace Jellyfin . Plugin . Themerr . Tests ;
813
14+ [ Collection ( "Fixture Collection" ) ]
915public class TestThemerrStartupService
1016{
1117 [ Fact ]
1218 [ Trait ( "Category" , "Unit" ) ]
1319 public async Task TestStartAndStop ( )
1420 {
21+ ClearThemerrPluginInstance ( ) ;
22+
1523 Mock < IApplicationPaths > mockApplicationPaths = TestHelper . GetMockApplicationPaths ( ) ;
1624 Mock < ILibraryManager > mockLibraryManager = new ( ) ;
1725 Mock < ILoggerFactory > mockLoggerFactory = new ( ) ;
26+ Mock < ITaskManager > mockTaskManager = new ( ) ;
1827
1928 mockLoggerFactory
2029 . Setup ( x => x . CreateLogger ( It . IsAny < string > ( ) ) )
2130 . Returns ( new Mock < ILogger > ( ) . Object ) ;
31+ mockTaskManager . Setup ( x => x . ScheduledTasks ) . Returns ( Array . Empty < IScheduledTaskWorker > ( ) ) ;
2232
2333 var service = new ThemerrStartupService (
2434 mockApplicationPaths . Object ,
2535 mockLibraryManager . Object ,
26- mockLoggerFactory . Object ) ;
36+ mockLoggerFactory . Object ,
37+ mockTaskManager . Object ) ;
2738
2839 var startTask = service . StartAsync ( CancellationToken . None ) ;
2940 Assert . True ( startTask . IsCompletedSuccessfully ) ;
@@ -33,4 +44,259 @@ public async Task TestStartAndStop()
3344 Assert . True ( stopTask . IsCompletedSuccessfully ) ;
3445 await stopTask ;
3546 }
47+
48+ [ Fact ]
49+ [ Trait ( "Category" , "Unit" ) ]
50+ public async Task TestConfigurationChangeUpdatesScheduledTaskTrigger ( )
51+ {
52+ Mock < IApplicationPaths > mockApplicationPaths = TestHelper . GetMockApplicationPaths ( ) ;
53+ Mock < ILibraryManager > mockLibraryManager = new ( ) ;
54+ Mock < ILoggerFactory > mockLoggerFactory = new ( ) ;
55+ Mock < IXmlSerializer > mockXmlSerializer = new ( ) ;
56+ Mock < ITaskManager > mockTaskManager = new ( ) ;
57+ Mock < IScheduledTaskWorker > mockTaskWorker = new ( ) ;
58+
59+ mockLoggerFactory
60+ . Setup ( x => x . CreateLogger ( It . IsAny < string > ( ) ) )
61+ . Returns ( new Mock < ILogger > ( ) . Object ) ;
62+
63+ _ = new ThemerrPlugin ( mockApplicationPaths . Object , mockXmlSerializer . Object ) ;
64+ ThemerrPlugin . Instance . UpdateConfiguration ( new Configuration . PluginConfiguration ( ) ) ;
65+
66+ var themerrTasks = new ScheduledTasks . ThemerrTasks (
67+ mockApplicationPaths . Object ,
68+ mockLibraryManager . Object ,
69+ Mock . Of < ILogger < ScheduledTasks . ThemerrTasks > > ( ) ,
70+ mockLoggerFactory . Object ) ;
71+
72+ mockTaskWorker . Setup ( x => x . ScheduledTask ) . Returns ( themerrTasks ) ;
73+ mockTaskWorker . SetupProperty ( x => x . Triggers , Array . Empty < TaskTriggerInfo > ( ) ) ;
74+ mockTaskManager . Setup ( x => x . ScheduledTasks ) . Returns ( new [ ] { mockTaskWorker . Object } ) ;
75+
76+ var service = new ThemerrStartupService (
77+ mockApplicationPaths . Object ,
78+ mockLibraryManager . Object ,
79+ mockLoggerFactory . Object ,
80+ mockTaskManager . Object ) ;
81+
82+ await service . StartAsync ( CancellationToken . None ) ;
83+
84+ var configuration = new Configuration . PluginConfiguration
85+ {
86+ UpdateInterval = 30 ,
87+ } ;
88+ ThemerrPlugin . Instance . UpdateConfiguration ( configuration ) ;
89+
90+ var trigger = Assert . Single ( mockTaskWorker . Object . Triggers ) ;
91+ Assert . Equal ( TimeSpan . FromMinutes ( 30 ) . Ticks , trigger . IntervalTicks ) ;
92+ mockTaskWorker . Verify ( x => x . ReloadTriggerEvents ( ) , Times . Exactly ( 2 ) ) ;
93+
94+ await service . StopAsync ( CancellationToken . None ) ;
95+
96+ configuration = new Configuration . PluginConfiguration
97+ {
98+ UpdateInterval = 45 ,
99+ } ;
100+ ThemerrPlugin . Instance . UpdateConfiguration ( configuration ) ;
101+
102+ mockTaskWorker . Verify ( x => x . ReloadTriggerEvents ( ) , Times . Exactly ( 2 ) ) ;
103+ }
104+
105+ [ Fact ]
106+ [ Trait ( "Category" , "Unit" ) ]
107+ public async Task TestConfigurationChangePreservesUserScheduledTaskTriggers ( )
108+ {
109+ Mock < IApplicationPaths > mockApplicationPaths = TestHelper . GetMockApplicationPaths ( ) ;
110+ Mock < ILibraryManager > mockLibraryManager = new ( ) ;
111+ Mock < ILoggerFactory > mockLoggerFactory = new ( ) ;
112+ Mock < IXmlSerializer > mockXmlSerializer = new ( ) ;
113+ Mock < ITaskManager > mockTaskManager = new ( ) ;
114+ Mock < IScheduledTaskWorker > mockTaskWorker = new ( ) ;
115+
116+ mockLoggerFactory
117+ . Setup ( x => x . CreateLogger ( It . IsAny < string > ( ) ) )
118+ . Returns ( new Mock < ILogger > ( ) . Object ) ;
119+
120+ _ = new ThemerrPlugin ( mockApplicationPaths . Object , mockXmlSerializer . Object ) ;
121+ ThemerrPlugin . Instance . UpdateConfiguration ( new Configuration . PluginConfiguration
122+ {
123+ UpdateInterval = 45 ,
124+ } ) ;
125+
126+ var themerrTasks = new ScheduledTasks . ThemerrTasks (
127+ mockApplicationPaths . Object ,
128+ mockLibraryManager . Object ,
129+ Mock . Of < ILogger < ScheduledTasks . ThemerrTasks > > ( ) ,
130+ mockLoggerFactory . Object ) ;
131+
132+ var userDailyTrigger = new TaskTriggerInfo
133+ {
134+ Type = TaskTriggerInfoType . DailyTrigger ,
135+ TimeOfDayTicks = TimeSpan . FromHours ( 1 ) . Ticks ,
136+ } ;
137+ var userStartupTrigger = new TaskTriggerInfo
138+ {
139+ Type = TaskTriggerInfoType . StartupTrigger ,
140+ } ;
141+
142+ mockTaskWorker . Setup ( x => x . ScheduledTask ) . Returns ( themerrTasks ) ;
143+ mockTaskWorker . SetupProperty (
144+ x => x . Triggers ,
145+ new [ ]
146+ {
147+ new TaskTriggerInfo
148+ {
149+ Type = TaskTriggerInfoType . IntervalTrigger ,
150+ IntervalTicks = TimeSpan . FromMinutes ( 15 ) . Ticks ,
151+ } ,
152+ userDailyTrigger ,
153+ userStartupTrigger ,
154+ } ) ;
155+ mockTaskManager . Setup ( x => x . ScheduledTasks ) . Returns ( new [ ] { mockTaskWorker . Object } ) ;
156+
157+ var service = new ThemerrStartupService (
158+ mockApplicationPaths . Object ,
159+ mockLibraryManager . Object ,
160+ mockLoggerFactory . Object ,
161+ mockTaskManager . Object ) ;
162+
163+ await service . StartAsync ( CancellationToken . None ) ;
164+
165+ Assert . Collection (
166+ mockTaskWorker . Object . Triggers ,
167+ trigger =>
168+ {
169+ Assert . Equal ( TaskTriggerInfoType . IntervalTrigger , trigger . Type ) ;
170+ Assert . Equal ( TimeSpan . FromMinutes ( 45 ) . Ticks , trigger . IntervalTicks ) ;
171+ } ,
172+ trigger => Assert . Same ( userDailyTrigger , trigger ) ,
173+ trigger => Assert . Same ( userStartupTrigger , trigger ) ) ;
174+
175+ ThemerrPlugin . Instance . UpdateConfiguration ( new Configuration . PluginConfiguration
176+ {
177+ UpdateInterval = 30 ,
178+ } ) ;
179+
180+ Assert . Collection (
181+ mockTaskWorker . Object . Triggers ,
182+ trigger =>
183+ {
184+ Assert . Equal ( TaskTriggerInfoType . IntervalTrigger , trigger . Type ) ;
185+ Assert . Equal ( TimeSpan . FromMinutes ( 30 ) . Ticks , trigger . IntervalTicks ) ;
186+ } ,
187+ trigger => Assert . Same ( userDailyTrigger , trigger ) ,
188+ trigger => Assert . Same ( userStartupTrigger , trigger ) ) ;
189+ mockTaskWorker . Verify ( x => x . ReloadTriggerEvents ( ) , Times . Exactly ( 2 ) ) ;
190+
191+ await service . StopAsync ( CancellationToken . None ) ;
192+ }
193+
194+ [ Fact ]
195+ [ Trait ( "Category" , "Unit" ) ]
196+ public async Task TestStartWithPluginWithoutConfigurationDoesNotUpdateScheduledTaskTrigger ( )
197+ {
198+ Mock < IApplicationPaths > mockApplicationPaths = TestHelper . GetMockApplicationPaths ( ) ;
199+ Mock < ILibraryManager > mockLibraryManager = new ( ) ;
200+ Mock < ILoggerFactory > mockLoggerFactory = new ( ) ;
201+ Mock < IXmlSerializer > mockXmlSerializer = new ( ) ;
202+ Mock < ITaskManager > mockTaskManager = new ( ) ;
203+ Mock < IScheduledTaskWorker > mockTaskWorker = new ( ) ;
204+
205+ mockLoggerFactory
206+ . Setup ( x => x . CreateLogger ( It . IsAny < string > ( ) ) )
207+ . Returns ( new Mock < ILogger > ( ) . Object ) ;
208+
209+ _ = new ThemerrPlugin ( mockApplicationPaths . Object , mockXmlSerializer . Object ) ;
210+ mockTaskManager . Setup ( x => x . ScheduledTasks ) . Returns ( new [ ] { mockTaskWorker . Object } ) ;
211+
212+ var service = new ThemerrStartupService (
213+ mockApplicationPaths . Object ,
214+ mockLibraryManager . Object ,
215+ mockLoggerFactory . Object ,
216+ mockTaskManager . Object ) ;
217+
218+ await service . StartAsync ( CancellationToken . None ) ;
219+
220+ mockTaskWorker . VerifySet ( x => x . Triggers = It . IsAny < IReadOnlyList < TaskTriggerInfo > > ( ) , Times . Never ) ;
221+ mockTaskWorker . Verify ( x => x . ReloadTriggerEvents ( ) , Times . Never ) ;
222+
223+ await service . StopAsync ( CancellationToken . None ) ;
224+ }
225+
226+ [ Fact ]
227+ [ Trait ( "Category" , "Unit" ) ]
228+ public async Task TestConfigurationChangeIgnoresNonPluginConfiguration ( )
229+ {
230+ Mock < IApplicationPaths > mockApplicationPaths = TestHelper . GetMockApplicationPaths ( ) ;
231+ Mock < ILibraryManager > mockLibraryManager = new ( ) ;
232+ Mock < ILoggerFactory > mockLoggerFactory = new ( ) ;
233+ Mock < IXmlSerializer > mockXmlSerializer = new ( ) ;
234+ Mock < ITaskManager > mockTaskManager = new ( ) ;
235+ Mock < IScheduledTaskWorker > mockTaskWorker = new ( ) ;
236+
237+ mockLoggerFactory
238+ . Setup ( x => x . CreateLogger ( It . IsAny < string > ( ) ) )
239+ . Returns ( new Mock < ILogger > ( ) . Object ) ;
240+
241+ _ = new ThemerrPlugin ( mockApplicationPaths . Object , mockXmlSerializer . Object ) ;
242+ ThemerrPlugin . Instance . UpdateConfiguration ( new PluginConfiguration ( ) ) ;
243+ mockTaskManager . Setup ( x => x . ScheduledTasks ) . Returns ( Array . Empty < IScheduledTaskWorker > ( ) ) ;
244+
245+ var service = new ThemerrStartupService (
246+ mockApplicationPaths . Object ,
247+ mockLibraryManager . Object ,
248+ mockLoggerFactory . Object ,
249+ mockTaskManager . Object ) ;
250+
251+ await service . StartAsync ( CancellationToken . None ) ;
252+
253+ ThemerrPlugin . Instance . ConfigurationChanged ? . Invoke ( this , new BasePluginConfiguration ( ) ) ;
254+
255+ mockTaskWorker . VerifySet ( x => x . Triggers = It . IsAny < IReadOnlyList < TaskTriggerInfo > > ( ) , Times . Never ) ;
256+ mockTaskWorker . Verify ( x => x . ReloadTriggerEvents ( ) , Times . Never ) ;
257+
258+ await service . StopAsync ( CancellationToken . None ) ;
259+ }
260+
261+ [ Fact ]
262+ [ Trait ( "Category" , "Unit" ) ]
263+ public async Task TestConfigurationChangeWithoutThemerrTaskDoesNotUpdateScheduledTaskTrigger ( )
264+ {
265+ Mock < IApplicationPaths > mockApplicationPaths = TestHelper . GetMockApplicationPaths ( ) ;
266+ Mock < ILibraryManager > mockLibraryManager = new ( ) ;
267+ Mock < ILoggerFactory > mockLoggerFactory = new ( ) ;
268+ Mock < IXmlSerializer > mockXmlSerializer = new ( ) ;
269+ Mock < ITaskManager > mockTaskManager = new ( ) ;
270+ Mock < IScheduledTaskWorker > mockTaskWorker = new ( ) ;
271+
272+ mockLoggerFactory
273+ . Setup ( x => x . CreateLogger ( It . IsAny < string > ( ) ) )
274+ . Returns ( new Mock < ILogger > ( ) . Object ) ;
275+
276+ _ = new ThemerrPlugin ( mockApplicationPaths . Object , mockXmlSerializer . Object ) ;
277+ ThemerrPlugin . Instance . UpdateConfiguration ( new PluginConfiguration ( ) ) ;
278+ mockTaskManager . Setup ( x => x . ScheduledTasks ) . Returns ( new [ ] { mockTaskWorker . Object } ) ;
279+
280+ var service = new ThemerrStartupService (
281+ mockApplicationPaths . Object ,
282+ mockLibraryManager . Object ,
283+ mockLoggerFactory . Object ,
284+ mockTaskManager . Object ) ;
285+
286+ await service . StartAsync ( CancellationToken . None ) ;
287+ ThemerrPlugin . Instance . UpdateConfiguration ( new PluginConfiguration { UpdateInterval = 30 } ) ;
288+
289+ mockTaskWorker . VerifySet ( x => x . Triggers = It . IsAny < IReadOnlyList < TaskTriggerInfo > > ( ) , Times . Never ) ;
290+ mockTaskWorker . Verify ( x => x . ReloadTriggerEvents ( ) , Times . Never ) ;
291+
292+ await service . StopAsync ( CancellationToken . None ) ;
293+ }
294+
295+ private static void ClearThemerrPluginInstance ( )
296+ {
297+ typeof ( ThemerrPlugin )
298+ . GetProperty ( nameof ( ThemerrPlugin . Instance ) , BindingFlags . Public | BindingFlags . Static )
299+ ? . GetSetMethod ( nonPublic : true )
300+ ? . Invoke ( null , new object ? [ ] { null } ) ;
301+ }
36302}
0 commit comments