@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.41

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.
Files changed (317) hide show
  1. package/android/build.gradle +13 -2
  2. package/android/proguard-rules.pro +8 -2
  3. package/android/src/bridge/BridgeReactInstance.kt +38 -0
  4. package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
  5. package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
  6. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  7. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
  8. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
  9. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  10. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  11. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
  12. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
  13. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  14. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  15. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  16. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  17. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  18. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  19. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  20. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  21. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  22. package/android/src/newarch/HotUpdater.kt +7 -9
  23. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  24. package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
  25. package/android/src/newarch/ReactIntegrationManager.kt +6 -32
  26. package/android/src/oldarch/HotUpdater.kt +7 -9
  27. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  28. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  29. package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
  30. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  31. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  32. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
  33. package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
  34. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  35. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  36. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  37. package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
  38. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
  39. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  40. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  41. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  42. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  43. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  44. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  45. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  46. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  47. package/lib/commonjs/appReady.js +96 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +121 -69
  50. package/lib/commonjs/checkForUpdate.js.map +1 -1
  51. package/lib/commonjs/clientPlugin.js +13 -0
  52. package/lib/commonjs/clientPlugin.js.map +1 -0
  53. package/lib/commonjs/error.js +5 -14
  54. package/lib/commonjs/error.js.map +1 -1
  55. package/lib/commonjs/fetchJSON.js +16 -27
  56. package/lib/commonjs/fetchJSON.js.map +1 -1
  57. package/lib/commonjs/fetchUpdateResponse.js +60 -0
  58. package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
  59. package/lib/commonjs/httpClient.js +64 -95
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +355 -324
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/init.types.js +6 -0
  64. package/lib/commonjs/init.types.js.map +1 -0
  65. package/lib/commonjs/native.js +77 -63
  66. package/lib/commonjs/native.js.map +1 -1
  67. package/lib/commonjs/pluginHost.js +41 -0
  68. package/lib/commonjs/pluginHost.js.map +1 -0
  69. package/lib/commonjs/releaseCatalogCache.js +36 -87
  70. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  71. package/lib/commonjs/sdkVersion.js +1 -1
  72. package/lib/commonjs/sdkVersion.js.map +1 -1
  73. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  74. package/lib/commonjs/store.js +3 -10
  75. package/lib/commonjs/store.js.map +1 -1
  76. package/lib/commonjs/updateError.js +145 -0
  77. package/lib/commonjs/updateError.js.map +1 -0
  78. package/lib/commonjs/wrap.js +46 -38
  79. package/lib/commonjs/wrap.js.map +1 -1
  80. package/lib/module/appReady.js +90 -0
  81. package/lib/module/appReady.js.map +1 -0
  82. package/lib/module/checkForUpdate.js +114 -64
  83. package/lib/module/checkForUpdate.js.map +1 -1
  84. package/lib/module/clientPlugin.js +4 -0
  85. package/lib/module/clientPlugin.js.map +1 -0
  86. package/lib/module/error.js +5 -14
  87. package/lib/module/error.js.map +1 -1
  88. package/lib/module/fetchJSON.js +14 -26
  89. package/lib/module/fetchJSON.js.map +1 -1
  90. package/lib/module/fetchUpdateResponse.js +55 -0
  91. package/lib/module/fetchUpdateResponse.js.map +1 -0
  92. package/lib/module/httpClient.js +63 -94
  93. package/lib/module/httpClient.js.map +1 -1
  94. package/lib/module/index.js +335 -327
  95. package/lib/module/index.js.map +1 -1
  96. package/lib/module/init.types.js +4 -0
  97. package/lib/module/init.types.js.map +1 -0
  98. package/lib/module/native.js +70 -56
  99. package/lib/module/native.js.map +1 -1
  100. package/lib/module/pluginHost.js +36 -0
  101. package/lib/module/pluginHost.js.map +1 -0
  102. package/lib/module/releaseCatalogCache.js +32 -82
  103. package/lib/module/releaseCatalogCache.js.map +1 -1
  104. package/lib/module/sdkVersion.js +1 -1
  105. package/lib/module/sdkVersion.js.map +1 -1
  106. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  107. package/lib/module/store.js +3 -10
  108. package/lib/module/store.js.map +1 -1
  109. package/lib/module/updateError.js +139 -0
  110. package/lib/module/updateError.js.map +1 -0
  111. package/lib/module/wrap.js +47 -38
  112. package/lib/module/wrap.js.map +1 -1
  113. package/lib/typescript/commonjs/appReady.d.ts +30 -0
  114. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
  116. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  118. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/error.d.ts +5 -14
  120. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  122. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  124. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/httpClient.d.ts +5 -34
  126. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/index.d.ts +124 -116
  128. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/init.types.d.ts +21 -0
  130. package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/native.d.ts +32 -25
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
  134. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  136. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  138. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  140. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/store.d.ts +1 -3
  142. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/types.d.ts +1 -1
  144. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  146. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/wrap.d.ts +2 -3
  148. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
  150. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  151. package/lib/typescript/module/appReady.d.ts +30 -0
  152. package/lib/typescript/module/appReady.d.ts.map +1 -0
  153. package/lib/typescript/module/checkForUpdate.d.ts +20 -6
  154. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  155. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  156. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  157. package/lib/typescript/module/error.d.ts +5 -14
  158. package/lib/typescript/module/error.d.ts.map +1 -1
  159. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  160. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  161. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  162. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  163. package/lib/typescript/module/httpClient.d.ts +5 -34
  164. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  165. package/lib/typescript/module/index.d.ts +124 -116
  166. package/lib/typescript/module/index.d.ts.map +1 -1
  167. package/lib/typescript/module/init.types.d.ts +21 -0
  168. package/lib/typescript/module/init.types.d.ts.map +1 -0
  169. package/lib/typescript/module/native.d.ts +32 -25
  170. package/lib/typescript/module/native.d.ts.map +1 -1
  171. package/lib/typescript/module/pluginHost.d.ts +8 -0
  172. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  173. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  174. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  175. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  176. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  177. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  178. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  179. package/lib/typescript/module/store.d.ts +1 -3
  180. package/lib/typescript/module/store.d.ts.map +1 -1
  181. package/lib/typescript/module/types.d.ts +1 -1
  182. package/lib/typescript/module/types.d.ts.map +1 -1
  183. package/lib/typescript/module/updateError.d.ts +26 -0
  184. package/lib/typescript/module/updateError.d.ts.map +1 -0
  185. package/lib/typescript/module/wrap.d.ts +2 -3
  186. package/lib/typescript/module/wrap.d.ts.map +1 -1
  187. package/lib/typescript/module/wrap.types.d.ts +13 -41
  188. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  189. package/package.json +8 -4
  190. package/src/appReady.ts +147 -0
  191. package/src/checkForUpdate.ts +156 -89
  192. package/src/clientPlugin.ts +21 -0
  193. package/src/error.ts +5 -15
  194. package/src/fetchJSON.ts +14 -30
  195. package/src/fetchUpdateResponse.ts +60 -0
  196. package/src/httpClient.ts +89 -160
  197. package/src/index.ts +413 -425
  198. package/src/init.types.ts +24 -0
  199. package/src/native.ts +101 -105
  200. package/src/pluginHost.ts +52 -0
  201. package/src/releaseCatalogCache.ts +43 -172
  202. package/src/sdkVersion.ts +1 -1
  203. package/src/specs/NativeHotUpdater.ts +34 -48
  204. package/src/store.ts +2 -23
  205. package/src/types.ts +1 -1
  206. package/src/updateError.ts +167 -0
  207. package/src/wrap.tsx +60 -43
  208. package/src/wrap.types.ts +18 -46
  209. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  210. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  211. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  212. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  213. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  214. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  215. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  216. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  217. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  218. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  219. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  220. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  221. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  222. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  223. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  224. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  225. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  226. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  227. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  228. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  229. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  230. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  231. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  232. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  233. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  234. package/lib/commonjs/httpClient.spec.js +0 -102
  235. package/lib/commonjs/httpClient.spec.js.map +0 -1
  236. package/lib/commonjs/index.spec.js +0 -302
  237. package/lib/commonjs/index.spec.js.map +0 -1
  238. package/lib/commonjs/iosInterop.spec.js +0 -18
  239. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  240. package/lib/commonjs/native.spec.js +0 -883
  241. package/lib/commonjs/native.spec.js.map +0 -1
  242. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  243. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  244. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  245. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  246. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  247. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  248. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  249. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  250. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  251. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  252. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  253. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  254. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  255. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  256. package/lib/commonjs/store.notification.spec.js +0 -76
  257. package/lib/commonjs/store.notification.spec.js.map +0 -1
  258. package/lib/commonjs/store.spec.js +0 -316
  259. package/lib/commonjs/store.spec.js.map +0 -1
  260. package/lib/commonjs/wrap.spec.js +0 -329
  261. package/lib/commonjs/wrap.spec.js.map +0 -1
  262. package/lib/module/catalogCacheNative.spec.js +0 -31
  263. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  264. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  265. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  266. package/lib/module/httpClient.spec.js +0 -102
  267. package/lib/module/httpClient.spec.js.map +0 -1
  268. package/lib/module/index.spec.js +0 -301
  269. package/lib/module/index.spec.js.map +0 -1
  270. package/lib/module/iosInterop.spec.js +0 -18
  271. package/lib/module/iosInterop.spec.js.map +0 -1
  272. package/lib/module/native.spec.js +0 -882
  273. package/lib/module/native.spec.js.map +0 -1
  274. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  275. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  276. package/lib/module/notifyAppReadyInsights.js +0 -128
  277. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  278. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  279. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  280. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  281. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  282. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  283. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  284. package/lib/module/releaseCatalogCache.spec.js +0 -195
  285. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  286. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  287. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  288. package/lib/module/store.notification.spec.js +0 -77
  289. package/lib/module/store.notification.spec.js.map +0 -1
  290. package/lib/module/store.spec.js +0 -315
  291. package/lib/module/store.spec.js.map +0 -1
  292. package/lib/module/wrap.spec.js +0 -330
  293. package/lib/module/wrap.spec.js.map +0 -1
  294. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  295. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  296. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  297. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  298. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  299. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  300. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  301. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  302. package/src/catalogCacheNative.spec.ts +0 -63
  303. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  304. package/src/httpClient.spec.ts +0 -132
  305. package/src/index.spec.ts +0 -337
  306. package/src/iosInterop.spec.ts +0 -30
  307. package/src/native.spec.ts +0 -945
  308. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  309. package/src/notifyAppReadyInsights.spec.ts +0 -243
  310. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  311. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  312. package/src/notifyAppReadyInsights.ts +0 -194
  313. package/src/releaseCatalogCache.spec.ts +0 -282
  314. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  315. package/src/store.notification.spec.ts +0 -92
  316. package/src/store.spec.ts +0 -372
  317. package/src/wrap.spec.ts +0 -406
