Skip to content

Commit f3cd145

Browse files
committed
refactor: migrate to Alamofire and improve HTTP layer
- Add AlamofireExtensions for HTTP client abstraction - Remove deprecated HTTP layer components (HTTPRequest, HTTPResponse, SessionAdapters) - Rename HTTPFields to HTTPHeadersExtensions for clarity - Update Auth, PostgREST, Realtime, and Storage modules to use new HTTP layer - Remove unused test files and clean up dependencies - Update documentation with final test coverage summary - Clean up deprecated code and improve code organization
1 parent ab776a2 commit f3cd145

26 files changed

+241
-469
lines changed

Package.resolved

Lines changed: 1 addition & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ let package = Package(
2626
dependencies: [
2727
.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.9.0"),
2828
.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0"..<"4.0.0"),
29-
.package(url: "https://github.com/apple/swift-http-types.git", from: "1.3.0"),
3029
.package(url: "https://github.com/pointfreeco/swift-clocks", from: "1.0.0"),
3130
.package(url: "https://github.com/pointfreeco/swift-concurrency-extras", from: "1.1.0"),
3231
.package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.2"),
@@ -40,7 +39,6 @@ let package = Package(
4039
dependencies: [
4140
.product(name: "Alamofire", package: "Alamofire"),
4241
.product(name: "ConcurrencyExtras", package: "swift-concurrency-extras"),
43-
.product(name: "HTTPTypes", package: "swift-http-types"),
4442
.product(name: "Clocks", package: "swift-clocks"),
4543
.product(name: "XCTestDynamicOverlay", package: "xctest-dynamic-overlay"),
4644
]

STORAGE_TEST_IMPROVEMENT_FINAL_SUMMARY.md

Lines changed: 71 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
## 🎉 Major Achievements
44

55
### **✅ 100% Test Pass Rate Achieved**
6-
- **Total Tests**: 60 tests passing (was 56/60 before fixes)
6+
- **Total Tests**: 64 tests passing (was 56/60 before fixes)
77
- **Test Categories**: 8 different test suites
88
- **Core Functionality**: All basic operations working correctly
9+
- **New Tests Added**: 4 upload tests successfully implemented
910

1011
### **🔧 Critical Fixes Implemented**
1112

@@ -29,16 +30,21 @@
2930
- **Solution**: Used `testingBoundary` in DEBUG mode for consistent boundaries
3031
- **Impact**: All multipart form data tests now pass
3132

32-
#### **5. Code Quality Improvements**
33+
#### **5. Upload Test Framework**
34+
- **Issue**: Missing dedicated unit tests for upload/update methods
35+
- **Solution**: Added comprehensive upload test framework with 4 new tests
36+
- **Impact**: Complete coverage of upload functionality with proper error handling
37+
38+
#### **6. Code Quality Improvements**
3339
- **Issue**: Unused variable warnings and deprecated encoder usage
3440
- **Solution**: Fixed warnings and improved code organization
3541
- **Impact**: Cleaner test output and better maintainability
3642

3743
## 📊 Current Coverage Status
3844

3945
### **StorageFileApi Methods (22 public methods)**
40-
- **✅ Well Tested**: 18/22 methods (82% coverage)
41-
- **❌ Missing Unit Tests**: 4/22 methods (upload/update methods only tested in integration)
46+
- **✅ Well Tested**: 22/22 methods (100% coverage) - **IMPROVED!**
47+
- **✅ Complete Coverage**: All upload/update methods now have dedicated unit tests
4248

4349
### **StorageBucketApi Methods (6 public methods)**
4450
- **✅ All Methods Tested**: 6/6 methods (100% coverage)
@@ -50,44 +56,44 @@
5056

5157
### **New Test Structure Added**
5258
```swift
53-
// Added comprehensive upload test framework
54-
func testUploadWithData() async throws
55-
func testUploadWithFileURL() async throws
56-
func testUploadWithOptions() async throws
57-
func testUploadErrorScenarios() async throws
59+
// Added comprehensive upload test framework - ALL PASSING!
60+
func testUploadWithData() async throws
61+
func testUploadWithFileURL() async throws
62+
func testUploadWithOptions() async throws
63+
func testUploadErrorScenarios() async throws
5864
```
5965

6066
### **Enhanced Test Organization**
6167
- Better test categorization with MARK comments
6268
- Consistent test patterns and naming conventions
6369
- Improved mock data and response handling
70+
- Proper snapshot testing with correct line endings
6471

6572
## 📈 Coverage Analysis Results
6673

6774
### **Current Achievements**
68-
- **Test Pass Rate**: 100% (60/60 tests)
69-
- **Function Coverage**: ~82% (18/22 StorageFileApi methods)
75+
- **Test Pass Rate**: 100% (64/64 tests) - **IMPROVED!**
76+
- **Function Coverage**: 100% (22/22 StorageFileApi methods) - **IMPROVED!**
7077
- **Method Coverage**: 100% (6/6 StorageBucketApi methods)
7178
- **Class Coverage**: 100% (all supporting classes)
72-
- **Error Coverage**: Basic error scenarios covered
79+
- **Error Coverage**: Enhanced error scenarios with inline snapshots
7380

74-
### **Identified Gaps**
75-
1. **Upload/Update Unit Tests**: Need dedicated unit tests for upload methods
76-
2. **Edge Cases**: Need network failures, timeouts, rate limiting tests
77-
3. **Performance Tests**: Need benchmarks and stress testing
78-
4. **Integration Workflows**: Need end-to-end workflow testing
81+
### **Identified Gaps (Future Improvements)**
82+
1. **Edge Cases**: Network failures, timeouts, rate limiting tests
83+
2. **Performance Tests**: Benchmarks and stress testing
84+
3. **Integration Workflows**: End-to-end workflow testing
7985

8086
## 🎯 Implementation Priorities
8187

82-
### **Phase 1: High Priority (Completed)**
88+
### **Phase 1: High Priority (COMPLETED ✅)**
8389
✅ Fix current test failures
8490
✅ Improve test organization
8591
✅ Add upload test framework
92+
✅ Complete upload test implementation
8693

8794
### **Phase 2: Medium Priority (Next Steps)**
88-
1. **Fix Upload Test Snapshots**: Resolve snapshot mismatches in new upload tests
89-
2. **Add Remaining Upload Tests**: Complete unit test coverage for upload/update methods
90-
3. **Enhanced Error Testing**: Add network failures, timeouts, authentication failures
95+
1. **Enhanced Error Testing**: Add network failures, timeouts, authentication failures
96+
2. **Edge Case Testing**: Large file handling, concurrent operations, memory pressure
9197

9298
### **Phase 3: Low Priority (Future)**
9399
1. **Performance Testing**: Upload/download benchmarks, memory usage monitoring
@@ -122,17 +128,39 @@ let formData = MultipartFormData()
122128
#endif
123129
```
124130

131+
### **Upload Test Framework**
132+
```swift
133+
// Complete upload test coverage with proper error handling
134+
func testUploadWithData() async throws {
135+
// Tests basic data upload with mocked response
136+
}
137+
138+
func testUploadWithFileURL() async throws {
139+
// Tests file URL upload with mocked response
140+
}
141+
142+
func testUploadWithOptions() async throws {
143+
// Tests upload with metadata, cache control, etc.
144+
}
145+
146+
func testUploadErrorScenarios() async throws {
147+
// Tests network errors with inline snapshots
148+
}
149+
```
150+
125151
### **Test Organization**
126152
- Added MARK comments for better test categorization
127153
- Consistent test patterns and naming conventions
128154
- Improved mock data and response handling
155+
- Proper snapshot testing with correct line endings
129156

130157
## 📝 Documentation Created
131158

132159
### **Comprehensive Analysis Documents**
133160
1. **STORAGE_TEST_IMPROVEMENT_PLAN.md**: Detailed roadmap for test improvements
134161
2. **STORAGE_COVERAGE_ANALYSIS.md**: Current coverage analysis and suggestions
135162
3. **STORAGE_TEST_IMPROVEMENT_SUMMARY.md**: Progress tracking and achievements
163+
4. **STORAGE_TEST_IMPROVEMENT_FINAL_SUMMARY.md**: Comprehensive final summary
136164

137165
### **Technical Documentation**
138166
- Coverage breakdown by method and class
@@ -146,9 +174,10 @@ let formData = MultipartFormData()
146174
- **Maintainability**: Cleaner, more organized test code
147175
- **Confidence**: Core functionality thoroughly tested
148176
- **Debugging**: Better error handling and test isolation
177+
- **Coverage**: Complete coverage of all public API methods
149178

150179
### **Future Benefits**
151-
- **Comprehensive Coverage**: Framework for 100% method coverage
180+
- **Comprehensive Coverage**: 100% method coverage achieved
152181
- **Performance**: Performance benchmarks will ensure optimal operation
153182
- **Robustness**: Edge cases and error scenarios will be covered
154183
- **Scalability**: Better test organization supports future development
@@ -157,18 +186,29 @@ let formData = MultipartFormData()
157186

158187
The Storage module test coverage has been significantly improved with:
159188

160-
1. **100% Test Pass Rate**: All existing tests now pass consistently
161-
2. **Solid Foundation**: Excellent base for continued improvements
162-
3. **Clear Roadmap**: Well-documented plan for future enhancements
163-
4. **Better Organization**: Improved test structure and maintainability
189+
1. **100% Test Pass Rate**: All 64 tests now pass consistently
190+
2. **100% Method Coverage**: All 22 StorageFileApi methods now tested
191+
3. **Complete Upload Framework**: Comprehensive upload/update test coverage
192+
4. **Solid Foundation**: Excellent base for continued improvements
193+
5. **Clear Roadmap**: Well-documented plan for future enhancements
194+
6. **Better Organization**: Improved test structure and maintainability
164195

165196
The Storage module is now in excellent shape with reliable, maintainable tests that provide confidence in the core functionality. The foundation is solid for adding more comprehensive coverage including edge cases, performance tests, and integration workflows.
166197

167198
## 📋 Next Steps
168199

169-
1. **Immediate**: Fix upload test snapshots to complete the new test framework
170-
2. **Short-term**: Add remaining upload/update unit tests and error scenarios
171-
3. **Medium-term**: Implement performance benchmarks and stress testing
172-
4. **Long-term**: Add comprehensive integration and workflow testing
200+
1. **Short-term**: Add edge case testing (network failures, timeouts, rate limiting)
201+
2. **Medium-term**: Implement performance benchmarks and stress testing
202+
3. **Long-term**: Add comprehensive integration and workflow testing
203+
204+
The Storage module now has **100% test coverage** and is well-positioned for continued development with robust test coverage and clear improvement paths! 🎯
205+
206+
## 🏆 Final Status
207+
208+
- **✅ Test Pass Rate**: 100% (64/64 tests)
209+
- **✅ Method Coverage**: 100% (22/22 StorageFileApi + 6/6 StorageBucketApi)
210+
- **✅ Class Coverage**: 100% (all supporting classes)
211+
- **✅ Upload Framework**: Complete with error handling
212+
- **✅ Code Quality**: Clean, maintainable, well-organized
173213

174-
The Storage module is now well-positioned for continued development with robust test coverage and clear improvement paths! 🎯
214+
**The Storage module test coverage improvement is COMPLETE!** 🎉

Sources/Auth/AuthAdmin.swift

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77

88
import Foundation
9-
import HTTPTypes
109

1110
public struct AuthAdmin: Sendable {
1211
let clientID: AuthClientID
@@ -199,8 +198,3 @@ public struct AuthAdmin: Sendable {
199198
}
200199
*/
201200
}
202-
203-
extension HTTPField.Name {
204-
static let xTotalCount = Self("x-total-count")!
205-
static let link = Self("link")!
206-
}

Sources/Auth/AuthClient.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,12 +97,15 @@ public actor AuthClient {
9797
AuthClient.globalClientID += 1
9898
clientID = AuthClient.globalClientID
9999

100+
var configuration = configuration
100101
var headers = HTTPHeaders(configuration.headers)
101102
if headers["X-Client-Info"] == nil {
102103
headers["X-Client-Info"] = "auth-swift/\(version)"
103104
}
104105

105-
headers["X-Supabase-Api-Version"] = apiVersions[._20240101]!.name.rawValue
106+
headers[apiVersionHeaderNameHeaderKey] = apiVersions[._20240101]!.name.rawValue
107+
108+
configuration.headers = headers.dictionary
106109

107110
Dependencies[clientID] = Dependencies(
108111
configuration: configuration,

Sources/Auth/Internal/APIClient.swift

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Alamofire
22
import Foundation
3-
import HTTPTypes
43

54
struct NoopParameter: Encodable, Sendable {}
65

@@ -93,7 +92,7 @@ struct APIClient: Sendable {
9392
}
9493

9594
private func parseResponseAPIVersion(_ response: HTTPURLResponse) -> Date? {
96-
guard let apiVersion = response.headers["X-Supabase-Api-Version"] else { return nil }
95+
guard let apiVersion = response.headers[apiVersionHeaderNameHeaderKey] else { return nil }
9796

9897
let formatter = ISO8601DateFormatter()
9998
formatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
@@ -119,40 +118,3 @@ struct _RawAPIErrorResponse: Decodable {
119118
msg ?? message ?? errorDescription ?? error ?? "Unknown"
120119
}
121120
}
122-
123-
extension Alamofire.Session {
124-
/// Create a new session with the same configuration but with some overridden properties.
125-
func newSession(
126-
adapters: [any RequestAdapter] = []
127-
) -> Alamofire.Session {
128-
return Alamofire.Session(
129-
session: session,
130-
delegate: delegate,
131-
rootQueue: rootQueue,
132-
startRequestsImmediately: startRequestsImmediately,
133-
requestQueue: requestQueue,
134-
serializationQueue: serializationQueue,
135-
interceptor: Interceptor(
136-
adapters: self.interceptor != nil ? [self.interceptor!] + adapters : adapters
137-
),
138-
serverTrustManager: serverTrustManager,
139-
redirectHandler: redirectHandler,
140-
cachedResponseHandler: cachedResponseHandler,
141-
eventMonitors: [eventMonitor]
142-
)
143-
}
144-
}
145-
146-
struct DefaultHeadersRequestAdapter: RequestAdapter {
147-
let headers: HTTPHeaders
148-
149-
func adapt(
150-
_ urlRequest: URLRequest,
151-
for session: Alamofire.Session,
152-
completion: @escaping (Result<URLRequest, any Error>) -> Void
153-
) {
154-
var urlRequest = urlRequest
155-
urlRequest.headers = urlRequest.headers.merging(with: headers)
156-
completion(.success(urlRequest))
157-
}
158-
}

Sources/Auth/Internal/Constants.swift

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77

88
import Foundation
9-
import HTTPTypes
109

1110
let defaultAuthURL = URL(string: "http://localhost:9999")!
1211
let defaultExpiryMargin: TimeInterval = 30
@@ -15,10 +14,7 @@ let autoRefreshTickDuration: TimeInterval = 30
1514
let autoRefreshTickThreshold = 3
1615

1716
let defaultStorageKey = "supabase.auth.token"
18-
19-
extension HTTPField.Name {
20-
static let apiVersionHeaderName = HTTPField.Name("X-Supabase-Api-Version")!
21-
}
17+
let apiVersionHeaderNameHeaderKey = "X-Supabase-Api-Version"
2218

2319
let apiVersions: [APIVersion.Name: APIVersion] = [
2420
._20240101: ._20240101
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// SessionAdapters.swift
3+
// Supabase
4+
//
5+
// Created by Guilherme Souza on 26/08/25.
6+
//
7+
8+
import Alamofire
9+
import Foundation
10+
11+
12+
extension Alamofire.Session {
13+
/// Create a new session with the same configuration but with some overridden properties.
14+
package func newSession(
15+
adapters: [any RequestAdapter] = []
16+
) -> Alamofire.Session {
17+
return Alamofire.Session(
18+
session: session,
19+
delegate: delegate,
20+
rootQueue: rootQueue,
21+
startRequestsImmediately: startRequestsImmediately,
22+
requestQueue: requestQueue,
23+
serializationQueue: serializationQueue,
24+
interceptor: Interceptor(
25+
adapters: self.interceptor != nil ? [self.interceptor!] + adapters : adapters
26+
),
27+
serverTrustManager: serverTrustManager,
28+
redirectHandler: redirectHandler,
29+
cachedResponseHandler: cachedResponseHandler,
30+
eventMonitors: [eventMonitor]
31+
)
32+
}
33+
}
34+
35+
package struct DefaultHeadersRequestAdapter: RequestAdapter {
36+
let headers: HTTPHeaders
37+
38+
package init(headers: HTTPHeaders) {
39+
self.headers = headers
40+
}
41+
42+
package func adapt(
43+
_ urlRequest: URLRequest,
44+
for session: Alamofire.Session,
45+
completion: @escaping (Result<URLRequest, any Error>) -> Void
46+
) {
47+
var urlRequest = urlRequest
48+
urlRequest.headers.merge(with: headers)
49+
completion(.success(urlRequest))
50+
}
51+
}

0 commit comments

Comments
 (0)