catalyst-core 1.0.0 → 1.0.1-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/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/CustomWebview.kt +1 -1
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/MainActivity.kt +44 -34
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/SplashActivity.kt +5 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/EdgeToEdgeUtils.kt +53 -0
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/KeyboardUtil.kt +30 -2
- package/dist/native/androidProject/app/src/main/java/io/yourname/androidproject/utils/SafeAreaUtils.kt +18 -21
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/WebCacheManagerTest.kt +26 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/EdgeToEdgeUtilsTest.kt +87 -0
- package/dist/native/androidProject/app/src/test/java/io/yourname/androidproject/utils/SafeAreaUtilsTest.kt +60 -11
- package/dist/native/bridge/hooks/aiProviders.d.ts +5 -0
- package/dist/native/bridge/hooks/aiProviders.js +27 -0
- package/dist/native/bridge/hooks/useAI.js +9 -8
- package/dist/native/bridge/hooks/useNativeTransition.js +2 -2
- package/dist/native/bridge/hooks/useSafeNavigate.d.ts +10 -0
- package/dist/native/bridge/hooks/useSafeNavigate.js +22 -0
- package/dist/native/buildAppIos.js +8 -2
- package/dist/native/buildIos/plugins.js +3 -1
- package/dist/native/internalPluginUtils.js +78 -0
- package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Utils/FrameworkServerUtils+NativeAI.swift +221 -0
- package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Utils/FrameworkServerUtils+Requests.swift +215 -0
- package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Utils/FrameworkServerUtils.swift +105 -100
- package/dist/native/iosnativeWebView/CoreLogic/Sources/CatalystCoreLogic/Utils/NativeAI.swift +76 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsConnectionHandlerTests.swift +133 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsLoopbackTests.swift +157 -40
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/FrameworkServerUtilsNativeAITests.swift +351 -0
- package/dist/native/iosnativeWebView/CoreLogic/Tests/CatalystCoreLogicTests/NetworkMonitorTests.swift +17 -10
- package/dist/native/iosnativeWebView/Sources/Core/Plugins/PluginBridge.swift +15 -0
- package/dist/native/iosnativeWebView/iosnativeWebViewTests/PluginBridgeTests.swift +23 -0
- package/dist/native/pluginComposerIos.js +5 -1
- package/dist/server/expressServer.js +1 -0
- package/mcp_v2/conversion-tasks.json +10 -9
- package/mcp_v2/knowledge-base.json +34 -4
- package/mcp_v2/test/config.test.ts +52 -0
- package/mcp_v2/tools/config.d.ts +18 -0
- package/mcp_v2/tools/config.js +40 -0
- package/mcp_v2/tools/conversion.js +2 -2
- package/package.json +7 -6
- package/test/hooks/aiProviders.test.ts +33 -0
- package/test/hooks/useSafeNavigate.test.ts +21 -0
- package/test/native/aiPluginComposition.test.js +161 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
import Foundation
|
|
3
|
+
import Network
|
|
4
|
+
@testable import CatalystCoreLogic
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Deterministic tests for FrameworkServerUtils' connection error paths.
|
|
8
|
+
*
|
|
9
|
+
* Whether Network.framework reports a dropped client as a `.failed` connection
|
|
10
|
+
* state, a receive error, or a send error is timing-dependent, so driving them
|
|
11
|
+
* with real sockets made the CoreLogic line-coverage number vary from run to
|
|
12
|
+
* run and turned the coverage-regression gate into a coin flip. These tests
|
|
13
|
+
* call the extracted handlers directly with synthetic errors so every branch
|
|
14
|
+
* runs every time.
|
|
15
|
+
*/
|
|
16
|
+
final class FrameworkServerUtilsConnectionHandlerTests: XCTestCase {
|
|
17
|
+
|
|
18
|
+
private var frameworkServer: FrameworkServerUtils!
|
|
19
|
+
|
|
20
|
+
/// A connection to a closed loopback port (discard, 9). It never carries data;
|
|
21
|
+
/// it only needs to be startable so that cancel() is observable as `.cancelled`.
|
|
22
|
+
private func makeIdleConnection() -> NWConnection {
|
|
23
|
+
NWConnection(host: "127.0.0.1", port: 9, using: .tcp)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/// Starts a connection, runs `action` on it, and asserts it ends up `.cancelled`.
|
|
27
|
+
private func assertCancelled(
|
|
28
|
+
by action: (NWConnection) -> Void,
|
|
29
|
+
file: StaticString = #filePath,
|
|
30
|
+
line: UInt = #line
|
|
31
|
+
) {
|
|
32
|
+
let connection = makeIdleConnection()
|
|
33
|
+
let cancelled = expectation(description: "connection cancelled")
|
|
34
|
+
connection.stateUpdateHandler = { state in
|
|
35
|
+
if case .cancelled = state { cancelled.fulfill() }
|
|
36
|
+
}
|
|
37
|
+
connection.start(queue: .global(qos: .utility))
|
|
38
|
+
|
|
39
|
+
action(connection)
|
|
40
|
+
|
|
41
|
+
wait(for: [cancelled], timeout: 5)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override func setUp() {
|
|
45
|
+
super.setUp()
|
|
46
|
+
frameworkServer = FrameworkServerUtils.shared
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
override func tearDown() {
|
|
50
|
+
if frameworkServer.isRunning() {
|
|
51
|
+
frameworkServer.stopServer()
|
|
52
|
+
}
|
|
53
|
+
frameworkServer = nil
|
|
54
|
+
super.tearDown()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
func testConnectionStateFailed_CancelsConnection() {
|
|
58
|
+
assertCancelled { connection in
|
|
59
|
+
frameworkServer.handleConnectionStateChange(.failed(.posix(.ECONNRESET)), on: connection)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func testConnectionStateCancelled_DoesNotThrowForUntrackedConnection() {
|
|
64
|
+
let connection = makeIdleConnection()
|
|
65
|
+
|
|
66
|
+
frameworkServer.handleConnectionStateChange(.cancelled, on: connection)
|
|
67
|
+
|
|
68
|
+
XCTAssertEqual(frameworkServer.activeConnectionCount(), 0)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func testConnectionStateWaiting_IsIgnored() {
|
|
72
|
+
let connection = makeIdleConnection()
|
|
73
|
+
|
|
74
|
+
frameworkServer.handleConnectionStateChange(.waiting(.posix(.ENETDOWN)), on: connection)
|
|
75
|
+
|
|
76
|
+
XCTAssertNotEqual(connection.state, .cancelled)
|
|
77
|
+
connection.cancel()
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
func testReceiveError_CancelsConnection() {
|
|
81
|
+
assertCancelled { connection in
|
|
82
|
+
frameworkServer.handleReceiveError(.posix(.ECONNRESET), on: connection)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
func testReceivedData_WithError_CancelsConnection() {
|
|
87
|
+
assertCancelled { connection in
|
|
88
|
+
frameworkServer.handleReceivedData(nil, isComplete: false, error: .posix(.ECONNRESET), on: connection)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
func testSendCompletion_WithError_CancelsConnection() {
|
|
93
|
+
assertCancelled { connection in
|
|
94
|
+
frameworkServer.handleSendCompletion(.posix(.EPIPE), on: connection)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
func testSendCompletion_WithoutError_CancelsConnection() {
|
|
99
|
+
assertCancelled { connection in
|
|
100
|
+
frameworkServer.handleSendCompletion(nil, on: connection)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// MARK: - Port selection
|
|
105
|
+
|
|
106
|
+
/// findAvailablePort() must skip a port that is already bound. Which ports
|
|
107
|
+
/// are busy on a CI runner is environmental, so the skip path only ran
|
|
108
|
+
/// sometimes; occupying the first port in the range makes it run every time.
|
|
109
|
+
func testStartServer_SkipsOccupiedPort() throws {
|
|
110
|
+
let firstPort = FrameworkServerUtils.FRAMEWORK_PORT_RANGE_START
|
|
111
|
+
|
|
112
|
+
let blocker = socket(AF_INET, SOCK_STREAM, 0)
|
|
113
|
+
try XCTSkipIf(blocker == -1, "could not create blocker socket")
|
|
114
|
+
defer { close(blocker) }
|
|
115
|
+
|
|
116
|
+
var addr = sockaddr_in()
|
|
117
|
+
addr.sin_family = sa_family_t(AF_INET)
|
|
118
|
+
addr.sin_port = firstPort.bigEndian
|
|
119
|
+
addr.sin_addr.s_addr = INADDR_ANY
|
|
120
|
+
let bound = withUnsafePointer(to: &addr) {
|
|
121
|
+
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
|
122
|
+
Darwin.bind(blocker, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// bind() failing means another process already holds the first port,
|
|
126
|
+
// which exercises the same skip path, so the assertions below still hold.
|
|
127
|
+
_ = bound
|
|
128
|
+
|
|
129
|
+
XCTAssertTrue(frameworkServer.startServer())
|
|
130
|
+
XCTAssertNotEqual(frameworkServer.getServerPort(), firstPort)
|
|
131
|
+
XCTAssertGreaterThan(frameworkServer.getServerPort(), firstPort)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -2,6 +2,13 @@ import XCTest
|
|
|
2
2
|
import Foundation
|
|
3
3
|
@testable import CatalystCoreLogic
|
|
4
4
|
|
|
5
|
+
/// Thrown on CI when the loopback server never becomes reachable, so the test
|
|
6
|
+
/// fails instead of silently skipping and dropping coverage.
|
|
7
|
+
private struct LoopbackServerUnavailable: Error, CustomStringConvertible {
|
|
8
|
+
let message: String
|
|
9
|
+
var description: String { message }
|
|
10
|
+
}
|
|
11
|
+
|
|
5
12
|
/**
|
|
6
13
|
* In-process loopback HTTP tests for FrameworkServerUtils.
|
|
7
14
|
*
|
|
@@ -18,10 +25,10 @@ import Foundation
|
|
|
18
25
|
* math showed 95% package-wide wasn't reachable while FrameworkServerUtils
|
|
19
26
|
* stayed at ~35%.
|
|
20
27
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
28
|
+
* Each test waits for the listener to accept connections before sending a
|
|
29
|
+
* request (see requireRunningServer). Outside CI, a sandbox that cannot bind
|
|
30
|
+
* skips the test; on CI an unavailable server fails it, so loopback coverage
|
|
31
|
+
* can never silently drop and skew the coverage-regression baseline.
|
|
25
32
|
*/
|
|
26
33
|
final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
27
34
|
|
|
@@ -39,22 +46,69 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
39
46
|
}
|
|
40
47
|
tempFileURL = nil
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
// Unconditional: a listener that failed asynchronously reports
|
|
50
|
+
// isRunning() == false but still holds its timer and listener, which
|
|
51
|
+
// must not leak into later tests.
|
|
52
|
+
frameworkServer.stopServer()
|
|
45
53
|
frameworkServer = nil
|
|
46
54
|
super.tearDown()
|
|
47
55
|
}
|
|
48
56
|
|
|
49
|
-
/// Starts the server and
|
|
50
|
-
///
|
|
51
|
-
///
|
|
52
|
-
///
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
/// Starts the server and blocks until it is actually accepting TCP
|
|
58
|
+
/// connections, so no test sends a request before NWListener is `.ready`.
|
|
59
|
+
///
|
|
60
|
+
/// NWListener reports readiness asynchronously and `startServer()` returns
|
|
61
|
+
/// before it settles, so a fixed sleep is a race that loses on slow CI
|
|
62
|
+
/// runners. Instead this polls a raw loopback connect until it succeeds or
|
|
63
|
+
/// `readinessTimeout` elapses.
|
|
64
|
+
///
|
|
65
|
+
/// If the server never comes up: on CI (`CI` env var set) the test fails,
|
|
66
|
+
/// because a silent skip would drop the loopback coverage and make the
|
|
67
|
+
/// coverage baseline non-deterministic. Outside CI (e.g. a restricted local
|
|
68
|
+
/// sandbox that cannot bind) it skips, as before.
|
|
69
|
+
private func requireRunningServer() throws {
|
|
70
|
+
let readinessTimeout: TimeInterval = 10
|
|
71
|
+
|
|
72
|
+
guard frameworkServer.startServer() else {
|
|
73
|
+
try serverUnavailable("startServer() returned false")
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
let deadline = Date().addingTimeInterval(readinessTimeout)
|
|
78
|
+
while Date() < deadline {
|
|
79
|
+
if frameworkServer.isRunning(),
|
|
80
|
+
Self.canConnect(toLoopbackPort: frameworkServer.getServerPort()) {
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
Thread.sleep(forTimeInterval: 0.02)
|
|
84
|
+
}
|
|
85
|
+
try serverUnavailable("not accepting connections within \(Int(readinessTimeout))s")
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private func serverUnavailable(_ reason: String) throws {
|
|
89
|
+
let message = "Loopback server unavailable: \(reason)"
|
|
90
|
+
if ProcessInfo.processInfo.environment["CI"] != nil {
|
|
91
|
+
throw LoopbackServerUnavailable(message: message)
|
|
92
|
+
}
|
|
93
|
+
throw XCTSkip("\(message) — skipping loopback test outside CI")
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
private static func canConnect(toLoopbackPort port: UInt16) -> Bool {
|
|
97
|
+
let fd = socket(AF_INET, SOCK_STREAM, 0)
|
|
98
|
+
guard fd != -1 else { return false }
|
|
99
|
+
defer { close(fd) }
|
|
100
|
+
|
|
101
|
+
var addr = sockaddr_in()
|
|
102
|
+
addr.sin_family = sa_family_t(AF_INET)
|
|
103
|
+
addr.sin_port = port.bigEndian
|
|
104
|
+
addr.sin_addr.s_addr = inet_addr("127.0.0.1")
|
|
105
|
+
|
|
106
|
+
let result = withUnsafePointer(to: &addr) {
|
|
107
|
+
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
|
108
|
+
connect(fd, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return result == 0
|
|
58
112
|
}
|
|
59
113
|
|
|
60
114
|
private func serveTemporaryFile(content: String, fileName: String, mimeType: String) throws -> String? {
|
|
@@ -79,12 +133,89 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
79
133
|
return (httpResponse, data)
|
|
80
134
|
}
|
|
81
135
|
|
|
136
|
+
/// Opens a raw TCP client to the running server and returns its fd.
|
|
137
|
+
private func openRawClient() throws -> Int32 {
|
|
138
|
+
let fd = socket(AF_INET, SOCK_STREAM, 0)
|
|
139
|
+
try XCTSkipIf(fd == -1, "could not create client socket")
|
|
140
|
+
|
|
141
|
+
var addr = sockaddr_in()
|
|
142
|
+
addr.sin_family = sa_family_t(AF_INET)
|
|
143
|
+
addr.sin_port = frameworkServer.getServerPort().bigEndian
|
|
144
|
+
addr.sin_addr.s_addr = inet_addr("127.0.0.1")
|
|
145
|
+
let result = withUnsafePointer(to: &addr) {
|
|
146
|
+
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
|
|
147
|
+
connect(fd, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
guard result == 0 else {
|
|
151
|
+
close(fd)
|
|
152
|
+
throw LoopbackServerUnavailable(message: "raw client could not connect (errno \(errno))")
|
|
153
|
+
}
|
|
154
|
+
return fd
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/// Polls until `condition` holds or `timeout` elapses. Returns whether it held.
|
|
158
|
+
private func waitUntil(timeout: TimeInterval = 5, _ condition: () -> Bool) -> Bool {
|
|
159
|
+
let deadline = Date().addingTimeInterval(timeout)
|
|
160
|
+
while Date() < deadline {
|
|
161
|
+
if condition() { return true }
|
|
162
|
+
Thread.sleep(forTimeInterval: 0.01)
|
|
163
|
+
}
|
|
164
|
+
return condition()
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// MARK: - Connection lifecycle
|
|
168
|
+
|
|
169
|
+
/// stopServer() must cancel and clear connections that are still open. A
|
|
170
|
+
/// request/response test only hits this when a keep-alive connection
|
|
171
|
+
/// happens to be alive at teardown, which is timing-dependent; holding a
|
|
172
|
+
/// client open here makes that path run every time.
|
|
173
|
+
func testStopServer_WithOpenConnection_ClearsActiveConnections() throws {
|
|
174
|
+
try requireRunningServer()
|
|
175
|
+
|
|
176
|
+
let client = try openRawClient()
|
|
177
|
+
defer { close(client) }
|
|
178
|
+
|
|
179
|
+
XCTAssertTrue(
|
|
180
|
+
waitUntil { frameworkServer.activeConnectionCount() >= 1 },
|
|
181
|
+
"server never registered the open client connection"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
frameworkServer.stopServer()
|
|
185
|
+
|
|
186
|
+
XCTAssertEqual(frameworkServer.activeConnectionCount(), 0)
|
|
187
|
+
XCTAssertFalse(frameworkServer.isRunning())
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/// A client that resets mid-request (RST via zero-linger close) must be
|
|
191
|
+
/// dropped from the tracking set, whichever of the server's failed or
|
|
192
|
+
/// cancelled connection handlers observes it.
|
|
193
|
+
func testClientResetMidRequest_RemovesConnection() throws {
|
|
194
|
+
try requireRunningServer()
|
|
195
|
+
|
|
196
|
+
let client = try openRawClient()
|
|
197
|
+
XCTAssertTrue(
|
|
198
|
+
waitUntil { frameworkServer.activeConnectionCount() >= 1 },
|
|
199
|
+
"server never registered the client connection"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
let partialRequest = Array("GET /partial".utf8)
|
|
203
|
+
_ = partialRequest.withUnsafeBytes { send(client, $0.baseAddress, $0.count, 0) }
|
|
204
|
+
|
|
205
|
+
var linger = linger(l_onoff: 1, l_linger: 0)
|
|
206
|
+
setsockopt(client, SOL_SOCKET, SO_LINGER, &linger, socklen_t(MemoryLayout<linger>.size))
|
|
207
|
+
close(client)
|
|
208
|
+
|
|
209
|
+
XCTAssertTrue(
|
|
210
|
+
waitUntil { frameworkServer.activeConnectionCount() == 0 },
|
|
211
|
+
"connection was not removed after the client reset"
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
|
|
82
215
|
// MARK: - Status endpoint
|
|
83
216
|
|
|
84
217
|
func testStatusEndpoint_ReturnsRunningJSON() async throws {
|
|
85
|
-
|
|
86
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
87
|
-
}
|
|
218
|
+
try requireRunningServer()
|
|
88
219
|
|
|
89
220
|
let port = frameworkServer.getServerPort()
|
|
90
221
|
let sessionId = frameworkServer.getSessionId()
|
|
@@ -104,9 +235,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
104
235
|
// MARK: - File serving: success path
|
|
105
236
|
|
|
106
237
|
func testFileRequest_ServesRealFileContent() async throws {
|
|
107
|
-
|
|
108
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
109
|
-
}
|
|
238
|
+
try requireRunningServer()
|
|
110
239
|
|
|
111
240
|
let content = "loopback test file content \(UUID().uuidString)"
|
|
112
241
|
let servedURLString = try XCTUnwrap(
|
|
@@ -122,9 +251,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
122
251
|
}
|
|
123
252
|
|
|
124
253
|
func testFileRequest_ContentLengthHeaderMatchesActualBytes() async throws {
|
|
125
|
-
|
|
126
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
127
|
-
}
|
|
254
|
+
try requireRunningServer()
|
|
128
255
|
|
|
129
256
|
let content = String(repeating: "x", count: 5000)
|
|
130
257
|
let servedURLString = try XCTUnwrap(
|
|
@@ -143,9 +270,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
143
270
|
// MARK: - File serving: error paths
|
|
144
271
|
|
|
145
272
|
func testFileRequest_UnknownFileId_Returns404() async throws {
|
|
146
|
-
|
|
147
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
148
|
-
}
|
|
273
|
+
try requireRunningServer()
|
|
149
274
|
|
|
150
275
|
let port = frameworkServer.getServerPort()
|
|
151
276
|
let sessionId = frameworkServer.getSessionId()
|
|
@@ -157,9 +282,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
157
282
|
}
|
|
158
283
|
|
|
159
284
|
func testInvalidRoute_Returns404() async throws {
|
|
160
|
-
|
|
161
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
162
|
-
}
|
|
285
|
+
try requireRunningServer()
|
|
163
286
|
|
|
164
287
|
let port = frameworkServer.getServerPort()
|
|
165
288
|
let url = try XCTUnwrap(URL(string: "http://localhost:\(port)/not-a-real-route"))
|
|
@@ -170,9 +293,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
170
293
|
}
|
|
171
294
|
|
|
172
295
|
func testNonGETMethod_Returns405() async throws {
|
|
173
|
-
|
|
174
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
175
|
-
}
|
|
296
|
+
try requireRunningServer()
|
|
176
297
|
|
|
177
298
|
let port = frameworkServer.getServerPort()
|
|
178
299
|
let sessionId = frameworkServer.getSessionId()
|
|
@@ -186,9 +307,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
186
307
|
// MARK: - Physical file missing after being registered (not just unknown id)
|
|
187
308
|
|
|
188
309
|
func testFileRequest_PhysicalFileDeletedAfterRegistration_Returns404() async throws {
|
|
189
|
-
|
|
190
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
191
|
-
}
|
|
310
|
+
try requireRunningServer()
|
|
192
311
|
|
|
193
312
|
let servedURLString = try XCTUnwrap(
|
|
194
313
|
try serveTemporaryFile(content: "will be deleted from cache", fileName: "loopback-vanish.txt", mimeType: "text/plain")
|
|
@@ -218,9 +337,7 @@ final class FrameworkServerUtilsLoopbackTests: XCTestCase {
|
|
|
218
337
|
// MARK: - removeServedFile
|
|
219
338
|
|
|
220
339
|
func testRemoveServedFile_SubsequentRequestReturns404() async throws {
|
|
221
|
-
|
|
222
|
-
throw XCTSkip("Server failed to start in this environment — skipping loopback test")
|
|
223
|
-
}
|
|
340
|
+
try requireRunningServer()
|
|
224
341
|
|
|
225
342
|
let servedURLString = try XCTUnwrap(
|
|
226
343
|
try serveTemporaryFile(content: "to be removed", fileName: "loopback-remove.txt", mimeType: "text/plain")
|