java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java
... ...
@@ -35,6 +35,7 @@ import javax.ws.rs.core.StreamingOutput;
35 35
import org.apache.shiro.SecurityUtils;
36 36
import org.apache.shiro.mgt.SecurityManager;
37 37
import org.apache.shiro.subject.Subject;
38
+import org.apache.shiro.util.ThreadContext;
38 39
import org.junit.jupiter.api.AfterEach;
39 40
import org.junit.jupiter.api.Assertions;
40 41
import org.junit.jupiter.api.BeforeEach;
... ...
@@ -194,6 +195,8 @@ public class MasterDataImportTest {
194 195
195 196
@AfterEach
196 197
public void tearDown() {
198
+ ThreadContext.unbindSecurityManager();
199
+ ThreadContext.unbindSubject();
197 200
deleteAllDataFromDatabase();
198 201
}
199 202
... ...
@@ -205,8 +208,16 @@ public class MasterDataImportTest {
205 208
securityService = Mockito.mock(SecurityService.class);
206 209
SecurityManager securityManager = Mockito.mock(org.apache.shiro.mgt.SecurityManager.class);
207 210
Subject fakeSubject = Mockito.mock(Subject.class);
208
- SecurityUtils.setSecurityManager(securityManager);
211
+ // Stub the mock BEFORE installing it as the global SecurityManager to avoid a race
212
+ // condition: SecurityUtils.setSecurityManager() sets a JVM-wide static singleton.
213
+ // Any thread that calls SecurityUtils.getSubject() (when no Subject is bound to its
214
+ // ThreadContext) will trigger securityManager.createSubject(). If that happens between
215
+ // the .when(securityManager) call (which sets pending doAnswer-style answers on the
216
+ // mock's InvocationContainer) and the .createSubject() call (which completes the
217
+ // stubbing), the other thread's call consumes the pending answers first, causing an
218
+ // AssertionError in InvocationContainerImpl.setMethodForStubbing (line 123).
209 219
Mockito.doReturn(fakeSubject).when(securityManager).createSubject(Mockito.any());
220
+ SecurityUtils.setSecurityManager(securityManager);
210 221
Mockito.doReturn(defaultTenant).when(securityService).getServerGroup();
211 222
Mockito.doReturn(currentUser).when(securityService).getCurrentUser();
212 223
Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any());
java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java
... ...
@@ -20,6 +20,8 @@ import java.util.UUID;
20 20
import org.apache.shiro.SecurityUtils;
21 21
import org.apache.shiro.mgt.SecurityManager;
22 22
import org.apache.shiro.subject.Subject;
23
+import org.apache.shiro.util.ThreadContext;
24
+import org.junit.jupiter.api.AfterEach;
23 25
import org.junit.jupiter.api.BeforeEach;
24 26
import org.junit.jupiter.api.Test;
25 27
import org.mockito.Mockito;
... ...
@@ -114,6 +116,12 @@ public class SearchServiceTest {
114 116
private DynamicTrackedRace aalOrcTrackedR2;
115 117
private SecurityService securityService;
116 118
119
+ @AfterEach
120
+ public void tearDown() {
121
+ ThreadContext.unbindSecurityManager();
122
+ ThreadContext.unbindSubject();
123
+ }
124
+
117 125
@BeforeEach
118 126
public void setUp() {
119 127
UserGroupImpl defaultTenant = new UserGroupImpl(new UUID(0, 1), "defaultTenant");
... ...
@@ -121,8 +129,16 @@ public class SearchServiceTest {
121 129
securityService = Mockito.mock(SecurityService.class);
122 130
SecurityManager securityManager = Mockito.mock(org.apache.shiro.mgt.SecurityManager.class);
123 131
Subject fakeSubject = Mockito.mock(Subject.class);
124
- SecurityUtils.setSecurityManager(securityManager);
132
+ // Stub the mock BEFORE installing it as the global SecurityManager to avoid a race
133
+ // condition: SecurityUtils.setSecurityManager() sets a JVM-wide static singleton.
134
+ // Any thread that calls SecurityUtils.getSubject() (when no Subject is bound to its
135
+ // ThreadContext) will trigger securityManager.createSubject(). If that happens between
136
+ // the .when(securityManager) call (which sets pending doAnswer-style answers on the
137
+ // mock's InvocationContainer) and the .createSubject() call (which completes the
138
+ // stubbing), the other thread's call consumes the pending answers first, causing an
139
+ // AssertionError in InvocationContainerImpl.setMethodForStubbing (line 123).
125 140
Mockito.doReturn(fakeSubject).when(securityManager).createSubject(Mockito.any());
141
+ SecurityUtils.setSecurityManager(securityManager);
126 142
Mockito.doReturn(defaultTenant).when(securityService).getServerGroup();
127 143
Mockito.doReturn(currentUser).when(securityService).getCurrentUser();
128 144
Mockito.doReturn(true).when(securityService).hasCurrentUserReadPermission(Mockito.any());