1   /*
2    * Logback: the reliable, generic, fast and flexible logging framework.
3    *  Copyright (C) 1999-2025, QOS.ch. All rights reserved.
4    *
5    * This program and the accompanying materials are dual-licensed under
6    * either the terms of the Eclipse Public License v1.0 as published by
7    * the Eclipse Foundation
8    *
9    *     or (per the licensee's choosing)
10   *
11   * under the terms of the GNU Lesser General Public License version 2.1
12   * as published by the Free Software Foundation.
13   */
14  
15  package ch.qos.logback.core.util;
16  
17  import org.junit.jupiter.api.*;
18  
19  import static org.junit.jupiter.api.Assertions.*;
20  
21  
22  class SimpleTimeBasedGuardTest {
23      //  Nov 24 20:10:52 UTC 2025
24      long UTC_2025_11_24H201052 = 1764015052000L;
25  
26  
27      private SimpleTimeBasedGuard guard;
28  
29      @BeforeEach
30      void setUp() {
31          guard = new SimpleTimeBasedGuard();
32      }
33  
34      @AfterEach
35      void tearDown() {
36          guard.clearCurrentTime();
37      }
38  
39      @Test
40      void allowsTwoPer30Minutes() {
41          guard.setCurrentTimeMillis(UTC_2025_11_24H201052);
42  
43          assertTrue(guard.allow());
44          assertTrue(guard.allow());
45          assertFalse(guard.allow());
46          assertFalse(guard.allow());
47  
48          assertEquals(0, guard.getAllowsRemaining());
49      }
50  
51      @Test
52      void resetsCompletelyAfter30Minutes() {
53          guard.setCurrentTimeMillis(UTC_2025_11_24H201052);
54          guard.allow();
55          guard.allow(); // use both
56  
57          // Move exactly 30 minutes forward
58          guard.incCurrentTimeMillis(30 * 60 * 1000);
59  
60          assertTrue(guard.allow());  // new window!
61          assertTrue(guard.allow());
62          assertFalse(guard.allow());
63      }
64  
65      @Test
66      void windowsAreFloorAlignedTo30MinuteBoundaries() {
67          guard.setCurrentTimeMillis(10_000); // t=10s
68          guard.allow();
69          guard.allow();
70  
71          guard.incCurrentTimeMillis(29 * 60 * 1000L + 48_000L); // still same window
72          assertFalse(guard.allow());
73  
74          guard.setCurrentTimeMillis(30 * 60 * 1000L+10_001); // exactly 30 min → new window
75          assertTrue(guard.allow());
76      }
77  
78      @Test
79      void worksWithRealTimeWhenNotInjected() throws Exception {
80          guard.clearCurrentTime();
81  
82          assertTrue(guard.allow());
83          assertTrue(guard.allow());
84          assertFalse(guard.allow());
85  
86          // Fast-forward real time by 30+ minutes (for CI, use fake time instead)
87          // In real app: just wait 30 min
88      }
89  
90      @Test
91      void configurableLimitsWork() {
92          int fiveMinutesMs = 5 * 60_000;
93          SimpleTimeBasedGuard guard = new SimpleTimeBasedGuard(fiveMinutesMs, 3); // 3 allows every 5 minutes
94  
95          guard.setCurrentTimeMillis(10);
96          assertTrue(guard.allow());
97          assertTrue(guard.allow());
98          assertTrue(guard.allow());
99          assertFalse(guard.allow());
100 
101         guard.setCurrentTimeMillis(fiveMinutesMs + 10); // exactly 5 minutes later
102         assertTrue(guard.allow()); // fresh window!
103     }
104 }