catalyst-core 0.3.0-beta.5 → 0.3.0-canary.2

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 (316) hide show
  1. package/changelog.md +185 -71
  2. package/dist/errors/generateDocs.js +100 -0
  3. package/dist/errors/index.js +243 -0
  4. package/dist/errors/registry.js +483 -0
  5. package/dist/errors-index.json +506 -0
  6. package/dist/globals.d.ts +62 -0
  7. package/dist/index.d.ts +5 -0
  8. package/dist/index.jsx +5 -8
  9. package/dist/logger.js +1 -1
  10. package/dist/native/androidProject/app/build.gradle.kts +142 -7
  11. package/dist/native/androidProject/app/src/main/AndroidManifest.xml +4 -0
  12. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +2 -2
  13. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +3 -0
  14. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +3 -0
  15. package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +43 -8
  16. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +181 -1
  17. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +156 -236
  18. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileSizeRouterUtilsTest.kt +471 -0
  19. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +222 -226
  20. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FrameworkServerUtilsTest.kt +300 -0
  21. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt +244 -0
  22. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/MetricsMonitorTest.kt +359 -0
  23. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +108 -63
  24. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +329 -385
  25. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/OfflineCacheServiceTest.kt +302 -0
  26. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/PerfEventBufferTest.kt +133 -0
  27. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/UtilsKtTest.kt +152 -0
  28. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +145 -324
  29. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/BarcodeDetectorTest.kt +132 -0
  30. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/HoldControllerTest.kt +155 -0
  31. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/TorchControllerTest.kt +150 -0
  32. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateMachineTest.kt +120 -0
  33. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateTest.kt +62 -0
  34. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ViewfinderMapperTest.kt +143 -0
  35. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ZoomControllerTest.kt +421 -0
  36. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/design/DesignTokensTest.kt +207 -0
  37. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +368 -0
  38. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/FridaDetectorTest.kt +193 -0
  39. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckManagerTest.kt +260 -0
  40. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckSchedulerTest.kt +166 -0
  41. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DeviceInfoUtilsTest.kt +167 -0
  42. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DownloadUtilsTest.kt +197 -0
  43. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/NetworkMonitorTest.kt +271 -0
  44. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/PushNotificationUtilsTest.kt +119 -0
  45. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +108 -0
  46. package/dist/native/androidProject/build.gradle.kts +1 -0
  47. package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
  48. package/dist/native/androidProject/settings.gradle.kts +11 -8
  49. package/dist/native/androidSetup.js +2 -2
  50. package/dist/native/bridge/WebBridge.d.ts +98 -0
  51. package/dist/native/bridge/WebBridge.js +40 -6
  52. package/dist/native/bridge/constants/NativeInterfaces.d.ts +391 -0
  53. package/dist/native/bridge/errors.d.ts +67 -0
  54. package/dist/native/bridge/errors.js +66 -151
  55. package/dist/native/bridge/hooks/index.d.ts +13 -0
  56. package/dist/native/bridge/hooks/index.js +0 -6
  57. package/dist/native/bridge/hooks/useAI.d.ts +1 -0
  58. package/dist/native/bridge/hooks/useAI.js +3 -1
  59. package/dist/native/bridge/hooks/useCamera.d.ts +86 -0
  60. package/dist/native/bridge/hooks/useCamera.js +35 -6
  61. package/dist/native/bridge/hooks/useDataProtection.d.ts +17 -0
  62. package/dist/native/bridge/hooks/useDeviceInfo.d.ts +34 -0
  63. package/dist/native/bridge/hooks/useDeviceInfo.js +5 -0
  64. package/dist/native/bridge/hooks/useFilePicker.d.ts +53 -0
  65. package/dist/native/bridge/hooks/useFilePicker.js +13 -0
  66. package/dist/native/bridge/hooks/useGoogleSignIn.d.ts +12 -0
  67. package/dist/native/bridge/hooks/useHapticFeedback.d.ts +76 -0
  68. package/dist/native/bridge/hooks/useHapticFeedback.js +6 -1
  69. package/dist/native/bridge/hooks/useIntent.d.ts +33 -0
  70. package/dist/native/bridge/hooks/useIntent.js +2 -0
  71. package/dist/native/bridge/hooks/useNativeTransition.d.ts +53 -0
  72. package/dist/native/bridge/hooks/useNativeTransition.js +12 -5
  73. package/dist/native/bridge/hooks/useNetworkStatus.d.ts +22 -0
  74. package/dist/native/bridge/hooks/useNetworkStatus.js +7 -0
  75. package/dist/native/bridge/hooks/useNotification.d.ts +83 -0
  76. package/dist/native/bridge/hooks/useNotification.js +81 -34
  77. package/dist/native/bridge/hooks/useSafeArea.d.ts +12 -0
  78. package/dist/native/bridge/hooks/useSafeArea.js +8 -0
  79. package/dist/native/bridge/hooks/useVideoStream.d.ts +67 -0
  80. package/dist/native/bridge/hooks/useVideoStream.js +38 -52
  81. package/dist/native/bridge/hooks/utils.d.ts +4 -0
  82. package/dist/native/bridge/hooks.d.ts +1 -0
  83. package/dist/native/bridge/perf/collectors/ApiCollector.d.ts +30 -0
  84. package/dist/native/bridge/perf/collectors/ApiCollector.js +4 -0
  85. package/dist/native/bridge/perf/collectors/CacheCollector.d.ts +43 -0
  86. package/dist/native/bridge/perf/collectors/CacheCollector.js +4 -0
  87. package/dist/native/bridge/perf/collectors/HookCollector.d.ts +37 -0
  88. package/dist/native/bridge/perf/collectors/HookCollector.js +4 -0
  89. package/dist/native/bridge/perf/collectors/InsightsCollector.d.ts +103 -0
  90. package/dist/native/bridge/perf/collectors/InsightsCollector.js +4 -0
  91. package/dist/native/bridge/perf/collectors/InteractionCollector.d.ts +53 -0
  92. package/dist/native/bridge/perf/collectors/InteractionCollector.js +4 -0
  93. package/dist/native/bridge/perf/collectors/KeyboardCollector.d.ts +30 -0
  94. package/dist/native/bridge/perf/collectors/KeyboardCollector.js +4 -0
  95. package/dist/native/bridge/perf/collectors/NavigationCollector.d.ts +37 -0
  96. package/dist/native/bridge/perf/collectors/NavigationCollector.js +4 -0
  97. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.d.ts +30 -0
  98. package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +4 -0
  99. package/dist/native/bridge/perf/collectors/RenderCollector.d.ts +47 -0
  100. package/dist/native/bridge/perf/collectors/RenderCollector.js +8 -0
  101. package/dist/native/bridge/perf/collectors/ScrollCollector.d.ts +29 -0
  102. package/dist/native/bridge/perf/collectors/ScrollCollector.js +4 -0
  103. package/dist/native/bridge/perf/collectors/ThreadCollector.d.ts +31 -0
  104. package/dist/native/bridge/perf/collectors/ThreadCollector.js +4 -0
  105. package/dist/native/bridge/perf/core/constants.d.ts +76 -0
  106. package/dist/native/bridge/perf/core/measure.d.ts +39 -0
  107. package/dist/native/bridge/perf/core/measure.js +4 -0
  108. package/dist/native/bridge/perf/core/session.d.ts +49 -0
  109. package/dist/native/bridge/perf/core/session.js +4 -0
  110. package/dist/native/bridge/perf/core/store.d.ts +37 -0
  111. package/dist/native/bridge/perf/core/store.js +4 -0
  112. package/dist/native/bridge/perf/core/summary.d.ts +60 -0
  113. package/dist/native/bridge/perf/core/traceExport.d.ts +10 -0
  114. package/dist/native/bridge/perf/core/waterfalls.d.ts +2 -0
  115. package/dist/native/bridge/perf/index.d.ts +63 -0
  116. package/dist/native/bridge/perf/index.js +4 -0
  117. package/dist/native/bridge/safeArea.d.ts +11 -0
  118. package/dist/native/bridge/safeArea.js +2 -0
  119. package/dist/native/bridge/useBaseHook.d.ts +137 -0
  120. package/dist/native/bridge/useBaseHook.js +34 -4
  121. package/dist/native/bridge/utils/CameraUtils.d.ts +94 -0
  122. package/dist/native/bridge/utils/CameraUtils.js +4 -0
  123. package/dist/native/bridge/utils/FileObjectConverter.d.ts +36 -0
  124. package/dist/native/bridge/utils/NativeBridge.d.ts +270 -0
  125. package/dist/native/bridge/utils/NativeBridge.js +10 -0
  126. package/dist/native/buildAndroid/index.js +1 -1
  127. package/dist/native/buildAppAndroid.js +9 -0
  128. package/dist/native/buildAppIos.js +16 -1
  129. package/dist/native/buildErrorFormat.js +57 -0
  130. package/dist/native/buildIos/config.js +15 -1
  131. package/dist/native/buildIos/index.js +1 -1
  132. package/dist/native/buildIos/plugins.js +10 -1
  133. package/dist/native/iosnativeWebView/CoreLogic/Package.swift +36 -0
  134. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamState.swift +1 -1
  135. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamStateMachine.swift +6 -4
  136. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/BootTimingUtility.swift +3 -3
  137. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/FrameworkServerUtils.swift +14 -9
  138. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NetworkMonitor.swift +11 -9
  139. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NotificationHandlerProvider.swift +1 -1
  140. package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/URLWhitelistManager.swift +7 -7
  141. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/AnyCodableTests.swift +154 -0
  142. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/BootTimingUtilityTests.swift +23 -5
  143. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/CatalystConstantsTests.swift +192 -0
  144. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/ConfigMappingTests.swift +1 -1
  145. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsErrorPathTests.swift +106 -0
  146. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +243 -0
  147. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/FrameworkServerUtilsTests.swift +1 -1
  148. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +119 -0
  149. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NotificationHandlerProviderTests.swift +59 -0
  150. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/NotificationHandlerTests.swift +1 -1
  151. package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/URLWhitelistManagerTests.swift +1 -1
  152. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateMachineTests.swift +171 -0
  153. package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateTests.swift +91 -0
  154. package/dist/native/iosnativeWebView/Package.swift +10 -9
  155. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +7 -2
  156. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +1 -2
  157. package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +5 -4
  158. package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +1 -0
  159. package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +1 -0
  160. package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +6 -5
  161. package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +1 -0
  162. package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +1 -0
  163. package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +31 -30
  164. package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +2 -1
  165. package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +1 -1
  166. package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +22 -19
  167. package/dist/native/iosnativeWebView/Sources/Core/Utils/ImageHandler.swift +40 -39
  168. package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +1 -0
  169. package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +15 -15
  170. package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +4 -3
  171. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +6 -1
  172. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +1 -0
  173. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +2 -1
  174. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +2 -1
  175. package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +3 -5
  176. package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +20 -13
  177. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +6 -5
  178. package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +14 -13
  179. package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +3 -3
  180. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +9 -8
  181. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +7 -6
  182. package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +24 -25
  183. package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +6 -1
  184. package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +1 -0
  185. package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +4 -0
  186. package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +2 -22
  187. package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +1 -0
  188. package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +1 -0
  189. package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +2 -1
  190. package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +15 -9
  191. package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +1 -0
  192. package/dist/native/setupEmulatorIos.js +2 -2
  193. package/dist/native/terminalProgress.js +17 -17
  194. package/dist/router/ClientRouter.d.ts +8 -0
  195. package/dist/router/ClientRouter.js +13 -11
  196. package/dist/router/ServerRouter.d.ts +13 -0
  197. package/dist/router/ServerRouter.js +19 -7
  198. package/dist/scripts/build.js +14 -2
  199. package/dist/scripts/loadEnvironmentVariables.js +3 -2
  200. package/dist/scripts/plugins.js +1 -1
  201. package/dist/scripts/preServerInit.js +49 -3
  202. package/dist/scripts/preflight.js +111 -0
  203. package/dist/scripts/scriptUtils.js +46 -1
  204. package/dist/scripts/serve.js +7 -3
  205. package/dist/scripts/start.js +8 -3
  206. package/dist/server/expressServer.d.ts +1 -0
  207. package/dist/server/expressServer.js +247 -271
  208. package/dist/server/manifestCache.d.ts +2 -0
  209. package/dist/server/manifestCache.js +25 -33
  210. package/dist/server/renderer/ChunkExtractor.d.ts +44 -0
  211. package/dist/server/renderer/ChunkExtractor.js +113 -125
  212. package/dist/server/renderer/document/Body.d.ts +21 -0
  213. package/dist/server/renderer/document/Body.jsx +45 -45
  214. package/dist/server/renderer/document/FastRefresh.d.ts +3 -0
  215. package/dist/server/renderer/document/FastRefresh.jsx +7 -13
  216. package/dist/server/renderer/document/Head.d.ts +20 -0
  217. package/dist/server/renderer/document/Head.jsx +32 -32
  218. package/dist/server/renderer/document/index.d.ts +2 -0
  219. package/dist/server/renderer/document/index.jsx +2 -2
  220. package/dist/server/renderer/extract.d.ts +67 -0
  221. package/dist/server/renderer/extract.js +92 -89
  222. package/dist/server/renderer/handler.d.ts +2 -0
  223. package/dist/server/renderer/handler.jsx +401 -404
  224. package/dist/server/renderer/index.d.ts +2 -0
  225. package/dist/server/renderer/index.js +5 -8
  226. package/dist/server/renderer/render.d.ts +35 -0
  227. package/dist/server/renderer/render.js +25 -32
  228. package/dist/server/utils/botDetectionMiddleware.d.ts +1 -0
  229. package/dist/server/utils/botDetectionMiddleware.js +33 -23
  230. package/dist/server/utils/cjsRequire.d.ts +8 -0
  231. package/dist/server/utils/cjsRequire.js +2 -2
  232. package/dist/server/utils/userAgentUtil.d.ts +7 -0
  233. package/dist/server/utils/userAgentUtil.js +68 -71
  234. package/dist/server/utils/validator.d.ts +31 -0
  235. package/dist/server/utils/validator.js +133 -158
  236. package/dist/vite/inject-cache-key-plugin.js +3 -27
  237. package/dist/vite/moduleAliases.js +36 -0
  238. package/dist/vite/node-loader.mjs +2 -29
  239. package/dist/vite/resolveDevServerConfig.js +4 -1
  240. package/dist/vite/vite.config.js +2 -29
  241. package/dist/web-router/components/MetaTag.d.ts +4 -0
  242. package/dist/web-router/components/MetaTag.jsx +26 -28
  243. package/dist/web-router/components/MetaTag.test.jsx +100 -0
  244. package/dist/web-router/components/RouterDataProvider.d.ts +96 -0
  245. package/dist/web-router/components/RouterDataProvider.jsx +260 -259
  246. package/dist/web-router/components/RouterDataProvider.server.test.jsx +98 -0
  247. package/dist/web-router/components/RouterDataProvider.test.jsx +310 -0
  248. package/dist/web-router/components/Split.d.ts +74 -0
  249. package/dist/web-router/components/Split.jsx +147 -156
  250. package/dist/web-router/components/Split.server.test.jsx +52 -0
  251. package/dist/web-router/components/Split.test.jsx +167 -0
  252. package/dist/web-router/components/SplitInview.d.ts +22 -0
  253. package/dist/web-router/components/SplitInview.jsx +78 -89
  254. package/dist/web-router/components/SplitInview.test.jsx +112 -0
  255. package/dist/web-router/components/SsrRequestContext.d.ts +20 -0
  256. package/dist/web-router/components/SsrRequestContext.jsx +17 -7
  257. package/dist/web-router/components/SsrRequestContext.test.jsx +53 -0
  258. package/dist/web-router/context.d.ts +15 -0
  259. package/dist/web-router/context.jsx +5 -5
  260. package/dist/web-router/context.test.jsx +10 -0
  261. package/dist/web-router/utils/metaDataUtils.d.ts +24 -0
  262. package/dist/web-router/utils/metaDataUtils.jsx +86 -86
  263. package/dist/web-router/utils/metaDataUtils.test.jsx +188 -0
  264. package/dist/web-router/vitest.setup.js +12 -0
  265. package/mcp_v2/knowledge-base.json +115 -17
  266. package/mcp_v2/mcp.js +141 -3
  267. package/mcp_v2/package-lock.json +2031 -0
  268. package/mcp_v2/package.json +13 -2
  269. package/mcp_v2/scripts/copyErrorsIndex.cjs +34 -0
  270. package/mcp_v2/test/errors.test.ts +123 -0
  271. package/mcp_v2/test/github.test.ts +177 -0
  272. package/mcp_v2/tools/errors.d.ts +20 -0
  273. package/mcp_v2/tools/errors.js +63 -0
  274. package/mcp_v2/tools/github.d.ts +41 -0
  275. package/mcp_v2/tools/github.js +231 -0
  276. package/mcp_v2/vitest.config.mjs +22 -0
  277. package/package.json +60 -11
  278. package/test/errors/docsDrift.test.ts +113 -0
  279. package/test/errors/index.test.ts +200 -0
  280. package/test/errors/registry.test.ts +108 -0
  281. package/test/scripts/preflight.test.ts +118 -0
  282. package/test/server/ChunkExtractor.test.ts +97 -0
  283. package/test/server/extract.test.ts +128 -0
  284. package/test/server/fixtures/template/server/document.jsx +19 -0
  285. package/test/server/fixtures/template/server/index.js +10 -0
  286. package/test/server/fixtures/template/src/js/containers/App/index.jsx +33 -0
  287. package/test/server/fixtures/template/src/js/routes/index.jsx +72 -0
  288. package/test/server/fixtures/template/src/js/routes/utils.jsx +39 -0
  289. package/test/server/fixtures/template/src/js/store/index.js +19 -0
  290. package/test/server/handler.assets.test.ts +330 -0
  291. package/test/server/handler.test.ts +208 -0
  292. package/test/server/manifestCache.test.ts +71 -0
  293. package/test/server/render.test.ts +71 -0
  294. package/test/server/scriptUtils.test.ts +103 -0
  295. package/test/server/userAgentUtil.test.ts +48 -0
  296. package/test/server/validator.test.ts +225 -0
  297. package/test/vite/resolveDevServerConfig.test.js +4 -1
  298. package/tsconfig.test.json +23 -0
  299. package/vitest.config.ts +93 -0
  300. package/dist/caching.js +0 -1
  301. package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +0 -187
  302. package/dist/native/bridge/standardizedHooks.js +0 -611
  303. package/dist/router.js +0 -1
  304. package/dist/scripts/validator.js +0 -149
  305. package/dist/web-router/hooks.jsx +0 -72
  306. package/dist/web-router/types/components/MetaTag.d.ts +0 -2
  307. package/dist/web-router/types/components/RouterDataProvider.d.ts +0 -95
  308. package/dist/web-router/types/context.d.ts +0 -14
  309. package/dist/web-router/types/hooks.d.ts +0 -1
  310. package/dist/web-router/types/index.d.ts +0 -3
  311. package/dist/web-router/types/utils/metaDataUtils.d.ts +0 -40
  312. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/CatalystConstants.swift +0 -0
  313. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/ConfigConstants.swift +0 -0
  314. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Notifications/NotificationModels.swift +0 -0
  315. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Protocols/NotificationHandlerProtocol.swift +0 -0
  316. /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NullNotificationHandler.swift +0 -0
