Skip to content

Commit

Permalink
test(storage): increase code coverage (#650)
Browse files Browse the repository at this point in the history
* test(storage): increase code coverage

* add more tests

* add tests

* more tests
  • Loading branch information
grdsdev authored Jan 30, 2025
1 parent faf78f6 commit ce5cf5f
Show file tree
Hide file tree
Showing 19 changed files with 1,494 additions and 65 deletions.
25 changes: 23 additions & 2 deletions .swiftpm/xcode/xcshareddata/xcschemes/Storage.xcscheme
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.7">
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
Expand All @@ -26,8 +26,29 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "Storage"
BuildableName = "Storage"
BlueprintName = "Storage"
ReferencedContainer = "container:">
</BuildableReference>
</CodeCoverageTargets>
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "StorageTests"
BuildableName = "StorageTests"
BlueprintName = "StorageTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
5 changes: 4 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,13 @@ let package = Package(
.product(name: "CustomDump", package: "swift-custom-dump"),
.product(name: "InlineSnapshotTesting", package: "swift-snapshot-testing"),
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
"Mocker",
"TestHelpers",
"Storage",
],
resources: [
.copy("sadcat.jpg")
.copy("sadcat.jpg"),
.process("Fixtures"),
]
),
.target(
Expand Down
36 changes: 36 additions & 0 deletions Sources/Helpers/Codable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
//
// Codable.swift
// Supabase
//
// Created by Guilherme Souza on 20/01/25.
//

import ConcurrencyExtras
import Foundation

extension JSONDecoder {
private static let supportedDateFormatters: [UncheckedSendable<ISO8601DateFormatter>] = [
ISO8601DateFormatter.iso8601WithFractionalSeconds,
ISO8601DateFormatter.iso8601,
]

/// Default `JSONDecoder` for decoding types from Supabase.
package static let `default`: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)

for formatter in supportedDateFormatters {
if let date = formatter.value.date(from: string) {
return date
}
}

throw DecodingError.dataCorruptedError(
in: container, debugDescription: "Invalid date format: \(string)"
)
}
return decoder
}()
}
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")!
}
29 changes: 7 additions & 22 deletions Sources/Storage/Codable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,19 @@ import ConcurrencyExtras
import Foundation

extension JSONEncoder {
@available(*, deprecated, message: "Access to storage encoder is going to be removed.")
public static let defaultStorageEncoder: JSONEncoder = {
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
return encoder
}()

static let unconfiguredEncoder: JSONEncoder = .init()
}

extension JSONDecoder {
public static let defaultStorageDecoder: JSONDecoder = {
let decoder = JSONDecoder()
let formatter = LockIsolated(ISO8601DateFormatter())
formatter.withValue {
$0.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
}

decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)

if let date = formatter.withValue({ $0.date(from: string) }) {
return date
}

throw DecodingError.dataCorruptedError(
in: container, debugDescription: "Invalid date format: \(string)"
)
}

return decoder
}()
@available(*, deprecated, message: "Access to storage decoder is going to be removed.")
public static var defaultStorageDecoder: JSONDecoder {
.default
}
}
9 changes: 6 additions & 3 deletions Sources/Storage/Deprecated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ extension StorageClientConfiguration {
@available(
*,
deprecated,
message: "Replace usages of this initializer with new init(url:headers:encoder:decoder:session:logger)"
message:
"Replace usages of this initializer with new init(url:headers:encoder:decoder:session:logger)"
)
public init(
url: URL,
Expand Down Expand Up @@ -101,7 +102,8 @@ extension StorageFileApi {
@available(
*,
deprecated,
message: "File was deprecated and it isn't used in the package anymore, if you're using it on your application, consider replacing it as it will be removed on the next major release."
message:
"File was deprecated and it isn't used in the package anymore, if you're using it on your application, consider replacing it as it will be removed on the next major release."
)
public struct File: Hashable, Equatable {
public var name: String
Expand All @@ -121,7 +123,8 @@ public struct File: Hashable, Equatable {
*,
deprecated,
renamed: "MultipartFormData",
message: "FormData was deprecated in favor of MultipartFormData, and it isn't used in the package anymore, if you're using it on your application, consider replacing it as it will be removed on the next major release."
message:
"FormData was deprecated in favor of MultipartFormData, and it isn't used in the package anymore, if you're using it on your application, consider replacing it as it will be removed on the next major release."
)
public class FormData {
var files: [File] = []
Expand Down
6 changes: 3 additions & 3 deletions Sources/Storage/Helpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import Helpers
kUTTagClassFilenameExtension, pathExtension as CFString, nil
)?.takeRetainedValue(),
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?
.takeRetainedValue()
.takeRetainedValue()
{
return contentType as String
}
Expand All @@ -43,7 +43,7 @@ import Helpers
kUTTagClassFilenameExtension, pathExtension as CFString, nil
)?.takeRetainedValue(),
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?
.takeRetainedValue()
.takeRetainedValue()
{
return contentType as String
}
Expand All @@ -62,7 +62,7 @@ import Helpers
kUTTagClassFilenameExtension, pathExtension as CFString, nil
)?.takeRetainedValue(),
let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?
.takeRetainedValue()
.takeRetainedValue()
{
return contentType as String
}
Expand Down
4 changes: 2 additions & 2 deletions Sources/Storage/StorageApi.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation
import Helpers
import HTTPTypes
import Helpers

#if canImport(FoundationNetworking)
import FoundationNetworking
Expand Down Expand Up @@ -36,7 +36,7 @@ public class StorageApi: @unchecked Sendable {

let response = try await http.send(request)

guard (200 ..< 300).contains(response.statusCode) else {
guard (200..<300).contains(response.statusCode) else {
if let error = try? configuration.decoder.decode(
StorageError.self,
from: response.data
Expand Down
2 changes: 1 addition & 1 deletion Sources/Storage/StorageError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ public struct StorageError: Error, Decodable, Sendable {
public var message: String
public var error: String?

public init(statusCode: String?, message: String, error: String?) {
public init(statusCode: String? = nil, message: String, error: String? = nil) {
self.statusCode = statusCode
self.message = message
self.error = error
Expand Down
Loading

0 comments on commit ce5cf5f

Please sign in to comment.