From 06649f47a8b1d65590ee8a8f78668d064e92f56b Mon Sep 17 00:00:00 2001 From: Navan Chauhan Date: Sun, 28 Jul 2024 14:55:56 -0600 Subject: rename and capitalize --- .spi.yml | 2 +- Package.swift | 12 +- Sources/SwiftGopherClient/gopherClient.swift | 161 +++++++++++++++++++++ .../gopherRequestResponseHandler.swift | 113 +++++++++++++++ Sources/swiftGopherClient/gopherClient.swift | 161 --------------------- .../gopherRequestResponseHandler.swift | 113 --------------- .../SwiftGopherClientTests.swift | 89 ++++++++++++ .../SwiftGopherServerTests.swift | 63 ++++++++ .../swiftGopherClientTests.swift | 89 ------------ .../swiftGopherServerTests.swift | 63 -------- 10 files changed, 433 insertions(+), 433 deletions(-) create mode 100644 Sources/SwiftGopherClient/gopherClient.swift create mode 100644 Sources/SwiftGopherClient/gopherRequestResponseHandler.swift delete mode 100644 Sources/swiftGopherClient/gopherClient.swift delete mode 100644 Sources/swiftGopherClient/gopherRequestResponseHandler.swift create mode 100644 Tests/SwiftGopherClientTests/SwiftGopherClientTests.swift create mode 100644 Tests/SwiftGopherServerTests/SwiftGopherServerTests.swift delete mode 100644 Tests/swiftGopherClientTests/swiftGopherClientTests.swift delete mode 100644 Tests/swiftGopherServerTests/swiftGopherServerTests.swift diff --git a/.spi.yml b/.spi.yml index 75437bd..7cd62b6 100644 --- a/.spi.yml +++ b/.spi.yml @@ -1,4 +1,4 @@ version: 1 builder: configs: - - documentation_targets: [swiftGopherClient, swift-gopher, GopherHelpers] + - documentation_targets: [SwiftGopherClient, swift-gopher, GopherHelpers] diff --git a/Package.swift b/Package.swift index 546655a..370414c 100644 --- a/Package.swift +++ b/Package.swift @@ -4,9 +4,9 @@ import PackageDescription let package = Package( - name: "swiftGopher", + name: "SwiftGopher", products: [ - .library(name: "SwiftGopherClient", targets: ["swiftGopherClient"]) + .library(name: "SwiftGopherClient", targets: ["SwiftGopherClient"]) ], dependencies: [ .package(url: "https://github.com/apple/swift-nio", from: "2.0.0"), @@ -36,7 +36,7 @@ let package = Package( ] ), .target( - name: "swiftGopherClient", + name: "SwiftGopherClient", dependencies: [ .product(name: "NIO", package: "swift-nio"), .product(name: "NIOTransportServices", package: "swift-nio-transport-services"), @@ -44,9 +44,9 @@ let package = Package( ] ), .testTarget( - name: "swiftGopherClientTests", - dependencies: ["swiftGopherClient"] + name: "SwiftGopherClientTests", + dependencies: ["SwiftGopherClient"] ), - .testTarget(name: "swiftGopherServerTests", dependencies: ["swift-gopher"]) + .testTarget(name: "SwiftGopherServerTests", dependencies: ["swift-gopher"]) ] ) diff --git a/Sources/SwiftGopherClient/gopherClient.swift b/Sources/SwiftGopherClient/gopherClient.swift new file mode 100644 index 0000000..3fd61ec --- /dev/null +++ b/Sources/SwiftGopherClient/gopherClient.swift @@ -0,0 +1,161 @@ +// +// gopherClient.swift +// +// +// Created by Navan Chauhan on 12/12/23. +// + +import Foundation +import GopherHelpers +import NIO +import NIOTransportServices + +/// `GopherClient` is a class for handling network connections and requests to Gopher servers. +/// +/// This client utilizes Swift NIO for efficient, non-blocking network operations. It automatically +/// chooses the appropriate `EventLoopGroup` based on the running platform: +/// - On iOS/macOS 10.14+, it uses `NIOTSEventLoopGroup` for optimal performance. +/// - On Linux or older Apple platforms, it falls back to `MultiThreadedEventLoopGroup`. +/// +/// The client supports both synchronous (completion handler-based) and asynchronous (Swift concurrency) APIs +/// for sending requests to Gopher servers. +public class GopherClient { + /// The event loop group used for managing network operations. + private let group: EventLoopGroup + + /// Initializes a new instance of `GopherClient`. + /// + /// This initializer automatically selects the appropriate `EventLoopGroup` based on the running platform. + public init() { + #if os(Linux) + self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + #else + if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, visionOS 1.0, *) { + self.group = NIOTSEventLoopGroup() + } else { + self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) + } + #endif + } + + /// Cleans up resources when the instance is deinitialized. + deinit { + self.shutdownEventLoopGroup() + } + + /// Sends a request to a Gopher server using a completion handler. + /// + /// This method asynchronously establishes a connection, sends the request, and calls the completion + /// handler with the result. + /// + /// - Parameters: + /// - host: The host address of the Gopher server. + /// - port: The port of the Gopher server. Defaults to 70. + /// - message: The message to be sent to the server. + /// - completion: A closure that handles the result of the request. It takes a `Result` type + /// which either contains an array of `gopherItem` on success or an `Error` on failure. + public func sendRequest( + to host: String, + port: Int = 70, + message: String, + completion: @escaping (Result<[gopherItem], Error>) -> Void + ) { + let bootstrap = self.createBootstrap(message: message, completion: completion) + bootstrap.connect(host: host, port: port).whenComplete { result in + switch result { + case .success(let channel): + channel.closeFuture.whenComplete { _ in + print("Connection closed") + } + case .failure(let error): + completion(.failure(error)) + } + } + } + + /// Sends a request to a Gopher server using Swift concurrency. + /// + /// This method asynchronously establishes a connection and sends the request, + /// returning the result as an array of `gopherItem`. + /// + /// - Parameters: + /// - host: The host address of the Gopher server. + /// - port: The port of the Gopher server. Defaults to 70. + /// - message: The message to be sent to the server. + /// + /// - Returns: An array of `gopherItem` representing the server's response. + /// + /// - Throws: An error if the connection fails or the server returns an invalid response. + @available(iOS 13.0, *) + @available(macOS 10.15, *) + public func sendRequest(to host: String, port: Int = 70, message: String) async throws + -> [gopherItem] + { + return try await withCheckedThrowingContinuation { continuation in + let bootstrap = self.createBootstrap(message: message) { result in + continuation.resume(with: result) + } + + bootstrap.connect(host: host, port: port).whenComplete { result in + switch result { + case .success(let channel): + channel.closeFuture.whenComplete { _ in + print("Connection Closed") + } + case .failure(let error): + continuation.resume(throwing: error) + } + } + } + } + + /// Creates a bootstrap for connecting to a Gopher server. + /// + /// This method sets up the appropriate bootstrap based on the platform and configures + /// the channel with a `GopherRequestResponseHandler`. + /// + /// - Parameters: + /// - message: The message to be sent to the server. + /// - completion: A closure that handles the result of the request. + /// + /// - Returns: A `NIOClientTCPBootstrapProtocol` configured for Gopher communication. + private func createBootstrap( + message: String, + completion: @escaping (Result<[gopherItem], Error>) -> Void + ) -> NIOClientTCPBootstrapProtocol { + let handler = GopherRequestResponseHandler(message: message, completion: completion) + + #if os(Linux) + return ClientBootstrap(group: group) + .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) + .channelInitializer { channel in + channel.pipeline.addHandler(handler) + } + #else + if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, visionOS 1.0, *) { + return NIOTSConnectionBootstrap(group: group) + .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) + .channelInitializer { channel in + channel.pipeline.addHandler(handler) + } + } else { + return ClientBootstrap(group: group) + .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) + .channelInitializer { channel in + channel.pipeline.addHandler(handler) + } + } + #endif + } + + /// Shuts down the event loop group, releasing any resources. + /// + /// This method is called during deinitialization to ensure clean shutdown of network resources. + private func shutdownEventLoopGroup() { + do { + try group.syncShutdownGracefully() + } catch { + print("Error shutting down event loop group: \(error)") + } + } +} diff --git a/Sources/SwiftGopherClient/gopherRequestResponseHandler.swift b/Sources/SwiftGopherClient/gopherRequestResponseHandler.swift new file mode 100644 index 0000000..10da67e --- /dev/null +++ b/Sources/SwiftGopherClient/gopherRequestResponseHandler.swift @@ -0,0 +1,113 @@ +// +// gopherRequestResponseHandler.swift +// +// +// Created by Navan Chauhan on 12/12/23. +// + +import Foundation +import GopherHelpers +import NIO + +final class GopherRequestResponseHandler: ChannelInboundHandler { + typealias InboundIn = ByteBuffer + typealias OutboundOut = ByteBuffer + + private var accumulatedData: ByteBuffer + private let message: String + private let completion: (Result<[gopherItem], Error>) -> Void + + init(message: String, completion: @escaping (Result<[gopherItem], Error>) -> Void) { + self.message = message + self.completion = completion + self.accumulatedData = ByteBuffer() + } + + func channelActive(context: ChannelHandlerContext) { + var buffer = context.channel.allocator.buffer(capacity: message.utf8.count) + buffer.writeString(message) + context.writeAndFlush(self.wrapOutboundOut(buffer), promise: nil) + } + + func channelRead(context: ChannelHandlerContext, data: NIOAny) { + var buffer = unwrapInboundIn(data) + accumulatedData.writeBuffer(&buffer) + } + + func channelInactive(context: ChannelHandlerContext) { + if let dataCopy = accumulatedData.getSlice(at: 0, length: accumulatedData.readableBytes) { + parseGopherServerResponse( + response: accumulatedData.readString(length: accumulatedData.readableBytes) ?? "", + originalBytes: dataCopy) + } + } + + func errorCaught(context: ChannelHandlerContext, error: Error) { + print("Error: ", error) + context.close(promise: nil) + } + + func createGopherItem(rawLine: String, itemType: gopherItemType = .info, rawData: ByteBuffer) + -> gopherItem + { + var item = gopherItem(rawLine: rawLine) + item.parsedItemType = itemType + item.rawData = rawData + + if rawLine.isEmpty { + item.valid = false + } else { + let components = rawLine.components(separatedBy: "\t") + + // Handle cases where rawLine does not have any itemType in the first character + item.message = String(components[0].dropFirst()) + + if components.indices.contains(1) { + item.selector = String(components[1]) + } + + if components.indices.contains(2) { + item.host = String(components[2]) + } + + if components.indices.contains(3) { + item.port = Int(String(components[3])) ?? 70 + } + } + + return item + } + + func parseGopherServerResponse(response: String, originalBytes: ByteBuffer) { + var gopherServerResponse: [gopherItem] = [] + + print("parsing") + let carriageReturnCount = response.filter({ $0 == "\r" }).count + let newlineCarriageReturnCount = response.filter({ $0 == "\r\n" }).count + print( + "Carriage Returns: \(carriageReturnCount), Newline + Carriage Returns: \(newlineCarriageReturnCount)" + ) + + if newlineCarriageReturnCount == 0 { + for line in response.split(separator: "\n") { + let lineItemType = getGopherFileType(item: "\(line.first ?? " ")") + let item = createGopherItem( + rawLine: String(line), itemType: lineItemType, rawData: originalBytes) + gopherServerResponse.append(item) + + } + } else { + for line in response.split(separator: "\r\n") { + let lineItemType = getGopherFileType(item: "\(line.first ?? " ")") + let item = createGopherItem( + rawLine: String(line), itemType: lineItemType, rawData: originalBytes) + gopherServerResponse.append(item) + + } + } + + print("done parsing") + + completion(.success(gopherServerResponse)) + } +} diff --git a/Sources/swiftGopherClient/gopherClient.swift b/Sources/swiftGopherClient/gopherClient.swift deleted file mode 100644 index 3fd61ec..0000000 --- a/Sources/swiftGopherClient/gopherClient.swift +++ /dev/null @@ -1,161 +0,0 @@ -// -// gopherClient.swift -// -// -// Created by Navan Chauhan on 12/12/23. -// - -import Foundation -import GopherHelpers -import NIO -import NIOTransportServices - -/// `GopherClient` is a class for handling network connections and requests to Gopher servers. -/// -/// This client utilizes Swift NIO for efficient, non-blocking network operations. It automatically -/// chooses the appropriate `EventLoopGroup` based on the running platform: -/// - On iOS/macOS 10.14+, it uses `NIOTSEventLoopGroup` for optimal performance. -/// - On Linux or older Apple platforms, it falls back to `MultiThreadedEventLoopGroup`. -/// -/// The client supports both synchronous (completion handler-based) and asynchronous (Swift concurrency) APIs -/// for sending requests to Gopher servers. -public class GopherClient { - /// The event loop group used for managing network operations. - private let group: EventLoopGroup - - /// Initializes a new instance of `GopherClient`. - /// - /// This initializer automatically selects the appropriate `EventLoopGroup` based on the running platform. - public init() { - #if os(Linux) - self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - #else - if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, visionOS 1.0, *) { - self.group = NIOTSEventLoopGroup() - } else { - self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - } - #endif - } - - /// Cleans up resources when the instance is deinitialized. - deinit { - self.shutdownEventLoopGroup() - } - - /// Sends a request to a Gopher server using a completion handler. - /// - /// This method asynchronously establishes a connection, sends the request, and calls the completion - /// handler with the result. - /// - /// - Parameters: - /// - host: The host address of the Gopher server. - /// - port: The port of the Gopher server. Defaults to 70. - /// - message: The message to be sent to the server. - /// - completion: A closure that handles the result of the request. It takes a `Result` type - /// which either contains an array of `gopherItem` on success or an `Error` on failure. - public func sendRequest( - to host: String, - port: Int = 70, - message: String, - completion: @escaping (Result<[gopherItem], Error>) -> Void - ) { - let bootstrap = self.createBootstrap(message: message, completion: completion) - bootstrap.connect(host: host, port: port).whenComplete { result in - switch result { - case .success(let channel): - channel.closeFuture.whenComplete { _ in - print("Connection closed") - } - case .failure(let error): - completion(.failure(error)) - } - } - } - - /// Sends a request to a Gopher server using Swift concurrency. - /// - /// This method asynchronously establishes a connection and sends the request, - /// returning the result as an array of `gopherItem`. - /// - /// - Parameters: - /// - host: The host address of the Gopher server. - /// - port: The port of the Gopher server. Defaults to 70. - /// - message: The message to be sent to the server. - /// - /// - Returns: An array of `gopherItem` representing the server's response. - /// - /// - Throws: An error if the connection fails or the server returns an invalid response. - @available(iOS 13.0, *) - @available(macOS 10.15, *) - public func sendRequest(to host: String, port: Int = 70, message: String) async throws - -> [gopherItem] - { - return try await withCheckedThrowingContinuation { continuation in - let bootstrap = self.createBootstrap(message: message) { result in - continuation.resume(with: result) - } - - bootstrap.connect(host: host, port: port).whenComplete { result in - switch result { - case .success(let channel): - channel.closeFuture.whenComplete { _ in - print("Connection Closed") - } - case .failure(let error): - continuation.resume(throwing: error) - } - } - } - } - - /// Creates a bootstrap for connecting to a Gopher server. - /// - /// This method sets up the appropriate bootstrap based on the platform and configures - /// the channel with a `GopherRequestResponseHandler`. - /// - /// - Parameters: - /// - message: The message to be sent to the server. - /// - completion: A closure that handles the result of the request. - /// - /// - Returns: A `NIOClientTCPBootstrapProtocol` configured for Gopher communication. - private func createBootstrap( - message: String, - completion: @escaping (Result<[gopherItem], Error>) -> Void - ) -> NIOClientTCPBootstrapProtocol { - let handler = GopherRequestResponseHandler(message: message, completion: completion) - - #if os(Linux) - return ClientBootstrap(group: group) - .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - .channelInitializer { channel in - channel.pipeline.addHandler(handler) - } - #else - if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, visionOS 1.0, *) { - return NIOTSConnectionBootstrap(group: group) - .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - .channelInitializer { channel in - channel.pipeline.addHandler(handler) - } - } else { - return ClientBootstrap(group: group) - .channelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1) - .channelInitializer { channel in - channel.pipeline.addHandler(handler) - } - } - #endif - } - - /// Shuts down the event loop group, releasing any resources. - /// - /// This method is called during deinitialization to ensure clean shutdown of network resources. - private func shutdownEventLoopGroup() { - do { - try group.syncShutdownGracefully() - } catch { - print("Error shutting down event loop group: \(error)") - } - } -} diff --git a/Sources/swiftGopherClient/gopherRequestResponseHandler.swift b/Sources/swiftGopherClient/gopherRequestResponseHandler.swift deleted file mode 100644 index 10da67e..0000000 --- a/Sources/swiftGopherClient/gopherRequestResponseHandler.swift +++ /dev/null @@ -1,113 +0,0 @@ -// -// gopherRequestResponseHandler.swift -// -// -// Created by Navan Chauhan on 12/12/23. -// - -import Foundation -import GopherHelpers -import NIO - -final class GopherRequestResponseHandler: ChannelInboundHandler { - typealias InboundIn = ByteBuffer - typealias OutboundOut = ByteBuffer - - private var accumulatedData: ByteBuffer - private let message: String - private let completion: (Result<[gopherItem], Error>) -> Void - - init(message: String, completion: @escaping (Result<[gopherItem], Error>) -> Void) { - self.message = message - self.completion = completion - self.accumulatedData = ByteBuffer() - } - - func channelActive(context: ChannelHandlerContext) { - var buffer = context.channel.allocator.buffer(capacity: message.utf8.count) - buffer.writeString(message) - context.writeAndFlush(self.wrapOutboundOut(buffer), promise: nil) - } - - func channelRead(context: ChannelHandlerContext, data: NIOAny) { - var buffer = unwrapInboundIn(data) - accumulatedData.writeBuffer(&buffer) - } - - func channelInactive(context: ChannelHandlerContext) { - if let dataCopy = accumulatedData.getSlice(at: 0, length: accumulatedData.readableBytes) { - parseGopherServerResponse( - response: accumulatedData.readString(length: accumulatedData.readableBytes) ?? "", - originalBytes: dataCopy) - } - } - - func errorCaught(context: ChannelHandlerContext, error: Error) { - print("Error: ", error) - context.close(promise: nil) - } - - func createGopherItem(rawLine: String, itemType: gopherItemType = .info, rawData: ByteBuffer) - -> gopherItem - { - var item = gopherItem(rawLine: rawLine) - item.parsedItemType = itemType - item.rawData = rawData - - if rawLine.isEmpty { - item.valid = false - } else { - let components = rawLine.components(separatedBy: "\t") - - // Handle cases where rawLine does not have any itemType in the first character - item.message = String(components[0].dropFirst()) - - if components.indices.contains(1) { - item.selector = String(components[1]) - } - - if components.indices.contains(2) { - item.host = String(components[2]) - } - - if components.indices.contains(3) { - item.port = Int(String(components[3])) ?? 70 - } - } - - return item - } - - func parseGopherServerResponse(response: String, originalBytes: ByteBuffer) { - var gopherServerResponse: [gopherItem] = [] - - print("parsing") - let carriageReturnCount = response.filter({ $0 == "\r" }).count - let newlineCarriageReturnCount = response.filter({ $0 == "\r\n" }).count - print( - "Carriage Returns: \(carriageReturnCount), Newline + Carriage Returns: \(newlineCarriageReturnCount)" - ) - - if newlineCarriageReturnCount == 0 { - for line in response.split(separator: "\n") { - let lineItemType = getGopherFileType(item: "\(line.first ?? " ")") - let item = createGopherItem( - rawLine: String(line), itemType: lineItemType, rawData: originalBytes) - gopherServerResponse.append(item) - - } - } else { - for line in response.split(separator: "\r\n") { - let lineItemType = getGopherFileType(item: "\(line.first ?? " ")") - let item = createGopherItem( - rawLine: String(line), itemType: lineItemType, rawData: originalBytes) - gopherServerResponse.append(item) - - } - } - - print("done parsing") - - completion(.success(gopherServerResponse)) - } -} diff --git a/Tests/SwiftGopherClientTests/SwiftGopherClientTests.swift b/Tests/SwiftGopherClientTests/SwiftGopherClientTests.swift new file mode 100644 index 0000000..2f559cd --- /dev/null +++ b/Tests/SwiftGopherClientTests/SwiftGopherClientTests.swift @@ -0,0 +1,89 @@ +// +// swiftGopherClientTests.swift +// +// +// Created by Navan Chauhan on 12/12/23. +// + +import NIO +import XCTest + +@testable import SwiftGopherClient + +final class GopherClientTests: XCTestCase { + + var client: GopherClient! + + override func setUp() { + super.setUp() + client = GopherClient() + } + + override func tearDown() { + client = nil + super.tearDown() + } + + func testInitialization() { + XCTAssertNotNil(client, "GopherClient should be initialized successfully") + } + + func testSendRequestCompletion() { + let expectation = XCTestExpectation(description: "Send request completion") + + client.sendRequest(to: "gopher.navan.dev", message: "\r\n") { result in + switch result { + case .success(let items): + XCTAssertFalse(items.isEmpty, "Response should contain gopher items") + case .failure(let error): + XCTFail("Request failed with error: \(error)") + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 10.0) + } + + @available(iOS 13.0, macOS 10.15, *) + func testSendRequestAsync() async throws { + do { + let items = try await client.sendRequest(to: "gopher.navan.dev", message: "\r\n") + XCTAssertFalse(items.isEmpty, "Response should contain gopher items") + } catch { + XCTFail("Async request failed with error: \(error)") + } + } + + func testInvalidHost() { + let expectation = XCTestExpectation(description: "Invalid host request") + + client.sendRequest(to: "invalid.host.example", message: "") { result in + switch result { + case .success: + XCTFail("Request should fail for invalid host") + case .failure: + // Expected failure + break + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 10.0) + } + + func testCustomPort() { + let expectation = XCTestExpectation(description: "Custom port request") + + client.sendRequest(to: "gopher.navan.dev", port: 70, message: "\r\n") { result in + switch result { + case .success(let items): + XCTAssertFalse(items.isEmpty, "Response should contain gopher items") + case .failure(let error): + XCTFail("Request failed with error: \(error)") + } + expectation.fulfill() + } + + wait(for: [expectation], timeout: 10.0) + } +} diff --git a/Tests/SwiftGopherServerTests/SwiftGopherServerTests.swift b/Tests/SwiftGopherServerTests/SwiftGopherServerTests.swift new file mode 100644 index 0000000..0413d90 --- /dev/null +++ b/Tests/SwiftGopherServerTests/SwiftGopherServerTests.swift @@ -0,0 +1,63 @@ +// +// swiftGopherServerTests.swift +// +// +// Created by Navan Chauhan on 7/28/24. +// + +import ArgumentParser +import NIO +import XCTest + +@testable import swift_gopher + +final class SwiftGopherTests: XCTestCase { + + func testDefaultValues() throws { + let gopher = try swiftGopher.parse([]) + + XCTAssertEqual(gopher.gopherHostName, "localhost") + XCTAssertEqual(gopher.host, "0.0.0.0") + XCTAssertEqual(gopher.port, 8080) + XCTAssertEqual(gopher.gopherDataDir, "./example-gopherdata") + XCTAssertFalse(gopher.disableSearch) + XCTAssertFalse(gopher.disableGophermap) + } + + func testCustomValues() throws { + let args = [ + "--gopher-host-name", "example.com", + "--host", "127.0.0.1", + "--port", "9090", + "--gopher-data-dir", "/custom/path", + "--disable-search", + "--disable-gophermap", + ] + + let gopher = try swiftGopher.parse(args) + + XCTAssertEqual(gopher.gopherHostName, "example.com") + XCTAssertEqual(gopher.host, "127.0.0.1") + XCTAssertEqual(gopher.port, 9090) + XCTAssertEqual(gopher.gopherDataDir, "/custom/path") + XCTAssertTrue(gopher.disableSearch) + XCTAssertTrue(gopher.disableGophermap) + } + + func testShortOptions() throws { + let args = [ + "-g", "short.com", + "-h", "192.168.1.1", + "-p", "7070", + "-d", "/short/path", + ] + + let gopher = try swiftGopher.parse(args) + + XCTAssertEqual(gopher.gopherHostName, "short.com") + XCTAssertEqual(gopher.host, "192.168.1.1") + XCTAssertEqual(gopher.port, 7070) + XCTAssertEqual(gopher.gopherDataDir, "/short/path") + } + +} diff --git a/Tests/swiftGopherClientTests/swiftGopherClientTests.swift b/Tests/swiftGopherClientTests/swiftGopherClientTests.swift deleted file mode 100644 index 088124d..0000000 --- a/Tests/swiftGopherClientTests/swiftGopherClientTests.swift +++ /dev/null @@ -1,89 +0,0 @@ -// -// swiftGopherClientTests.swift -// -// -// Created by Navan Chauhan on 12/12/23. -// - -import NIO -import XCTest - -@testable import swiftGopherClient - -final class GopherClientTests: XCTestCase { - - var client: GopherClient! - - override func setUp() { - super.setUp() - client = GopherClient() - } - - override func tearDown() { - client = nil - super.tearDown() - } - - func testInitialization() { - XCTAssertNotNil(client, "GopherClient should be initialized successfully") - } - - func testSendRequestCompletion() { - let expectation = XCTestExpectation(description: "Send request completion") - - client.sendRequest(to: "gopher.navan.dev", message: "\r\n") { result in - switch result { - case .success(let items): - XCTAssertFalse(items.isEmpty, "Response should contain gopher items") - case .failure(let error): - XCTFail("Request failed with error: \(error)") - } - expectation.fulfill() - } - - wait(for: [expectation], timeout: 10.0) - } - - @available(iOS 13.0, macOS 10.15, *) - func testSendRequestAsync() async throws { - do { - let items = try await client.sendRequest(to: "gopher.navan.dev", message: "\r\n") - XCTAssertFalse(items.isEmpty, "Response should contain gopher items") - } catch { - XCTFail("Async request failed with error: \(error)") - } - } - - func testInvalidHost() { - let expectation = XCTestExpectation(description: "Invalid host request") - - client.sendRequest(to: "invalid.host.example", message: "") { result in - switch result { - case .success: - XCTFail("Request should fail for invalid host") - case .failure: - // Expected failure - break - } - expectation.fulfill() - } - - wait(for: [expectation], timeout: 10.0) - } - - func testCustomPort() { - let expectation = XCTestExpectation(description: "Custom port request") - - client.sendRequest(to: "gopher.navan.dev", port: 70, message: "\r\n") { result in - switch result { - case .success(let items): - XCTAssertFalse(items.isEmpty, "Response should contain gopher items") - case .failure(let error): - XCTFail("Request failed with error: \(error)") - } - expectation.fulfill() - } - - wait(for: [expectation], timeout: 10.0) - } -} diff --git a/Tests/swiftGopherServerTests/swiftGopherServerTests.swift b/Tests/swiftGopherServerTests/swiftGopherServerTests.swift deleted file mode 100644 index 0413d90..0000000 --- a/Tests/swiftGopherServerTests/swiftGopherServerTests.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// swiftGopherServerTests.swift -// -// -// Created by Navan Chauhan on 7/28/24. -// - -import ArgumentParser -import NIO -import XCTest - -@testable import swift_gopher - -final class SwiftGopherTests: XCTestCase { - - func testDefaultValues() throws { - let gopher = try swiftGopher.parse([]) - - XCTAssertEqual(gopher.gopherHostName, "localhost") - XCTAssertEqual(gopher.host, "0.0.0.0") - XCTAssertEqual(gopher.port, 8080) - XCTAssertEqual(gopher.gopherDataDir, "./example-gopherdata") - XCTAssertFalse(gopher.disableSearch) - XCTAssertFalse(gopher.disableGophermap) - } - - func testCustomValues() throws { - let args = [ - "--gopher-host-name", "example.com", - "--host", "127.0.0.1", - "--port", "9090", - "--gopher-data-dir", "/custom/path", - "--disable-search", - "--disable-gophermap", - ] - - let gopher = try swiftGopher.parse(args) - - XCTAssertEqual(gopher.gopherHostName, "example.com") - XCTAssertEqual(gopher.host, "127.0.0.1") - XCTAssertEqual(gopher.port, 9090) - XCTAssertEqual(gopher.gopherDataDir, "/custom/path") - XCTAssertTrue(gopher.disableSearch) - XCTAssertTrue(gopher.disableGophermap) - } - - func testShortOptions() throws { - let args = [ - "-g", "short.com", - "-h", "192.168.1.1", - "-p", "7070", - "-d", "/short/path", - ] - - let gopher = try swiftGopher.parse(args) - - XCTAssertEqual(gopher.gopherHostName, "short.com") - XCTAssertEqual(gopher.host, "192.168.1.1") - XCTAssertEqual(gopher.port, 7070) - XCTAssertEqual(gopher.gopherDataDir, "/short/path") - } - -} -- cgit v1.2.3