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.
- package/changelog.md +185 -71
- package/dist/errors/generateDocs.js +100 -0
- package/dist/errors/index.js +243 -0
- package/dist/errors/registry.js +483 -0
- package/dist/errors-index.json +506 -0
- package/dist/globals.d.ts +62 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.jsx +5 -8
- package/dist/logger.js +1 -1
- package/dist/native/androidProject/app/build.gradle.kts +142 -7
- package/dist/native/androidProject/app/src/main/AndroidManifest.xml +4 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/NativeBridge.kt +2 -2
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/BarcodeDetector.kt +3 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/camera/CameraSessionManager.kt +3 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/FrameworkServerUtils.kt +43 -8
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/BridgeUtilsTest.kt +181 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/CameraUtilsTest.kt +156 -236
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileSizeRouterUtilsTest.kt +471 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FileUtilsTest.kt +222 -226
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/FrameworkServerUtilsTest.kt +300 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/IntentUtilsTest.kt +244 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/MetricsMonitorTest.kt +359 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NativeBridgeTest.kt +108 -63
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/NotificationUtilsTest.kt +329 -385
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/OfflineCacheServiceTest.kt +302 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/PerfEventBufferTest.kt +133 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/UtilsKtTest.kt +152 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +145 -324
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/BarcodeDetectorTest.kt +132 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/HoldControllerTest.kt +155 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/TorchControllerTest.kt +150 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateMachineTest.kt +120 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/VideoStreamStateTest.kt +62 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ViewfinderMapperTest.kt +143 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/camera/ZoomControllerTest.kt +421 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/design/DesignTokensTest.kt +207 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/plugins/PluginBridgeTest.kt +368 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/FridaDetectorTest.kt +193 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckManagerTest.kt +260 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/security/SecurityCheckSchedulerTest.kt +166 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DeviceInfoUtilsTest.kt +167 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/DownloadUtilsTest.kt +197 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/NetworkMonitorTest.kt +271 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/PushNotificationUtilsTest.kt +119 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +108 -0
- package/dist/native/androidProject/build.gradle.kts +1 -0
- package/dist/native/androidProject/gradle/libs.versions.toml +26 -0
- package/dist/native/androidProject/settings.gradle.kts +11 -8
- package/dist/native/androidSetup.js +2 -2
- package/dist/native/bridge/WebBridge.d.ts +98 -0
- package/dist/native/bridge/WebBridge.js +40 -6
- package/dist/native/bridge/constants/NativeInterfaces.d.ts +391 -0
- package/dist/native/bridge/errors.d.ts +67 -0
- package/dist/native/bridge/errors.js +66 -151
- package/dist/native/bridge/hooks/index.d.ts +13 -0
- package/dist/native/bridge/hooks/index.js +0 -6
- package/dist/native/bridge/hooks/useAI.d.ts +1 -0
- package/dist/native/bridge/hooks/useAI.js +3 -1
- package/dist/native/bridge/hooks/useCamera.d.ts +86 -0
- package/dist/native/bridge/hooks/useCamera.js +35 -6
- package/dist/native/bridge/hooks/useDataProtection.d.ts +17 -0
- package/dist/native/bridge/hooks/useDeviceInfo.d.ts +34 -0
- package/dist/native/bridge/hooks/useDeviceInfo.js +5 -0
- package/dist/native/bridge/hooks/useFilePicker.d.ts +53 -0
- package/dist/native/bridge/hooks/useFilePicker.js +13 -0
- package/dist/native/bridge/hooks/useGoogleSignIn.d.ts +12 -0
- package/dist/native/bridge/hooks/useHapticFeedback.d.ts +76 -0
- package/dist/native/bridge/hooks/useHapticFeedback.js +6 -1
- package/dist/native/bridge/hooks/useIntent.d.ts +33 -0
- package/dist/native/bridge/hooks/useIntent.js +2 -0
- package/dist/native/bridge/hooks/useNativeTransition.d.ts +53 -0
- package/dist/native/bridge/hooks/useNativeTransition.js +12 -5
- package/dist/native/bridge/hooks/useNetworkStatus.d.ts +22 -0
- package/dist/native/bridge/hooks/useNetworkStatus.js +7 -0
- package/dist/native/bridge/hooks/useNotification.d.ts +83 -0
- package/dist/native/bridge/hooks/useNotification.js +81 -34
- package/dist/native/bridge/hooks/useSafeArea.d.ts +12 -0
- package/dist/native/bridge/hooks/useSafeArea.js +8 -0
- package/dist/native/bridge/hooks/useVideoStream.d.ts +67 -0
- package/dist/native/bridge/hooks/useVideoStream.js +38 -52
- package/dist/native/bridge/hooks/utils.d.ts +4 -0
- package/dist/native/bridge/hooks.d.ts +1 -0
- package/dist/native/bridge/perf/collectors/ApiCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/ApiCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.d.ts +43 -0
- package/dist/native/bridge/perf/collectors/CacheCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/HookCollector.d.ts +37 -0
- package/dist/native/bridge/perf/collectors/HookCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.d.ts +103 -0
- package/dist/native/bridge/perf/collectors/InsightsCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.d.ts +53 -0
- package/dist/native/bridge/perf/collectors/InteractionCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/KeyboardCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.d.ts +37 -0
- package/dist/native/bridge/perf/collectors/NavigationCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.d.ts +30 -0
- package/dist/native/bridge/perf/collectors/NetworkTimingCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.d.ts +47 -0
- package/dist/native/bridge/perf/collectors/RenderCollector.js +8 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.d.ts +29 -0
- package/dist/native/bridge/perf/collectors/ScrollCollector.js +4 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.d.ts +31 -0
- package/dist/native/bridge/perf/collectors/ThreadCollector.js +4 -0
- package/dist/native/bridge/perf/core/constants.d.ts +76 -0
- package/dist/native/bridge/perf/core/measure.d.ts +39 -0
- package/dist/native/bridge/perf/core/measure.js +4 -0
- package/dist/native/bridge/perf/core/session.d.ts +49 -0
- package/dist/native/bridge/perf/core/session.js +4 -0
- package/dist/native/bridge/perf/core/store.d.ts +37 -0
- package/dist/native/bridge/perf/core/store.js +4 -0
- package/dist/native/bridge/perf/core/summary.d.ts +60 -0
- package/dist/native/bridge/perf/core/traceExport.d.ts +10 -0
- package/dist/native/bridge/perf/core/waterfalls.d.ts +2 -0
- package/dist/native/bridge/perf/index.d.ts +63 -0
- package/dist/native/bridge/perf/index.js +4 -0
- package/dist/native/bridge/safeArea.d.ts +11 -0
- package/dist/native/bridge/safeArea.js +2 -0
- package/dist/native/bridge/useBaseHook.d.ts +137 -0
- package/dist/native/bridge/useBaseHook.js +34 -4
- package/dist/native/bridge/utils/CameraUtils.d.ts +94 -0
- package/dist/native/bridge/utils/CameraUtils.js +4 -0
- package/dist/native/bridge/utils/FileObjectConverter.d.ts +36 -0
- package/dist/native/bridge/utils/NativeBridge.d.ts +270 -0
- package/dist/native/bridge/utils/NativeBridge.js +10 -0
- package/dist/native/buildAndroid/index.js +1 -1
- package/dist/native/buildAppAndroid.js +9 -0
- package/dist/native/buildAppIos.js +16 -1
- package/dist/native/buildErrorFormat.js +57 -0
- package/dist/native/buildIos/config.js +15 -1
- package/dist/native/buildIos/index.js +1 -1
- package/dist/native/buildIos/plugins.js +10 -1
- package/dist/native/iosnativeWebView/CoreLogic/Package.swift +36 -0
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamState.swift +1 -1
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Camera/VideoStreamStateMachine.swift +6 -4
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/BootTimingUtility.swift +3 -3
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/FrameworkServerUtils.swift +14 -9
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NetworkMonitor.swift +11 -9
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/NotificationHandlerProvider.swift +1 -1
- package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Utils/URLWhitelistManager.swift +7 -7
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/AnyCodableTests.swift +154 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/BootTimingUtilityTests.swift +23 -5
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/CatalystConstantsTests.swift +192 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/ConfigMappingTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsErrorPathTests.swift +106 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +243 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/FrameworkServerUtilsTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +119 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NotificationHandlerProviderTests.swift +59 -0
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/NotificationHandlerTests.swift +1 -1
- package/dist/native/iosnativeWebView/{iosnativeWebViewTests → CoreLogic/Tests/CatalystCoreLogicTests}/URLWhitelistManagerTests.swift +1 -1
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateMachineTests.swift +171 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/VideoStreamStateTests.swift +91 -0
- package/dist/native/iosnativeWebView/Package.swift +10 -9
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/LocalNotificationHandler.swift +7 -2
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/NotificationManager.swift +1 -2
- package/dist/native/iosnativeWebView/Sources/CatalystNotifications/PushNotificationHandler.swift +5 -4
- package/dist/native/iosnativeWebView/Sources/Core/Camera/CameraSessionManager.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/HoldController.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Camera/NativeCameraManager.swift +6 -5
- package/dist/native/iosnativeWebView/Sources/Core/Camera/ZoomController.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +31 -30
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CameraTransportUtils.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/DeviceInfoUtils.swift +1 -1
- package/dist/native/iosnativeWebView/Sources/Core/Utils/FilePickerHandler.swift +22 -19
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ImageHandler.swift +40 -39
- package/dist/native/iosnativeWebView/Sources/Core/Utils/PerfEventBuffer.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/Utils/ResourceURLProtocol.swift +15 -15
- package/dist/native/iosnativeWebView/Sources/Core/Utils/WebKitConfig.swift +4 -3
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeCommandHandler.swift +6 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeDelegateHandler.swift +1 -0
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeFileHandler.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeJavaScriptInterface.swift +2 -1
- package/dist/native/iosnativeWebView/Sources/Core/WebView/BridgeMessageValidator.swift +3 -5
- package/dist/native/iosnativeWebView/Sources/Core/WebView/NativeBridge.swift +20 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashView.swift +6 -5
- package/dist/native/iosnativeWebView/Sources/Core/WebView/SplashViewModel.swift +14 -13
- package/dist/native/iosnativeWebView/Sources/Core/WebView/TransitionManager.swift +3 -3
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebView.swift +9 -8
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewModel.swift +7 -6
- package/dist/native/iosnativeWebView/Sources/Core/WebView/WebViewNavigationDelegate.swift +24 -25
- package/dist/native/iosnativeWebView/iosnativeWebView/AppDelegate.swift +6 -1
- package/dist/native/iosnativeWebView/iosnativeWebView/ContentView.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebView/iosnativeWebViewApp.swift +4 -0
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.pbxproj +2 -22
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeMessageValidatorTests.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/CacheManagerTests.swift +1 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/FilePickerHandlerTests.swift +2 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/NativeBridgeTests.swift +15 -9
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +1 -0
- package/dist/native/setupEmulatorIos.js +2 -2
- package/dist/native/terminalProgress.js +17 -17
- package/dist/router/ClientRouter.d.ts +8 -0
- package/dist/router/ClientRouter.js +13 -11
- package/dist/router/ServerRouter.d.ts +13 -0
- package/dist/router/ServerRouter.js +19 -7
- package/dist/scripts/build.js +14 -2
- package/dist/scripts/loadEnvironmentVariables.js +3 -2
- package/dist/scripts/plugins.js +1 -1
- package/dist/scripts/preServerInit.js +49 -3
- package/dist/scripts/preflight.js +111 -0
- package/dist/scripts/scriptUtils.js +46 -1
- package/dist/scripts/serve.js +7 -3
- package/dist/scripts/start.js +8 -3
- package/dist/server/expressServer.d.ts +1 -0
- package/dist/server/expressServer.js +247 -271
- package/dist/server/manifestCache.d.ts +2 -0
- package/dist/server/manifestCache.js +25 -33
- package/dist/server/renderer/ChunkExtractor.d.ts +44 -0
- package/dist/server/renderer/ChunkExtractor.js +113 -125
- package/dist/server/renderer/document/Body.d.ts +21 -0
- package/dist/server/renderer/document/Body.jsx +45 -45
- package/dist/server/renderer/document/FastRefresh.d.ts +3 -0
- package/dist/server/renderer/document/FastRefresh.jsx +7 -13
- package/dist/server/renderer/document/Head.d.ts +20 -0
- package/dist/server/renderer/document/Head.jsx +32 -32
- package/dist/server/renderer/document/index.d.ts +2 -0
- package/dist/server/renderer/document/index.jsx +2 -2
- package/dist/server/renderer/extract.d.ts +67 -0
- package/dist/server/renderer/extract.js +92 -89
- package/dist/server/renderer/handler.d.ts +2 -0
- package/dist/server/renderer/handler.jsx +401 -404
- package/dist/server/renderer/index.d.ts +2 -0
- package/dist/server/renderer/index.js +5 -8
- package/dist/server/renderer/render.d.ts +35 -0
- package/dist/server/renderer/render.js +25 -32
- package/dist/server/utils/botDetectionMiddleware.d.ts +1 -0
- package/dist/server/utils/botDetectionMiddleware.js +33 -23
- package/dist/server/utils/cjsRequire.d.ts +8 -0
- package/dist/server/utils/cjsRequire.js +2 -2
- package/dist/server/utils/userAgentUtil.d.ts +7 -0
- package/dist/server/utils/userAgentUtil.js +68 -71
- package/dist/server/utils/validator.d.ts +31 -0
- package/dist/server/utils/validator.js +133 -158
- package/dist/vite/inject-cache-key-plugin.js +3 -27
- package/dist/vite/moduleAliases.js +36 -0
- package/dist/vite/node-loader.mjs +2 -29
- package/dist/vite/resolveDevServerConfig.js +4 -1
- package/dist/vite/vite.config.js +2 -29
- package/dist/web-router/components/MetaTag.d.ts +4 -0
- package/dist/web-router/components/MetaTag.jsx +26 -28
- package/dist/web-router/components/MetaTag.test.jsx +100 -0
- package/dist/web-router/components/RouterDataProvider.d.ts +96 -0
- package/dist/web-router/components/RouterDataProvider.jsx +260 -259
- package/dist/web-router/components/RouterDataProvider.server.test.jsx +98 -0
- package/dist/web-router/components/RouterDataProvider.test.jsx +310 -0
- package/dist/web-router/components/Split.d.ts +74 -0
- package/dist/web-router/components/Split.jsx +147 -156
- package/dist/web-router/components/Split.server.test.jsx +52 -0
- package/dist/web-router/components/Split.test.jsx +167 -0
- package/dist/web-router/components/SplitInview.d.ts +22 -0
- package/dist/web-router/components/SplitInview.jsx +78 -89
- package/dist/web-router/components/SplitInview.test.jsx +112 -0
- package/dist/web-router/components/SsrRequestContext.d.ts +20 -0
- package/dist/web-router/components/SsrRequestContext.jsx +17 -7
- package/dist/web-router/components/SsrRequestContext.test.jsx +53 -0
- package/dist/web-router/context.d.ts +15 -0
- package/dist/web-router/context.jsx +5 -5
- package/dist/web-router/context.test.jsx +10 -0
- package/dist/web-router/utils/metaDataUtils.d.ts +24 -0
- package/dist/web-router/utils/metaDataUtils.jsx +86 -86
- package/dist/web-router/utils/metaDataUtils.test.jsx +188 -0
- package/dist/web-router/vitest.setup.js +12 -0
- package/mcp_v2/knowledge-base.json +115 -17
- package/mcp_v2/mcp.js +141 -3
- package/mcp_v2/package-lock.json +2031 -0
- package/mcp_v2/package.json +13 -2
- package/mcp_v2/scripts/copyErrorsIndex.cjs +34 -0
- package/mcp_v2/test/errors.test.ts +123 -0
- package/mcp_v2/test/github.test.ts +177 -0
- package/mcp_v2/tools/errors.d.ts +20 -0
- package/mcp_v2/tools/errors.js +63 -0
- package/mcp_v2/tools/github.d.ts +41 -0
- package/mcp_v2/tools/github.js +231 -0
- package/mcp_v2/vitest.config.mjs +22 -0
- package/package.json +60 -11
- package/test/errors/docsDrift.test.ts +113 -0
- package/test/errors/index.test.ts +200 -0
- package/test/errors/registry.test.ts +108 -0
- package/test/scripts/preflight.test.ts +118 -0
- package/test/server/ChunkExtractor.test.ts +97 -0
- package/test/server/extract.test.ts +128 -0
- package/test/server/fixtures/template/server/document.jsx +19 -0
- package/test/server/fixtures/template/server/index.js +10 -0
- package/test/server/fixtures/template/src/js/containers/App/index.jsx +33 -0
- package/test/server/fixtures/template/src/js/routes/index.jsx +72 -0
- package/test/server/fixtures/template/src/js/routes/utils.jsx +39 -0
- package/test/server/fixtures/template/src/js/store/index.js +19 -0
- package/test/server/handler.assets.test.ts +330 -0
- package/test/server/handler.test.ts +208 -0
- package/test/server/manifestCache.test.ts +71 -0
- package/test/server/render.test.ts +71 -0
- package/test/server/scriptUtils.test.ts +103 -0
- package/test/server/userAgentUtil.test.ts +48 -0
- package/test/server/validator.test.ts +225 -0
- package/test/vite/resolveDevServerConfig.test.js +4 -1
- package/tsconfig.test.json +23 -0
- package/vitest.config.ts +93 -0
- package/dist/caching.js +0 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/DeviceInfoUtilsTest.kt +0 -187
- package/dist/native/bridge/standardizedHooks.js +0 -611
- package/dist/router.js +0 -1
- package/dist/scripts/validator.js +0 -149
- package/dist/web-router/hooks.jsx +0 -72
- package/dist/web-router/types/components/MetaTag.d.ts +0 -2
- package/dist/web-router/types/components/RouterDataProvider.d.ts +0 -95
- package/dist/web-router/types/context.d.ts +0 -14
- package/dist/web-router/types/hooks.d.ts +0 -1
- package/dist/web-router/types/index.d.ts +0 -3
- package/dist/web-router/types/utils/metaDataUtils.d.ts +0 -40
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/CatalystConstants.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Constants/ConfigConstants.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Notifications/NotificationModels.swift +0 -0
- /package/dist/native/iosnativeWebView/{Sources/Core → CoreLogic/Sources/CatalystCoreLogic}/Protocols/NotificationHandlerProtocol.swift +0 -0
- /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
|
|
14
|
+
import kotlin.io.path.createTempDirectory
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
|
-
* Unit tests for WebCacheManager
|
|
11
|
-
*
|
|
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
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
@
|
|
55
|
-
fun
|
|
56
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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 `
|
|
100
|
-
val
|
|
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 `
|
|
121
|
-
val
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
//
|
|
98
|
+
// getCachedResponseSync — no entry present
|
|
146
99
|
// ============================================================
|
|
147
100
|
|
|
148
101
|
@Test
|
|
149
|
-
fun `
|
|
150
|
-
|
|
151
|
-
val
|
|
152
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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 `
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
//
|
|
229
|
-
val
|
|
230
|
-
|
|
231
|
-
|
|
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
|
-
//
|
|
164
|
+
// clearAll / cleanup
|
|
236
165
|
// ============================================================
|
|
237
166
|
|
|
238
167
|
@Test
|
|
239
|
-
fun `
|
|
240
|
-
|
|
241
|
-
val
|
|
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
|
-
|
|
174
|
+
manager.clearAll()
|
|
244
175
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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 `
|
|
278
|
-
|
|
279
|
-
|
|
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 `
|
|
301
|
-
|
|
302
|
-
val
|
|
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
|
|
193
|
+
val freshFile = File(webviewCacheDir, "fresh-entry")
|
|
194
|
+
freshFile.writeText("fresh")
|
|
307
195
|
|
|
308
|
-
|
|
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
|
-
|
|
311
|
-
val maxCacheSize = 100 * 1024 * 1024L // 100MB
|
|
201
|
+
manager.cleanup()
|
|
312
202
|
|
|
313
|
-
assertTrue("
|
|
314
|
-
|
|
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 `
|
|
346
|
-
|
|
347
|
-
|
|
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
|
}
|