This commit is contained in:
127
e2e/swift_e2e/Tests/KreuzbergE2ETests/TestHelpers.swift
generated
Normal file
127
e2e/swift_e2e/Tests/KreuzbergE2ETests/TestHelpers.swift
generated
Normal file
@@ -0,0 +1,127 @@
|
||||
// This file is auto-generated by alef — DO NOT EDIT.
|
||||
// alef:hash:4e15143f4af1ae8bafbdb1506ef057da924484c66a19483966333558ad437e75
|
||||
// To regenerate: alef generate
|
||||
// To verify freshness: alef verify --exit-code
|
||||
// Issues & docs: https://github.com/kreuzberg-dev/alef
|
||||
// swift-format-ignore-file
|
||||
|
||||
import Foundation
|
||||
#if canImport(FoundationNetworking)
|
||||
// URLSession, URLRequest, HTTPURLResponse, and URLSessionTaskDelegate live in
|
||||
// the FoundationNetworking submodule on swift-corelibs-foundation (Linux). On
|
||||
// Apple platforms these types remain in plain Foundation and this submodule
|
||||
// does not exist; the canImport guard skips the import there.
|
||||
import FoundationNetworking
|
||||
#endif
|
||||
import RustBridge
|
||||
|
||||
// Make `RustString` print its content in XCTest failure output. Without this,
|
||||
// every error thrown from the swift-bridge layer surfaces as
|
||||
// `caught error: "RustBridge.RustString"` with the actual message hidden
|
||||
// inside the opaque class instance. The `@retroactive` keyword acknowledges
|
||||
// that the conformed-to protocol (`CustomStringConvertible`) and the
|
||||
// conforming type (`RustString`) both live outside this module — required by
|
||||
// Swift 6 to silence the retroactive-conformance warning. swift-bridge does
|
||||
// not give `RustString` a `description` of its own, so there is no conflict.
|
||||
extension RustString: @retroactive CustomStringConvertible {
|
||||
public var description: String { self.toString() }
|
||||
}
|
||||
|
||||
// Spawns the alef mock-server once per test process and exposes its base URL.
|
||||
// SwiftPM/XCTest has no global "before all tests" hook that can inject environment
|
||||
// variables (the JVM-style listener trick used by the Java/Kotlin backends), so the
|
||||
// server is started lazily on first access of `baseURL` and kept alive for the
|
||||
// lifetime of the process. A pre-set `MOCK_SERVER_URL` (e.g. exported by CI) wins.
|
||||
enum AlefE2EMockServer {
|
||||
static let baseURL: String = AlefE2EMockServer.start()
|
||||
|
||||
// Retain the child process so it is not reaped while tests run.
|
||||
nonisolated(unsafe) private static var process: Process?
|
||||
|
||||
private static func start() -> String {
|
||||
if let preset = ProcessInfo.processInfo.environment["MOCK_SERVER_URL"], !preset.isEmpty {
|
||||
return preset
|
||||
}
|
||||
let fileManager = FileManager.default
|
||||
var dir = URL(fileURLWithPath: fileManager.currentDirectoryPath)
|
||||
var fixturesDir: URL?
|
||||
for _ in 0..<16 {
|
||||
let candidate = dir.appendingPathComponent("fixtures")
|
||||
var isDir: ObjCBool = false
|
||||
if fileManager.fileExists(atPath: candidate.path, isDirectory: &isDir), isDir.boolValue {
|
||||
fixturesDir = candidate
|
||||
break
|
||||
}
|
||||
let parent = dir.deletingLastPathComponent()
|
||||
if parent.path == dir.path { break }
|
||||
dir = parent
|
||||
}
|
||||
guard let fixtures = fixturesDir else {
|
||||
fatalError("AlefE2EMockServer: could not locate fixtures/ above \(fileManager.currentDirectoryPath)")
|
||||
}
|
||||
let repoRoot = fixtures.deletingLastPathComponent()
|
||||
let binary = repoRoot.appendingPathComponent("e2e/rust/target/release/mock-server")
|
||||
guard fileManager.fileExists(atPath: binary.path) else {
|
||||
fatalError("AlefE2EMockServer: mock-server binary not found at \(binary.path) — run: cargo build --manifest-path e2e/rust/Cargo.toml --bin mock-server --release")
|
||||
}
|
||||
let proc = Process()
|
||||
proc.executableURL = binary
|
||||
proc.arguments = [fixtures.path]
|
||||
let stdoutPipe = Pipe()
|
||||
proc.standardOutput = stdoutPipe
|
||||
// Keep stdin open so the server does not see EOF and exit immediately.
|
||||
proc.standardInput = Pipe()
|
||||
do {
|
||||
try proc.run()
|
||||
} catch {
|
||||
fatalError("AlefE2EMockServer: failed to start mock-server: \(error)")
|
||||
}
|
||||
process = proc
|
||||
let handle = stdoutPipe.fileHandleForReading
|
||||
var buffer = Data()
|
||||
var resolved: String?
|
||||
for _ in 0..<500 {
|
||||
let chunk = handle.availableData
|
||||
if chunk.isEmpty { break }
|
||||
buffer.append(chunk)
|
||||
if let text = String(data: buffer, encoding: .utf8) {
|
||||
for line in text.split(separator: "\n") {
|
||||
if line.hasPrefix("MOCK_SERVER_URL=") {
|
||||
resolved = String(line.dropFirst("MOCK_SERVER_URL=".count)).trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if resolved != nil { break }
|
||||
}
|
||||
guard let url = resolved else {
|
||||
proc.terminate()
|
||||
fatalError("AlefE2EMockServer: mock-server did not emit MOCK_SERVER_URL")
|
||||
}
|
||||
// Drain remaining stdout in the background so a full pipe never blocks the server.
|
||||
DispatchQueue.global(qos: .background).async {
|
||||
while !handle.availableData.isEmpty {}
|
||||
}
|
||||
return url
|
||||
}
|
||||
}
|
||||
|
||||
// URLSession that does not follow redirects, so tests can assert on 3xx status codes
|
||||
// and Location headers instead of transparently chasing them to the final response.
|
||||
final class AlefE2ENoRedirectDelegate: NSObject, URLSessionTaskDelegate {
|
||||
func urlSession(
|
||||
_ session: URLSession,
|
||||
task: URLSessionTask,
|
||||
willPerformHTTPRedirection response: HTTPURLResponse,
|
||||
newRequest request: URLRequest,
|
||||
completionHandler: @escaping (URLRequest?) -> Void
|
||||
) {
|
||||
completionHandler(nil)
|
||||
}
|
||||
}
|
||||
|
||||
let alefE2ESession = URLSession(
|
||||
configuration: .ephemeral,
|
||||
delegate: AlefE2ENoRedirectDelegate(),
|
||||
delegateQueue: nil
|
||||
)
|
||||
Reference in New Issue
Block a user