Skip to content

Commit

Permalink
Add some basic integration tests
Browse files Browse the repository at this point in the history
It’s unlikely that we’re going to have a working unified test suite
before the beta release, so here are some very basic smoke tests just to
give us a _little bit_ of confidence that things are kind of working and
that we don’t introduce major regressions.

Would be good to have a way of separating these from the unit tests so
that they don’t slow them down, but can figure that out later; I don’t
have loads of time to spend on this at the moment.
  • Loading branch information
lawrence-forooghian committed Nov 7, 2024
1 parent 99136ef commit e136ae4
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "Tests/AblyChatTests/ably-common"]
path = Tests/AblyChatTests/ably-common
url = https://github.com/ably/ably-common
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# Don’t try and format the asset catalogue JSON files, which are managed by Xcode
*.xcassets/

# Submodules
Tests/AblyChatTests/ably-common
1 change: 1 addition & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ To check formatting and code quality, run `swift run BuildTool lint`. Run with `
## Development guidelines

- The aim of the [example app](README.md#example-app) is that it demonstrate all of the core functionality of the SDK. So if you add a new feature, try to add something to the example app to demonstrate this feature.
- If you add a new feature, try to extend the `IntegrationTests` tests to perform a smoke test of its core functionality.
- We should aim to make it easy for consumers of the SDK to be able to mock out the SDK in the tests for their own code. A couple of things that will aid with this:
- Describe the SDK’s functionality via protocols (when doing so would still be sufficiently idiomatic to Swift).
- When defining a `struct` that is emitted by the public API of the library, make sure to define a public memberwise initializer so that users can create one to be emitted by their mocks. (There is no way to make Swift’s autogenerated memberwise initializer public, so you will need to write one yourself. In Xcode, you can do this by clicking at the start of the type declaration and doing Editor → Refactor → Generate Memberwise Initializer.)
Expand Down
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ let package = Package(
name: "AsyncAlgorithms",
package: "swift-async-algorithms"
),
],
resources: [
.copy("ably-common"),
]
),
.executableTarget(
Expand Down
50 changes: 50 additions & 0 deletions Tests/AblyChatTests/Helpers/Sandbox.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Foundation

/// Provides the ``createAPIKey()`` function to create an API key for the Ably sandbox environment.
enum Sandbox {
private struct TestApp: Codable {
var keys: [Key]

struct Key: Codable {
var keyStr: String
}
}

enum Error: Swift.Error {
case badResponseStatus(Int)
}

private static func loadAppCreationRequestBody() async throws -> Data {
let testAppSetupFileURL = Bundle.module.url(
forResource: "test-app-setup",
withExtension: "json",
subdirectory: "ably-common/test-resources"
)!

let (data, _) = try await URLSession.shared.data(for: .init(url: testAppSetupFileURL))
// swiftlint:disable:next force_cast
let dictionary = try JSONSerialization.jsonObject(with: data) as! [String: Any]
return try JSONSerialization.data(withJSONObject: dictionary["post_apps"]!)
}

static func createAPIKey() async throws -> String {
var request = URLRequest(url: .init(string: "https://sandbox-rest.ably.io/apps")!)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try await loadAppCreationRequestBody()

let (data, response) = try await URLSession.shared.data(for: request)

// swiftlint:disable:next force_cast
let statusCode = (response as! HTTPURLResponse).statusCode

guard (200 ..< 300).contains(statusCode) else {
throw Error.badResponseStatus(statusCode)
}

let testApp = try JSONDecoder().decode(TestApp.self, from: data)

// From JS chat repo at 7985ab7 — "The key we need to use is the one at index 5, which gives enough permissions to interact with Chat and Channels"
return testApp.keys[5].keyStr
}
}
58 changes: 58 additions & 0 deletions Tests/AblyChatTests/IntegrationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import Ably
import AblyChat
import Testing

/// Some very basic integration tests, just to check that things are kind of working.
@Suite(
.timeLimit(.minutes(1))
)
struct IntegrationTests {
private static func createSandboxRealtime(apiKey: String) -> ARTRealtime {
let realtimeOptions = ARTClientOptions(key: apiKey)
realtimeOptions.environment = "sandbox"
realtimeOptions.clientId = UUID().uuidString

return ARTRealtime(options: realtimeOptions)
}

private static func createSandboxChatClient(apiKey: String) -> DefaultChatClient {
let realtime = createSandboxRealtime(apiKey: apiKey)
return DefaultChatClient(realtime: realtime, clientOptions: nil)
}

@Test
func basicIntegrationTest() async throws {
let apiKey = try await Sandbox.createAPIKey()

// 1. Create a couple of chat clients — one for sending and one for receiving
let txClient = Self.createSandboxChatClient(apiKey: apiKey)
let rxClient = Self.createSandboxChatClient(apiKey: apiKey)

// 2. Fetch a room
let roomID = "basketball"
let txRoom = try await txClient.rooms.get(roomID: roomID, options: .init())
let rxRoom = try await rxClient.rooms.get(roomID: roomID, options: .init())

// 3. Attach the room so we can receive messages on it
try await rxRoom.attach()

// 4. Send a message before subscribing to messages, so that later on we can check history works
let txMessageBeforeRxSubscribe = try await txRoom.messages.send(params: .init(text: "Hello from txRoom, before rxRoom subscribe"))

// 5. Subscribe to messages
let rxMessageSubscription = try await rxRoom.messages.subscribe(bufferingPolicy: .unbounded)

// 6. Now that we’re subscribed to messages, send a message on the other client and check that we receive it on the subscription
let txMessageAfterRxSubscribe = try await txRoom.messages.send(params: .init(text: "Hello from txRoom, after rxRoom subscribe"))
let rxMessageFromSubscription = try #require(await rxMessageSubscription.first { _ in true })
#expect(rxMessageFromSubscription == txMessageAfterRxSubscribe)

// 7. Fetch historical messages from before subscribing, and check we get txMessageBeforeRxSubscribe
let rxMessagesBeforeSubscribing = try await rxMessageSubscription.getPreviousMessages(params: .init())
#expect(rxMessagesBeforeSubscribing.items.count == 1)
#expect(rxMessagesBeforeSubscribing.items[0] == txMessageBeforeRxSubscribe)

// 8. Detach the room
try await rxRoom.detach()
}
}
1 change: 1 addition & 0 deletions Tests/AblyChatTests/ably-common
Submodule ably-common added at 60fd9c

0 comments on commit e136ae4

Please sign in to comment.