react-native-notify-kit 9.4.0 → 9.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,279 @@
1
+ package app.notifee.core.database;
2
+
3
+ /*
4
+ * Copyright (c) 2016-present Invertase Limited & Contributors
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this library except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ */
12
+
13
+ import static org.junit.Assert.assertFalse;
14
+ import static org.junit.Assert.assertNotNull;
15
+ import static org.junit.Assert.assertSame;
16
+ import static org.junit.Assert.assertTrue;
17
+ import static org.junit.Assert.fail;
18
+ import static org.mockito.Mockito.doAnswer;
19
+ import static org.mockito.Mockito.doThrow;
20
+ import static org.mockito.Mockito.mock;
21
+ import static org.mockito.Mockito.verify;
22
+
23
+ import com.google.common.util.concurrent.ListenableFuture;
24
+ import com.google.common.util.concurrent.ListeningExecutorService;
25
+ import com.google.common.util.concurrent.MoreExecutors;
26
+ import java.util.Collections;
27
+ import java.util.concurrent.CountDownLatch;
28
+ import java.util.concurrent.ExecutionException;
29
+ import java.util.concurrent.Executors;
30
+ import java.util.concurrent.TimeUnit;
31
+ import org.junit.After;
32
+ import org.junit.Before;
33
+ import org.junit.Test;
34
+
35
+ /**
36
+ * Locks in the post-#549 contract that every {@link WorkDataRepository} mutation method returns a
37
+ * non-null {@link ListenableFuture} that only transitions to done after the underlying DAO call has
38
+ * actually returned — and that DAO exceptions propagate through {@code ExecutionException}.
39
+ *
40
+ * <p>Before the #549 fix, these methods were {@code void} and enqueued their work fire-and-forget
41
+ * on a cached thread pool. Any future refactor that drops the future return type or silently
42
+ * swallows DAO exceptions would be a regression and must fail these tests.
43
+ */
44
+ public class WorkDataRepositoryFutureContractTest {
45
+
46
+ private WorkDataDao mockDao;
47
+ private ListeningExecutorService executor;
48
+ private WorkDataRepository repo;
49
+
50
+ @Before
51
+ public void setUp() {
52
+ mockDao = mock(WorkDataDao.class);
53
+ // Single-threaded so we can deterministically gate the DAO call with a latch
54
+ // and assert future.isDone() before the DAO has returned.
55
+ executor = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor());
56
+ repo = new WorkDataRepository(mockDao, executor);
57
+ }
58
+
59
+ @After
60
+ public void tearDown() {
61
+ executor.shutdownNow();
62
+ }
63
+
64
+ // -------- insert --------
65
+
66
+ @Test
67
+ public void insert_returnsNonNullFuture() {
68
+ assertNotNull(repo.insert(newEntity("a")));
69
+ }
70
+
71
+ @Test
72
+ public void insert_futureNotDoneUntilDaoReturns() throws Exception {
73
+ CountDownLatch daoEntered = new CountDownLatch(1);
74
+ CountDownLatch daoGate = new CountDownLatch(1);
75
+ WorkDataEntity entity = newEntity("a");
76
+ doAnswer(
77
+ inv -> {
78
+ daoEntered.countDown();
79
+ daoGate.await();
80
+ return null;
81
+ })
82
+ .when(mockDao)
83
+ .insert(entity);
84
+
85
+ ListenableFuture<Void> f = repo.insert(entity);
86
+ assertTrue("DAO should be entered within 1s", daoEntered.await(1, TimeUnit.SECONDS));
87
+ assertFalse("insert future must not be done while DAO is blocked", f.isDone());
88
+
89
+ daoGate.countDown();
90
+ f.get(1, TimeUnit.SECONDS);
91
+ assertTrue(f.isDone());
92
+ verify(mockDao).insert(entity);
93
+ }
94
+
95
+ @Test
96
+ public void insert_propagatesDaoException() throws Exception {
97
+ RuntimeException boom = new RuntimeException("disk full");
98
+ WorkDataEntity entity = newEntity("a");
99
+ doThrow(boom).when(mockDao).insert(entity);
100
+
101
+ ListenableFuture<Void> f = repo.insert(entity);
102
+ try {
103
+ f.get(1, TimeUnit.SECONDS);
104
+ fail("Expected ExecutionException wrapping the DAO failure");
105
+ } catch (ExecutionException e) {
106
+ assertSame(boom, e.getCause());
107
+ }
108
+ }
109
+
110
+ // -------- deleteById --------
111
+
112
+ @Test
113
+ public void deleteById_returnsNonNullFuture() {
114
+ assertNotNull(repo.deleteById("a"));
115
+ }
116
+
117
+ @Test
118
+ public void deleteById_futureNotDoneUntilDaoReturns() throws Exception {
119
+ CountDownLatch daoEntered = new CountDownLatch(1);
120
+ CountDownLatch daoGate = new CountDownLatch(1);
121
+ doAnswer(
122
+ inv -> {
123
+ daoEntered.countDown();
124
+ daoGate.await();
125
+ return null;
126
+ })
127
+ .when(mockDao)
128
+ .deleteById("a");
129
+
130
+ ListenableFuture<Void> f = repo.deleteById("a");
131
+ assertTrue(daoEntered.await(1, TimeUnit.SECONDS));
132
+ assertFalse(f.isDone());
133
+
134
+ daoGate.countDown();
135
+ f.get(1, TimeUnit.SECONDS);
136
+ verify(mockDao).deleteById("a");
137
+ }
138
+
139
+ @Test
140
+ public void deleteById_propagatesDaoException() throws Exception {
141
+ RuntimeException boom = new RuntimeException("row missing");
142
+ doThrow(boom).when(mockDao).deleteById("a");
143
+
144
+ ListenableFuture<Void> f = repo.deleteById("a");
145
+ try {
146
+ f.get(1, TimeUnit.SECONDS);
147
+ fail("Expected ExecutionException wrapping the DAO failure");
148
+ } catch (ExecutionException e) {
149
+ assertSame(boom, e.getCause());
150
+ }
151
+ }
152
+
153
+ // -------- deleteByIds --------
154
+
155
+ @Test
156
+ public void deleteByIds_futureNotDoneUntilDaoReturns() throws Exception {
157
+ CountDownLatch daoEntered = new CountDownLatch(1);
158
+ CountDownLatch daoGate = new CountDownLatch(1);
159
+ doAnswer(
160
+ inv -> {
161
+ daoEntered.countDown();
162
+ daoGate.await();
163
+ return null;
164
+ })
165
+ .when(mockDao)
166
+ .deleteByIds(Collections.singletonList("a"));
167
+
168
+ ListenableFuture<Void> f = repo.deleteByIds(Collections.singletonList("a"));
169
+ assertTrue(daoEntered.await(1, TimeUnit.SECONDS));
170
+ assertFalse(f.isDone());
171
+
172
+ daoGate.countDown();
173
+ f.get(1, TimeUnit.SECONDS);
174
+ verify(mockDao).deleteByIds(Collections.singletonList("a"));
175
+ }
176
+
177
+ @Test
178
+ public void deleteByIds_propagatesDaoException() throws Exception {
179
+ RuntimeException boom = new RuntimeException("constraint violation");
180
+ doThrow(boom).when(mockDao).deleteByIds(Collections.singletonList("a"));
181
+
182
+ ListenableFuture<Void> f = repo.deleteByIds(Collections.singletonList("a"));
183
+ try {
184
+ f.get(1, TimeUnit.SECONDS);
185
+ fail("Expected ExecutionException");
186
+ } catch (ExecutionException e) {
187
+ assertSame(boom, e.getCause());
188
+ }
189
+ }
190
+
191
+ // -------- deleteAll --------
192
+
193
+ @Test
194
+ public void deleteAll_returnsNonNullFuture() {
195
+ assertNotNull(repo.deleteAll());
196
+ }
197
+
198
+ @Test
199
+ public void deleteAll_futureNotDoneUntilDaoReturns() throws Exception {
200
+ CountDownLatch daoEntered = new CountDownLatch(1);
201
+ CountDownLatch daoGate = new CountDownLatch(1);
202
+ doAnswer(
203
+ inv -> {
204
+ daoEntered.countDown();
205
+ daoGate.await();
206
+ return null;
207
+ })
208
+ .when(mockDao)
209
+ .deleteAll();
210
+
211
+ ListenableFuture<Void> f = repo.deleteAll();
212
+ assertTrue(daoEntered.await(1, TimeUnit.SECONDS));
213
+ assertFalse(f.isDone());
214
+
215
+ daoGate.countDown();
216
+ f.get(1, TimeUnit.SECONDS);
217
+ verify(mockDao).deleteAll();
218
+ }
219
+
220
+ @Test
221
+ public void deleteAll_propagatesDaoException() throws Exception {
222
+ RuntimeException boom = new RuntimeException("db locked");
223
+ doThrow(boom).when(mockDao).deleteAll();
224
+
225
+ ListenableFuture<Void> f = repo.deleteAll();
226
+ try {
227
+ f.get(1, TimeUnit.SECONDS);
228
+ fail("Expected ExecutionException");
229
+ } catch (ExecutionException e) {
230
+ assertSame(boom, e.getCause());
231
+ }
232
+ }
233
+
234
+ // -------- update --------
235
+
236
+ @Test
237
+ public void update_futureNotDoneUntilDaoReturns() throws Exception {
238
+ CountDownLatch daoEntered = new CountDownLatch(1);
239
+ CountDownLatch daoGate = new CountDownLatch(1);
240
+ WorkDataEntity entity = newEntity("a");
241
+ doAnswer(
242
+ inv -> {
243
+ daoEntered.countDown();
244
+ daoGate.await();
245
+ return null;
246
+ })
247
+ .when(mockDao)
248
+ .update(entity);
249
+
250
+ ListenableFuture<Void> f = repo.update(entity);
251
+ assertTrue(daoEntered.await(1, TimeUnit.SECONDS));
252
+ assertFalse(f.isDone());
253
+
254
+ daoGate.countDown();
255
+ f.get(1, TimeUnit.SECONDS);
256
+ verify(mockDao).update(entity);
257
+ }
258
+
259
+ @Test
260
+ public void update_propagatesDaoException() throws Exception {
261
+ RuntimeException boom = new RuntimeException("schema mismatch");
262
+ WorkDataEntity entity = newEntity("a");
263
+ doThrow(boom).when(mockDao).update(entity);
264
+
265
+ ListenableFuture<Void> f = repo.update(entity);
266
+ try {
267
+ f.get(1, TimeUnit.SECONDS);
268
+ fail("Expected ExecutionException");
269
+ } catch (ExecutionException e) {
270
+ assertSame(boom, e.getCause());
271
+ }
272
+ }
273
+
274
+ // -------- helpers --------
275
+
276
+ private static WorkDataEntity newEntity(String id) {
277
+ return new WorkDataEntity(id, new byte[0], new byte[0], false);
278
+ }
279
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const version = "9.4.0";
1
+ export declare const version = "9.5.0";
package/dist/version.js CHANGED
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '9.4.0';
2
+ export const version = '9.5.0';
3
3
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-notify-kit",
3
- "version": "9.4.0",
3
+ "version": "9.5.0",
4
4
  "author": "Marco Crupi",
5
5
  "description": "Maintained Notifee-compatible fork for React Native — advanced local notifications for Android & iOS.",
6
6
  "main": "dist/index.js",
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '9.4.0';
2
+ export const version = '9.5.0';