Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions Sources/ipinfoKit/API/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,30 +37,34 @@ extension Service.Router {
// MARK: - Service

@MainActor
class Service {
public class Service {

// MARK: Lifecycle

private init() { }

// MARK: Internal

static let shared = Service()
public static let shared = Service()

var ipInfoURL = "https://ipinfo.io"
let headers: HTTPHeaders = [
"Authorization": "Bearer \(Constants.ACCESS_TOKEN)",
"User-Agent": "IPinfoClient/Swift/\(Constants.SDK_VERSION)",
]

/// Custom session for testing. When nil, uses default AF session.
public var session: Session?

@MainActor
func requestAPI(
URL: Service.Router,
method: HTTPMethod,
params: Parameters? = nil,
completion: @escaping (_ status: Response,_ data: Data,_ msg: String) -> Void) {

AF.request(URL.endPoint , method: method, parameters: params , encoding: JSONEncoding.default, headers: headers)
let requestSession = session ?? AF
requestSession.request(URL.endPoint , method: method, parameters: params , encoding: JSONEncoding.default, headers: headers)
.response { response in
DispatchQueue.main.async {
switch response.result {
Expand Down
65 changes: 62 additions & 3 deletions Tests/ipinfoKitTests/ResproxyTests.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,53 @@
import Alamofire
import Foundation
import Testing
import ipinfoKit

/// Custom URLProtocol that only mocks specific URLs for resproxy tests
private class ResproxyMockURLProtocol: URLProtocol {
nonisolated(unsafe) static var mockResponses: [String: Data] = [:]

override class func canInit(with request: URLRequest) -> Bool {
guard let url = request.url?.absoluteString else { return false }
return mockResponses.keys.contains(url)
}

override class func canonicalRequest(for request: URLRequest) -> URLRequest {
return request
}

override func startLoading() {
guard let url = request.url?.absoluteString,
let data = ResproxyMockURLProtocol.mockResponses[url] else {
client?.urlProtocol(self, didFailWithError: NSError(domain: "ResproxyMockURLProtocol", code: 404))
return
}

let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: ["Content-Type": "application/json"])!
client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
client?.urlProtocol(self, didLoad: data)
client?.urlProtocolDidFinishLoading(self)
}

override func stopLoading() {}
}

@MainActor
struct ResproxyTests {

@Test func resproxyTest() async throws {
// Set up mock response
let mockData = """
{"ip":"175.107.211.204","last_seen":"2025-01-20","percent_days_seen":0.85,"service":"example_service"}
""".data(using: .utf8)!
ResproxyMockURLProtocol.mockResponses["https://ipinfo.io/resproxy/175.107.211.204"] = mockData

// Configure mock session for this test
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [ResproxyMockURLProtocol.self]
let mockSession = Session(configuration: configuration)
Service.shared.session = mockSession

let response = try await withCheckedThrowingContinuation { continuation in
IPINFO.shared.getResproxy(ip: "175.107.211.204") { status, response, msg in
switch status {
Expand All @@ -26,13 +69,26 @@ struct ResproxyTests {
}
}

Service.shared.session = nil
ResproxyMockURLProtocol.mockResponses.removeAll()

#expect(response.ip == "175.107.211.204")
#expect(response.lastSeen != nil)
#expect(response.percentDaysSeen != nil)
#expect(response.service != nil)
#expect(response.lastSeen == "2025-01-20")
#expect(response.percentDaysSeen == 0.85)
#expect(response.service == "example_service")
}

@Test func resproxyEmptyTest() async throws {
// Set up mock response for empty result
let mockData = "{}".data(using: .utf8)!
ResproxyMockURLProtocol.mockResponses["https://ipinfo.io/resproxy/8.8.8.8"] = mockData

// Configure mock session for this test
let configuration = URLSessionConfiguration.ephemeral
configuration.protocolClasses = [ResproxyMockURLProtocol.self]
let mockSession = Session(configuration: configuration)
Service.shared.session = mockSession

let response = try await withCheckedThrowingContinuation { continuation in
IPINFO.shared.getResproxy(ip: "8.8.8.8") { status, response, msg in
switch status {
Expand All @@ -54,6 +110,9 @@ struct ResproxyTests {
}
}

Service.shared.session = nil
ResproxyMockURLProtocol.mockResponses.removeAll()

#expect(response.ip == nil)
#expect(response.lastSeen == nil)
#expect(response.percentDaysSeen == nil)
Expand Down