Skip to content

Commit

Permalink
add more tests for postgrest
Browse files Browse the repository at this point in the history
  • Loading branch information
grdsdev committed Jan 22, 2025
1 parent 3a698c6 commit 208a0c2
Show file tree
Hide file tree
Showing 7 changed files with 399 additions and 63 deletions.
6 changes: 2 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ let package = Package(
"Mocker",
"Functions",
"TestHelpers",
],
exclude: ["__Snapshots__"]
]
),
.testTarget(
name: "IntegrationTests",
Expand Down Expand Up @@ -124,8 +123,7 @@ let package = Package(
"Helpers",
"PostgREST",
"TestHelpers",
],
exclude: ["__Snapshots__"]
]
),
.target(
name: "Realtime",
Expand Down
61 changes: 47 additions & 14 deletions Sources/Helpers/HTTP/HTTPFields.swift
Original file line number Diff line number Diff line change
@@ -1,37 +1,70 @@
import HTTPTypes

package extension HTTPFields {
init(_ dictionary: [String: String]) {
extension HTTPFields {
package init(_ dictionary: [String: String]) {
self.init(dictionary.map { .init(name: .init($0.key)!, value: $0.value) })
}
var dictionary: [String: String] {

package var dictionary: [String: String] {
let keyValues = self.map {
($0.name.rawName, $0.value)
}

return .init(keyValues, uniquingKeysWith: { $1 })
}
mutating func merge(with other: Self) {

package mutating func merge(with other: Self) {
for field in other {
self[field.name] = field.value
}
}
func merging(with other: Self) -> Self {

package func merging(with other: Self) -> Self {
var copy = self

for field in other {
copy[field.name] = field.value
}

return copy
}

/// Append or update a value in header.
///
/// Example:
/// ```swift
/// var headers: HTTPFields = [
/// "Prefer": "count=exact,return=representation"
/// ]
///
/// headers.appendOrUpdate(.prefer, value: "return=minimal")
/// #expect(headers == ["Prefer": "count=exact,return=minimal"]
/// ```
package mutating func appendOrUpdate(
_ name: HTTPField.Name,
value: String,
separator: String = ","
) {
if let currentValue = self[name] {
var components = currentValue.components(separatedBy: separator)

if let key = value.split(separator: "=").first,
let index = components.firstIndex(where: { $0.hasPrefix("\(key)=") })
{
components[index] = value
} else {
components.append(value)
}

self[name] = components.joined(separator: separator)
} else {
self[name] = value
}
}
}

package extension HTTPField.Name {
static let xClientInfo = HTTPField.Name("X-Client-Info")!
static let xRegion = HTTPField.Name("x-region")!
static let xRelayError = HTTPField.Name("x-relay-error")!
extension HTTPField.Name {
package static let xClientInfo = HTTPField.Name("X-Client-Info")!
package static let xRegion = HTTPField.Name("x-region")!
package static let xRelayError = HTTPField.Name("x-relay-error")!
}
6 changes: 1 addition & 5 deletions Sources/PostgREST/PostgrestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,7 @@ public class PostgrestBuilder: @unchecked Sendable {
}

if let count = $0.fetchOptions.count {
if let prefer = $0.request.headers[.prefer] {
$0.request.headers[.prefer] = "\(prefer),count=\(count.rawValue)"
} else {
$0.request.headers[.prefer] = "count=\(count.rawValue)"
}
$0.request.headers.appendOrUpdate(.prefer, value: "count=\(count.rawValue)")
}

if $0.request.headers[.accept] == nil {
Expand Down
44 changes: 7 additions & 37 deletions Sources/PostgREST/PostgrestTransformBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,7 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
.joined(separator: "")
mutableState.withValue {
$0.request.query.appendOrUpdate(URLQueryItem(name: "select", value: cleanedColumns))

if let prefer = $0.request.headers[.prefer] {
var components = prefer.components(separatedBy: ",")

if let index = components.firstIndex(where: { $0.hasPrefix("return=") }) {
components[index] = "return=representation"
} else {
components.append("return=representation")
}

$0.request.headers[.prefer] = components.joined(separator: ",")
} else {
$0.request.headers[.prefer] = "return=representation"
}
$0.request.headers.appendOrUpdate(.prefer, value: "return=representation")
}
return self
}
Expand Down Expand Up @@ -64,7 +51,7 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
"\(column).\(ascending ? "asc" : "desc").\(nullsFirst ? "nullsfirst" : "nullslast")"

if let existingOrderIndex,
let currentValue = $0.request.query[existingOrderIndex].value
let currentValue = $0.request.query[existingOrderIndex].value
{
$0.request.query[existingOrderIndex] = URLQueryItem(
name: key,
Expand All @@ -85,11 +72,7 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
public func limit(_ count: Int, referencedTable: String? = nil) -> PostgrestTransformBuilder {
mutableState.withValue {
let key = referencedTable.map { "\($0).limit" } ?? "limit"
if let index = $0.request.query.firstIndex(where: { $0.name == key }) {
$0.request.query[index] = URLQueryItem(name: key, value: "\(count)")
} else {
$0.request.query.append(URLQueryItem(name: key, value: "\(count)"))
}
$0.request.query.appendOrUpdate(URLQueryItem(name: key, value: "\(count)"))
}
return self
}
Expand All @@ -113,24 +96,10 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
let keyLimit = referencedTable.map { "\($0).limit" } ?? "limit"

mutableState.withValue {
if let index = $0.request.query.firstIndex(where: { $0.name == keyOffset }) {
$0.request.query[index] = URLQueryItem(name: keyOffset, value: "\(from)")
} else {
$0.request.query.append(URLQueryItem(name: keyOffset, value: "\(from)"))
}
$0.request.query.appendOrUpdate(URLQueryItem(name: keyOffset, value: "\(from)"))

// Range is inclusive, so add 1
if let index = $0.request.query.firstIndex(where: { $0.name == keyLimit }) {
$0.request.query[index] = URLQueryItem(
name: keyLimit,
value: "\(to - from + 1)"
)
} else {
$0.request.query.append(URLQueryItem(
name: keyLimit,
value: "\(to - from + 1)"
))
}
$0.request.query.appendOrUpdate(URLQueryItem(name: keyLimit, value: "\(to - from + 1)"))
}

return self
Expand Down Expand Up @@ -195,7 +164,8 @@ public class PostgrestTransformBuilder: PostgrestBuilder, @unchecked Sendable {
.compactMap { $0 }
.joined(separator: "|")
let forMediaType = $0.request.headers[.accept] ?? "application/json"
$0.request.headers[.accept] = "application/vnd.pgrst.plan+\"\(format)\"; for=\(forMediaType); options=\(options);"
$0.request.headers[.accept] =
"application/vnd.pgrst.plan+\"\(format)\"; for=\(forMediaType); options=\(options);"
}

return self
Expand Down
152 changes: 152 additions & 0 deletions Tests/PostgRESTTests/PostgrestBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,156 @@ final class PostgrestBuilderTests: PostgrestQueryTests {
XCTAssertEqual(error.response.statusCode, 400)
}
}

func testExecuteWithHead() async throws {
Mock(
url: url.appendingPathComponent("users"),
ignoreQuery: true,
statusCode: 200,
data: [
.head: Data()
]
)
.snapshotRequest {
#"""
curl \
--head \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "X-Client-Info: postgrest-swift/0.0.0" \
--header "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" \
"http://localhost:54321/rest/v1/users?select=*"
"""#
}
.register()

try await sut.from("users")
.select()
.execute(options: FetchOptions(head: true))
}

func testExecuteWithCount() async throws {
Mock(
url: url.appendingPathComponent("users"),
ignoreQuery: true,
statusCode: 200,
data: [
.get: Data("[]".utf8)
]
)
.snapshotRequest {
#"""
curl \
--header "Accept: application/json" \
--header "Content-Type: application/json" \
--header "Prefer: count=exact" \
--header "X-Client-Info: postgrest-swift/0.0.0" \
--header "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" \
"http://localhost:54321/rest/v1/users?select=*"
"""#
}
.register()

try await sut.from("users")
.select()
.execute(options: FetchOptions(count: .exact))
}

func testExecuteWithCustomSchema() async throws {
Mock(
url: url.appendingPathComponent("users"),
ignoreQuery: true,
statusCode: 200,
data: [
.get: Data("[]".utf8)
]
)
.snapshotRequest {
#"""
curl \
--header "Accept: application/json" \
--header "Accept-Profile: private" \
--header "Content-Type: application/json" \
--header "X-Client-Info: postgrest-swift/0.0.0" \
--header "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" \
"http://localhost:54321/rest/v1/users?select=*"
"""#
}
.register()

try await sut
.schema("private")
.from("users")
.select()
.execute()
}

func testExecuteWithCustomSchemaAndHeadMethod() async throws {
Mock(
url: url.appendingPathComponent("users"),
ignoreQuery: true,
statusCode: 200,
data: [
.head: Data()
]
)
.snapshotRequest {
#"""
curl \
--head \
--header "Accept: application/json" \
--header "Accept-Profile: private" \
--header "Content-Type: application/json" \
--header "X-Client-Info: postgrest-swift/0.0.0" \
--header "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" \
"http://localhost:54321/rest/v1/users?select=*"
"""#
}
.register()

try await sut
.schema("private")
.from("users")
.select()
.execute(options: FetchOptions(head: true))
}

func testExecuteWithCustomSchemaAndPostMethod() async throws {
Mock(
url: url.appendingPathComponent("users"),
ignoreQuery: true,
statusCode: 201,
data: [
.post: Data()
]
)
.snapshotRequest {
#"""
curl \
--request POST \
--header "Accept: application/json" \
--header "Content-Length: 19" \
--header "Content-Profile: private" \
--header "Content-Type: application/json" \
--header "X-Client-Info: postgrest-swift/0.0.0" \
--header "apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0" \
--data "{\"username\":\"test\"}" \
"http://localhost:54321/rest/v1/users"
"""#
}
.register()

try await sut
.schema("private")
.from("users")
.insert(["username": "test"])
.execute()
}

func testSetHeader() {
let query = sut.from("users")
.setHeader(name: "key", value: "value")

XCTAssertEqual(query.mutableState.request.headers[.init("key")!], "value")
}
}
Loading

0 comments on commit 208a0c2

Please sign in to comment.