@@ -1,391 +1,212 @@
1
1
  package io.yourname.androidproject
2
2
 
3
+ import android.content.Context
4
+ import android.webkit.WebResourceResponse
5
+ import org.junit.After
3
6
  import org.junit.Assert.*
7
+ import org.junit.Before
4
8
  import org.junit.Test
9
+ import org.mockito.kotlin.doReturn
10
+ import org.mockito.kotlin.mock
11
+ import java.io.File
5
12
  import java.security.MessageDigest
6
13
  import java.util.Properties
7
- import java.util.concurrent.TimeUnit
14
+ import kotlin.io.path.createTempDirectory
8
15
 
9
16
  /**
10
- * Unit tests for WebCacheManager
11
- * Tests cache logic, expiration rules, size management, and revalidation concepts
17
+ * Unit tests for WebCacheManager (Android coverage batch 3), replacing the
18
+ * previous WebCacheManagerTest.kt, which never instantiated the class —
19
+ * it hand-reimplemented cache-key generation using MD5 (the real
20
+ * implementation uses SHA-256, so those tests were validating a different
21
+ * algorithm entirely) and asserted against inline duration-math literals,
22
+ * same pattern as the vanity tests fixed in coverage batch 1
23
+ * (CameraUtilsTest/FileUtilsTest/NotificationUtilsTest).
12
24
  *
13
- * Coverage:
14
- * - Cache CRUD Operations (6 tests)
15
- * - Cache Expiration (5 tests)
16
- * - Cache Size Management (4 tests)
17
- * - Revalidation Logic (3 tests)
25
+ * Context.cacheDir is mocked to point at a real temp directory, matching
26
+ * OfflineCacheServiceTest's established pattern — everything downstream
27
+ * (File I/O, LruCache, MessageDigest) is then real code running against a
28
+ * real filesystem, not further mocked.
18
29
  *
19
- * Total: 18 tests
20
- *
21
- * Note: Tests focus on cache logic and algorithms without requiring full Android Context
30
+ * Deliberately out of scope: fetchAndCacheResourceBlocking's actual network
31
+ * path (real HttpURLConnection to a live URL) and revalidateInBackground's
32
+ * scope.launch coroutine body — both need either a real HTTP server (like
33
+ * FrameworkServerUtilsTest's loopback pattern, out of scope for this
34
+ * class-sized batch item) or extensive java.net mocking that would test the
35
+ * mock more than the code. The synchronous read path
36
+ * (getCachedResponseSync / getCachedResponseOrFetchSync's cache-hit
37
+ * branch), cache-key generation, mime/charset parsing, response validation,
38
+ * header replay filtering, and cleanup/clearAll's file-management logic are
39
+ * all real, fully JVM-testable, and were the actual 0%-covered surface.
22
40
  */
