PageRenderTime 47ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/sdk/core/azure-core/src/test/java/com/azure/core/util/polling/PollerTests.java

http://github.com/WindowsAzure/azure-sdk-for-java
Java | 979 lines | 820 code | 144 blank | 15 comment | 49 complexity | 20a80a114ace7b49f92c3d08b2ebda91 MD5 | raw file
Possible License(s): MIT
  1. // Copyright (c) Microsoft Corporation. All rights reserved.
  2. // Licensed under the MIT License.
  3. package com.azure.core.util.polling;
  4. import org.junit.jupiter.api.AfterAll;
  5. import org.junit.jupiter.api.AfterEach;
  6. import org.junit.jupiter.api.Assertions;
  7. import org.junit.jupiter.api.BeforeAll;
  8. import org.junit.jupiter.api.BeforeEach;
  9. import org.junit.jupiter.api.Test;
  10. import org.mockito.Mock;
  11. import org.mockito.Mockito;
  12. import org.mockito.MockitoAnnotations;
  13. import org.mockito.stubbing.Answer;
  14. import reactor.core.publisher.Mono;
  15. import reactor.test.StepVerifier;
  16. import java.time.Duration;
  17. import java.util.ArrayList;
  18. import java.util.List;
  19. import java.util.concurrent.CountDownLatch;
  20. import java.util.concurrent.TimeUnit;
  21. import java.util.concurrent.atomic.AtomicReference;
  22. import java.util.function.BiFunction;
  23. import java.util.function.Function;
  24. import static com.azure.core.util.polling.LongRunningOperationStatus.FAILED;
  25. import static com.azure.core.util.polling.LongRunningOperationStatus.IN_PROGRESS;
  26. import static com.azure.core.util.polling.LongRunningOperationStatus.SUCCESSFULLY_COMPLETED;
  27. import static com.azure.core.util.polling.PollerFlux.create;
  28. import static com.azure.core.util.polling.PollerFlux.error;
  29. import static org.junit.jupiter.api.Assertions.assertEquals;
  30. import static org.junit.jupiter.api.Assertions.assertThrows;
  31. import static org.junit.jupiter.api.Assertions.fail;
  32. import static org.mockito.ArgumentMatchers.any;
  33. import static org.mockito.Mockito.when;
  34. @SuppressWarnings("unchecked")
  35. public class PollerTests {
  36. @Mock
  37. private Function<PollingContext<Response>, Mono<Response>> activationOperation;
  38. @Mock
  39. private Function<PollingContext<Response>, Mono<PollResponse<Response>>> activationOperationWithResponse;
  40. @Mock
  41. private Function<PollingContext<Response>, Mono<PollResponse<Response>>> pollOperation;
  42. @Mock
  43. private Function<PollingContext<Response>, Mono<CertificateOutput>> fetchResultOperation;
  44. @Mock
  45. private BiFunction<PollingContext<Response>, PollResponse<Response>, Mono<Response>> cancelOperation;
  46. private AutoCloseable openMocks;
  47. @BeforeEach
  48. public void beforeTest() {
  49. this.openMocks = MockitoAnnotations.openMocks(this);
  50. }
  51. @AfterEach
  52. public void afterTest() throws Exception {
  53. openMocks.close();
  54. Mockito.framework().clearInlineMock(this);
  55. }
  56. @Test
  57. public void asyncPollerConstructorPollIntervalZero() {
  58. assertThrows(IllegalArgumentException.class, () -> new PollerFlux<>(
  59. Duration.ZERO,
  60. activationOperation,
  61. pollOperation,
  62. cancelOperation,
  63. fetchResultOperation));
  64. }
  65. @Test
  66. public void asyncPollerConstructorPollIntervalNegative() {
  67. assertThrows(IllegalArgumentException.class, () -> new PollerFlux<>(
  68. Duration.ofSeconds(-1),
  69. activationOperation,
  70. pollOperation,
  71. cancelOperation,
  72. fetchResultOperation));
  73. }
  74. @Test
  75. public void asyncPollerConstructorPollIntervalNull() {
  76. assertThrows(NullPointerException.class, () -> new PollerFlux<>(
  77. null,
  78. activationOperation,
  79. pollOperation,
  80. cancelOperation,
  81. fetchResultOperation));
  82. }
  83. @Test
  84. public void asyncPollerConstructorActivationOperationNull() {
  85. assertThrows(NullPointerException.class, () -> new PollerFlux<>(
  86. Duration.ofSeconds(1),
  87. null,
  88. pollOperation,
  89. cancelOperation,
  90. fetchResultOperation));
  91. }
  92. @Test
  93. public void asyncPollerConstructorPollOperationNull() {
  94. assertThrows(NullPointerException.class, () -> new PollerFlux<>(
  95. Duration.ofSeconds(1),
  96. activationOperation,
  97. null,
  98. cancelOperation,
  99. fetchResultOperation));
  100. }
  101. @Test
  102. public void asyncPollerConstructorCancelOperationNull() {
  103. assertThrows(NullPointerException.class, () -> new PollerFlux<>(
  104. Duration.ofSeconds(1),
  105. activationOperation,
  106. pollOperation,
  107. null,
  108. fetchResultOperation));
  109. }
  110. @Test
  111. public void asyncPollerConstructorFetchResultOperationNull() {
  112. assertThrows(NullPointerException.class, () -> new PollerFlux<>(
  113. Duration.ofSeconds(1),
  114. activationOperation,
  115. pollOperation,
  116. cancelOperation,
  117. null));
  118. }
  119. @Test
  120. public void subscribeToSpecificOtherOperationStatusTest() {
  121. // Arrange
  122. final Duration retryAfter = Duration.ofMillis(10);
  123. //
  124. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  125. new Response("0"), retryAfter);
  126. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  127. new Response("1"), retryAfter);
  128. PollResponse<Response> response2 = new PollResponse<>(
  129. LongRunningOperationStatus.fromString("OTHER_1", false),
  130. new Response("2"), retryAfter);
  131. PollResponse<Response> response3 = new PollResponse<>(
  132. LongRunningOperationStatus.fromString("OTHER_2", false),
  133. new Response("3"), retryAfter);
  134. PollResponse<Response> response4 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  135. new Response("4"), retryAfter);
  136. when(activationOperation.apply(any())).thenReturn(Mono.empty());
  137. when(pollOperation.apply(any())).thenReturn(
  138. Mono.just(response0),
  139. Mono.just(response1),
  140. Mono.just(response2),
  141. Mono.just(response3),
  142. Mono.just(response4));
  143. // Act
  144. PollerFlux<Response, CertificateOutput> pollerFlux = new PollerFlux<>(
  145. Duration.ofMillis(10),
  146. activationOperation,
  147. pollOperation,
  148. cancelOperation,
  149. fetchResultOperation);
  150. // Assert
  151. StepVerifier.create(pollerFlux)
  152. .expectSubscription()
  153. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response0.getStatus())
  154. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response1.getStatus())
  155. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response2.getStatus())
  156. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response3.getStatus())
  157. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response4.getStatus())
  158. .verifyComplete();
  159. }
  160. @Test
  161. public void noPollingForSynchronouslyCompletedActivationTest() {
  162. int[] activationCallCount = new int[1];
  163. activationCallCount[0] = 0;
  164. when(activationOperationWithResponse.apply(any())).thenReturn(Mono.defer(() -> {
  165. activationCallCount[0]++;
  166. return Mono.just(new PollResponse<Response>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  167. new Response("ActivationDone")));
  168. }));
  169. PollerFlux<Response, CertificateOutput> pollerFlux = create(
  170. Duration.ofMillis(10),
  171. activationOperationWithResponse,
  172. pollOperation,
  173. cancelOperation,
  174. fetchResultOperation);
  175. when(pollOperation.apply(any())).thenReturn(
  176. Mono.error(new RuntimeException("Polling shouldn't happen for synchronously completed activation.")));
  177. StepVerifier.create(pollerFlux)
  178. .expectSubscription()
  179. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus()
  180. == LongRunningOperationStatus.SUCCESSFULLY_COMPLETED)
  181. .verifyComplete();
  182. assertEquals(1, activationCallCount[0]);
  183. }
  184. @Test
  185. public void noPollingForSynchronouslyCompletedActivationInSyncPollerTest() {
  186. int[] activationCallCount = new int[1];
  187. activationCallCount[0] = 0;
  188. when(activationOperationWithResponse.apply(any())).thenReturn(Mono.defer(() -> {
  189. activationCallCount[0]++;
  190. return Mono.just(new PollResponse<Response>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  191. new Response("ActivationDone")));
  192. }));
  193. SyncPoller<Response, CertificateOutput> syncPoller = create(
  194. Duration.ofMillis(10),
  195. activationOperationWithResponse,
  196. pollOperation,
  197. cancelOperation,
  198. fetchResultOperation)
  199. .getSyncPoller();
  200. when(pollOperation.apply(any())).thenReturn(
  201. Mono.error(new RuntimeException("Polling shouldn't happen for synchronously completed activation.")));
  202. try {
  203. PollResponse<Response> response = syncPoller.waitForCompletion(Duration.ofSeconds(1));
  204. assertEquals(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, response.getStatus());
  205. assertEquals(1, activationCallCount[0]);
  206. } catch (Exception e) {
  207. fail("SyncPoller did not complete on activation", e);
  208. }
  209. }
  210. @Test
  211. public void ensurePollingForInProgressActivationResponseTest() {
  212. final Duration retryAfter = Duration.ofMillis(10);
  213. int[] activationCallCount = new int[1];
  214. activationCallCount[0] = 0;
  215. when(activationOperationWithResponse.apply(any())).thenReturn(Mono.defer(() -> {
  216. activationCallCount[0]++;
  217. return Mono.just(new PollResponse<Response>(IN_PROGRESS,
  218. new Response("ActivationDone")));
  219. }));
  220. PollerFlux<Response, CertificateOutput> pollerFlux = create(
  221. Duration.ofMillis(10),
  222. activationOperationWithResponse,
  223. pollOperation,
  224. cancelOperation,
  225. fetchResultOperation);
  226. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  227. new Response("0"), retryAfter);
  228. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  229. new Response("1"), retryAfter);
  230. PollResponse<Response> response2 = new PollResponse<>(
  231. LongRunningOperationStatus.fromString("OTHER_1", false),
  232. new Response("2"), retryAfter);
  233. PollResponse<Response> response3 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  234. new Response("3"), retryAfter);
  235. when(pollOperation.apply(any())).thenReturn(
  236. Mono.just(response0),
  237. Mono.just(response1),
  238. Mono.just(response2),
  239. Mono.just(response3));
  240. StepVerifier.create(pollerFlux)
  241. .expectSubscription()
  242. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response0.getStatus())
  243. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response1.getStatus())
  244. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response2.getStatus())
  245. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response3.getStatus())
  246. .verifyComplete();
  247. assertEquals(1, activationCallCount[0]);
  248. }
  249. @Test
  250. public void subscribeToActivationOnlyOnceTest() {
  251. // Arrange
  252. final Duration retryAfter = Duration.ofMillis(10);
  253. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  254. new Response("0"), retryAfter);
  255. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  256. new Response("1"), retryAfter);
  257. PollResponse<Response> response2 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  258. new Response("2"), retryAfter);
  259. int[] activationCallCount = new int[1];
  260. activationCallCount[0] = 0;
  261. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> {
  262. activationCallCount[0]++;
  263. return Mono.just(new Response("ActivationDone"));
  264. }));
  265. PollerFlux<Response, CertificateOutput> pollerFlux = new PollerFlux<>(
  266. Duration.ofMillis(10),
  267. activationOperation,
  268. pollOperation,
  269. cancelOperation,
  270. fetchResultOperation);
  271. when(pollOperation.apply(any())).thenReturn(
  272. Mono.just(response0),
  273. Mono.just(response1),
  274. Mono.just(response2));
  275. StepVerifier.create(pollerFlux)
  276. .expectSubscription()
  277. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response0.getStatus())
  278. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response1.getStatus())
  279. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response2.getStatus())
  280. .verifyComplete();
  281. when(pollOperation.apply(any())).thenReturn(
  282. Mono.just(response0),
  283. Mono.just(response1),
  284. Mono.just(response2));
  285. StepVerifier.create(pollerFlux)
  286. .expectSubscription()
  287. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response0.getStatus())
  288. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response1.getStatus())
  289. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == response2.getStatus())
  290. .verifyComplete();
  291. assertEquals(1, activationCallCount[0]);
  292. }
  293. @Test
  294. public void cancellationCanBeCalledFromOperatorChainTest() {
  295. final Duration retryAfter = Duration.ofMillis(10);
  296. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  297. new Response("0"), retryAfter);
  298. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  299. new Response("1"), retryAfter);
  300. PollResponse<Response> response2 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  301. new Response("2"), retryAfter);
  302. final Response activationResponse = new Response("Foo");
  303. when(activationOperation.apply(any()))
  304. .thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  305. final List<Object> cancelParameters = new ArrayList<>();
  306. when(cancelOperation.apply(any(), any())).thenAnswer((Answer) invocation -> {
  307. for (Object argument : invocation.getArguments()) {
  308. cancelParameters.add(argument);
  309. }
  310. return Mono.just(new Response("OperationCancelled"));
  311. });
  312. PollerFlux<Response, CertificateOutput> pollerFlux = new PollerFlux<>(
  313. Duration.ofMillis(10),
  314. activationOperation,
  315. pollOperation,
  316. cancelOperation,
  317. fetchResultOperation);
  318. when(pollOperation.apply(any())).thenReturn(
  319. Mono.just(response0),
  320. Mono.just(response1),
  321. Mono.just(response2));
  322. @SuppressWarnings({"rawtypes"})
  323. final AsyncPollResponse<Response, CertificateOutput>[] secondAsyncResponse = new AsyncPollResponse[1];
  324. secondAsyncResponse[0] = null;
  325. //
  326. Response cancelResponse = pollerFlux
  327. .take(2)
  328. .last()
  329. .flatMap((Function<AsyncPollResponse<Response, CertificateOutput>, Mono<Response>>) asyncPollResponse -> {
  330. secondAsyncResponse[0] = asyncPollResponse;
  331. return asyncPollResponse.cancelOperation();
  332. }).block();
  333. Assertions.assertNotNull(cancelResponse);
  334. Assertions.assertTrue(cancelResponse.getResponse().equalsIgnoreCase("OperationCancelled"));
  335. Assertions.assertNotNull(secondAsyncResponse[0]);
  336. Assertions.assertTrue(secondAsyncResponse[0].getValue().getResponse().equalsIgnoreCase("1"));
  337. assertEquals(2, cancelParameters.size());
  338. cancelParameters.get(0).equals(activationResponse);
  339. cancelParameters.get(1).equals(response1);
  340. }
  341. @Test
  342. public void getResultCanBeCalledFromOperatorChainTest() {
  343. final Duration retryAfter = Duration.ofMillis(10);
  344. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  345. new Response("0"), retryAfter);
  346. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  347. new Response("1"), retryAfter);
  348. PollResponse<Response> response2 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  349. new Response("2"), retryAfter);
  350. final Response activationResponse = new Response("Foo");
  351. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  352. final List<Object> fetchResultParameters = new ArrayList<>();
  353. when(fetchResultOperation.apply(any())).thenAnswer((Answer) invocation -> {
  354. for (Object argument : invocation.getArguments()) {
  355. fetchResultParameters.add(argument);
  356. }
  357. return Mono.just(new CertificateOutput("LROFinalResult"));
  358. });
  359. PollerFlux<Response, CertificateOutput> pollerFlux = new PollerFlux<>(
  360. Duration.ofMillis(10),
  361. activationOperation,
  362. pollOperation,
  363. cancelOperation,
  364. fetchResultOperation);
  365. when(pollOperation.apply(any())).thenReturn(
  366. Mono.just(response0),
  367. Mono.just(response1),
  368. Mono.just(response2));
  369. @SuppressWarnings({"rawtypes"})
  370. final AsyncPollResponse<Response, CertificateOutput>[] terminalAsyncResponse = new AsyncPollResponse[1];
  371. terminalAsyncResponse[0] = null;
  372. //
  373. CertificateOutput lroResult = pollerFlux
  374. .takeUntil(apr -> apr.getStatus().isComplete())
  375. .last()
  376. .flatMap((Function<AsyncPollResponse<Response, CertificateOutput>, Mono<CertificateOutput>>)
  377. asyncPollResponse -> {
  378. terminalAsyncResponse[0] = asyncPollResponse;
  379. return asyncPollResponse.getFinalResult();
  380. }).block();
  381. Assertions.assertNotNull(lroResult);
  382. Assertions.assertTrue(lroResult.getName().equalsIgnoreCase("LROFinalResult"));
  383. Assertions.assertNotNull(terminalAsyncResponse[0]);
  384. Assertions.assertTrue(terminalAsyncResponse[0].getValue().getResponse().equalsIgnoreCase("2"));
  385. assertEquals(1, fetchResultParameters.size());
  386. Assertions.assertTrue(fetchResultParameters.get(0) instanceof PollingContext);
  387. PollingContext<Response> pollingContext = (PollingContext<Response>) fetchResultParameters.get(0);
  388. pollingContext.getActivationResponse().equals(activationResponse);
  389. pollingContext.getLatestResponse().equals(response2);
  390. }
  391. @Test
  392. public void verifyExceptionPropagationFromPollingOperation() {
  393. final Response activationResponse = new Response("Foo");
  394. when(activationOperation.apply(any()))
  395. .thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  396. final AtomicReference<Integer> cnt = new AtomicReference<>(0);
  397. pollOperation = (pollingContext) -> {
  398. cnt.getAndSet(cnt.get() + 1);
  399. if (cnt.get() <= 2) {
  400. return Mono.just(new PollResponse<Response>(IN_PROGRESS, new Response("1")));
  401. } else if (cnt.get() == 3) {
  402. throw new RuntimeException("Polling operation failed!");
  403. } else if (cnt.get() == 4) {
  404. return Mono.just(new PollResponse<Response>(IN_PROGRESS, new Response("2")));
  405. } else {
  406. return Mono.just(new PollResponse<Response>(SUCCESSFULLY_COMPLETED, new Response("3")));
  407. }
  408. };
  409. PollerFlux<Response, CertificateOutput> pollerFlux = new PollerFlux<>(
  410. Duration.ofMillis(10),
  411. activationOperation,
  412. pollOperation,
  413. cancelOperation,
  414. fetchResultOperation);
  415. StepVerifier.create(pollerFlux)
  416. .expectSubscription()
  417. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == IN_PROGRESS)
  418. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == IN_PROGRESS)
  419. .expectErrorMessage("Polling operation failed!")
  420. .verify();
  421. }
  422. @Test
  423. public void verifyErrorFromPollingOperation() {
  424. final Response activationResponse = new Response("Foo");
  425. when(activationOperation.apply(any()))
  426. .thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  427. final AtomicReference<Integer> cnt = new AtomicReference<>(0);
  428. pollOperation = (pollingContext) -> {
  429. cnt.getAndSet(cnt.get() + 1);
  430. if (cnt.get() <= 2) {
  431. return Mono.just(new PollResponse<Response>(IN_PROGRESS, new Response("1")));
  432. } else if (cnt.get() == 3) {
  433. return Mono.just(new PollResponse<Response>(FAILED, new Response("2")));
  434. } else if (cnt.get() == 4) {
  435. return Mono.just(new PollResponse<Response>(IN_PROGRESS, new Response("3")));
  436. } else {
  437. return Mono.just(new PollResponse<Response>(SUCCESSFULLY_COMPLETED, new Response("4")));
  438. }
  439. };
  440. PollerFlux<Response, CertificateOutput> pollerFlux = new PollerFlux<>(
  441. Duration.ofMillis(10),
  442. activationOperation,
  443. pollOperation,
  444. cancelOperation,
  445. fetchResultOperation);
  446. StepVerifier.create(pollerFlux)
  447. .expectSubscription()
  448. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == IN_PROGRESS)
  449. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == IN_PROGRESS)
  450. .expectNextMatches(asyncPollResponse -> asyncPollResponse.getStatus() == FAILED)
  451. .verifyComplete();
  452. }
  453. @Test
  454. public void syncPollerConstructorPollIntervalZero() {
  455. assertThrows(IllegalArgumentException.class, () -> new DefaultSyncPoller<>(
  456. Duration.ZERO,
  457. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  458. activationOperation.apply(cxt).block()),
  459. pollOperation,
  460. cancelOperation,
  461. fetchResultOperation));
  462. }
  463. @Test
  464. public void syncPollerConstructorPollIntervalNegative() {
  465. assertThrows(IllegalArgumentException.class, () -> new DefaultSyncPoller<>(
  466. Duration.ofSeconds(-1),
  467. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  468. activationOperation.apply(cxt).block()),
  469. pollOperation,
  470. cancelOperation,
  471. fetchResultOperation));
  472. }
  473. @Test
  474. public void syncPollerConstructorPollIntervalNull() {
  475. assertThrows(NullPointerException.class, () -> new DefaultSyncPoller<>(
  476. null,
  477. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  478. activationOperation.apply(cxt).block()),
  479. pollOperation,
  480. cancelOperation,
  481. fetchResultOperation));
  482. }
  483. @Test
  484. public void syncConstructorActivationOperationNull() {
  485. assertThrows(NullPointerException.class, () -> new DefaultSyncPoller<>(
  486. Duration.ofSeconds(1),
  487. null,
  488. pollOperation,
  489. cancelOperation,
  490. fetchResultOperation));
  491. }
  492. @Test
  493. public void syncPollerConstructorPollOperationNull() {
  494. assertThrows(NullPointerException.class, () -> new DefaultSyncPoller<>(
  495. Duration.ofSeconds(1),
  496. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  497. activationOperation.apply(cxt).block()),
  498. null,
  499. cancelOperation,
  500. fetchResultOperation));
  501. }
  502. @Test
  503. public void syncPollerConstructorCancelOperationNull() {
  504. assertThrows(NullPointerException.class, () -> new DefaultSyncPoller<>(
  505. Duration.ofSeconds(1),
  506. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  507. activationOperation.apply(cxt).block()),
  508. pollOperation,
  509. null,
  510. fetchResultOperation));
  511. }
  512. @Test
  513. public void syncPollerConstructorFetchResultOperationNull() {
  514. assertThrows(NullPointerException.class, () -> new DefaultSyncPoller<>(
  515. Duration.ofSeconds(1),
  516. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  517. activationOperation.apply(cxt).block()),
  518. pollOperation,
  519. cancelOperation,
  520. null));
  521. }
  522. @Test
  523. public void syncPollerShouldCallActivationFromConstructor() {
  524. Boolean[] activationCalled = new Boolean[1];
  525. activationCalled[0] = false;
  526. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> {
  527. activationCalled[0] = true;
  528. return Mono.just(new Response("ActivationDone"));
  529. }));
  530. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  531. Duration.ofMillis(10),
  532. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  533. activationOperation.apply(cxt).block()),
  534. pollOperation,
  535. cancelOperation,
  536. fetchResultOperation);
  537. Assertions.assertTrue(activationCalled[0]);
  538. }
  539. @Test
  540. public void eachPollShouldReceiveLastPollResponse() {
  541. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> Mono.just(new Response("A"))));
  542. when(pollOperation.apply(any())).thenAnswer((Answer) invocation -> {
  543. assertEquals(1, invocation.getArguments().length);
  544. Assertions.assertTrue(invocation.getArguments()[0] instanceof PollingContext);
  545. PollingContext<Response> pollingContext = (PollingContext<Response>) invocation.getArguments()[0];
  546. Assertions.assertTrue(pollingContext.getActivationResponse() instanceof PollResponse);
  547. Assertions.assertTrue(pollingContext.getLatestResponse() instanceof PollResponse);
  548. PollResponse<Response> latestResponse = pollingContext.getLatestResponse();
  549. Assertions.assertNotNull(latestResponse);
  550. PollResponse<Response> nextResponse = new PollResponse<>(IN_PROGRESS,
  551. new Response(latestResponse.getValue().toString() + "A"), Duration.ofMillis(10));
  552. return Mono.just(nextResponse);
  553. });
  554. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  555. Duration.ofMillis(10),
  556. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  557. activationOperation.apply(cxt).block()),
  558. pollOperation,
  559. cancelOperation,
  560. fetchResultOperation);
  561. PollResponse<Response> pollResponse = poller.poll();
  562. Assertions.assertNotNull(pollResponse);
  563. Assertions.assertNotNull(pollResponse.getValue().getResponse());
  564. Assertions.assertTrue(pollResponse.getValue()
  565. .getResponse()
  566. .equalsIgnoreCase("Response: AA"));
  567. //
  568. pollResponse = poller.poll();
  569. Assertions.assertNotNull(pollResponse);
  570. Assertions.assertNotNull(pollResponse.getValue().getResponse());
  571. Assertions.assertTrue(pollResponse.getValue()
  572. .getResponse()
  573. .equalsIgnoreCase("Response: Response: AAA"));
  574. //
  575. pollResponse = poller.poll();
  576. Assertions.assertNotNull(pollResponse);
  577. Assertions.assertNotNull(pollResponse.getValue().getResponse());
  578. Assertions.assertTrue(pollResponse.getValue()
  579. .getResponse()
  580. .equalsIgnoreCase("Response: Response: Response: AAAA"));
  581. }
  582. @Test
  583. public void waitForCompletionShouldReturnTerminalPollResponse() {
  584. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  585. new Response("0"), Duration.ofMillis(10));
  586. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  587. new Response("1"), Duration.ofMillis(10));
  588. PollResponse<Response> response2 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  589. new Response("2"), Duration.ofMillis(10));
  590. final Response activationResponse = new Response("Activated");
  591. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  592. when(pollOperation.apply(any())).thenReturn(
  593. Mono.just(response0),
  594. Mono.just(response1),
  595. Mono.just(response2));
  596. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  597. Duration.ofMillis(10),
  598. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  599. activationOperation.apply(cxt).block()),
  600. pollOperation,
  601. cancelOperation,
  602. fetchResultOperation);
  603. PollResponse<Response> pollResponse = poller.waitForCompletion();
  604. Assertions.assertNotNull(pollResponse.getValue());
  605. assertEquals(response2.getValue().getResponse(), pollResponse.getValue().getResponse());
  606. assertEquals(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, pollResponse.getStatus());
  607. }
  608. @Test
  609. public void getResultShouldPollUntilCompletionAndFetchResult() {
  610. final Response activationResponse = new Response("Activated");
  611. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  612. int[] invocationCount = new int[1];
  613. invocationCount[0] = -1;
  614. //
  615. when(pollOperation.apply(any())).thenAnswer((Answer<Mono<PollResponse<Response>>>) invocationOnMock -> {
  616. invocationCount[0]++;
  617. switch (invocationCount[0]) {
  618. case 0:
  619. return Mono.just(new PollResponse<>(IN_PROGRESS,
  620. new Response("0"), Duration.ofMillis(10)));
  621. case 1:
  622. return Mono.just(new PollResponse<>(IN_PROGRESS,
  623. new Response("1"), Duration.ofMillis(10)));
  624. case 2:
  625. return Mono.just(new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  626. new Response("2"), Duration.ofMillis(10)));
  627. default:
  628. throw new RuntimeException("Poll should not be called after terminal response");
  629. }
  630. });
  631. when(fetchResultOperation.apply(any())).thenReturn(Mono.defer(() -> {
  632. return Mono.just(new CertificateOutput("cert1"));
  633. }));
  634. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  635. Duration.ofMillis(10),
  636. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  637. activationOperation.apply(cxt).block()),
  638. pollOperation,
  639. cancelOperation,
  640. fetchResultOperation);
  641. CertificateOutput certificateOutput = poller.getFinalResult();
  642. Assertions.assertNotNull(certificateOutput);
  643. assertEquals("cert1", certificateOutput.getName());
  644. assertEquals(2, invocationCount[0]);
  645. }
  646. @Test
  647. public void getResultShouldNotPollOnCompletedPoller() {
  648. PollResponse<Response> response0 = new PollResponse<>(IN_PROGRESS,
  649. new Response("0"), Duration.ofMillis(10));
  650. PollResponse<Response> response1 = new PollResponse<>(IN_PROGRESS,
  651. new Response("1"), Duration.ofMillis(10));
  652. PollResponse<Response> response2 = new PollResponse<>(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED,
  653. new Response("2"), Duration.ofMillis(10));
  654. final Response activationResponse = new Response("Activated");
  655. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  656. when(fetchResultOperation.apply(any())).thenReturn(Mono.defer(() -> {
  657. return Mono.just(new CertificateOutput("cert1"));
  658. }));
  659. when(pollOperation.apply(any())).thenReturn(
  660. Mono.just(response0),
  661. Mono.just(response1),
  662. Mono.just(response2));
  663. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  664. Duration.ofMillis(10),
  665. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  666. activationOperation.apply(cxt).block()),
  667. pollOperation,
  668. cancelOperation,
  669. fetchResultOperation);
  670. PollResponse<Response> pollResponse = poller.waitForCompletion();
  671. Assertions.assertNotNull(pollResponse.getValue());
  672. assertEquals(response2.getValue().getResponse(), pollResponse.getValue().getResponse());
  673. assertEquals(LongRunningOperationStatus.SUCCESSFULLY_COMPLETED, pollResponse.getStatus());
  674. //
  675. when(pollOperation.apply(any())).thenAnswer((Answer<Mono<PollResponse<Response>>>) invocationOnMock -> {
  676. Assertions.assertTrue(true, "A Poll after completion should be called");
  677. return Mono.empty();
  678. });
  679. CertificateOutput certificateOutput = poller.getFinalResult();
  680. Assertions.assertNotNull(certificateOutput);
  681. assertEquals("cert1", certificateOutput.getName());
  682. }
  683. @Test
  684. public void waitUntilShouldPollAfterMatchingStatus() {
  685. final Response activationResponse = new Response("Activated");
  686. when(activationOperation.apply(any())).thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  687. LongRunningOperationStatus matchStatus
  688. = LongRunningOperationStatus.fromString("OTHER_1", false);
  689. int[] invocationCount = new int[1];
  690. invocationCount[0] = -1;
  691. //
  692. when(pollOperation.apply(any())).thenAnswer((Answer<Mono<PollResponse<Response>>>) invocationOnMock -> {
  693. invocationCount[0]++;
  694. switch (invocationCount[0]) {
  695. case 0:
  696. return Mono.just(new PollResponse<>(IN_PROGRESS,
  697. new Response("0"), Duration.ofMillis(10)));
  698. case 1:
  699. return Mono.just(new PollResponse<>(IN_PROGRESS,
  700. new Response("1"), Duration.ofMillis(10)));
  701. case 2:
  702. return Mono.just(new PollResponse<>(matchStatus,
  703. new Response("1"), Duration.ofMillis(10)));
  704. default:
  705. throw new RuntimeException("Poll should not be called after matching response");
  706. }
  707. });
  708. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  709. Duration.ofMillis(10),
  710. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  711. activationOperation.apply(cxt).block()),
  712. pollOperation,
  713. cancelOperation,
  714. fetchResultOperation);
  715. PollResponse<Response> pollResponse = poller.waitUntil(matchStatus);
  716. assertEquals(matchStatus, pollResponse.getStatus());
  717. assertEquals(2, invocationCount[0]);
  718. }
  719. @Test
  720. public void verifyExceptionPropagationFromPollingOperationSyncPoller() {
  721. final Response activationResponse = new Response("Foo");
  722. when(activationOperation.apply(any()))
  723. .thenReturn(Mono.defer(() -> Mono.just(activationResponse)));
  724. final AtomicReference<Integer> cnt = new AtomicReference<>(0);
  725. pollOperation = (pollingContext) -> {
  726. cnt.getAndSet(cnt.get() + 1);
  727. if (cnt.get() <= 2) {
  728. return Mono.just(new PollResponse<Response>(IN_PROGRESS, new Response("1")));
  729. } else if (cnt.get() == 3) {
  730. throw new RuntimeException("Polling operation failed!");
  731. } else if (cnt.get() == 4) {
  732. return Mono.just(new PollResponse<Response>(IN_PROGRESS, new Response("2")));
  733. } else {
  734. return Mono.just(new PollResponse<Response>(SUCCESSFULLY_COMPLETED, new Response("3")));
  735. }
  736. };
  737. SyncPoller<Response, CertificateOutput> poller = new DefaultSyncPoller<>(
  738. Duration.ofMillis(10),
  739. cxt -> new PollResponse<>(LongRunningOperationStatus.NOT_STARTED,
  740. activationOperation.apply(cxt).block()),
  741. pollOperation,
  742. cancelOperation,
  743. fetchResultOperation);
  744. RuntimeException exception = assertThrows(RuntimeException.class,
  745. () -> poller.getFinalResult());
  746. assertEquals(exception.getMessage(), "Polling operation failed!");
  747. }
  748. @Test
  749. public void testPollerFluxError() throws InterruptedException {
  750. IllegalArgumentException expectedException = new IllegalArgumentException();
  751. PollerFlux<String, String> pollerFlux = error(expectedException);
  752. CountDownLatch countDownLatch = new CountDownLatch(1);
  753. pollerFlux.subscribe(
  754. response -> Assertions.fail("Did not expect a response"),
  755. ex -> {
  756. countDownLatch.countDown();
  757. Assertions.assertSame(expectedException, ex);
  758. },
  759. () -> Assertions.fail("Did not expect the flux to complete")
  760. );
  761. boolean completed = countDownLatch.await(1, TimeUnit.SECONDS);
  762. Assertions.assertTrue(completed);
  763. }
  764. @Test
  765. public void testSyncPollerError() {
  766. PollerFlux<String, String> pollerFlux = error(new IllegalArgumentException());
  767. // should getSyncPoller() be lazy?
  768. Assertions.assertThrows(IllegalArgumentException.class, () -> pollerFlux.getSyncPoller());
  769. }
  770. @BeforeAll
  771. static void beforeAll() {
  772. StepVerifier.setDefaultTimeout(Duration.ofSeconds(30));
  773. }
  774. @AfterAll
  775. static void afterAll() {
  776. StepVerifier.resetDefaultTimeout();
  777. }
  778. @Test
  779. public void testUpdatePollingIntervalWithoutVirtualTimer() {
  780. PollerFlux<String, String> pollerFlux = PollerFlux.create(Duration.ofMillis(10),
  781. context -> Mono.just(new PollResponse<>(IN_PROGRESS, "Activation")),
  782. context -> Mono.just(new PollResponse<>(IN_PROGRESS, "PollOperation")),
  783. (context, response) -> Mono.just("Cancel"),
  784. context -> Mono.just("FinalResult"));
  785. pollerFlux.setPollInterval(Duration.ofMillis(200));
  786. StepVerifier.create(pollerFlux.take(5))
  787. .thenAwait(Duration.ofSeconds(1))
  788. .expectNextCount(5)
  789. .verifyComplete();
  790. }
  791. @Test
  792. public void testUpdatePollingInterval() {
  793. PollerFlux<String, String> pollerFlux = PollerFlux.create(Duration.ofMillis(10),
  794. context -> Mono.just(new PollResponse<>(IN_PROGRESS, "Activation")),
  795. context -> Mono.just(new PollResponse<>(IN_PROGRESS, "PollOperation")),
  796. (context, response) -> Mono.just("Cancel"),
  797. context -> Mono.just("FinalResult"));
  798. StepVerifier.create(pollerFlux.take(5))
  799. .thenAwait(Duration.ofMillis(55))
  800. .expectNextCount(5)
  801. .verifyComplete();
  802. pollerFlux.setPollInterval(Duration.ofMillis(50));
  803. StepVerifier.create(pollerFlux.take(5))
  804. .thenAwait(Duration.ofMillis(255))
  805. .expectNextCount(5)
  806. .verifyComplete();
  807. pollerFlux.setPollInterval(Duration.ofMillis(195));
  808. StepVerifier.create(pollerFlux.take(5))
  809. .thenAwait(Duration.ofSeconds(1))
  810. .expectNextCount(5)
  811. .verifyComplete();
  812. }
  813. public static class Response {
  814. private final String response;
  815. public Response(String response) {
  816. this.response = response;
  817. }
  818. public String getResponse() {
  819. return response;
  820. }
  821. @Override
  822. public String toString() {
  823. return "Response: " + response;
  824. }
  825. }
  826. public class CertificateOutput {
  827. String name;
  828. public CertificateOutput(String certName) {
  829. name = certName;
  830. }
  831. public String getName() {
  832. return name;
  833. }
  834. }
  835. }