Start a TrailBase instance as part of the swift test suite.

Next we can run the tests on CI.
This commit is contained in:
Sebastian Jeltsch
2025-05-14 11:50:18 +02:00
parent c6342434c6
commit d6efb53553
3 changed files with 151 additions and 29 deletions

View File

@@ -0,0 +1,24 @@
{
"originHash" : "f178e8cc250f0464a9dbb351c0d4ce7859ce76516acf66e503459441d4892791",
"pins" : [
{
"identity" : "swift-subprocess",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-subprocess.git",
"state" : {
"branch" : "main",
"revision" : "288df8180ce5d8221a40b57f2ac43a4d1870ece5"
}
},
{
"identity" : "swift-system",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-system",
"state" : {
"revision" : "a34201439c74b53f0fd71ef11741af7e7caf01e1",
"version" : "1.4.2"
}
}
],
"version" : 3
}

View File

@@ -4,25 +4,31 @@
import PackageDescription
let package = Package(
name: "TrailBase",
platforms: [
.iOS(.v13),
.macCatalyst(.v13),
.macOS(.v10_15),
.watchOS(.v6),
.tvOS(.v13),
],
products: [
.library(
name: "TrailBase",
targets: ["TrailBase"]),
],
targets: [
.target(
name: "TrailBase"),
.testTarget(
name: "TrailBaseTests",
dependencies: ["TrailBase"]
),
]
name: "TrailBase",
platforms: [
.iOS(.v13),
.macCatalyst(.v13),
.macOS(.v10_15),
.watchOS(.v6),
.tvOS(.v13),
],
products: [
.library(
name: "TrailBase",
targets: ["TrailBase"])
],
dependencies: [
.package(url: "https://github.com/swiftlang/swift-subprocess.git", branch: "main")
],
targets: [
.target(
name: "TrailBase"),
.testTarget(
name: "TrailBaseTests",
dependencies: [
"TrailBase",
.product(name: "Subprocess", package: "swift-subprocess"),
]
),
]
)

View File

@@ -1,8 +1,18 @@
import Foundation
import FoundationNetworking
import Subprocess
import SystemPackage
import Testing
@testable import TrailBase
let PORT: UInt16 = 4058
func panic(_ msg: String) -> Never {
print("ABORT: \(msg)", FileHandle.standardError)
abort()
}
struct SimpleStrict: Codable, Equatable {
var id: String? = nil
@@ -12,23 +22,105 @@ struct SimpleStrict: Codable, Equatable {
}
func connect() async throws -> Client {
let client = try Client(site: URL(string: "http://localhost:4000")!, tokens: nil)
let client = try Client(site: URL(string: "http://localhost:\(PORT)")!, tokens: nil)
let _ = try await client.login(email: "admin@localhost", password: "secret")
return client
}
// TODO: Bootstrap a shared test-local TrailBase instance to make the test hermetic.
@Suite struct Tests {
@Test func testAuth() async throws {
public enum StartupError: Error {
case buildFailed(stdout: String?, stderr: String?)
case startupTimeout
}
func startTrailBase() async throws -> ProcessIdentifier {
let cwd = FilePath("../..")
let depotPath = "client/testfixture"
let build = try await Subprocess.run(
.name("cargo"), arguments: ["build"], workingDirectory: cwd, output: .string, error: .string)
if !build.terminationStatus.isSuccess {
throw StartupError.buildFailed(stdout: build.standardOutput, stderr: build.standardError)
}
let arguments: Arguments = [
"run",
"--",
"--data-dir=\(depotPath)",
"run",
"--address=127.0.0.1:\(PORT)",
"--js-runtime-threads=2",
]
let process = try Subprocess.runDetached(
.name("cargo"),
arguments: arguments,
workingDirectory: cwd,
output: .standardOutput,
error: .standardError,
)
// Make sure it's up and running.
let request = URLRequest(url: URL(string: "http://127.0.0.1:\(PORT)/api/healthcheck")!)
for _ in 0...100 {
do {
let (data, _) = try await URLSession.shared.data(for: request)
let body = String(data: data, encoding: .utf8)!
if body.uppercased() == "OK" {
return process
}
} catch {
}
usleep(500 * 1000)
}
kill(process.value, SIGKILL)
throw StartupError.startupTimeout
}
final class SetupTrailBaseTrait: SuiteTrait, TestScoping {
// Only apply to Suite and not recursively to tests (also is default).
public var isRecursive: Bool { false }
func provideScope(
for test: Test,
testCase: Test.Case?,
performing: () async throws -> Void
) async throws {
// Setup
print("Starting TrailBase \(test.name)")
let process = try await startTrailBase()
// Run the actual test suite, i.e. all tests:
do {
try await performing()
} catch {
}
// Tear-down
print("Killing TrailBase \(test.name)")
kill(process.value, SIGKILL)
}
}
extension Trait where Self == SetupTrailBaseTrait {
static var setupTrailBase: Self { Self() }
}
@Suite(.setupTrailBase) struct ClientTestSuite {
@Test("Test Authentication") func testAuth() async throws {
let client = try await connect()
assert(client.tokens?.refresh_token != nil)
assert(client.user!.email == "admin@localhost")
#expect(client.tokens?.refresh_token != nil)
#expect(client.user!.email == "admin@localhost")
try await client.refresh()
try await client.logout()
assert(client.tokens == nil)
assert(client.user == nil)
#expect(client.tokens == nil)
#expect(client.user == nil)
}
@Test func recordTest() async throws {