Skip to content

Commit 3c5d074

Browse files
test: cover @requestScope isolation in multi-user tests
1 parent 38279ec commit 3c5d074

3 files changed

Lines changed: 107 additions & 0 deletions

File tree

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright 2000-2026 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.testapp.sessionscope;
17+
18+
import org.springframework.stereotype.Component;
19+
import org.springframework.web.context.annotation.RequestScope;
20+
21+
import com.vaadin.flow.signals.local.ValueSignal;
22+
23+
/**
24+
* A Spring request-scoped bean holding a per-request local signal, used to
25+
* reproduce
26+
* <a href="https://github.com/vaadin/browserless-test/issues/110">#110</a> for
27+
* {@code @RequestScope}.
28+
*/
29+
@Component
30+
@RequestScope
31+
public class RequestPrefs {
32+
private final ValueSignal<String> color = new ValueSignal<>("white");
33+
34+
public ValueSignal<String> color() {
35+
return color;
36+
}
37+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright 2000-2026 Vaadin Ltd.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package com.testapp.sessionscope;
17+
18+
import com.vaadin.flow.component.html.Div;
19+
import com.vaadin.flow.router.Route;
20+
import com.vaadin.flow.signals.local.ValueSignal;
21+
22+
/**
23+
* View that binds a request-scoped local signal, used to reproduce
24+
* <a href="https://github.com/vaadin/browserless-test/issues/110">#110</a> for
25+
* {@code @RequestScope}.
26+
*/
27+
@Route("repro-request")
28+
public class RequestScopeView extends Div {
29+
30+
private final ValueSignal<String> boundSignal;
31+
32+
public RequestScopeView(RequestPrefs prefs) {
33+
// Resolve the request-scoped signal once. With per-user request-context
34+
// binding this is the active user's own request signal; without it the
35+
// second user reuses the first user's signal and the binding below
36+
// throws a cross-session IllegalStateException.
37+
this.boundSignal = prefs.color();
38+
getStyle().bind("background-color", boundSignal);
39+
}
40+
41+
/**
42+
* The request-scoped {@link ValueSignal} resolved for this view's user.
43+
*/
44+
public ValueSignal<String> boundSignal() {
45+
return boundSignal;
46+
}
47+
}

spring/src/test/java/com/vaadin/browserless/MultiUserSessionScopeReproTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
import com.testapp.sessionscope.Prefs;
1919
import com.testapp.sessionscope.ReproView;
20+
import com.testapp.sessionscope.RequestScopeView;
2021
import org.junit.jupiter.api.AfterEach;
2122
import org.junit.jupiter.api.Assertions;
2223
import org.junit.jupiter.api.BeforeEach;
@@ -89,6 +90,28 @@ void twoUsersEachGetOwnSessionScopedBean() {
8990
"Each user must resolve its own session-scoped signal instance");
9091
}
9192

93+
@Test
94+
void twoUsersEachGetOwnRequestScopedBean() {
95+
var alice = app.newUser("alice", "USER");
96+
var bob = app.newUser("bob", "USER");
97+
98+
var aliceWindow = alice.newWindow();
99+
aliceWindow.navigate(RequestScopeView.class);
100+
RequestScopeView aliceView = (RequestScopeView) aliceWindow
101+
.getCurrentView();
102+
103+
// Navigating bob would throw a cross-session IllegalStateException if
104+
// bob reused alice's request-scoped bean (and therefore alice's
105+
// signal).
106+
var bobWindow = bob.newWindow();
107+
bobWindow.navigate(RequestScopeView.class);
108+
RequestScopeView bobView = (RequestScopeView) bobWindow
109+
.getCurrentView();
110+
111+
Assertions.assertNotSame(aliceView.boundSignal(), bobView.boundSignal(),
112+
"Each user must resolve its own request-scoped signal instance");
113+
}
114+
92115
@Configuration
93116
@ComponentScan(basePackageClasses = Prefs.class)
94117
static class TestConfig {

0 commit comments

Comments
 (0)