diff --git a/Tests/OrdersTests/OrdersTests.swift b/Tests/OrdersTests/OrdersTests.swift index 3fb6ee1..6558aa3 100644 --- a/Tests/OrdersTests/OrdersTests.swift +++ b/Tests/OrdersTests/OrdersTests.swift @@ -16,7 +16,7 @@ final class OrdersTests: XCTestCase { OrdersService.register(migrations: app.migrations) app.migrations.add(CreateOrderData()) - ordersService = try OrdersService(app: app, delegate: orderDelegate) + ordersService = try OrdersService(app: app, delegate: orderDelegate, pushRoutesMiddleware: SecretMiddleware(secret: "foo")) app.databases.middleware.use(OrderDataMiddleware(service: ordersService), on: .sqlite) try await app.autoMigrate() @@ -35,13 +35,14 @@ final class OrdersTests: XCTestCase { try await orderData.create(on: app.db) let order = try await orderData.$order.get(on: app.db) let deviceLibraryIdentifier = "abcdefg" + let pushToken = "1234567890" try await app.test( .POST, "\(ordersURI)devices/\(deviceLibraryIdentifier)/registrations/\(order.orderTypeIdentifier)/\(order.requireID())", headers: ["Authorization": "AppleOrder \(order.authenticationToken)"], beforeRequest: { req async throws in - try req.content.encode(RegistrationDTO(pushToken: "1234567890")) + try req.content.encode(RegistrationDTO(pushToken: pushToken)) }, afterResponse: { res async throws in XCTAssertEqual(res.status, .created) @@ -53,13 +54,38 @@ final class OrdersTests: XCTestCase { "\(ordersURI)devices/\(deviceLibraryIdentifier)/registrations/\(order.orderTypeIdentifier)/\(order.requireID())", headers: ["Authorization": "AppleOrder \(order.authenticationToken)"], beforeRequest: { req async throws in - try req.content.encode(RegistrationDTO(pushToken: "1234567890")) + try req.content.encode(RegistrationDTO(pushToken: pushToken)) }, afterResponse: { res async throws in XCTAssertEqual(res.status, .ok) } ) + let dateFormatter = ISO8601DateFormatter() + dateFormatter.formatOptions = .withInternetDateTime + try await app.test( + .GET, + "\(ordersURI)devices/\(deviceLibraryIdentifier)/registrations/\(order.orderTypeIdentifier)?ordersModifiedSince=\(dateFormatter.string(from: Date.distantPast))", + afterResponse: { res async throws in + let orders = try res.content.decode(OrdersForDeviceDTO.self) + XCTAssertEqual(orders.orderIdentifiers.count, 1) + let orderID = try order.requireID() + XCTAssertEqual(orders.orderIdentifiers[0], orderID.uuidString) + XCTAssertEqual(orders.lastModified, dateFormatter.string(from: order.updatedAt!)) + } + ) + + try await app.test( + .GET, + "\(ordersURI)push/\(order.orderTypeIdentifier)/\(order.requireID())", + headers: ["X-Secret": "foo"], + afterResponse: { res async throws in + let pushTokens = try res.content.decode([String].self) + XCTAssertEqual(pushTokens.count, 1) + XCTAssertEqual(pushTokens[0], pushToken) + } + ) + try await app.test( .DELETE, "\(ordersURI)devices/\(deviceLibraryIdentifier)/registrations/\(order.orderTypeIdentifier)/\(order.requireID())", @@ -69,4 +95,25 @@ final class OrdersTests: XCTestCase { } ) } + + func testErrorLog() async throws { + let log1 = "Error 1" + let log2 = "Error 2" + + try await app.test( + .POST, + "\(ordersURI)log", + beforeRequest: { req async throws in + try req.content.encode(ErrorLogDTO(logs: [log1, log2])) + }, + afterResponse: { res async throws in + XCTAssertEqual(res.status, .ok) + } + ) + + let logs = try await OrdersErrorLog.query(on: app.db).all() + XCTAssertEqual(logs.count, 2) + XCTAssertEqual(logs[0].message, log1) + XCTAssertEqual(logs[1].message, log2) + } } diff --git a/Tests/OrdersTests/SecretMiddleware.swift b/Tests/OrdersTests/SecretMiddleware.swift new file mode 100644 index 0000000..7da13bb --- /dev/null +++ b/Tests/OrdersTests/SecretMiddleware.swift @@ -0,0 +1,23 @@ +// +// SecretMiddleware.swift +// PassKit +// +// Created by Francesco Paolo Severino on 09/08/24. +// + +import Vapor + +struct SecretMiddleware: AsyncMiddleware { + let secret: String + + init(secret: String) { + self.secret = secret + } + + func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response { + guard request.headers.first(name: "X-Secret") == secret else { + throw Abort(.unauthorized, reason: "Incorrect X-Secret header.") + } + return try await next.respond(to: request) + } +} diff --git a/Tests/PassesTests/PassesTests.swift b/Tests/PassesTests/PassesTests.swift index dcbdeb2..7cac819 100644 --- a/Tests/PassesTests/PassesTests.swift +++ b/Tests/PassesTests/PassesTests.swift @@ -16,7 +16,7 @@ final class PassesTests: XCTestCase { PassesService.register(migrations: app.migrations) app.migrations.add(CreatePassData()) - passesService = try PassesService(app: app, delegate: passDelegate) + passesService = try PassesService(app: app, delegate: passDelegate, pushRoutesMiddleware: SecretMiddleware(secret: "foo")) app.databases.middleware.use(PassDataMiddleware(service: passesService), on: .sqlite) try await app.autoMigrate() @@ -62,13 +62,14 @@ final class PassesTests: XCTestCase { try await passData.create(on: app.db) let pass = try await passData.$pass.get(on: app.db) let deviceLibraryIdentifier = "abcdefg" + let pushToken = "1234567890" try await app.test( .POST, "\(passesURI)devices/\(deviceLibraryIdentifier)/registrations/\(pass.passTypeIdentifier)/\(pass.requireID())", headers: ["Authorization": "ApplePass \(pass.authenticationToken)"], beforeRequest: { req async throws in - try req.content.encode(RegistrationDTO(pushToken: "1234567890")) + try req.content.encode(RegistrationDTO(pushToken: pushToken)) }, afterResponse: { res async throws in XCTAssertEqual(res.status, .created) @@ -80,13 +81,36 @@ final class PassesTests: XCTestCase { "\(passesURI)devices/\(deviceLibraryIdentifier)/registrations/\(pass.passTypeIdentifier)/\(pass.requireID())", headers: ["Authorization": "ApplePass \(pass.authenticationToken)"], beforeRequest: { req async throws in - try req.content.encode(RegistrationDTO(pushToken: "1234567890")) + try req.content.encode(RegistrationDTO(pushToken: pushToken)) }, afterResponse: { res async throws in XCTAssertEqual(res.status, .ok) } ) + try await app.test( + .GET, + "\(passesURI)devices/\(deviceLibraryIdentifier)/registrations/\(pass.passTypeIdentifier)?passesUpdatedSince=0", + afterResponse: { res async throws in + let passes = try res.content.decode(PassesForDeviceDTO.self) + XCTAssertEqual(passes.serialNumbers.count, 1) + let passID = try pass.requireID() + XCTAssertEqual(passes.serialNumbers[0], passID.uuidString) + XCTAssertEqual(passes.lastUpdated, String(pass.updatedAt!.timeIntervalSince1970)) + } + ) + + try await app.test( + .GET, + "\(passesURI)push/\(pass.passTypeIdentifier)/\(pass.requireID())", + headers: ["X-Secret": "foo"], + afterResponse: { res async throws in + let pushTokens = try res.content.decode([String].self) + XCTAssertEqual(pushTokens.count, 1) + XCTAssertEqual(pushTokens[0], pushToken) + } + ) + try await app.test( .DELETE, "\(passesURI)devices/\(deviceLibraryIdentifier)/registrations/\(pass.passTypeIdentifier)/\(pass.requireID())", @@ -96,4 +120,25 @@ final class PassesTests: XCTestCase { } ) } + + func testErrorLog() async throws { + let log1 = "Error 1" + let log2 = "Error 2" + + try await app.test( + .POST, + "\(passesURI)log", + beforeRequest: { req async throws in + try req.content.encode(ErrorLogDTO(logs: [log1, log2])) + }, + afterResponse: { res async throws in + XCTAssertEqual(res.status, .ok) + } + ) + + let logs = try await PassesErrorLog.query(on: app.db).all() + XCTAssertEqual(logs.count, 2) + XCTAssertEqual(logs[0].message, log1) + XCTAssertEqual(logs[1].message, log2) + } } diff --git a/Tests/PassesTests/SecretMiddleware.swift b/Tests/PassesTests/SecretMiddleware.swift new file mode 100644 index 0000000..7da13bb --- /dev/null +++ b/Tests/PassesTests/SecretMiddleware.swift @@ -0,0 +1,23 @@ +// +// SecretMiddleware.swift +// PassKit +// +// Created by Francesco Paolo Severino on 09/08/24. +// + +import Vapor + +struct SecretMiddleware: AsyncMiddleware { + let secret: String + + init(secret: String) { + self.secret = secret + } + + func respond(to request: Request, chainingTo next: any AsyncResponder) async throws -> Response { + guard request.headers.first(name: "X-Secret") == secret else { + throw Abort(.unauthorized, reason: "Incorrect X-Secret header.") + } + return try await next.respond(to: request) + } +}