23
41
  class WebCacheManagerTest {
24
42
 
25
- // ============================================================
26
- // CATEGORY 1: CACHE CRUD OPERATIONS (6 tests)
27
- // ============================================================
28
-
29
- @Test
30
- fun `test MD5 cache key generation algorithm`() {
31
- // Test the MD5 hashing algorithm used by WebCacheManager
32
- val url1 = "https://example.com/resource.js"
33
- val url2 = "https://example.com/resource.js"
34
- val url3 = "https://example.com/different.js"
35
-
36
- val key1 = generateMD5CacheKey(url1)
37
- val key2 = generateMD5CacheKey(url2)
38
- val key3 = generateMD5CacheKey(url3)
43
+ private lateinit var cacheDir: File
44
+ private lateinit var context: Context
39
45
 
40
- // Same URLs should produce same keys
41
- assertEquals("Same URL should produce same cache key", key1, key2)
42
-
43
- // Different URLs should produce different keys
44
- assertNotEquals("Different URLs should produce different cache keys", key1, key3)
45
-
46
- // Cache key should be 32 characters (MD5 hex)
47
- assertEquals("MD5 hash should be 32 characters", 32, key1.length)
48
-
49
- // Cache key should only contain hex characters
50
- assertTrue("Cache key should only contain hex characters",
51
- key1.all { it in '0'..'9' || it in 'a'..'f' })
46
+ @Before
47
+ fun setUp() {
48
+ cacheDir = createTempDirectory(prefix = "catalyst-webcache-test").toFile()
49
+ context = mock {
50
+ on { getCacheDir() } doReturn cacheDir
51
+ }
52
52
  }
53
53
 
54
- @Test
55
- fun `test cache key generation consistency`() {
56
- val url = "https://example.com/test.js"
57
- val keys = (1..10).map { generateMD5CacheKey(url) }
58
-
59
- // All generated keys should be identical
60
- assertTrue("Cache key generation should be consistent",
61
- keys.all { it == keys.first() })
54
+ @After
55
+ fun tearDown() {
56
+ cacheDir.deleteRecursively()
62
57
  }
63
58
 
64
- @Test
65
- fun `test cache key handles special characters in URL`() {
66
- val urlWithParams = "https://example.com/api?param=value&foo=bar"
67
- val urlWithHash = "https://example.com/page#section"
68
- val urlWithUnicode = "https://example.com/文档/test.js"
69
-
70
- val key1 = generateMD5CacheKey(urlWithParams)
71
- val key2 = generateMD5CacheKey(urlWithHash)
72
- val key3 = generateMD5CacheKey(urlWithUnicode)
73
-
74
- // All should generate valid MD5 hashes
75
- assertEquals(32, key1.length)
76
- assertEquals(32, key2.length)
77
- assertEquals(32, key3.length)
78
-
79
- // Each should be unique
80
- assertNotEquals(key1, key2)
81
- assertNotEquals(key2, key3)
82
- assertNotEquals(key1, key3)
59
+ private fun sha256(url: String): String {
60
+ val md = MessageDigest.getInstance("SHA-256")
61
+ return md.digest(url.toByteArray()).joinToString("") { "%02x".format(it) }
83
62
  }
84
63
 
85
- @Test
86
- fun `test cache hit scenario with valid entry`() {
87
- // Simulate cache hit: entry exists and is fresh
88
- val currentTime = System.currentTimeMillis()
89
- val cacheTimestamp = currentTime - TimeUnit.HOURS.toMillis(1) // 1 hour old
90
- val maxAge = TimeUnit.HOURS.toMillis(24) // 24 hours
91
-
92
- val age = currentTime - cacheTimestamp
93
- val isFresh = age <= maxAge
64
+ // ============================================================
65
+ // Construction
66
+ // ============================================================
94
67
 
95
- assertTrue("1 hour old entry should be fresh (within 24h)", isFresh)
68
+ @Test
69
+ fun `constructing WebCacheManager creates the cache directory`() {
70
+ WebCacheManager(context)
71
+ val webviewCacheDir = File(cacheDir, "webview_cache")
72
+ assertTrue("webview_cache subdirectory should exist after construction", webviewCacheDir.exists())
96
73
  }
97
74
 
98
75
  @Test
99
- fun `test cache configuration properties parsing`() {
100
- val customProperties = Properties().apply {
76
+ fun `constructing with custom properties does not throw`() {
77
+ val props = Properties().apply {
101
78
  setProperty("cache.maxAge", "48")
102
79
  setProperty("cache.staleWhileRevalidate", "2")
103
80
  setProperty("cache.maxSize", "200")
104
81
  setProperty("cache.memoryFraction", "4")
105
82
  }
106
-
107
- // Verify property parsing logic
108
- val maxAge = customProperties.getProperty("cache.maxAge", "24")?.toLongOrNull() ?: 24L
109
- val staleWhileRevalidate = customProperties.getProperty("cache.staleWhileRevalidate", "1")?.toLongOrNull() ?: 1L
110
- val maxSize = customProperties.getProperty("cache.maxSize", "100")?.toLongOrNull() ?: 100L
111
- val memoryFraction = customProperties.getProperty("cache.memoryFraction", "8")?.toIntOrNull() ?: 8
112
-
113
- assertEquals("maxAge should be parsed correctly", 48L, maxAge)
114
- assertEquals("staleWhileRevalidate should be parsed correctly", 2L, staleWhileRevalidate)
115
- assertEquals("maxSize should be parsed correctly", 200L, maxSize)
116
- assertEquals("memoryFraction should be parsed correctly", 4, memoryFraction)
83
+ WebCacheManager(context, props)
117
84
  }
118
85
 
119
86
  @Test
120
- fun `test cache uses default values for missing properties`() {
121
- val emptyProperties = Properties()
122
-
123
- // Test default value fallback logic
124
- val maxAge = emptyProperties.getProperty("cache.maxAge", "24")?.toLongOrNull() ?: 24L
125
- val staleWhileRevalidate = emptyProperties.getProperty("cache.staleWhileRevalidate", "1")?.toLongOrNull() ?: 1L
126
- val maxSize = emptyProperties.getProperty("cache.maxSize", "100")?.toLongOrNull() ?: 100L
127
-
128
- assertEquals("Default maxAge should be 24 hours", 24L, maxAge)
129
- assertEquals("Default staleWhileRevalidate should be 1 hour", 1L, staleWhileRevalidate)
130
- assertEquals("Default maxSize should be 100 MB", 100L, maxSize)
131
- }
132
-
133
- // Helper method to replicate WebCacheManager's cache key generation
134
- private fun generateMD5CacheKey(url: String): String {
135
- return try {
136
- val md = MessageDigest.getInstance("MD5")
137
- val digest = md.digest(url.toByteArray())
138
- digest.joinToString("") { "%02x".format(it) }
139
- } catch (e: Exception) {
140
- url.hashCode().toString()
87
+ fun `constructing with malformed property values falls back to defaults without throwing`() {
88
+ val props = Properties().apply {
89
+ setProperty("cache.maxAge", "not-a-number")
90
+ setProperty("cache.memoryFraction", "also-not-a-number")
141
91
  }
92
+ // toLongOrNull()/toIntOrNull() should return null for these, and the
93
+ // ?: default should kick in rather than throwing.
94
+ WebCacheManager(context, props)
142
95
  }
143
96
 
144
97
  // ============================================================
145
- // CATEGORY 2: CACHE EXPIRATION (5 tests)
98
+ // getCachedResponseSync — no entry present
146
99
  // ============================================================
147
100
 
148
101
  @Test
149
- fun `test cache entry within maxAge is fresh`() {
150
- // Test freshness calculation
151
- val currentTime = System.currentTimeMillis()
152
- val cacheTimestamp = currentTime - TimeUnit.HOURS.toMillis(12) // 12 hours old
153
- val maxAge = TimeUnit.HOURS.toMillis(24) // 24 hour maxAge
154
-
155
- val age = currentTime - cacheTimestamp
156
- val isFresh = age <= maxAge
157
-
158
- assertTrue("12 hours should be within 24 hour maxAge", isFresh)
159
- assertEquals("Age should be 12 hours", TimeUnit.HOURS.toMillis(12), age)
160
- }
161
-
162
- @Test
163
- fun `test cache entry beyond maxAge is stale`() {
164
- // Test stale content detection
165
- val currentTime = System.currentTimeMillis()
166
- val cacheTimestamp = currentTime - TimeUnit.HOURS.toMillis(30) // 30 hours old
167
- val maxAge = TimeUnit.HOURS.toMillis(24) // 24 hour maxAge
168
-
169
- val age = currentTime - cacheTimestamp
170
- val isStale = age > maxAge
171
-
172
- assertTrue("30 hours should be beyond 24 hour maxAge", isStale)
173
- }
174
-
175
- @Test
176
- fun `test stale-while-revalidate window allows stale content`() {
177
- // Test stale-while-revalidate logic
178
- val currentTime = System.currentTimeMillis()
179
- val maxAge = TimeUnit.HOURS.toMillis(24)
180
- val staleWhileRevalidate = TimeUnit.HOURS.toMillis(1)
181
- val totalWindow = maxAge + staleWhileRevalidate
182
-
183
- // 24.5 hours old - stale but within revalidate window
184
- val cacheTimestamp = currentTime - TimeUnit.HOURS.toMillis(24) - TimeUnit.MINUTES.toMillis(30)
185
- val age = currentTime - cacheTimestamp
186
-
187
- val isWithinRevalidateWindow = age > maxAge && age <= totalWindow
188
-
189
- assertTrue("Content should be stale", age > maxAge)
190
- assertTrue("Content should be within stale-while-revalidate window",
191
- age <= totalWindow)
192
- assertTrue("Content within stale-while-revalidate should be valid",
193
- isWithinRevalidateWindow)
102
+ fun `getCachedResponseSync returns null when nothing is cached`() {
103
+ val manager = WebCacheManager(context)
104
+ val result = manager.getCachedResponseSync("https://example.com/a.js", emptyMap())
105
+ assertNull(result)
194
106
  }
195
107
 
196
- @Test
197
- fun `test expired cache beyond revalidate window is invalid`() {
198
- // Test expired content detection
199
- val currentTime = System.currentTimeMillis()
200
- val maxAge = TimeUnit.HOURS.toMillis(24)
201
- val staleWhileRevalidate = TimeUnit.HOURS.toMillis(1)
202
- val totalWindow = maxAge + staleWhileRevalidate
203
-
204
- // 26 hours old - beyond revalidate window
205
- val cacheTimestamp = currentTime - TimeUnit.HOURS.toMillis(26)
206
- val age = currentTime - cacheTimestamp
207
-
208
- val isExpired = age > totalWindow
209
-
210
- assertTrue("26 hours should be beyond 25 hour total window", isExpired)
211
- }
108
+ // ============================================================
109
+ // getCachedResponseSync — populate via fetchAndCacheResourceBlocking's
110
+ // sibling write path is out of scope (network), so populate the disk
111
+ // cache directly the way the production code itself would lay it out,
112
+ // then verify the read path picks it up correctly.
113
+ // ============================================================
212
114
 
213
115
  @Test
214
- fun `test cache expiration window boundaries`() {
215
- val maxAge = TimeUnit.HOURS.toMillis(24)
216
- val staleWhileRevalidate = TimeUnit.HOURS.toMillis(1)
217
-
218
- // Test exact boundary: 24 hours (fresh/stale boundary)
219
- val exactMaxAge = TimeUnit.HOURS.toMillis(24)
220
- assertTrue("Exactly maxAge should be at boundary",
221
- exactMaxAge <= maxAge)
222
-
223
- // Test exact boundary: 25 hours (stale/expired boundary)
224
- val exactTotalWindow = TimeUnit.HOURS.toMillis(25)
225
- assertTrue("Exactly total window should be at boundary",
226
- exactTotalWindow <= maxAge + staleWhileRevalidate)
227
-
228
- // Test just beyond: 25 hours + 1ms (expired)
229
- val justExpired = TimeUnit.HOURS.toMillis(25) + 1
230
- assertTrue("Just beyond window should be expired",
231
- justExpired > maxAge + staleWhileRevalidate)
116
+ fun `getCachedResponseSync serves a fresh disk-cached entry with no metadata sidecar`() {
117
+ // WebCacheManager.CacheMetadata is private, so a test in a different
118
+ // class can't construct a structurally-compatible .meta sidecar to
119
+ // exercise the "metadata present" branch directly. What IS reachable
120
+ // from outside the class: writing only the cache file itself (no
121
+ // .meta), which is exactly the state loadMetadata() must handle
122
+ // gracefully -- it falls back to a default CacheMetadata rather than
123
+ // throwing, and getCachedResponseSync should still serve the file.
124
+ //
125
+ // Note: WebResourceResponse is itself an android.webkit SDK stub
126
+ // class -- under the mockable jar (isReturnDefaultValues=true) its
127
+ // constructor is a no-op and its getters return defaults (null/0)
128
+ // regardless of what's passed in, so this can only assert that a
129
+ // response object was returned (proving WebCacheManager reached and
130
+ // completed the disk-cache-hit branch), not inspect its contents.
131
+ val manager = WebCacheManager(context)
132
+ val url = "https://example.com/fresh.js"
133
+ val cacheKey = sha256(url)
134
+ val webviewCacheDir = File(cacheDir, "webview_cache")
135
+
136
+ val cacheFile = File(webviewCacheDir, cacheKey)
137
+ cacheFile.writeBytes("console.log('cached');".toByteArray())
138
+ // No .meta file written -- loadMetadata()'s catch branch is what
139
+ // this test actually exercises.
140
+
141
+ val result = manager.getCachedResponseSync(url, emptyMap())
142
+ assertNotNull("Expected a cached response despite missing metadata sidecar", result)
143
+ }
144
+
145
+ @Test
146
+ fun `getCachedResponseSync ignores an expired disk-cached entry`() {
147
+ val manager = WebCacheManager(context)
148
+ val url = "https://example.com/stale.js"
149
+ val cacheKey = sha256(url)
150
+ val webviewCacheDir = File(cacheDir, "webview_cache")
151
+
152
+ val cacheFile = File(webviewCacheDir, cacheKey)
153
+ cacheFile.writeBytes("stale".toByteArray())
154
+ // Default maxAge is 24h + 1h stale-while-revalidate = 25h window.
155
+ // Set lastModified far enough in the past to fall outside it.
156
+ val twentySixHoursAgo = System.currentTimeMillis() - java.util.concurrent.TimeUnit.HOURS.toMillis(26)
157
+ cacheFile.setLastModified(twentySixHoursAgo)
158
+
159
+ val result = manager.getCachedResponseSync(url, emptyMap())
160
+ assertNull("Expired entry should not be served", result)
232
161
  }
233
162
 
234
163
  // ============================================================
235
- // CATEGORY 3: CACHE SIZE MANAGEMENT (4 tests)
164
+ // clearAll / cleanup
236
165
  // ============================================================
237
166
 
238
167
  @Test
239
- fun `test cache respects max size limit`() {
240
- // Test max cache size calculations
241
- val maxCacheSize = 100 * 1024 * 1024L // 100MB
168
+ fun `clearAll deletes all files in the cache directory`() {
169
+ val manager = WebCacheManager(context)
170
+ val webviewCacheDir = File(cacheDir, "webview_cache")
171
+ File(webviewCacheDir, "leftover1").writeText("x")
172
+ File(webviewCacheDir, "leftover2").writeText("y")
242
173
 
243
- assertTrue("Max cache size should be positive", maxCacheSize > 0)
174
+ manager.clearAll()
244
175
 
245
- // 50MB should be within limit
246
- val validSize = 50 * 1024 * 1024L
247
- assertTrue("50MB should be within 100MB limit", validSize <= maxCacheSize)
248
-
249
- // 150MB should exceed limit
250
- val oversizedCache = 150 * 1024 * 1024L
251
- assertTrue("150MB should exceed 100MB limit", oversizedCache > maxCacheSize)
252
- }
253
-
254
- @Test
255
- fun `test LRU eviction logic removes oldest first`() {
256
- // Test LRU ordering logic
257
- val time1 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(3) // 3 days ago
258
- val time2 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(2) // 2 days ago
259
- val time3 = System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1) // 1 day ago
260
-
261
- // Create file timestamps map
262
- val files = mapOf(
263
- "oldest.cache" to time1,
264
- "middle.cache" to time2,
265
- "newest.cache" to time3
176
+ assertTrue(
177
+ "webview_cache directory should be empty after clearAll",
178
+ webviewCacheDir.listFiles()?.isEmpty() ?: true
266
179
  )
267
-
268
- // Sort by timestamp (LRU logic)
269
- val sortedFiles = files.entries.sortedBy { it.value }
270
-
271
- // Verify LRU ordering
272
- assertEquals("First should be oldest", "oldest.cache", sortedFiles[0].key)
273
- assertEquals("Last should be newest", "newest.cache", sortedFiles[2].key)
274
180
  }
275
181
 
276
182
  @Test
277
- fun `test memory cache size calculation`() {
278
- // Test memory cache size calculation based on runtime memory
279
- val maxMemory = Runtime.getRuntime().maxMemory()
280
-
281
- // Default: 1/8 of max memory
282
- val memoryCacheFraction = 8
283
- val expectedCacheSize = (maxMemory / 1024 / memoryCacheFraction).toInt()
284
-
285
- assertTrue("Memory cache size should be positive", expectedCacheSize > 0)
286
-
287
- // Verify it's a reasonable size (at least 1MB)
288
- assertTrue("Memory cache should be at least 1MB",
289
- expectedCacheSize >= 1024)
290
-
291
- // Test custom fraction: 1/4 of max memory
292
- val customFraction = 4
293
- val customCacheSize = (maxMemory / 1024 / customFraction).toInt()
294
-
295
- assertTrue("Custom cache should be larger than default",
296
- customCacheSize > expectedCacheSize)
183
+ fun `clearAll on an already-empty cache does not throw`() {
184
+ val manager = WebCacheManager(context)
185
+ manager.clearAll()
297
186
  }
298
187
 
299
188
  @Test
300
- fun `test disk usage tracking and file size summation`() {
301
- // Test file size calculation logic
302
- val file1Size = 1024L * 50 // 50KB
303
- val file2Size = 1024L * 100 // 100KB
304
- val file3Size = 1024L * 200 // 200KB
189
+ fun `cleanup deletes expired files and keeps fresh ones`() = kotlinx.coroutines.test.runTest {
190
+ val manager = WebCacheManager(context)
191
+ val webviewCacheDir = File(cacheDir, "webview_cache")
305
192
 
306
- val totalSize = file1Size + file2Size + file3Size
193
+ val freshFile = File(webviewCacheDir, "fresh-entry")
194
+ freshFile.writeText("fresh")
307
195
 
308
- assertEquals("Total should be 350KB", 1024L * 350, totalSize)
196
+ val expiredFile = File(webviewCacheDir, "expired-entry")
197
+ expiredFile.writeText("expired")
198
+ val twentySixHoursAgo = System.currentTimeMillis() - java.util.concurrent.TimeUnit.HOURS.toMillis(26)
199
+ expiredFile.setLastModified(twentySixHoursAgo)
309
200
 
310
- // Test against max cache size
311
- val maxCacheSize = 100 * 1024 * 1024L // 100MB
201
+ manager.cleanup()
312
202
 
313
- assertTrue("350KB should be well under 100MB limit",
314
- totalSize < maxCacheSize)
315
-
316
- // Test eviction needed scenario
317
- val largeTotalSize = 120 * 1024 * 1024L // 120MB
318
- assertTrue("120MB should exceed 100MB limit and trigger eviction",
319
- largeTotalSize > maxCacheSize)
320
- }
321
-
322
- // ============================================================
323
- // CATEGORY 4: REVALIDATION LOGIC (3 tests)
324
- // ============================================================
325
-
326
- @Test
327
- fun `test ETag header format and validation`() {
328
- // Test ETag format validation
329
- val strongETag = "\"33a64df551425fcc55e4d42a148795d9f25f89d4\""
330
- val weakETag = "W/\"33a64df551425fcc55e4d42a148795d9f25f89d4\""
331
-
332
- assertNotNull("Strong ETag should not be null", strongETag)
333
- assertTrue("Strong ETag should be quoted",
334
- strongETag.startsWith("\"") && strongETag.endsWith("\""))
335
-
336
- assertNotNull("Weak ETag should not be null", weakETag)
337
- assertTrue("Weak ETag should have W/ prefix", weakETag.startsWith("W/"))
338
-
339
- // Test If-None-Match header construction
340
- val ifNoneMatch = strongETag
341
- assertEquals("If-None-Match should match ETag", strongETag, ifNoneMatch)
203
+ assertTrue("Fresh file should survive cleanup", freshFile.exists())
204
+ assertFalse("Expired file should be deleted by cleanup", expiredFile.exists())
342
205
  }
343
206
 
344
207
  @Test
345
- fun `test Last-Modified header format and validation`() {
346
- // Test Last-Modified HTTP date format
347
- val lastModified = "Wed, 21 Oct 2015 07:28:00 GMT"
348
-
349
- assertNotNull("Last-Modified should not be null", lastModified)
350
- assertTrue("Last-Modified should be non-empty", lastModified.isNotEmpty())
351
- assertTrue("Last-Modified should contain GMT",
352
- lastModified.endsWith("GMT"))
353
-
354
- // Verify format matches HTTP-date (RFC 7231)
355
- // Format: <day-name>, <day> <month> <year> <hour>:<minute>:<second> GMT
356
- val httpDatePattern = Regex("""\w{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT""")
357
- assertTrue("Last-Modified should match HTTP date format",
358
- lastModified.matches(httpDatePattern))
359
-
360
- // Test If-Modified-Since header construction
361
- val ifModifiedSince = lastModified
362
- assertEquals("If-Modified-Since should match Last-Modified",
363
- lastModified, ifModifiedSince)
364
- }
365
-
366
- @Test
367
- fun `test revalidation prevents duplicate requests`() {
368
- // Test duplicate revalidation prevention logic
369
- val cacheKey = generateMD5CacheKey("https://example.com/resource.js")
370
- val ongoingRevalidations = mutableSetOf<String>()
371
-
372
- // First revalidation attempt
373
- val added1 = ongoingRevalidations.add(cacheKey)
374
- assertTrue("First revalidation should be allowed", added1)
375
- assertTrue("Cache key should be in ongoing set",
376
- ongoingRevalidations.contains(cacheKey))
377
-
378
- // Second revalidation attempt (duplicate)
379
- val added2 = ongoingRevalidations.add(cacheKey)
380
- assertFalse("Duplicate revalidation should be prevented", added2)
381
-
382
- // After completion
383
- ongoingRevalidations.remove(cacheKey)
384
- assertFalse("Cache key should be removed after completion",
385
- ongoingRevalidations.contains(cacheKey))
386
-
387
- // Third revalidation attempt (allowed after completion)
388
- val added3 = ongoingRevalidations.add(cacheKey)
389
- assertTrue("Revalidation should be allowed after previous completion", added3)
208
+ fun `cleanup on an empty cache directory does not throw`() = kotlinx.coroutines.test.runTest {
209
+ val manager = WebCacheManager(context)
210
+ manager.cleanup()
390
211
  }
391
212
  }