diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc594e16a..09797b4dc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -64,6 +64,7 @@ jobs: - 'scripts/check-file-sizes-core.mjs' - 'scripts/check-file-sizes-core.test.mjs' - 'mobile/**' + - 'crates/buzz-push-gateway/tests/vectors/**' - 'scripts/mobile-release.sh' - 'scripts/mobile-worktree-overrides.sh' - 'scripts/mobile-worktree-clean.sh' @@ -849,6 +850,19 @@ jobs: - name: Build Android debug APK run: just mobile-build-android + mobile-swift: + name: Mobile Swift + runs-on: macos-latest + timeout-minutes: 10 + needs: [changes] + if: needs.changes.outputs.mobile == 'true' + steps: + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + - name: Build + run: swift build --package-path mobile/ios/BuzzPushKit + - name: Test + run: swift test --package-path mobile/ios/BuzzPushKit + security: name: Security runs-on: ubuntu-latest diff --git a/mobile/ios/BuzzPushKit/Package.swift b/mobile/ios/BuzzPushKit/Package.swift index e3d4fe5b3..ebfc64431 100644 --- a/mobile/ios/BuzzPushKit/Package.swift +++ b/mobile/ios/BuzzPushKit/Package.swift @@ -15,6 +15,10 @@ let package = Package( name: "BuzzPushKit", dependencies: [.product(name: "P256K", package: "swift-secp256k1")] ), - .testTarget(name: "BuzzPushKitTests", dependencies: ["BuzzPushKit"]), + .testTarget( + name: "BuzzPushKitTests", + dependencies: ["BuzzPushKit"], + resources: [.copy("Fixtures/app_attest_transcripts.json")] + ), ] ) diff --git a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzDevPushEnrollmentDriverTests.swift b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzDevPushEnrollmentDriverTests.swift index 5e4699742..ed6c67612 100644 --- a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzDevPushEnrollmentDriverTests.swift +++ b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzDevPushEnrollmentDriverTests.swift @@ -470,13 +470,14 @@ } private static func fixture() throws -> Fixture { - let here = URL(fileURLWithPath: #filePath) - let repoRoot = (0..<6).reduce(here) { value, _ in value.deletingLastPathComponent() } - let data = try Data( - contentsOf: repoRoot.appendingPathComponent( - "crates/buzz-push-gateway/tests/vectors/app_attest_transcripts.json" - ) + let path = try XCTUnwrap( + Bundle.module.url( + forResource: "app_attest_transcripts", + withExtension: "json" + ), + "missing bundled gateway transcript fixture app_attest_transcripts.json in \(Bundle.module.bundleURL.path)" ) + let data = try Data(contentsOf: path) return try JSONDecoder().decode(Fixture.self, from: data) } diff --git a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushTranscriptTests.swift b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushTranscriptTests.swift index d4b0fd357..bdc5aa0fb 100644 --- a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushTranscriptTests.swift +++ b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/BuzzPushTranscriptTests.swift @@ -23,21 +23,22 @@ final class BuzzPushTranscriptTests: XCTestCase { let vectors: [Vector] } - static let fixture: Fixture = { - // Tests/BuzzPushKitTests/… → repo root is five levels up from this file's dir. - let here = URL(fileURLWithPath: #filePath) - let repoRoot = here - .deletingLastPathComponent() // BuzzPushKitTests - .deletingLastPathComponent() // Tests - .deletingLastPathComponent() // BuzzPushKit - .deletingLastPathComponent() // ios - .deletingLastPathComponent() // mobile - .deletingLastPathComponent() // repo root - let path = repoRoot - .appendingPathComponent("crates/buzz-push-gateway/tests/vectors/app_attest_transcripts.json") - let data = try! Data(contentsOf: path) - return try! JSONDecoder().decode(Fixture.self, from: data) - }() + static func fixture( + file: StaticString = #filePath, + line: UInt = #line + ) throws -> Fixture { + let path = try XCTUnwrap( + Bundle.module.url( + forResource: "app_attest_transcripts", + withExtension: "json" + ), + "missing bundled gateway transcript fixture app_attest_transcripts.json in \(Bundle.module.bundleURL.path)", + file: file, + line: line + ) + let data = try Data(contentsOf: path) + return try JSONDecoder().decode(Fixture.self, from: data) + } // Deterministic inputs mirroring the fixture's `inputs` block. static let challengeId = UUID(uuidString: "11111111-1111-4111-8111-111111111111")! @@ -52,7 +53,7 @@ final class BuzzPushTranscriptTests: XCTestCase { private func assertMatchesVector(_ name: String, _ bytes: Data, file: StaticString = #filePath, line: UInt = #line) throws { - guard let vector = Self.fixture.vectors.first(where: { $0.name == name }) else { + guard let vector = try Self.fixture(file: file, line: line).vectors.first(where: { $0.name == name }) else { XCTFail("missing fixture vector \(name)", file: file, line: line) return } @@ -123,9 +124,9 @@ final class BuzzPushTranscriptTests: XCTestCase { )) } - func testAllFixtureVectorsCovered() { + func testAllFixtureVectorsCovered() throws { XCTAssertEqual( - Set(Self.fixture.vectors.map(\.name)), + Set(try Self.fixture().vectors.map(\.name)), ["enroll", "delegate", "rotate_endpoint", "revoke_delegation", "revoke_installation"], "fixture gained or lost a vector; add/remove the matching known-answer test" ) diff --git a/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Fixtures b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Fixtures new file mode 120000 index 000000000..cbfddb7a2 --- /dev/null +++ b/mobile/ios/BuzzPushKit/Tests/BuzzPushKitTests/Fixtures @@ -0,0 +1 @@ +../../../../../crates/buzz-push-gateway/tests/vectors \ No newline at end of file