diff options
Diffstat (limited to 'language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java')
-rw-r--r-- | language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java new file mode 100644 index 00000000..6ad82d7f --- /dev/null +++ b/language-web/src/test/java/tools/refinery/language/web/xtext/servlet/TransactionExecutorTest.java | |||
@@ -0,0 +1,151 @@ | |||
1 | package tools.refinery.language.web.xtext.servlet; | ||
2 | |||
3 | import static org.hamcrest.MatcherAssert.assertThat; | ||
4 | import static org.hamcrest.Matchers.equalTo; | ||
5 | import static org.hamcrest.Matchers.hasProperty; | ||
6 | import static org.hamcrest.Matchers.instanceOf; | ||
7 | import static org.junit.jupiter.api.Assertions.fail; | ||
8 | import static org.mockito.Mockito.mock; | ||
9 | import static org.mockito.Mockito.times; | ||
10 | import static org.mockito.Mockito.verify; | ||
11 | |||
12 | import java.io.IOException; | ||
13 | import java.util.List; | ||
14 | import java.util.Map; | ||
15 | import java.util.UUID; | ||
16 | |||
17 | import org.eclipse.xtext.resource.IResourceServiceProvider; | ||
18 | import org.eclipse.xtext.testing.InjectWith; | ||
19 | import org.eclipse.xtext.testing.extensions.InjectionExtension; | ||
20 | import org.eclipse.xtext.web.server.ServiceConflictResult; | ||
21 | import org.eclipse.xtext.web.server.model.DocumentStateResult; | ||
22 | import org.eclipse.xtext.web.server.validation.ValidationResult; | ||
23 | import org.hamcrest.Matcher; | ||
24 | import org.junit.jupiter.api.BeforeEach; | ||
25 | import org.junit.jupiter.api.Test; | ||
26 | import org.junit.jupiter.api.extension.ExtendWith; | ||
27 | import org.mockito.ArgumentCaptor; | ||
28 | import org.mockito.junit.jupiter.MockitoExtension; | ||
29 | |||
30 | import com.google.inject.Inject; | ||
31 | |||
32 | @ExtendWith(MockitoExtension.class) | ||
33 | @ExtendWith(InjectionExtension.class) | ||
34 | @InjectWith(ProblemWebInjectorProvider.class) | ||
35 | class TransactionExecutorTest { | ||
36 | private static final String RESOURCE_NAME = "test.problem"; | ||
37 | |||
38 | private static final String INVALID_STATE_ID = "<invalid_state>"; | ||
39 | |||
40 | private static final String TEST_PROBLEM = """ | ||
41 | class Person { | ||
42 | Person friend[0..*] opposite friend | ||
43 | } | ||
44 | |||
45 | friend(a, b). | ||
46 | """; | ||
47 | |||
48 | private static final Map<String, String> UPDATE_FULL_TEXT_PARAMS = Map.of("serviceType", "update", "fullText", | ||
49 | TEST_PROBLEM); | ||
50 | |||
51 | private static final Map<String, String> VALIDATE_PARAMS = Map.of("serviceType", "validate"); | ||
52 | |||
53 | @Inject | ||
54 | private IResourceServiceProvider.Registry resourceServiceProviderRegistry; | ||
55 | |||
56 | private TransactionExecutor transactionExecutor; | ||
57 | |||
58 | @BeforeEach | ||
59 | void beforeEach() { | ||
60 | transactionExecutor = new TransactionExecutor(new SimpleSession(), resourceServiceProviderRegistry); | ||
61 | } | ||
62 | |||
63 | @Test | ||
64 | void emptyBatchTest() { | ||
65 | performBatchRequest(null); | ||
66 | } | ||
67 | |||
68 | @Test | ||
69 | void fullTextUpdateTest() { | ||
70 | var response = performSingleRequest(null, UPDATE_FULL_TEXT_PARAMS); | ||
71 | assertThat(response, hasResponseData(instanceOf(DocumentStateResult.class))); | ||
72 | } | ||
73 | |||
74 | @Test | ||
75 | void validationAfterFullTextUpdateInSameBatchTest() { | ||
76 | var response = performBatchRequest(null, UPDATE_FULL_TEXT_PARAMS, VALIDATE_PARAMS).get(1); | ||
77 | assertThat(response, hasResponseData(instanceOf(ValidationResult.class))); | ||
78 | } | ||
79 | |||
80 | @Test | ||
81 | void validationAfterFullTextUpdateInDifferentBatchTest() { | ||
82 | var stateId = updateFullText(); | ||
83 | var validateResponse = performSingleRequest(stateId, VALIDATE_PARAMS); | ||
84 | assertThat(validateResponse, hasResponseData(instanceOf(ValidationResult.class))); | ||
85 | } | ||
86 | |||
87 | @Test | ||
88 | void conflictTest() { | ||
89 | updateFullText(); | ||
90 | var response = performSingleRequest(INVALID_STATE_ID, VALIDATE_PARAMS); | ||
91 | assertThat(response, hasResponseData(instanceOf(ServiceConflictResult.class))); | ||
92 | } | ||
93 | |||
94 | @Test | ||
95 | void transactionCancelledDueToConflictTest() { | ||
96 | updateFullText(); | ||
97 | var response = performBatchRequest(INVALID_STATE_ID, VALIDATE_PARAMS, VALIDATE_PARAMS).get(1); | ||
98 | assertThat(response, hasErrorKind(equalTo(XtextWebSocketErrorKind.TRANSACTION_CANCELLED))); | ||
99 | } | ||
100 | |||
101 | @SafeVarargs | ||
102 | private List<XtextWebSocketResponse> performBatchRequest(String requiredStateId, Map<String, String>... params) { | ||
103 | var id = UUID.randomUUID().toString(); | ||
104 | var request = new XtextWebSocketRequest(id, RESOURCE_NAME, null, requiredStateId, List.of(params)); | ||
105 | |||
106 | var responseHandler = mock(ResponseHandler.class); | ||
107 | try { | ||
108 | transactionExecutor.handleRequest(request, responseHandler); | ||
109 | } catch (IOException e) { | ||
110 | fail("Unexpected IOException", e); | ||
111 | } | ||
112 | |||
113 | var captor = ArgumentCaptor.forClass(XtextWebSocketResponse.class); | ||
114 | int nParams = params.length; | ||
115 | try { | ||
116 | verify(responseHandler, times(nParams)).onResponse(captor.capture()); | ||
117 | } catch (IOException e) { | ||
118 | throw new RuntimeException("Mockito threw unexcepted exception", e); | ||
119 | } | ||
120 | var allResponses = captor.getAllValues(); | ||
121 | for (int i = 0; i < nParams; i++) { | ||
122 | var response = allResponses.get(i); | ||
123 | assertThat(response, hasProperty("id", equalTo(id))); | ||
124 | assertThat(response, hasProperty("index", equalTo(i))); | ||
125 | } | ||
126 | return allResponses; | ||
127 | } | ||
128 | |||
129 | private XtextWebSocketResponse performSingleRequest(String requiredStateId, Map<String, String> param) { | ||
130 | return performBatchRequest(requiredStateId, param).get(0); | ||
131 | } | ||
132 | |||
133 | private String updateFullText() { | ||
134 | var updateResponse = (XtextWebSocketOkResponse) performSingleRequest(null, UPDATE_FULL_TEXT_PARAMS); | ||
135 | var documentStateResult = (DocumentStateResult) updateResponse.getResponseData(); | ||
136 | var stateId = documentStateResult.getStateId(); | ||
137 | if (INVALID_STATE_ID.equals(stateId)) { | ||
138 | throw new RuntimeException("Service returned unexpected stateId: " + stateId); | ||
139 | } | ||
140 | return stateId; | ||
141 | } | ||
142 | |||
143 | private static Matcher<XtextWebSocketResponse> hasResponseData(Matcher<?> responseDataMatcher) { | ||
144 | return hasProperty("responseData", responseDataMatcher); | ||
145 | } | ||
146 | |||
147 | private static Matcher<XtextWebSocketResponse> hasErrorKind( | ||
148 | Matcher<? extends XtextWebSocketErrorKind> errorKindMatcher) { | ||
149 | return hasProperty("errorKind", errorKindMatcher); | ||
150 | } | ||
151 | } | ||