1
1
/*
2
- * Copyright 2002-2024 the original author or authors.
2
+ * Copyright 2002-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
19
19
import java .time .Clock ;
20
20
import java .time .Duration ;
21
21
import java .time .Instant ;
22
- import java .util .Map ;
23
22
import java .util .stream .IntStream ;
24
23
25
24
import org .junit .jupiter .api .Test ;
26
25
import reactor .core .scheduler .Schedulers ;
26
+ import reactor .test .StepVerifier ;
27
27
28
28
import org .springframework .beans .DirectFieldAccessor ;
29
29
import org .springframework .web .server .WebSession ;
35
35
* Tests for {@link InMemoryWebSessionStore}.
36
36
*
37
37
* @author Rob Winch
38
+ * @author Sam Brannen
38
39
*/
39
40
class InMemoryWebSessionStoreTests {
40
41
41
- private InMemoryWebSessionStore store = new InMemoryWebSessionStore ();
42
+ private final InMemoryWebSessionStore store = new InMemoryWebSessionStore ();
42
43
43
44
44
45
@ Test
@@ -53,13 +54,14 @@ void startsSessionExplicitly() {
53
54
void startsSessionImplicitly () {
54
55
WebSession session = this .store .createWebSession ().block ();
55
56
assertThat (session ).isNotNull ();
56
- session .start ();
57
+ // We intentionally do not invoke start().
58
+ // session.start();
57
59
session .getAttributes ().put ("foo" , "bar" );
58
60
assertThat (session .isStarted ()).isTrue ();
59
61
}
60
62
61
63
@ Test // gh-24027, gh-26958
62
- public void createSessionDoesNotBlock () {
64
+ void createSessionDoesNotBlock () {
63
65
this .store .createWebSession ()
64
66
.doOnNext (session -> assertThat (Schedulers .isInNonBlockingThread ()).isTrue ())
65
67
.block ();
@@ -103,7 +105,7 @@ void lastAccessTimeIsUpdatedOnRetrieve() {
103
105
}
104
106
105
107
@ Test // SPR-17051
106
- public void sessionInvalidatedBeforeSave () {
108
+ void sessionInvalidatedBeforeSave () {
107
109
// Request 1 creates session
108
110
WebSession session1 = this .store .createWebSession ().block ();
109
111
assertThat (session1 ).isNotNull ();
@@ -132,33 +134,69 @@ public void sessionInvalidatedBeforeSave() {
132
134
133
135
@ Test
134
136
void expirationCheckPeriod () {
135
-
136
- DirectFieldAccessor accessor = new DirectFieldAccessor (this .store );
137
- Map <?,?> sessions = (Map <?, ?>) accessor .getPropertyValue ("sessions" );
138
- assertThat (sessions ).isNotNull ();
139
-
140
137
// Create 100 sessions
141
- IntStream .range ( 0 , 100 ).forEach (i -> insertSession ());
142
- assertThat ( sessions ). hasSize (100 );
138
+ IntStream .rangeClosed ( 1 , 100 ).forEach (i -> insertSession ());
139
+ assertNumSessions (100 );
143
140
144
- // Force a new clock (31 min later), don't use setter which would clean expired sessions
141
+ // Force a new clock (31 min later). Don't use setter which would clean expired sessions.
142
+ DirectFieldAccessor accessor = new DirectFieldAccessor (this .store );
145
143
accessor .setPropertyValue ("clock" , Clock .offset (this .store .getClock (), Duration .ofMinutes (31 )));
146
- assertThat ( sessions ). hasSize (100 );
144
+ assertNumSessions (100 );
147
145
148
- // Create 1 more which forces a time-based check (clock moved forward)
146
+ // Create 1 more which forces a time-based check (clock moved forward).
149
147
insertSession ();
150
- assertThat ( sessions ). hasSize (1 );
148
+ assertNumSessions (1 );
151
149
}
152
150
153
151
@ Test
154
152
void maxSessions () {
153
+ this .store .setMaxSessions (10 );
154
+
155
+ IntStream .rangeClosed (1 , 10 ).forEach (i -> insertSession ());
156
+ assertThatIllegalStateException ()
157
+ .isThrownBy (this ::insertSession )
158
+ .withMessage ("Max sessions limit reached: 10" );
159
+ }
155
160
156
- IntStream .range (0 , 10000 ).forEach (i -> insertSession ());
157
- assertThatIllegalStateException ().isThrownBy (
158
- this ::insertSession )
159
- .withMessage ("Max sessions limit reached: 10000" );
161
+ @ Test
162
+ void updateSession () {
163
+ WebSession session = insertSession ();
164
+
165
+ StepVerifier .create (session .save ())
166
+ .expectComplete ()
167
+ .verify ();
168
+ }
169
+
170
+ @ Test // gh-35013
171
+ void updateSessionAfterMaxSessionLimitIsExceeded () {
172
+ this .store .setMaxSessions (10 );
173
+
174
+ WebSession session = insertSession ();
175
+ assertNumSessions (1 );
176
+
177
+ IntStream .rangeClosed (1 , 9 ).forEach (i -> insertSession ());
178
+ assertNumSessions (10 );
179
+
180
+ // Updating an existing session should succeed.
181
+ StepVerifier .create (session .save ())
182
+ .expectComplete ()
183
+ .verify ();
184
+ assertNumSessions (10 );
185
+
186
+ // Saving an additional new session should fail.
187
+ assertThatIllegalStateException ()
188
+ .isThrownBy (this ::insertSession )
189
+ .withMessage ("Max sessions limit reached: 10" );
190
+ assertNumSessions (10 );
191
+
192
+ // Updating an existing session again should still succeed.
193
+ StepVerifier .create (session .save ())
194
+ .expectComplete ()
195
+ .verify ();
196
+ assertNumSessions (10 );
160
197
}
161
198
199
+
162
200
private WebSession insertSession () {
163
201
WebSession session = this .store .createWebSession ().block ();
164
202
assertThat (session ).isNotNull ();
@@ -167,4 +205,8 @@ private WebSession insertSession() {
167
205
return session ;
168
206
}
169
207
208
+ private void assertNumSessions (int numSessions ) {
209
+ assertThat (store .getSessions ()).hasSize (numSessions );
210
+ }
211
+
170
212
}
0 commit comments