Skip to content

Commit f278f03

Browse files
grdsdevclaude
andcommitted
test: add comprehensive Realtime test coverage
Add 84 new tests across 8 test files covering core Realtime functionality: - PostgresAction: 15 tests for PostgreSQL change events and data structures - RealtimeError: 5 tests for error handling and descriptions - Types: 14 tests for client options and configuration - RealtimeJoinConfig: 22 tests for channel join configurations - PushV2: 4 tests for message pushing functionality - WebSocket: 8 tests for WebSocket protocol abstraction - URLSessionWebSocket: 15 tests for URLSession WebSocket implementation - Exports: 1 test validating @_exported imports All tests follow existing project conventions and provide comprehensive coverage for public APIs, error handling, edge cases, and protocol conformances. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 098e672 commit f278f03

8 files changed

+1347
-0
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// ExportsTests.swift
3+
// Supabase
4+
//
5+
// Created by Guilherme Souza on 29/07/25.
6+
//
7+
8+
import XCTest
9+
10+
@testable import Realtime
11+
12+
final class ExportsTests: XCTestCase {
13+
func testHelperImportIsAccessible() {
14+
// Test that the Helpers module is properly exported
15+
// This is a simple validation that the @_exported import works
16+
17+
// Test that we can access JSONObject from Helpers via Realtime
18+
let jsonObject: JSONObject = [:]
19+
XCTAssertNotNil(jsonObject)
20+
21+
// Test that we can access AnyJSON from Helpers via Realtime
22+
let anyJSON: AnyJSON = .string("test")
23+
XCTAssertEqual(anyJSON, .string("test"))
24+
25+
// Test that we can access ObservationToken from Helpers via Realtime
26+
let token = ObservationToken {
27+
// Empty cleanup
28+
}
29+
XCTAssertNotNil(token)
30+
}
31+
}
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
//
2+
// PostgresActionTests.swift
3+
// Supabase
4+
//
5+
// Created by Guilherme Souza on 29/07/25.
6+
//
7+
8+
import XCTest
9+
10+
@testable import Realtime
11+
12+
final class PostgresActionTests: XCTestCase {
13+
private let sampleMessage = RealtimeMessageV2(
14+
joinRef: nil,
15+
ref: nil,
16+
topic: "test:table",
17+
event: "postgres_changes",
18+
payload: [:]
19+
)
20+
21+
private let sampleColumns = [
22+
Column(name: "id", type: "int8"),
23+
Column(name: "name", type: "text"),
24+
Column(name: "email", type: "text")
25+
]
26+
27+
private let sampleDate = Date(timeIntervalSince1970: 1722246000) // Fixed timestamp for consistency
28+
29+
func testColumnEquality() {
30+
let column1 = Column(name: "id", type: "int8")
31+
let column2 = Column(name: "id", type: "int8")
32+
let column3 = Column(name: "email", type: "text")
33+
34+
XCTAssertEqual(column1, column2)
35+
XCTAssertNotEqual(column1, column3)
36+
}
37+
38+
func testInsertActionEventType() {
39+
XCTAssertEqual(InsertAction.eventType, .insert)
40+
}
41+
42+
func testInsertActionProperties() {
43+
let record: JSONObject = ["id": .string("123"), "name": .string("John")]
44+
let insertAction = InsertAction(
45+
columns: sampleColumns,
46+
commitTimestamp: sampleDate,
47+
record: record,
48+
rawMessage: sampleMessage
49+
)
50+
51+
XCTAssertEqual(insertAction.columns, sampleColumns)
52+
XCTAssertEqual(insertAction.commitTimestamp, sampleDate)
53+
XCTAssertEqual(insertAction.record, record)
54+
XCTAssertEqual(insertAction.rawMessage.topic, "test:table")
55+
}
56+
57+
func testUpdateActionEventType() {
58+
XCTAssertEqual(UpdateAction.eventType, .update)
59+
}
60+
61+
func testUpdateActionProperties() {
62+
let record: JSONObject = ["id": .string("123"), "name": .string("John Updated")]
63+
let oldRecord: JSONObject = ["id": .string("123"), "name": .string("John")]
64+
65+
let updateAction = UpdateAction(
66+
columns: sampleColumns,
67+
commitTimestamp: sampleDate,
68+
record: record,
69+
oldRecord: oldRecord,
70+
rawMessage: sampleMessage
71+
)
72+
73+
XCTAssertEqual(updateAction.columns, sampleColumns)
74+
XCTAssertEqual(updateAction.commitTimestamp, sampleDate)
75+
XCTAssertEqual(updateAction.record, record)
76+
XCTAssertEqual(updateAction.oldRecord, oldRecord)
77+
XCTAssertEqual(updateAction.rawMessage.topic, "test:table")
78+
}
79+
80+
func testDeleteActionEventType() {
81+
XCTAssertEqual(DeleteAction.eventType, .delete)
82+
}
83+
84+
func testDeleteActionProperties() {
85+
let oldRecord: JSONObject = ["id": .string("123"), "name": .string("John")]
86+
87+
let deleteAction = DeleteAction(
88+
columns: sampleColumns,
89+
commitTimestamp: sampleDate,
90+
oldRecord: oldRecord,
91+
rawMessage: sampleMessage
92+
)
93+
94+
XCTAssertEqual(deleteAction.columns, sampleColumns)
95+
XCTAssertEqual(deleteAction.commitTimestamp, sampleDate)
96+
XCTAssertEqual(deleteAction.oldRecord, oldRecord)
97+
XCTAssertEqual(deleteAction.rawMessage.topic, "test:table")
98+
}
99+
100+
func testAnyActionEventType() {
101+
XCTAssertEqual(AnyAction.eventType, .all)
102+
}
103+
104+
func testAnyActionInsertCase() {
105+
let record: JSONObject = ["id": .string("123"), "name": .string("John")]
106+
let insertAction = InsertAction(
107+
columns: sampleColumns,
108+
commitTimestamp: sampleDate,
109+
record: record,
110+
rawMessage: sampleMessage
111+
)
112+
113+
let anyAction = AnyAction.insert(insertAction)
114+
XCTAssertEqual(anyAction.rawMessage.topic, "test:table")
115+
116+
if case let .insert(wrappedAction) = anyAction {
117+
XCTAssertEqual(wrappedAction.record, record)
118+
} else {
119+
XCTFail("Expected insert case")
120+
}
121+
}
122+
123+
func testAnyActionUpdateCase() {
124+
let record: JSONObject = ["id": .string("123"), "name": .string("John Updated")]
125+
let oldRecord: JSONObject = ["id": .string("123"), "name": .string("John")]
126+
127+
let updateAction = UpdateAction(
128+
columns: sampleColumns,
129+
commitTimestamp: sampleDate,
130+
record: record,
131+
oldRecord: oldRecord,
132+
rawMessage: sampleMessage
133+
)
134+
135+
let anyAction = AnyAction.update(updateAction)
136+
XCTAssertEqual(anyAction.rawMessage.topic, "test:table")
137+
138+
if case let .update(wrappedAction) = anyAction {
139+
XCTAssertEqual(wrappedAction.record, record)
140+
XCTAssertEqual(wrappedAction.oldRecord, oldRecord)
141+
} else {
142+
XCTFail("Expected update case")
143+
}
144+
}
145+
146+
func testAnyActionDeleteCase() {
147+
let oldRecord: JSONObject = ["id": .string("123"), "name": .string("John")]
148+
149+
let deleteAction = DeleteAction(
150+
columns: sampleColumns,
151+
commitTimestamp: sampleDate,
152+
oldRecord: oldRecord,
153+
rawMessage: sampleMessage
154+
)
155+
156+
let anyAction = AnyAction.delete(deleteAction)
157+
XCTAssertEqual(anyAction.rawMessage.topic, "test:table")
158+
159+
if case let .delete(wrappedAction) = anyAction {
160+
XCTAssertEqual(wrappedAction.oldRecord, oldRecord)
161+
} else {
162+
XCTFail("Expected delete case")
163+
}
164+
}
165+
166+
func testAnyActionEquality() {
167+
let record: JSONObject = ["id": .string("123")]
168+
let insertAction1 = InsertAction(
169+
columns: sampleColumns,
170+
commitTimestamp: sampleDate,
171+
record: record,
172+
rawMessage: sampleMessage
173+
)
174+
let insertAction2 = InsertAction(
175+
columns: sampleColumns,
176+
commitTimestamp: sampleDate,
177+
record: record,
178+
rawMessage: sampleMessage
179+
)
180+
181+
let anyAction1 = AnyAction.insert(insertAction1)
182+
let anyAction2 = AnyAction.insert(insertAction2)
183+
184+
XCTAssertEqual(anyAction1, anyAction2)
185+
}
186+
187+
func testDecodeRecord() throws {
188+
struct TestRecord: Codable, Equatable {
189+
let id: String
190+
let name: String
191+
let email: String?
192+
}
193+
194+
let record: JSONObject = [
195+
"id": .string("123"),
196+
"name": .string("John"),
197+
"email": .string("john@example.com")
198+
]
199+
200+
let insertAction = InsertAction(
201+
columns: sampleColumns,
202+
commitTimestamp: sampleDate,
203+
record: record,
204+
rawMessage: sampleMessage
205+
)
206+
207+
let decoder = JSONDecoder()
208+
let decodedRecord = try insertAction.decodeRecord(as: TestRecord.self, decoder: decoder)
209+
210+
let expectedRecord = TestRecord(id: "123", name: "John", email: "john@example.com")
211+
XCTAssertEqual(decodedRecord, expectedRecord)
212+
}
213+
214+
func testDecodeOldRecord() throws {
215+
struct TestRecord: Codable, Equatable {
216+
let id: String
217+
let name: String
218+
}
219+
220+
let record: JSONObject = ["id": .string("123"), "name": .string("John Updated")]
221+
let oldRecord: JSONObject = ["id": .string("123"), "name": .string("John")]
222+
223+
let updateAction = UpdateAction(
224+
columns: sampleColumns,
225+
commitTimestamp: sampleDate,
226+
record: record,
227+
oldRecord: oldRecord,
228+
rawMessage: sampleMessage
229+
)
230+
231+
let decoder = JSONDecoder()
232+
let decodedOldRecord = try updateAction.decodeOldRecord(as: TestRecord.self, decoder: decoder)
233+
234+
let expectedOldRecord = TestRecord(id: "123", name: "John")
235+
XCTAssertEqual(decodedOldRecord, expectedOldRecord)
236+
}
237+
238+
func testDecodeRecordError() {
239+
struct TestRecord: Codable {
240+
let id: Int // This will cause decode error since we're passing string
241+
let name: String
242+
}
243+
244+
let record: JSONObject = [
245+
"id": .string("not-a-number"), // This should cause decoding to fail
246+
"name": .string("John")
247+
]
248+
249+
let insertAction = InsertAction(
250+
columns: sampleColumns,
251+
commitTimestamp: sampleDate,
252+
record: record,
253+
rawMessage: sampleMessage
254+
)
255+
256+
let decoder = JSONDecoder()
257+
XCTAssertThrowsError(try insertAction.decodeRecord(as: TestRecord.self, decoder: decoder))
258+
}
259+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// PushV2Tests.swift
3+
// Supabase
4+
//
5+
// Created by Guilherme Souza on 29/07/25.
6+
//
7+
8+
import XCTest
9+
10+
@testable import Realtime
11+
12+
final class PushV2Tests: XCTestCase {
13+
14+
func testPushStatusValues() {
15+
XCTAssertEqual(PushStatus.ok.rawValue, "ok")
16+
XCTAssertEqual(PushStatus.error.rawValue, "error")
17+
XCTAssertEqual(PushStatus.timeout.rawValue, "timeout")
18+
}
19+
20+
func testPushStatusFromRawValue() {
21+
XCTAssertEqual(PushStatus(rawValue: "ok"), .ok)
22+
XCTAssertEqual(PushStatus(rawValue: "error"), .error)
23+
XCTAssertEqual(PushStatus(rawValue: "timeout"), .timeout)
24+
XCTAssertNil(PushStatus(rawValue: "invalid"))
25+
}
26+
27+
@MainActor
28+
func testPushV2InitializationWithNilChannel() {
29+
let sampleMessage = RealtimeMessageV2(
30+
joinRef: "ref1",
31+
ref: "ref2",
32+
topic: "test:channel",
33+
event: "broadcast",
34+
payload: ["data": "test"]
35+
)
36+
37+
let push = PushV2(channel: nil, message: sampleMessage)
38+
39+
XCTAssertEqual(push.message.topic, "test:channel")
40+
XCTAssertEqual(push.message.event, "broadcast")
41+
}
42+
43+
@MainActor
44+
func testSendWithNilChannelReturnsError() async {
45+
let sampleMessage = RealtimeMessageV2(
46+
joinRef: "ref1",
47+
ref: "ref2",
48+
topic: "test:channel",
49+
event: "broadcast",
50+
payload: ["data": "test"]
51+
)
52+
53+
let push = PushV2(channel: nil, message: sampleMessage)
54+
55+
let status = await push.send()
56+
57+
XCTAssertEqual(status, .error)
58+
}
59+
}

0 commit comments

Comments
 (0)