@@ -1,273 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertNull
5
- import org.junit.Assert.assertTrue
6
- import org.junit.Test
7
-
8
- class HotUpdaterImplTest {
9
- @Test
10
- fun `getBundleId returns built in while native launch selection is missing`() {
11
- val impl =
12
- createImpl(
13
- storageBundleId = "staged-bundle",
14
- storageManifest = mapOf("bundleId" to "staged-bundle"),
15
- storageBaseURL = "file:///bundle-store/staged-bundle",
16
- )
17
-
18
- assertNull(impl.getBundleId())
19
- assertTrue(impl.getManifest().isEmpty())
20
- assertNull(impl.getBaseURL())
21
- }
22
-
23
- @Test
24
- fun `getBundleId returns current launched bundle over staged metadata`() {
25
- val impl = createImpl(storageBundleId = "staged-bundle")
26
-
27
- setCurrentLaunchSelection(
28
- impl,
29
- LaunchSelection(
30
- bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
31
- launchedBundleId = "launched-bundle",
32
- shouldRollbackOnCrash = false,
33
- ),
34
- )
35
-
36
- assertEquals("launched-bundle", impl.getBundleId())
37
- }
38
-
39
- @Test
40
- fun `getBundleId returns null for built in launch even when staged metadata exists`() {
41
- val impl = createImpl(storageBundleId = "staged-bundle")
42
-
43
- setCurrentLaunchSelection(
44
- impl,
45
- LaunchSelection(
46
- bundleUrl = "assets://index.android.bundle",
47
- launchedBundleId = null,
48
- shouldRollbackOnCrash = false,
49
- ),
50
- )
51
-
52
- assertNull(impl.getBundleId())
53
- }
54
-
55
- @Test
56
- fun `getManifest and getBaseURL return current launched bundle over staged metadata`() {
57
- val impl =
58
- createImpl(
59
- storageBundleId = "staged-bundle",
60
- storageManifest = mapOf("bundleId" to "staged-bundle"),
61
- storageBaseURL = "file:///bundle-store/staged-bundle",
62
- launchedBundleManifests =
63
- mapOf(
64
- "launched-bundle" to mapOf("bundleId" to "launched-bundle"),
65
- ),
66
- launchedBundleBaseURLs =
67
- mapOf(
68
- "launched-bundle" to "file:///bundle-store/launched-bundle",
69
- ),
70
- )
71
-
72
- setCurrentLaunchSelection(
73
- impl,
74
- LaunchSelection(
75
- bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
76
- launchedBundleId = "launched-bundle",
77
- shouldRollbackOnCrash = false,
78
- ),
79
- )
80
-
81
- assertEquals(
82
- mapOf("bundleId" to "launched-bundle"),
83
- impl.getManifest(),
84
- )
85
- assertEquals("file:///bundle-store/launched-bundle", impl.getBaseURL())
86
- }
87
-
88
- @Test
89
- fun `getManifest and getBaseURL return built in values for built in launch`() {
90
- val impl =
91
- createImpl(
92
- storageBundleId = "staged-bundle",
93
- storageManifest = mapOf("bundleId" to "staged-bundle"),
94
- storageBaseURL = "file:///bundle-store/staged-bundle",
95
- )
96
-
97
- setCurrentLaunchSelection(
98
- impl,
99
- LaunchSelection(
100
- bundleUrl = "assets://index.android.bundle",
101
- launchedBundleId = null,
102
- shouldRollbackOnCrash = false,
103
- ),
104
- )
105
-
106
- assertTrue(impl.getManifest().isEmpty())
107
- assertNull(impl.getBaseURL())
108
- }
109
-
110
- @Test
111
- fun `notifyAppReady reads the report without completing the current launch`() {
112
- val storage = FakeBundleStorageService(bundleId = null)
113
- val impl = allocateWithoutConstructor<HotUpdaterImpl>()
114
- setField(impl, "bundleStorage", storage)
115
- setCurrentLaunchSelection(
116
- impl,
117
- LaunchSelection(
118
- bundleUrl = "file:///bundle-store/launched-bundle/index.android.bundle",
119
- launchedBundleId = "launched-bundle",
120
- shouldRollbackOnCrash = false,
121
- ),
122
- )
123
-
124
- assertEquals(mapOf("status" to "UNCHANGED"), impl.notifyAppReady())
125
- assertNull(storage.lastCompletedBundleId)
126
- }
127
-
128
- @Test
129
- fun `getInstallId and setUser delegate to bundle storage`() {
130
- val storage = FakeBundleStorageService(bundleId = null)
131
- val impl = allocateWithoutConstructor<HotUpdaterImpl>()
132
- setField(impl, "bundleStorage", storage)
133
-
134
- assertEquals("test-install-id", impl.getInstallId())
135
-
136
- impl.setUser("user-123", "alice")
137
- assertEquals("user-123", storage.lastUserId)
138
- assertEquals("alice", storage.lastUsername)
139
-
140
- impl.setUser(null, null)
141
- assertNull(storage.lastUserId)
142
- assertNull(storage.lastUsername)
143
- }
144
-
145
- private fun createImpl(
146
- storageBundleId: String?,
147
- storageManifest: Map<String, Any?> = emptyMap(),
148
- storageBaseURL: String = "",
149
- launchedBundleManifests: Map<String, Map<String, Any?>> = emptyMap(),
150
- launchedBundleBaseURLs: Map<String, String> = emptyMap(),
151
- ): HotUpdaterImpl =
152
- allocateWithoutConstructor<HotUpdaterImpl>().also { impl ->
153
- setField(
154
- impl,
155
- "bundleStorage",
156
- FakeBundleStorageService(
157
- bundleId = storageBundleId,
158
- manifest = storageManifest,
159
- baseURL = storageBaseURL,
160
- launchedBundleManifests = launchedBundleManifests,
161
- launchedBundleBaseURLs = launchedBundleBaseURLs,
162
- ),
163
- )
164
- }
165
-
166
- private fun setCurrentLaunchSelection(
167
- impl: HotUpdaterImpl,
168
- selection: LaunchSelection,
169
- ) {
170
- setField(impl, "currentLaunchSelection", selection)
171
- }
172
-
173
- private fun setField(
174
- target: Any,
175
- fieldName: String,
176
- value: Any?,
177
- ) {
178
- val field = HotUpdaterImpl::class.java.getDeclaredField(fieldName)
179
- field.isAccessible = true
180
- field.set(target, value)
181
- }
182
-
183
- private inline fun <reified T> allocateWithoutConstructor(): T {
184
- val field = Class.forName("sun.misc.Unsafe").getDeclaredField("theUnsafe")
185
- field.isAccessible = true
186
- val unsafe = field.get(null)
187
- val allocateInstance = unsafe.javaClass.getMethod("allocateInstance", Class::class.java)
188
- @Suppress("UNCHECKED_CAST")
189
- return allocateInstance.invoke(unsafe, T::class.java) as T
190
- }
191
-
192
- private class FakeBundleStorageService(
193
- private val bundleId: String?,
194
- private val manifest: Map<String, Any?> = emptyMap(),
195
- private val baseURL: String = "",
196
- private val launchedBundleManifests: Map<String, Map<String, Any?>> =
197
- emptyMap(),
198
- private val launchedBundleBaseURLs: Map<String, String> = emptyMap(),
199
- ) : BundleStorageService {
200
- var lastCompletedBundleId: String? = null
201
- var lastUserId: String? = null
202
- var lastUsername: String? = null
203
-
204
- override fun setBundleURL(localPath: String?): Boolean = true
205
-
206
- override fun getCachedBundleURL(): String? = null
207
-
208
- override fun getFallbackBundleURL(): String = "assets://index.android.bundle"
209
-
210
- override fun prepareLaunch(pendingRecovery: PendingCrashRecovery?): LaunchSelection =
211
- LaunchSelection(
212
- bundleUrl = "assets://index.android.bundle",
213
- launchedBundleId = null,
214
- shouldRollbackOnCrash = false,
215
- )
216
-
217
- override suspend fun updateBundle(
218
- bundleId: String,
219
- fileUrl: String?,
220
- fileHash: String?,
221
- manifestUrl: String?,
222
- manifestFileHash: String?,
223
- changedAssets: Map<String, ChangedAssetDescriptor>?,
224
- progressCallback: (UpdateProgressPayload) -> Unit,
225
- ) = Unit
226
-
227
- override fun markLaunchCompleted(currentBundleId: String?) {
228
- lastCompletedBundleId = currentBundleId
229
- }
230
-
231
- override fun notifyAppReady(): Map<String, Any?> = mapOf("status" to "UNCHANGED")
232
-
233
- override fun getInstallId(): String = "test-install-id"
234
-
235
- override fun getUserId(): String? = lastUserId
236
-
237
- override fun getUsername(): String? = lastUsername
238
-
239
- override fun setUser(
240
- userId: String?,
241
- username: String?,
242
- ) {
243
- lastUserId = userId
244
- lastUsername = username
245
- }
246
-
247
- override fun getCrashHistory(): CrashedHistory = CrashedHistory()
248
-
249
- override fun clearCrashHistory(): Boolean = true
250
-
251
- override fun getBaseURL(): String = baseURL
252
-
253
- override fun getBaseURLForBundle(bundleId: String?): String =
254
- if (bundleId == null) {
255
- ""
256
- } else {
257
- launchedBundleBaseURLs[bundleId] ?: ""
258
- }
259
-
260
- override fun getBundleId(): String? = bundleId
261
-
262
- override fun getManifest(): Map<String, Any?> = manifest
263
-
264
- override fun getManifestForBundle(bundleId: String?): Map<String, Any?> =
265
- if (bundleId == null) {
266
- emptyMap()
267
- } else {
268
- launchedBundleManifests[bundleId] ?: emptyMap()
269
- }
270
-
271
- override suspend fun resetChannel(): Boolean = true
272
- }
273
- }
@@ -1,20 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertNull
5
- import org.junit.Test
6
-
7
- class NativeConfigUtilsTest {
8
- @Test
9
- fun `coerces typed manifest metadata to strings`() {
10
- assertEquals("123", NativeConfigUtils.coerceManifestMetaDataString(123))
11
- assertEquals("true", NativeConfigUtils.coerceManifestMetaDataString(true))
12
- assertEquals("-16711936", NativeConfigUtils.coerceManifestMetaDataString(-16711936))
13
- }
14
-
15
- @Test
16
- fun `ignores missing and empty manifest metadata`() {
17
- assertNull(NativeConfigUtils.coerceManifestMetaDataString(null))
18
- assertNull(NativeConfigUtils.coerceManifestMetaDataString(""))
19
- }
20
- }
@@ -1,111 +0,0 @@
1
- package com.hotupdater
2
-
3
- import kotlinx.coroutines.runBlocking
4
- import org.junit.Assert.assertArrayEquals
5
- import org.junit.Assert.assertEquals
6
- import org.junit.Assert.assertNull
7
- import org.junit.Assert.assertTrue
8
- import org.junit.Rule
9
- import org.junit.Test
10
- import org.junit.rules.TemporaryFolder
11
- import java.io.File
12
- import java.net.InetAddress
13
- import java.net.ServerSocket
14
- import java.net.Socket
15
- import java.net.URL
16
- import kotlin.concurrent.thread
17
-
18
- class OkHttpDownloadServiceTest {
19
- @get:Rule
20
- val temporaryFolder = TemporaryFolder()
21
-
22
- @Test
23
- fun `downloadFile succeeds when content length is unknown`() =
24
- runBlocking {
25
- val payload = "bundle-content-without-content-length".repeat(512).toByteArray()
26
- val server = ChunkedResponseServer(payload)
27
-
28
- try {
29
- val destinationDir = temporaryFolder.newFolder("downloads")
30
- val destination = File(destinationDir, "bundle.android.bundle")
31
- var reportedSize: Long? = null
32
-
33
- val result =
34
- OkHttpDownloadService().downloadFile(
35
- fileUrl = URL("http://127.0.0.1:${server.port}/bundle"),
36
- destination = destination,
37
- fileSizeCallback = { size -> reportedSize = size },
38
- progressCallback = {},
39
- )
40
-
41
- assertTrue(result is DownloadResult.Success)
42
- assertEquals(payload.size.toLong(), destination.length())
43
- assertArrayEquals(payload, destination.readBytes())
44
- assertNull(reportedSize)
45
- } finally {
46
- server.close()
47
- }
48
- }
49
-
50
- private class ChunkedResponseServer(
51
- private val payload: ByteArray,
52
- ) : AutoCloseable {
53
- private val serverSocket = ServerSocket(0, 1, InetAddress.getByName("127.0.0.1"))
54
- private val worker =
55
- thread(start = true, isDaemon = true) {
56
- serverSocket.use { socket ->
57
- socket.accept().use(::respond)
58
- }
59
- }
60
-
61
- val port: Int = serverSocket.localPort
62
-
63
- private fun respond(client: Socket) {
64
- drainRequestHeaders(client)
65
-
66
- client.getOutputStream().use { output ->
67
- output.write(
68
- (
69
- "HTTP/1.1 200 OK\r\n" +
70
- "Content-Type: application/octet-stream\r\n" +
71
- "Transfer-Encoding: chunked\r\n" +
72
- "Connection: close\r\n" +
73
- "\r\n"
74
- ).toByteArray(),
75
- )
76
- output.write(payload.size.toString(16).toByteArray())
77
- output.write("\r\n".toByteArray())
78
- output.write(payload)
79
- output.write("\r\n0\r\n\r\n".toByteArray())
80
- output.flush()
81
- }
82
- }
83
-
84
- private fun drainRequestHeaders(client: Socket) {
85
- val input = client.getInputStream()
86
- var matched = 0
87
- val terminator = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte(), '\r'.code.toByte(), '\n'.code.toByte())
88
-
89
- while (matched < terminator.size) {
90
- val next = input.read()
91
- if (next == -1) {
92
- break
93
- }
94
-
95
- matched =
96
- if (next.toByte() == terminator[matched]) {
97
- matched + 1
98
- } else if (next.toByte() == terminator[0]) {
99
- 1
100
- } else {
101
- 0
102
- }
103
- }
104
- }
105
-
106
- override fun close() {
107
- serverSocket.close()
108
- worker.join(1_000)
109
- }
110
- }
111
- }
@@ -1,41 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertNull
5
- import org.junit.Rule
6
- import org.junit.Test
7
- import org.junit.rules.TemporaryFolder
8
- import java.io.File
9
-
10
- class RelativePathResolverTest {
11
- @get:Rule
12
- val temporaryFolder = TemporaryFolder()
13
-
14
- @Test
15
- fun `resolves a standard archive directory entry ending in a slash`() {
16
- val root = temporaryFolder.root
17
-
18
- val resolved = RelativePathResolver.resolveInside(root, "drawable-mdpi/")
19
-
20
- assertEquals(
21
- File(root, "drawable-mdpi").canonicalFile,
22
- resolved?.canonicalFile,
23
- )
24
- }
25
-
26
- @Test
27
- fun `rejects unsafe archive directory paths ending in a slash`() {
28
- val root = temporaryFolder.root
29
- val unsafePaths =
30
- listOf(
31
- "/",
32
- "../",
33
- "drawable-mdpi/../",
34
- "drawable-mdpi//",
35
- )
36
-
37
- unsafePaths.forEach { path ->
38
- assertNull(path, RelativePathResolver.resolveInside(root, path))
39
- }
40
- }
41
- }
@@ -1,118 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.junit.Assert.assertEquals
4
- import org.junit.Assert.assertFalse
5
- import org.junit.Assert.assertNull
6
- import org.junit.Assert.assertTrue
7
- import org.junit.Rule
8
- import org.junit.Test
9
- import org.junit.rules.TemporaryFolder
10
- import java.io.File
11
- import java.nio.file.Files
12
- import java.nio.file.StandardCopyOption
13
-
14
- class ReleaseCatalogCacheServiceTest {
15
- @get:Rule
16
- val temporaryFolder = TemporaryFolder()
17
-
18
- @Test
19
- fun `persists checksum verified entries without credential filenames`() {
20
- val root = temporaryFolder.newFolder("catalog-cache")
21
- val first = createService(root)
22
- val partition = "https://updates.example.com|x-api-key=raw-secret"
23
-
24
- assertTrue(first.set(partition, "validated-catalog"))
25
- assertEquals("validated-catalog", createService(root).get(partition))
26
-
27
- val entry = root.listFiles()!!.single()
28
- assertTrue(entry.name.matches(Regex("[0-9a-f]{64}\\.catalog")))
29
- assertFalse(entry.name.contains("raw-secret"))
30
- assertFalse(entry.readText().contains("raw-secret"))
31
-
32
- entry.appendText("corrupt")
33
- assertNull(createService(root).get(partition))
34
- assertFalse(entry.exists())
35
- }
36
-
37
- @Test
38
- fun `failed atomic replacement preserves the previous entry`() {
39
- val root = temporaryFolder.newFolder("interrupted-write")
40
- val partition = "partition"
41
- assertTrue(createService(root).set(partition, "previous"))
42
-
43
- val interrupted =
44
- ReleaseCatalogCacheService(
45
- rootDirectory = root,
46
- atomicReader = ::readForTest,
47
- atomicWriter = { _, _ -> false },
48
- atomicRemover = ::removeForTest,
49
- )
50
- assertFalse(interrupted.set(partition, "replacement"))
51
- assertEquals("previous", createService(root).get(partition))
52
- }
53
-
54
- @Test
55
- fun `evicts least recently used entries and rejects oversized payloads`() {
56
- val root = temporaryFolder.newFolder("bounded-cache")
57
- val service =
58
- ReleaseCatalogCacheService(
59
- rootDirectory = root,
60
- maxEntries = 2,
61
- maxEntryBytes = 16,
62
- maxTotalBytes = 200,
63
- atomicReader = ::readForTest,
64
- atomicWriter = ::writeForTest,
65
- atomicRemover = ::removeForTest,
66
- )
67
-
68
- assertTrue(service.set("one", "first"))
69
- Thread.sleep(5)
70
- assertTrue(service.set("two", "second"))
71
- Thread.sleep(5)
72
- assertTrue(service.set("three", "third"))
73
-
74
- assertNull(service.get("one"))
75
- assertEquals("second", service.get("two"))
76
- assertEquals("third", service.get("three"))
77
- assertFalse(service.set("two", "x".repeat(17)))
78
- assertEquals("second", service.get("two"))
79
- }
80
-
81
- private fun createService(root: File): ReleaseCatalogCacheService =
82
- ReleaseCatalogCacheService(
83
- rootDirectory = root,
84
- atomicReader = ::readForTest,
85
- atomicWriter = ::writeForTest,
86
- atomicRemover = ::removeForTest,
87
- )
88
-
89
- private fun readForTest(file: File): ByteArray? = if (file.isFile) file.readBytes() else null
90
-
91
- private fun removeForTest(file: File): Boolean = !file.exists() || file.delete()
92
-
93
- private fun writeForTest(
94
- destination: File,
95
- bytes: ByteArray,
96
- ): Boolean =
97
- try {
98
- val temporary = File.createTempFile("entry", ".tmp", destination.parentFile)
99
- temporary.writeBytes(bytes)
100
- try {
101
- Files.move(
102
- temporary.toPath(),
103
- destination.toPath(),
104
- StandardCopyOption.ATOMIC_MOVE,
105
- StandardCopyOption.REPLACE_EXISTING,
106
- )
107
- } catch (_: Exception) {
108
- Files.move(
109
- temporary.toPath(),
110
- destination.toPath(),
111
- StandardCopyOption.REPLACE_EXISTING,
112
- )
113
- }
114
- true
115
- } catch (_: Exception) {
116
- false
117
- }
118
- }
@@ -1,68 +0,0 @@
1
- package com.hotupdater
2
-
3
- import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream
4
- import org.junit.Assert.assertArrayEquals
5
- import org.junit.Assert.assertEquals
6
- import org.junit.Assert.assertNull
7
- import org.junit.Assert.assertThrows
8
- import org.junit.Test
9
- import java.io.ByteArrayInputStream
10
- import java.io.ByteArrayOutputStream
11
- import org.apache.commons.compress.archivers.tar.TarArchiveEntry as CommonsTarArchiveEntry
12
-
13
- class TarArchiveInputStreamTest {
14
- @Test
15
- fun `reads a long basename from a POSIX PAX header`() {
16
- val path = "raw/${"long-name-".repeat(14)}asset.bmp"
17
- val contents = "pax asset".toByteArray()
18
- val archive = createPaxArchive(path, contents)
19
-
20
- TarArchiveInputStream(ByteArrayInputStream(archive)).use { tar ->
21
- val entry = tar.getNextEntry()
22
-
23
- assertEquals(path, entry?.name)
24
- assertArrayEquals(contents, tar.readBytes())
25
- assertNull(tar.getNextEntry())
26
- }
27
- }
28
-
29
- @Test
30
- fun `rejects path traversal from a POSIX PAX header`() {
31
- val path = "../${"long-name-".repeat(14)}asset.bmp"
32
- val archive =
33
- createPaxArchive(
34
- path = path,
35
- contents = "malicious".toByteArray(),
36
- headerPath = "safe.bmp",
37
- )
38
-
39
- val error =
40
- assertThrows(SecurityException::class.java) {
41
- TarArchiveInputStream(ByteArrayInputStream(archive)).use { tar ->
42
- tar.getNextEntry()
43
- }
44
- }
45
-
46
- assertEquals("Path traversal detected: $path", error.message)
47
- }
48
-
49
- private fun createPaxArchive(
50
- path: String,
51
- contents: ByteArray,
52
- headerPath: String = path,
53
- ): ByteArray {
54
- val output = ByteArrayOutputStream()
55
- TarArchiveOutputStream(output).use { tar ->
56
- tar.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX)
57
- val entry =
58
- CommonsTarArchiveEntry(headerPath).apply {
59
- size = contents.size.toLong()
60
- if (path != headerPath) addPaxHeader("path", path)
61
- }
62
- tar.putArchiveEntry(entry)
63
- tar.write(contents)
64
- tar.closeArchiveEntry()
65
- }
66
- return output.toByteArray()
67
- }
68
- }