diff --git a/Tests/CouchDBClientTests/CouchDBClientTests.swift b/Tests/CouchDBClientTests/CouchDBClientTests.swift index a9ac55b..b4ed3da 100644 --- a/Tests/CouchDBClientTests/CouchDBClientTests.swift +++ b/Tests/CouchDBClientTests/CouchDBClientTests.swift @@ -1,13 +1,32 @@ -import XCTest +import Foundation +import Testing import NIO import AsyncHTTPClient @testable import CouchDBClient -final class CouchDBClientTests: XCTestCase { +fileprivate let config = CouchDBClient.Config( + couchProtocol: .http, + couchHost: "127.0.0.1", + couchPort: 5984, + userName: "admin", + userPassword: (ProcessInfo.processInfo.environment["COUCHDB_PASS"] ?? ""), + requestsTimeout: 30 +) + +fileprivate let httpClient = HTTPClient() + +@Suite(.serialized) +struct CouchDBClientTests { struct ExpectedDoc: CouchDBRepresentable { var name: String - var _id: String = NSUUID().uuidString + var _id: String = { + #if canImport(Foundation) + return Foundation.UUID().uuidString + #else + return "id-" + String(Int.random(in: 0..<1000000)) + #endif + }() var _rev: String? func updateRevision(_ newRevision: String) -> Self { @@ -17,497 +36,343 @@ final class CouchDBClientTests: XCTestCase { let testsDB = "fortests" - let config = CouchDBClient.Config( - couchProtocol: .http, - couchHost: "127.0.0.1", - couchPort: 5984, - userName: "admin", - userPassword: ProcessInfo.processInfo.environment["COUCHDB_PASS"] ?? "", - requestsTimeout: 30 - ) - - lazy var couchDBClient = CouchDBClient(config: config) - - let httpClient = HTTPClient() - - override func setUp() async throws { - try await super.setUp() - } - - func test00_CreateDB() async throws { - do { - let exists = try await couchDBClient.dbExists(testsDB) - if exists { - try await couchDBClient.deleteDB(testsDB) - } + let couchDBClient = CouchDBClient(config: config) - try await couchDBClient.createDB(testsDB) - } catch { - XCTFail(error.localizedDescription) + @Test("Initial creation of database for testing. Should create or recreate db") + func createDB() async throws { + let exists = try await couchDBClient.dbExists(testsDB) + if exists { + try await couchDBClient.deleteDB(testsDB) } + try await couchDBClient.createDB(testsDB) } - func test01_DBExists() async throws { - do { - let exists = try await couchDBClient.dbExists(testsDB) - XCTAssertTrue(exists) - } catch { - XCTFail(error.localizedDescription) - } + @Test("Test dbExists method. Should return true for existing db") + func DBExists() async throws { + let exists = try await couchDBClient.dbExists(testsDB) + #expect(exists == true) } - func test02_DBDoesNotExist() async throws { + @Test("Test dbExists method. Should return false for non existing db") + func DBDoesNotExist() async throws { let nonExistentDB = "db_should_not_exist" - do { - let exists = try await couchDBClient.dbExists(nonExistentDB) - XCTAssertFalse(exists) - } catch { - XCTFail(error.localizedDescription) - } + let exists = try await couchDBClient.dbExists(nonExistentDB) + #expect(exists == false) } - func test03_GetAllDbs() async throws { - do { - let dbs = try await couchDBClient.getAllDBs() - - XCTAssertNotNil(dbs) - XCTAssertFalse(dbs.isEmpty) - XCTAssertTrue(dbs.contains(testsDB)) - } catch { - XCTFail(error.localizedDescription) - } + @Test("Get all dbs") + func getAllDbs() async throws { + let dbs = try await couchDBClient.getAllDBs() + #expect(!dbs.isEmpty) + #expect(dbs.contains(testsDB)) } - func test04_updateAndDeleteDocMethods() async throws { + @Test("Update and delete document") + func updateAndDeleteDocMethods() async throws { var testDoc = ExpectedDoc(name: "test name") - var expectedInsertId: String = "" - var expectedInsertRev: String = "" - - // insert - do { - testDoc = try await couchDBClient.insert( - dbName: testsDB, - doc: testDoc - ) - } catch CouchDBClientError.insertError(let error) { - XCTFail(error.reason) - return - } catch { - XCTFail(error.localizedDescription) - return - } + testDoc = try await couchDBClient.insert(dbName: testsDB, doc: testDoc) + let expectedInsertId = testDoc._id + let expectedInsertRev = testDoc._rev! - expectedInsertId = testDoc._id - expectedInsertRev = testDoc._rev! - - // get inserted doc - do { - testDoc = try await couchDBClient.get(fromDB: testsDB, uri: expectedInsertId) - } catch CouchDBClientError.getError(let error) { - XCTFail(error.reason) - return - } catch { - XCTFail(error.localizedDescription) - return - } + testDoc = try await couchDBClient.get(fromDB: testsDB, uri: expectedInsertId) - // Test update doc testDoc.name = "test name 3" let expectedName = testDoc.name + testDoc = try await couchDBClient.update(dbName: testsDB, doc: testDoc) + #expect(testDoc._rev != expectedInsertRev) + #expect(testDoc._id == expectedInsertId) - do { - testDoc = try await couchDBClient.update( - dbName: testsDB, - doc: testDoc - ) - } catch CouchDBClientError.updateError(let error) { - XCTFail(error.reason) - return - } catch { - XCTFail(error.localizedDescription) - return - } - - XCTAssertNotEqual(testDoc._rev, expectedInsertRev) - XCTAssertEqual(testDoc._id, expectedInsertId) - - // get updated doc - let getResponse2 = try await couchDBClient.get( - fromDB: testsDB, - uri: expectedInsertId - ) - XCTAssertNotNil(getResponse2.body) - + let getResponse2 = try await couchDBClient.get(fromDB: testsDB, uri: expectedInsertId) let expectedBytes2 = getResponse2.headers.first(name: "content-length").flatMap(Int.init) ?? 1024 * 1024 * 10 var bytes2 = try await getResponse2.body.collect(upTo: expectedBytes2) let data2 = bytes2.readData(length: bytes2.readableBytes) - testDoc = try JSONDecoder().decode( - ExpectedDoc.self, - from: data2! - ) - - XCTAssertEqual(expectedName, testDoc.name) - - // Test delete doc - do { - let response = try await couchDBClient.delete( - fromDb: testsDB, - doc: testDoc - ) + testDoc = try JSONDecoder().decode(ExpectedDoc.self, from: data2!) + #expect(expectedName == testDoc.name) - XCTAssertEqual(response.ok, true) - XCTAssertNotNil(response.id) - XCTAssertNotNil(response.rev) - } catch let error { - XCTFail(error.localizedDescription) - } + let response = try await couchDBClient.delete(fromDb: testsDB, doc: testDoc) + #expect(response.ok == true) + #expect(!response.id.isEmpty) + #expect(!response.rev.isEmpty) } - func test05_InsertGetUpdateDelete() async throws { + @Test("Insert, Get, Update and Delete methods") + func insertGetUpdateDelete() async throws { var testDoc = ExpectedDoc(name: "test name") - var expectedInsertId: String = "" - var expectedInsertRev: String = "" - - // test Insert - do { - let insertEncodeData = try JSONEncoder().encode(testDoc) - let response = try await couchDBClient.insert( - dbName: testsDB, - body: .bytes(ByteBuffer(data: insertEncodeData)) - ) - - XCTAssertEqual(response.ok, true) - XCTAssertFalse(response.id.isEmpty) - XCTAssertFalse(response.rev.isEmpty) - - expectedInsertId = response.id - expectedInsertRev = response.rev - } catch let error { - XCTFail(error.localizedDescription) - } + let insertEncodeData = try JSONEncoder().encode(testDoc) + let response = try await couchDBClient.insert( + dbName: testsDB, + body: HTTPClientRequest.Body.bytes(ByteBuffer(data: insertEncodeData)) + ) + #expect(response.ok == true) + #expect(!response.id.isEmpty) + #expect(!response.rev.isEmpty) + + let expectedInsertId = response.id + let expectedInsertRev = response.rev - // Test Get var expectedName = testDoc.name - do { - let response = try await couchDBClient.get(fromDB: testsDB, uri: expectedInsertId) - XCTAssertNotNil(response.body) - - let expectedBytes = response.headers.first(name: "content-length").flatMap(Int.init) ?? 1024 * 1024 * 10 - var bytes = try await response.body.collect(upTo: expectedBytes) - let data = bytes.readData(length: bytes.readableBytes) - - testDoc = try JSONDecoder().decode( - ExpectedDoc.self, - from: data! - ) - - XCTAssertEqual(expectedName, testDoc.name) - XCTAssertEqual(testDoc._rev, expectedInsertRev) - XCTAssertEqual(testDoc._id, expectedInsertId) - } catch let error { - XCTFail(error.localizedDescription) - } + let getResponse = try await couchDBClient.get(fromDB: testsDB, uri: expectedInsertId) + let expectedBytes = getResponse.headers.first(name: "content-length").flatMap(Int.init) ?? 1024 * 1024 * 10 + var bytes = try await getResponse.body.collect(upTo: expectedBytes) + + let data = bytes.readData(length: bytes.readableBytes) + testDoc = try JSONDecoder().decode(ExpectedDoc.self, from: data!) + + #expect(expectedName == testDoc.name) + #expect(testDoc._rev == expectedInsertRev) + #expect(testDoc._id == expectedInsertId) - // Test update with body testDoc.name = "test name 2" expectedName = testDoc.name - do { - let updateEncodedData = try JSONEncoder().encode(testDoc) - let body: HTTPClientRequest.Body = .bytes(ByteBuffer(data: updateEncodedData)) - - let updateResponse = try await couchDBClient.update( - dbName: testsDB, - uri: expectedInsertId, - body: body - ) - - XCTAssertFalse(updateResponse.rev.isEmpty) - XCTAssertFalse(updateResponse.id.isEmpty) - XCTAssertNotEqual(updateResponse.rev, expectedInsertRev) - XCTAssertEqual(updateResponse.id, expectedInsertId) - - let getResponse = try await couchDBClient.get( - fromDB: testsDB, - uri: expectedInsertId - ) - XCTAssertNotNil(getResponse.body) - - let expectedBytes = getResponse.headers.first(name: "content-length").flatMap(Int.init) ?? 1024 * 1024 * 10 - var bytes = try await getResponse.body.collect(upTo: expectedBytes) - let data = bytes.readData(length: bytes.readableBytes) - - testDoc = try JSONDecoder().decode( - ExpectedDoc.self, - from: data! - ) - - XCTAssertEqual(expectedName, testDoc.name) - } catch let error { - XCTFail(error.localizedDescription) - } + let updateEncodedData = try JSONEncoder().encode(testDoc) + let body: HTTPClientRequest.Body = .bytes(ByteBuffer(data: updateEncodedData)) + let updateResponse = try await couchDBClient.update( + dbName: testsDB, + uri: expectedInsertId, + body: body + ) - // Test delete - do { - let response = try await couchDBClient.delete( - fromDb: testsDB, - uri: testDoc._id, - rev: testDoc._rev! - ) + #expect(!updateResponse.rev.isEmpty) + #expect(!updateResponse.id.isEmpty) + #expect(updateResponse.rev != expectedInsertRev) + #expect(updateResponse.id == expectedInsertId) - XCTAssertEqual(response.ok, true) - XCTAssertNotNil(response.id) - XCTAssertNotNil(response.rev) + let getResponse2 = try await couchDBClient.get(fromDB: testsDB, uri: expectedInsertId) + let expectedBytes2 = getResponse2.headers.first(name: "content-length").flatMap(Int.init) ?? 1024 * 1024 * 10 + var bytes2 = try await getResponse2.body.collect(upTo: expectedBytes2) - } catch let error { - XCTFail(error.localizedDescription) - } - } + let data2 = bytes2.readData(length: bytes2.readableBytes) + testDoc = try JSONDecoder().decode(ExpectedDoc.self, from: data2!) - func test06_BuildUrl() async { - let expectedUrl = "http://127.0.0.1:5984?key=testKey" - let url = await couchDBClient.buildUrl( - path: "", - query: [ - URLQueryItem(name: "key", value: "testKey") - ]) - XCTAssertEqual(url, expectedUrl) - } + #expect(expectedName == testDoc.name) - func test07_Auth() async throws { - let session: CreateSessionResponse? = try await couchDBClient.authIfNeed() - XCTAssertNotNil(session) - XCTAssertEqual(true, session?.ok) - let sessionCookieExpires = await couchDBClient.sessionCookieExpires - XCTAssertNotNil(sessionCookieExpires) + let deleteResponse = try await couchDBClient.delete( + fromDb: testsDB, + uri: testDoc._id, + rev: testDoc._rev! + ) + #expect(deleteResponse.ok == true) + #expect(!deleteResponse.id.isEmpty) + #expect(!deleteResponse.rev.isEmpty) } - func test08_find_with_body() async throws { - do { - let testDoc = ExpectedDoc(name: "Greg") - let insertEncodedData = try JSONEncoder().encode(testDoc) - let insertResponse = try await couchDBClient.insert( - dbName: testsDB, - body: .bytes(ByteBuffer(data: insertEncodedData)) - ) - - let selector = ["selector": ["name": "Greg"]] - let bodyData = try JSONEncoder().encode(selector) - let requestBody: HTTPClientRequest.Body = .bytes(ByteBuffer(data: bodyData)) - - let findResponse = try await couchDBClient.find( - inDB: testsDB, - body: requestBody - ) - - let body = findResponse.body - let expectedBytes = findResponse.headers.first(name: "content-length").flatMap(Int.init) - var bytes = try await body.collect(upTo: expectedBytes ?? 1024 * 1024 * 10) - - guard let data = bytes.readData(length: bytes.readableBytes) else { - throw CouchDBClientError.noData - } - - let decodedResponse = try JSONDecoder().decode(CouchDBFindResponse.self, from: data) - - XCTAssertTrue(decodedResponse.docs.count > 0) - XCTAssertTrue(decodedResponse.docs.contains(where: { $0._id == insertResponse.id })) - - _ = try await couchDBClient.delete( - fromDb: testsDB, - uri: insertResponse.id, - rev: insertResponse.rev - ) - } catch { - XCTFail(error.localizedDescription) - } + @Test("Authorization works. Should set session cookie") + func authorization() async throws { + let session: CreateSessionResponse = try #require( + try await couchDBClient.authIfNeed() + ) + #expect(session.ok == true) + + _ = try #require( + await couchDBClient.sessionCookieExpires + ) } - func test09_find_with_generics() async throws { - do { - let testDoc = ExpectedDoc(name: "Sam") - let insertEncodedData = try JSONEncoder().encode(testDoc) - let insertResponse = try await couchDBClient.insert( - dbName: testsDB, - body: .bytes(ByteBuffer(data: insertEncodedData)) - ) - - let selector = ["selector": ["name": "Sam"]] - let docs: [ExpectedDoc] = try await couchDBClient.find(inDB: testsDB, selector: selector) - - XCTAssertTrue(docs.count > 0) - XCTAssertEqual(docs.first!._id, insertResponse.id) - - _ = try await couchDBClient.delete( - fromDb: testsDB, - uri: docs.first!._id, - rev: docs.first!._rev! - ) - } catch { - XCTFail(error.localizedDescription) + @Test("Find request with providing body") + func find_with_body() async throws { + let testDoc = ExpectedDoc(name: "Greg") + let insertEncodedData = try JSONEncoder().encode(testDoc) + + let insertResponse = try await couchDBClient.insert( + dbName: testsDB, + body: HTTPClientRequest.Body.bytes(ByteBuffer(data: insertEncodedData)) + ) + + let selector = ["selector": ["name": "Greg"]] + let bodyData = try JSONEncoder().encode(selector) + let requestBody: HTTPClientRequest.Body = .bytes(ByteBuffer(data: bodyData)) + + let findResponse = try await couchDBClient.find( + inDB: testsDB, + body: requestBody + ) + + let expectedBytes = findResponse.headers.first(name: "content-length").flatMap(Int.init) + var bytes = try await findResponse.body.collect(upTo: expectedBytes ?? 1024 * 1024 * 10) + guard let data = bytes.readData(length: bytes.readableBytes) else { + throw CouchDBClientError.noData } - } - func test10_provide_HTTPClient() async throws { - let couchDBClient = CouchDBClient(config: config, httpClient: self.httpClient) + let decodedResponse = try JSONDecoder().decode(CouchDBFindResponse.self, from: data) - let httpClientProvided = await couchDBClient.httpClient - XCTAssertNotNil(httpClientProvided) + #expect(decodedResponse.docs.count > 0) + #expect(decodedResponse.docs.contains(where: { $0._id == insertResponse.id })) - let httpClientCreatedIfNeed = await couchDBClient.createHTTPClientIfNeed() - XCTAssertTrue(httpClientProvided === httpClientCreatedIfNeed) - XCTAssertTrue(httpClientProvided === self.httpClient) + _ = try await couchDBClient.delete( + fromDb: testsDB, + uri: insertResponse.id, + rev: insertResponse.rev + ) } - func test11_shutdown() async throws { - let client = CouchDBClient( - config: config, - httpClient: HTTPClient() + @Test("Find request using generics") + func find_with_generics() async throws { + let testDoc = ExpectedDoc(name: "Sam") + let insertEncodedData = try JSONEncoder().encode(testDoc) + + let insertResponse = try await couchDBClient.insert( + dbName: testsDB, + body: HTTPClientRequest.Body.bytes(ByteBuffer(data: insertEncodedData)) ) - do { - try await client.shutdown() - } catch { - XCTFail(error.localizedDescription) - } + let selector = ["selector": ["name": "Sam"]] + let docs: [ExpectedDoc] = try await couchDBClient.find(inDB: testsDB, selector: selector) + + #expect(docs.count > 0) + let id = try #require(docs.first?._id) + #expect(id == insertResponse.id) + + _ = try await couchDBClient.delete( + fromDb: testsDB, + uri: docs.first!._id, + rev: docs.first!._rev! + ) } - func test12_createDB_conflict() async throws { - // Try to create a DB that already exists, should throw insertError - do { + @Test("Create DB, handle conflict") + func createDB_conflict() async throws { + let error = await #expect(throws: CouchDBClientError.self) { _ = try await couchDBClient.createDB(testsDB) - XCTFail("Expected conflict error not thrown") - } catch CouchDBClientError.insertError(let error) { - XCTAssertTrue(error.error == "file_exists" || error.error == "conflict") - } catch { - XCTFail(error.localizedDescription) } + + #expect( + { + switch error { + case .insertError(let error): + return error.error == "file_exists" || error.error == "conflict" + default: return false + } + }(), "Expected CouchDBClientError.insertError") + } - func test13_deleteDB_not_found() async throws { + @Test("Delete non existing DB. Should throw deleteError") + func delete_non_existing_DB() async throws { let nonExistentDB = "db_should_not_exist" - do { + + let error = await #expect(throws: CouchDBClientError.self) { _ = try await couchDBClient.deleteDB(nonExistentDB) - XCTFail("Expected deleteError not thrown") - } catch CouchDBClientError.deleteError(let error) { - XCTAssertTrue(error.error == "not_found") - } catch { - XCTFail(error.localizedDescription) } + + #expect( + { + switch error { + case .deleteError(let error): + return error.error == "not_found" + default: return false + } + }(), "Expected CouchDBClientError.deleteError") } - func test14_get_document_not_found() async throws { - do { - let _: ExpectedDoc = try await couchDBClient.get(fromDB: testsDB, uri: "aaaaa") - } catch CouchDBClientError.notFound(_) { - // all good - } catch { - XCTFail(error.localizedDescription) - return - } - - do { - _ = try await couchDBClient.get(fromDB: testsDB, uri: "aaaaa") - } catch CouchDBClientError.notFound(_) { - return - } catch { - XCTFail(error.localizedDescription) - return - } - - XCTFail("Expected not found error") + @Test("Get non existing document. Should throw notFound") + func get_non_existing_document() async throws { + let error = await #expect(throws: CouchDBClientError.self) { + let _: ExpectedDoc = try await couchDBClient.get(fromDB: testsDB, uri: "aaaaa") + } + + #expect( + { + switch error { + case .notFound(_): return true + default: return false + } + }(), "Expected CouchDBClientError.notFound") } - func test15_update_document_conflict() async throws { + @Test("Update document without updating rev. Should throw conflict") + func update_document_conflict() async throws { let doc = ExpectedDoc(name: "should not exist", _id: "nonexistent_doc_id", _rev: "1-abc") var insertedDoc: ExpectedDoc! - do { + + let error = await #expect(throws: CouchDBClientError.self) { insertedDoc = try await couchDBClient.insert(dbName: testsDB, doc: doc) _ = try await couchDBClient.update(dbName: testsDB, doc: doc) - XCTFail("Expected conflictError not thrown") - } catch CouchDBClientError.conflictError(error: let error) { - XCTAssertTrue(error.error == "conflict") - } catch { - XCTFail(error.localizedDescription) } - do { - try await couchDBClient.delete(fromDb: testsDB, doc: insertedDoc) - } catch { - XCTFail("Could not cleanup inserted document: \(error.localizedDescription)") - } + #expect( + { + switch error { + case .conflictError(let error): + return error.error == "conflict" + default: return false + } + }(), "Expected CouchDBClientError.conflictError") + + _ = try await couchDBClient.delete(fromDb: testsDB, doc: insertedDoc) } - func test16_delete_document_not_found() async throws { + @Test("Delete non existing document. Should throw deleteError") + func delete_non_existing_document() async throws { let doc = ExpectedDoc(name: "should not exist", _id: "nonexistent_doc_id", _rev: "1-abc") - do { + + let error = await #expect(throws: CouchDBClientError.self) { _ = try await couchDBClient.delete(fromDb: testsDB, doc: doc) - XCTFail("Expected deleteError not thrown") - } catch CouchDBClientError.deleteError(let error) { - XCTAssertTrue(error.error == "not_found") - } catch { - XCTFail(error.localizedDescription) } + + #expect( + { + switch error { + case .deleteError(let error): + return error.error == "not_found" + default: return false + } + }(), "Expected CouchDBClientError.deleteError") } - func test17_getAllDBs_with_eventLoopGroup() async throws { + @Test("Call getAllDBs providing an EventLoopGroup") + func getAllDBs_with_eventLoopGroup() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - do { - let dbs = try await couchDBClient.getAllDBs(eventLoopGroup: group) - XCTAssertTrue(dbs.contains(testsDB)) - } catch { - XCTFail(error.localizedDescription) - } + let dbs = try await couchDBClient.getAllDBs(eventLoopGroup: group) + + #expect(dbs.contains(testsDB)) try await group.shutdownGracefully() } - func test18_dbExists_with_eventLoopGroup() async throws { + @Test("Call dbExists providing an EventLoopGroup") + func dbExists_with_eventLoopGroup() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) - do { - let exists = try await couchDBClient.dbExists(testsDB, eventLoopGroup: group) - XCTAssertTrue(exists) - } catch { - XCTFail(error.localizedDescription) - } + + #expect( + try await couchDBClient.dbExists(testsDB, eventLoopGroup: group) + ) try await group.shutdownGracefully() } - func test19_createDB_with_eventLoopGroup() async throws { + @Test("Calling createDB providing an EventLoopGroup") + func createDB_with_eventLoopGroup() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) let tempDB = "tempdb_for_eventloop" - defer { - } - do { - _ = try await couchDBClient.createDB(tempDB, eventLoopGroup: group) - let exists = try await couchDBClient.dbExists(tempDB) - XCTAssertTrue(exists) - } catch { - XCTFail(error.localizedDescription) - } + _ = try await couchDBClient.createDB(tempDB, eventLoopGroup: group) + + #expect( + try await couchDBClient.dbExists(tempDB) + ) try await couchDBClient.deleteDB(tempDB) try await group.shutdownGracefully() } + @Test("Calling deleteDB providing an EventLoopGroup") func test20_deleteDB_with_eventLoopGroup() async throws { let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) let tempDB = "tempdb_for_eventloop_delete" - do { - _ = try await couchDBClient.createDB(tempDB) - _ = try await couchDBClient.deleteDB(tempDB, eventLoopGroup: group) - let exists = try await couchDBClient.dbExists(tempDB) - XCTAssertFalse(exists) - } catch { - XCTFail(error.localizedDescription) - } + + _ = try await couchDBClient.createDB(tempDB) + _ = try await couchDBClient.deleteDB(tempDB, eventLoopGroup: group) + + #expect( + try await couchDBClient.dbExists(tempDB) == false + ) try await group.shutdownGracefully() } + @Test("Calling find with custom date decoding strategy") func test21_find_with_custom_date_decoding_strategy() async throws { let testDoc = ExpectedDoc(name: "DateTest") let insertEncodedData = try JSONEncoder().encode(testDoc) @@ -523,7 +388,7 @@ final class CouchDBClientTests: XCTestCase { dateDecodingStrategy: .iso8601 ) - XCTAssertTrue(docs.contains(where: { $0._id == insertResponse.id })) + #expect(docs.contains(where: { $0._id == insertResponse.id })) _ = try await couchDBClient.delete( fromDb: testsDB, @@ -532,11 +397,8 @@ final class CouchDBClientTests: XCTestCase { ) } - func test99_deleteDB() async throws { - do { - try await couchDBClient.deleteDB(testsDB) - } catch { - XCTFail(error.localizedDescription) - } + @Test("Cleanup: Delete Test Database") + func deleteDB() async throws { + try await couchDBClient.deleteDB(testsDB) } } diff --git a/Tests/CouchDBClientTests/HTTPClientTests.swift b/Tests/CouchDBClientTests/HTTPClientTests.swift new file mode 100644 index 0000000..4b5c375 --- /dev/null +++ b/Tests/CouchDBClientTests/HTTPClientTests.swift @@ -0,0 +1,59 @@ +// +// HTTPClientTests.swift +// couchdb-swift +// +// Created by Sergei Armodin on 09.07.2025. +// + +import Foundation +import Testing +import AsyncHTTPClient +@testable import CouchDBClient + + +@Suite("HTTP Client tests") +struct HTTPClientTests { + let config = CouchDBClient.Config( + couchProtocol: .http, + couchHost: "127.0.0.1", + couchPort: 5984, + userName: "admin", + userPassword: (ProcessInfo.processInfo.environment["COUCHDB_PASS"] ?? ""), + requestsTimeout: 30 + ) + + @Test("Test buildUrl method") + func buildUrl() async { + let couchDBClient = CouchDBClient(config: config) + let expectedUrl = "http://127.0.0.1:5984?key=testKey" + let url = await couchDBClient.buildUrl( + path: "", + query: [ + URLQueryItem(name: "key", value: "testKey") + ]) + #expect(url == expectedUrl) + } + + @Test("Provide own HTTPClient") + func provide_HTTPClient() async throws { + let httpClient = HTTPClient() + let couchDBClient = CouchDBClient(config: config, httpClient: httpClient) + + let httpClientProvided = try #require(await couchDBClient.httpClient) + + let httpClientCreatedIfNeed = await couchDBClient.createHTTPClientIfNeed() + #expect(httpClientProvided === httpClientCreatedIfNeed) + #expect(httpClientProvided === httpClient) + + try await httpClient.shutdown() + } + + @Test("HTTPClient shutdown") + func shutdown() async throws { + let client = CouchDBClient( + config: config, + httpClient: HTTPClient() + ) + try await client.shutdown() + } +}