catalyst-core-internal 0.1.0 → 0.1.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/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/BridgeMessageValidator.kt +10 -2
- package/dist/native/bridge/hooks.js +4 -4
- package/dist/native/bridge/useBaseHook.js +3 -4
- package/dist/native/bridge/utils/NativeBridge.js +4 -4
- package/dist/native/iosnativeWebView/Sources/Core/Utils/CacheManager.swift +2 -13
- package/dist/native/iosnativeWebView/iosnativeWebView.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +0 -36
- package/dist/native/iosnativeWebView/iosnativeWebView.xctestplan +0 -1
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/FrameworkServerUtilsTests.swift +4 -14
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/WebViewTests.swift +21 -9
- package/mcp_v2/conversion-tasks.json +371 -0
- package/mcp_v2/knowledge-base.json +1450 -0
- package/mcp_v2/lib/helpers.js +145 -0
- package/mcp_v2/mcp.js +366 -0
- package/mcp_v2/package.json +13 -0
- package/mcp_v2/schema.sql +88 -0
- package/mcp_v2/setup.js +262 -0
- package/mcp_v2/tools/build.js +449 -0
- package/mcp_v2/tools/config.js +262 -0
- package/mcp_v2/tools/conversion.js +492 -0
- package/mcp_v2/tools/debug.js +62 -0
- package/mcp_v2/tools/knowledge.js +213 -0
- package/mcp_v2/tools/sync.js +21 -0
- package/mcp_v2/tools/tasks.js +844 -0
- package/package.json +1 -1
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/SecurityBridgeTest.kt +0 -199
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeCommandHandlerSecurityTests.swift +0 -212
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/ScreenSecureManagerTests.swift +0 -121
package/package.json
CHANGED
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
package io.yourname.androidproject
|
|
2
|
-
|
|
3
|
-
import org.json.JSONObject
|
|
4
|
-
import org.junit.Assert.*
|
|
5
|
-
import org.junit.Test
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Unit tests for security bridge command routing and JSON injection safety.
|
|
9
|
-
*
|
|
10
|
-
* Coverage:
|
|
11
|
-
* - setScreenSecure command routing (5 tests)
|
|
12
|
-
* - getScreenSecure command routing (2 tests)
|
|
13
|
-
* - clearWebData command routing (2 tests)
|
|
14
|
-
* - JSON injection safety in error responses (3 tests)
|
|
15
|
-
*
|
|
16
|
-
* Total: 12 tests
|
|
17
|
-
*
|
|
18
|
-
* Note: These tests operate on static parsing/validation logic and JSON data
|
|
19
|
-
* structures. Full lifecycle (FLAG_SECURE, CookieManager) is exercised in
|
|
20
|
-
* device integration tests; here we confirm command acceptance, param parsing,
|
|
21
|
-
* and that error payloads produced via JSONObject cannot carry injected content.
|
|
22
|
-
*/
|
|
23
|
-
class SecurityBridgeTest {
|
|
24
|
-
|
|
25
|
-
// ============================================================
|
|
26
|
-
// CATEGORY 1: setScreenSecure COMMAND ROUTING (5 tests)
|
|
27
|
-
// ============================================================
|
|
28
|
-
|
|
29
|
-
@Test
|
|
30
|
-
fun `test setScreenSecure command is accepted by validator`() {
|
|
31
|
-
val messageJson = """{"command": "setScreenSecure", "data": {"enable": true}}"""
|
|
32
|
-
|
|
33
|
-
val result = NativeBridge.parseAndValidateMessage(messageJson)
|
|
34
|
-
|
|
35
|
-
assertTrue("setScreenSecure should be a valid command", result.isValid)
|
|
36
|
-
assertEquals("setScreenSecure", result.command)
|
|
37
|
-
assertNull(result.error)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
@Test
|
|
41
|
-
fun `test setScreenSecure enable true parsed from params`() {
|
|
42
|
-
val params = JSONObject().apply { put("enable", true) }
|
|
43
|
-
|
|
44
|
-
val enable = params.optBoolean("enable", false)
|
|
45
|
-
|
|
46
|
-
assertTrue("enable should be true", enable)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@Test
|
|
50
|
-
fun `test setScreenSecure enable false parsed from params`() {
|
|
51
|
-
val params = JSONObject().apply { put("enable", false) }
|
|
52
|
-
|
|
53
|
-
val enable = params.optBoolean("enable", true)
|
|
54
|
-
|
|
55
|
-
assertFalse("enable should be false", enable)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
@Test
|
|
59
|
-
fun `test setScreenSecure defaults to true when params are malformed`() {
|
|
60
|
-
// NativeBridge.setScreenSecure falls back to true when JSON parse fails
|
|
61
|
-
val malformedParams = "not-json"
|
|
62
|
-
val enable = try {
|
|
63
|
-
JSONObject(malformedParams).optBoolean("enable", true)
|
|
64
|
-
} catch (e: Exception) {
|
|
65
|
-
true
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
assertTrue("Malformed params should default to enable=true", enable)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
@Test
|
|
72
|
-
fun `test setScreenSecure success response shape`() {
|
|
73
|
-
val enable = true
|
|
74
|
-
val response = JSONObject().apply {
|
|
75
|
-
put("secure", enable)
|
|
76
|
-
put("success", true)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
assertTrue(response.getBoolean("secure"))
|
|
80
|
-
assertTrue(response.getBoolean("success"))
|
|
81
|
-
// Verify no extra fields bleed in
|
|
82
|
-
assertEquals(2, response.length())
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ============================================================
|
|
86
|
-
// CATEGORY 2: getScreenSecure COMMAND ROUTING (2 tests)
|
|
87
|
-
// ============================================================
|
|
88
|
-
|
|
89
|
-
@Test
|
|
90
|
-
fun `test getScreenSecure command is accepted by validator`() {
|
|
91
|
-
val messageJson = """{"command": "getScreenSecure", "data": {}}"""
|
|
92
|
-
|
|
93
|
-
val result = NativeBridge.parseAndValidateMessage(messageJson)
|
|
94
|
-
|
|
95
|
-
assertTrue("getScreenSecure should be a valid command", result.isValid)
|
|
96
|
-
assertEquals("getScreenSecure", result.command)
|
|
97
|
-
assertNull(result.error)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
@Test
|
|
101
|
-
fun `test getScreenSecure status response shape`() {
|
|
102
|
-
// Validate that the JSON payload produced by getScreenSecure is well-formed
|
|
103
|
-
val isSecure = false
|
|
104
|
-
val response = JSONObject().apply {
|
|
105
|
-
put("secure", isSecure)
|
|
106
|
-
put("success", true)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
assertFalse(response.getBoolean("secure"))
|
|
110
|
-
assertTrue(response.getBoolean("success"))
|
|
111
|
-
assertEquals(2, response.length())
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// ============================================================
|
|
115
|
-
// CATEGORY 3: clearWebData COMMAND ROUTING (2 tests)
|
|
116
|
-
// ============================================================
|
|
117
|
-
|
|
118
|
-
@Test
|
|
119
|
-
fun `test clearWebData command is accepted by validator`() {
|
|
120
|
-
val messageJson = """{"command": "clearWebData", "data": {}}"""
|
|
121
|
-
|
|
122
|
-
val result = NativeBridge.parseAndValidateMessage(messageJson)
|
|
123
|
-
|
|
124
|
-
assertTrue("clearWebData should be a valid command", result.isValid)
|
|
125
|
-
assertEquals("clearWebData", result.command)
|
|
126
|
-
assertNull(result.error)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
@Test
|
|
130
|
-
fun `test clearWebData success response shape`() {
|
|
131
|
-
val cookiesRemoved = true
|
|
132
|
-
val response = JSONObject().apply {
|
|
133
|
-
put("success", true)
|
|
134
|
-
put("cookiesRemoved", cookiesRemoved)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
assertTrue(response.getBoolean("success"))
|
|
138
|
-
assertTrue(response.getBoolean("cookiesRemoved"))
|
|
139
|
-
assertEquals(2, response.length())
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
// ============================================================
|
|
143
|
-
// CATEGORY 4: JSON INJECTION SAFETY (3 tests)
|
|
144
|
-
// ============================================================
|
|
145
|
-
|
|
146
|
-
@Test
|
|
147
|
-
fun `test error message with injection characters is safe via JSONObject`() {
|
|
148
|
-
// Simulate an exception message that contains characters dangerous in naive JS
|
|
149
|
-
// string interpolation: double-quotes and backslashes.
|
|
150
|
-
// JSONObject must escape them so the serialized JSON stays valid and cannot
|
|
151
|
-
// break out of the surrounding JS string literal in notifyWebJson.
|
|
152
|
-
val injectionPayload = "failed\"; window[\"evil\"]=\"injected"
|
|
153
|
-
|
|
154
|
-
val response = JSONObject().apply {
|
|
155
|
-
put("success", false)
|
|
156
|
-
put("error", injectionPayload)
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
val serialized = response.toString()
|
|
160
|
-
|
|
161
|
-
// The raw unescaped double-quote sequence that would break JS must not appear
|
|
162
|
-
assertFalse(
|
|
163
|
-
"Unescaped double-quote injection sequence must not appear in serialized JSON",
|
|
164
|
-
serialized.contains("\"evil\"")
|
|
165
|
-
)
|
|
166
|
-
// The value must survive a round-trip intact
|
|
167
|
-
assertEquals(injectionPayload, JSONObject(serialized).getString("error"))
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
@Test
|
|
171
|
-
fun `test error message with backslash sequences is safe via JSONObject`() {
|
|
172
|
-
val backslashPayload = "err\\\"injected\\\""
|
|
173
|
-
|
|
174
|
-
val response = JSONObject().apply {
|
|
175
|
-
put("success", false)
|
|
176
|
-
put("error", backslashPayload)
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Round-trip must survive without throwing
|
|
180
|
-
val roundTripped = JSONObject(response.toString()).getString("error")
|
|
181
|
-
assertEquals(backslashPayload, roundTripped)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
@Test
|
|
185
|
-
fun `test setScreenSecure and clearWebData commands are in VALID_COMMANDS whitelist`() {
|
|
186
|
-
assertTrue(
|
|
187
|
-
"setScreenSecure must be whitelisted",
|
|
188
|
-
CatalystConstants.Bridge.VALID_COMMANDS.contains("setScreenSecure")
|
|
189
|
-
)
|
|
190
|
-
assertTrue(
|
|
191
|
-
"getScreenSecure must be whitelisted",
|
|
192
|
-
CatalystConstants.Bridge.VALID_COMMANDS.contains("getScreenSecure")
|
|
193
|
-
)
|
|
194
|
-
assertTrue(
|
|
195
|
-
"clearWebData must be whitelisted",
|
|
196
|
-
CatalystConstants.Bridge.VALID_COMMANDS.contains("clearWebData")
|
|
197
|
-
)
|
|
198
|
-
}
|
|
199
|
-
}
|
package/dist/native/iosnativeWebView/iosnativeWebViewTests/BridgeCommandHandlerSecurityTests.swift
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import XCTest
|
|
2
|
-
import WebKit
|
|
3
|
-
@testable import CatalystCore
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Unit tests for BridgeCommandHandler — security commands.
|
|
7
|
-
*
|
|
8
|
-
* Coverage:
|
|
9
|
-
* 1. setScreenSecure routing (3 tests)
|
|
10
|
-
* 2. getScreenSecure routing (2 tests)
|
|
11
|
-
* 3. clearWebData routing (3 tests)
|
|
12
|
-
*
|
|
13
|
-
* Total: 8 tests
|
|
14
|
-
*
|
|
15
|
-
* Each test drives the full bridge pipeline (NativeBridge → BridgeCommandHandler)
|
|
16
|
-
* via a MockWKWebView and captures the evaluateJavaScript call that the delegate
|
|
17
|
-
* would fire back to the web layer.
|
|
18
|
-
*
|
|
19
|
-
* MockWKWebView and MockWKScriptMessage are defined in NativeBridgeTests.swift
|
|
20
|
-
* and BridgeMessageValidatorTests.swift respectively (shared across the test target).
|
|
21
|
-
*/
|
|
22
|
-
final class BridgeCommandHandlerSecurityTests: XCTestCase {
|
|
23
|
-
|
|
24
|
-
var bridge: NativeBridge!
|
|
25
|
-
var mockWebView: MockWKWebView!
|
|
26
|
-
var mockViewController: UIViewController!
|
|
27
|
-
|
|
28
|
-
override func setUp() {
|
|
29
|
-
super.setUp()
|
|
30
|
-
mockWebView = MockWKWebView()
|
|
31
|
-
mockViewController = UIViewController()
|
|
32
|
-
bridge = NativeBridge(webView: mockWebView, viewController: mockViewController)
|
|
33
|
-
bridge.register()
|
|
34
|
-
// Ensure a clean screen-secure state before each test
|
|
35
|
-
ScreenSecureManager.shared.setScreenSecure(false)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
override func tearDown() {
|
|
39
|
-
bridge.unregister()
|
|
40
|
-
bridge = nil
|
|
41
|
-
mockWebView = nil
|
|
42
|
-
mockViewController = nil
|
|
43
|
-
ScreenSecureManager.shared.setScreenSecure(false)
|
|
44
|
-
super.tearDown()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ============================================================
|
|
48
|
-
// CATEGORY 1: setScreenSecure routing (3 tests)
|
|
49
|
-
// ============================================================
|
|
50
|
-
|
|
51
|
-
func testSetScreenSecure_Enable_FiresOnScreenSecureSetCallback() {
|
|
52
|
-
let exp = expectation(description: "ON_SCREEN_SECURE_SET callback fired")
|
|
53
|
-
|
|
54
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
55
|
-
if script.contains("ON_SCREEN_SECURE_SET") {
|
|
56
|
-
exp.fulfill()
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let message = createMessage(command: "setScreenSecure", data: ["enable": true])
|
|
61
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
62
|
-
didReceive: message)
|
|
63
|
-
|
|
64
|
-
wait(for: [exp], timeout: 2.0)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
func testSetScreenSecure_Enable_CallbackContainsSecureTrue() {
|
|
68
|
-
let exp = expectation(description: "ON_SCREEN_SECURE_SET payload has secure:true")
|
|
69
|
-
|
|
70
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
71
|
-
if script.contains("ON_SCREEN_SECURE_SET") {
|
|
72
|
-
XCTAssertTrue(
|
|
73
|
-
script.contains("\"secure\"") || script.contains("secure"),
|
|
74
|
-
"Callback should include secure field"
|
|
75
|
-
)
|
|
76
|
-
XCTAssertTrue(
|
|
77
|
-
script.contains("true"),
|
|
78
|
-
"secure should be true after enabling"
|
|
79
|
-
)
|
|
80
|
-
exp.fulfill()
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
let message = createMessage(command: "setScreenSecure", data: ["enable": true])
|
|
85
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
86
|
-
didReceive: message)
|
|
87
|
-
|
|
88
|
-
wait(for: [exp], timeout: 2.0)
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
func testSetScreenSecure_InvalidParams_FiresErrorCallback() {
|
|
92
|
-
// Passing a non-dict, non-string param is handled by the fallback in
|
|
93
|
-
// BridgeCommandHandler.setScreenSecure — it fires ON_SCREEN_SECURE_ERROR.
|
|
94
|
-
let exp = expectation(description: "ON_SCREEN_SECURE_ERROR fired for nil params")
|
|
95
|
-
|
|
96
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
97
|
-
if script.contains("ON_SCREEN_SECURE_ERROR") {
|
|
98
|
-
exp.fulfill()
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Route via bridge with data that contains no "enable" key at all
|
|
103
|
-
let message = createMessage(command: "setScreenSecure", data: [:])
|
|
104
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
105
|
-
didReceive: message)
|
|
106
|
-
|
|
107
|
-
// The empty dict triggers the else branch → ON_SCREEN_SECURE_ERROR
|
|
108
|
-
wait(for: [exp], timeout: 2.0)
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// ============================================================
|
|
112
|
-
// CATEGORY 2: getScreenSecure routing (2 tests)
|
|
113
|
-
// ============================================================
|
|
114
|
-
|
|
115
|
-
func testGetScreenSecure_FiresOnScreenSecureStatusCallback() {
|
|
116
|
-
let exp = expectation(description: "ON_SCREEN_SECURE_STATUS callback fired")
|
|
117
|
-
|
|
118
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
119
|
-
if script.contains("ON_SCREEN_SECURE_STATUS") {
|
|
120
|
-
exp.fulfill()
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
let message = createMessage(command: "getScreenSecure", data: [:])
|
|
125
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
126
|
-
didReceive: message)
|
|
127
|
-
|
|
128
|
-
wait(for: [exp], timeout: 2.0)
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
func testGetScreenSecure_ReflectsCurrentState() {
|
|
132
|
-
// Set state first, then query it via the bridge
|
|
133
|
-
ScreenSecureManager.shared.setScreenSecure(true)
|
|
134
|
-
|
|
135
|
-
let exp = expectation(description: "ON_SCREEN_SECURE_STATUS reflects secure:true")
|
|
136
|
-
|
|
137
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
138
|
-
if script.contains("ON_SCREEN_SECURE_STATUS") {
|
|
139
|
-
XCTAssertTrue(script.contains("true"),
|
|
140
|
-
"Status callback should reflect secure=true")
|
|
141
|
-
exp.fulfill()
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
let message = createMessage(command: "getScreenSecure", data: [:])
|
|
146
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
147
|
-
didReceive: message)
|
|
148
|
-
|
|
149
|
-
wait(for: [exp], timeout: 2.0)
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// ============================================================
|
|
153
|
-
// CATEGORY 3: clearWebData routing (3 tests)
|
|
154
|
-
// ============================================================
|
|
155
|
-
|
|
156
|
-
func testClearWebData_WhenWebViewPresent_FiresOnWebDataClearedCallback() {
|
|
157
|
-
// BridgeCommandHandler needs a webView injected to proceed past the guard
|
|
158
|
-
// NativeBridge injects it during register(), so the mock is already set.
|
|
159
|
-
let exp = expectation(description: "ON_WEB_DATA_CLEARED callback fired")
|
|
160
|
-
|
|
161
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
162
|
-
if script.contains("ON_WEB_DATA_CLEARED") {
|
|
163
|
-
exp.fulfill()
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
let message = createMessage(command: "clearWebData", data: [:])
|
|
168
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
169
|
-
didReceive: message)
|
|
170
|
-
|
|
171
|
-
// clearWebData is async (WKWebsiteDataStore removal) — allow extra time
|
|
172
|
-
wait(for: [exp], timeout: 5.0)
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
func testClearWebData_SuccessPayloadContainsSuccessTrue() {
|
|
176
|
-
let exp = expectation(description: "clearWebData success payload has success:true")
|
|
177
|
-
|
|
178
|
-
mockWebView.onEvaluateJavaScript = { script in
|
|
179
|
-
if script.contains("ON_WEB_DATA_CLEARED") {
|
|
180
|
-
XCTAssertTrue(script.contains("true"),
|
|
181
|
-
"success field should be true in cleared callback")
|
|
182
|
-
exp.fulfill()
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
let message = createMessage(command: "clearWebData", data: [:])
|
|
187
|
-
bridge.userContentController(mockWebView.configuration.userContentController,
|
|
188
|
-
didReceive: message)
|
|
189
|
-
|
|
190
|
-
wait(for: [exp], timeout: 5.0)
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
func testClearWebData_CommandAcceptedByValidator() {
|
|
194
|
-
// Validates command reaches the handler without being rejected by BridgeMessageValidator
|
|
195
|
-
let messageBody: [String: Any] = ["command": "clearWebData", "data": [:]]
|
|
196
|
-
let message = MockWKScriptMessage(name: "NativeBridge", body: messageBody)
|
|
197
|
-
let result = BridgeMessageValidator.validate(message: message)
|
|
198
|
-
|
|
199
|
-
XCTAssertTrue(result.isValid, "clearWebData should pass BridgeMessageValidator")
|
|
200
|
-
XCTAssertEqual(result.command, "clearWebData")
|
|
201
|
-
XCTAssertNil(result.error)
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// ============================================================
|
|
205
|
-
// Helpers
|
|
206
|
-
// ============================================================
|
|
207
|
-
|
|
208
|
-
private func createMessage(command: String, data: [String: Any]) -> WKScriptMessage {
|
|
209
|
-
let body: [String: Any] = ["command": command, "data": data]
|
|
210
|
-
return MockWKScriptMessage(name: "NativeBridge", body: body)
|
|
211
|
-
}
|
|
212
|
-
}
|
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
import XCTest
|
|
2
|
-
@testable import CatalystCore
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Unit tests for ScreenSecureManager
|
|
6
|
-
*
|
|
7
|
-
* Coverage:
|
|
8
|
-
* 1. State management (3 tests)
|
|
9
|
-
* 2. setScreenSecure behaviour (3 tests)
|
|
10
|
-
* 3. Overlay race condition guard (2 tests)
|
|
11
|
-
*
|
|
12
|
-
* Total: 8 tests
|
|
13
|
-
*
|
|
14
|
-
* Note: UIWindow creation and scene observation require a running app / UI host.
|
|
15
|
-
* Tests here focus on the state machine and guard logic that can be exercised
|
|
16
|
-
* without a UIWindowScene. Overlay installation side-effects are verified
|
|
17
|
-
* indirectly via the guard flags and isScreenSecure state.
|
|
18
|
-
*/
|
|
19
|
-
final class ScreenSecureManagerTests: XCTestCase {
|
|
20
|
-
|
|
21
|
-
var manager: ScreenSecureManager!
|
|
22
|
-
|
|
23
|
-
override func setUp() {
|
|
24
|
-
super.setUp()
|
|
25
|
-
// Use the shared singleton; reset state by disabling screen security
|
|
26
|
-
manager = ScreenSecureManager.shared
|
|
27
|
-
manager.setScreenSecure(false)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
override func tearDown() {
|
|
31
|
-
// Leave the singleton clean for other tests
|
|
32
|
-
manager.setScreenSecure(false)
|
|
33
|
-
super.tearDown()
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// ============================================================
|
|
37
|
-
// CATEGORY 1: State management (3 tests)
|
|
38
|
-
// ============================================================
|
|
39
|
-
|
|
40
|
-
func testInitialState_IsNotSecure() {
|
|
41
|
-
// After setUp calls setScreenSecure(false), isScreenSecure must be false
|
|
42
|
-
XCTAssertFalse(manager.isScreenSecure, "Initial state should not be secure")
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
func testSetScreenSecure_True_UpdatesState() {
|
|
46
|
-
manager.setScreenSecure(true)
|
|
47
|
-
|
|
48
|
-
XCTAssertTrue(manager.isScreenSecure, "isScreenSecure should be true after enabling")
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
func testSetScreenSecure_False_UpdatesState() {
|
|
52
|
-
manager.setScreenSecure(true)
|
|
53
|
-
manager.setScreenSecure(false)
|
|
54
|
-
|
|
55
|
-
XCTAssertFalse(manager.isScreenSecure, "isScreenSecure should be false after disabling")
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// ============================================================
|
|
59
|
-
// CATEGORY 2: setScreenSecure behaviour (3 tests)
|
|
60
|
-
// ============================================================
|
|
61
|
-
|
|
62
|
-
func testSetScreenSecure_EnableThenDisable_StateIsConsistent() {
|
|
63
|
-
manager.setScreenSecure(true)
|
|
64
|
-
XCTAssertTrue(manager.isScreenSecure)
|
|
65
|
-
|
|
66
|
-
manager.setScreenSecure(false)
|
|
67
|
-
XCTAssertFalse(manager.isScreenSecure)
|
|
68
|
-
|
|
69
|
-
// Re-enable
|
|
70
|
-
manager.setScreenSecure(true)
|
|
71
|
-
XCTAssertTrue(manager.isScreenSecure)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
func testSetScreenSecure_DisableWhenAlreadyDisabled_NoStateChange() {
|
|
75
|
-
// Should be a no-op and not throw
|
|
76
|
-
manager.setScreenSecure(false)
|
|
77
|
-
manager.setScreenSecure(false)
|
|
78
|
-
|
|
79
|
-
XCTAssertFalse(manager.isScreenSecure, "Repeated disable should leave state as false")
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
func testSetScreenSecure_EnableWhenAlreadyEnabled_NoStateChange() {
|
|
83
|
-
manager.setScreenSecure(true)
|
|
84
|
-
manager.setScreenSecure(true)
|
|
85
|
-
|
|
86
|
-
XCTAssertTrue(manager.isScreenSecure, "Repeated enable should leave state as true")
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ============================================================
|
|
90
|
-
// CATEGORY 3: Overlay race condition guard (2 tests)
|
|
91
|
-
// ============================================================
|
|
92
|
-
|
|
93
|
-
func testSceneWillDeactivate_WhenNotSecure_OverlayNotInstalled() {
|
|
94
|
-
// When isScreenSecure is false, sceneWillDeactivate must be a no-op.
|
|
95
|
-
// We verify by checking state is still false after the notification fires.
|
|
96
|
-
manager.setScreenSecure(false)
|
|
97
|
-
|
|
98
|
-
NotificationCenter.default.post(
|
|
99
|
-
name: UIScene.willDeactivateNotification,
|
|
100
|
-
object: nil
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
// State must not have changed
|
|
104
|
-
XCTAssertFalse(manager.isScreenSecure,
|
|
105
|
-
"State should remain false when secure mode is off")
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
func testSceneDidActivate_AfterEnable_StateUnchanged() {
|
|
109
|
-
// sceneDidActivate removes the overlay but does NOT reset isScreenSecure.
|
|
110
|
-
manager.setScreenSecure(true)
|
|
111
|
-
|
|
112
|
-
NotificationCenter.default.post(
|
|
113
|
-
name: UIScene.didActivateNotification,
|
|
114
|
-
object: nil
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
// The secure flag must survive a return-to-foreground event
|
|
118
|
-
XCTAssertTrue(manager.isScreenSecure,
|
|
119
|
-
"isScreenSecure should remain true after scene reactivation")
|
|
120
|
-
}
|
|
121
|
-
}
|