diff --git a/.github/workflows/qappleRepoCI.yml b/.github/workflows/qappleRepoCI.yml new file mode 100644 index 0000000..759eabb --- /dev/null +++ b/.github/workflows/qappleRepoCI.yml @@ -0,0 +1,33 @@ +# This workflow will build a Swift project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-swift + +name: Package Test + +on: + push: + branches: + - develop + - main + pull_request: + branches: + - develop + - main + +jobs: + test: + runs-on: macos-latest + + steps: + - name: Branch Checkout βœ” + uses: actions/checkout@v4 + + - name: Setup Swift πŸ¦‰ + uses: swift-actions/setup-swift@v2.2.0 + with: + swift-version: "6" + + - name: Build πŸ’» + run: swift build -v + + - name: Test πŸ§ͺ + run: TEST_URL=${{secrets.TEST_URL}} PRODUCTION_URL=${{secrets.PRODUCTION_URL}} PORT_NUM=${{secrets.PORT_NUM}} CERT_CODE=${{secrets.CERT_CODE}} swift test -v diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..88e669d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,28 @@ +name: Release Tag 🏷️ + +on: + push: + branches: + - main + + +jobs: + release: + runs-on: ubuntu-latest + + steps: + - name: Branch Checkout + uses: actions/checkout@v4 + + - name: 버전 정보 μΆ”μΆœ + run: echo "##[set-output name=version;]$(echo '${{ github.event.head_commit.message }}' | egrap -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}')" + id: extract_version_name + + - name: Release & Tag 생성 🏎️ + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + with: + tag_name: ${{steps.extract_version_name.outputs.version}} + release_name: ${{steps.extract_version_name.outputs.version}} + body: ${{github.event.pull_request.body}} diff --git a/Sources/QappleRepository/API/API.swift b/Sources/QappleRepository/API/API.swift index 178d7dc..2d8658f 100644 --- a/Sources/QappleRepository/API/API.swift +++ b/Sources/QappleRepository/API/API.swift @@ -52,10 +52,10 @@ extension RawRepresentable where RawValue == String, Self: API { let baseURLString = try BaseURL.fetch(from: server) guard let baseURL = URL(string: baseURLString)? .appendingPathComponent(Self.basePath) else { - throw APIError.invalidBaseUrl("BaseURL Error: \(baseURLString)") + throw RepositoryError.invalidBaseUrl("BaseURL Error: \(baseURLString)") } guard let url = URL(string: baseURL.absoluteString + rawValue) else { - throw APIError.invalidBaseUrl("RawValue Error: \(rawValue)") + throw RepositoryError.invalidBaseUrl("RawValue Error: \(rawValue)") } return url } diff --git a/Sources/QappleRepository/API/APIError.swift b/Sources/QappleRepository/API/APIError.swift deleted file mode 100644 index a069037..0000000 --- a/Sources/QappleRepository/API/APIError.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// APIError.swift -// Qapple-Repository -// -// Created by κΉ€λ―Όμ€€ on 2/9/25. -// - -import Foundation - -/// Repository μ—λŸ¬ μ—΄κ±°ν˜• -public enum APIError: Error { - - /// μœ νš¨ν•˜μ§€ μ•Šμ€ SecretKey μ—λŸ¬ - case invalidSecretKey(String) - - /// κΈ°λ³Έ URL μ—λŸ¬ - case invalidBaseUrl(String) -} diff --git a/Sources/QappleRepository/API/BaseURL.swift b/Sources/QappleRepository/API/BaseURL.swift index 071b4d2..41b5e06 100644 --- a/Sources/QappleRepository/API/BaseURL.swift +++ b/Sources/QappleRepository/API/BaseURL.swift @@ -25,18 +25,18 @@ enum BaseURL { static func fetch(from server: Server) throws -> String { if Bundle.main == Bundle(for: PackageClass.self) { guard let host = Bundle.main.infoDictionary?[serverKey(server)] as? String else { - throw APIError.invalidSecretKey("HOST_URL_\(server.rawValue.uppercased())") + throw RepositoryError.invalidSecretKey("HOST_URL_\(server.rawValue.uppercased())") } guard let port = Bundle.main.infoDictionary?["PORT_NUM"] as? String else { - throw APIError.invalidSecretKey("PORT_NUM") + throw RepositoryError.invalidSecretKey("PORT_NUM") } return "\(scheme)://\(host):\(port)" } else { guard let host = ProcessInfo.processInfo.environment[serverKey(server)] else { - throw APIError.invalidSecretKey("HOST_URL_\(server.rawValue.uppercased())") + throw RepositoryError.invalidSecretKey("HOST_URL_\(server.rawValue.uppercased())") } guard let port = ProcessInfo.processInfo.environment["PORT_NUM"] else { - throw APIError.invalidSecretKey("PORT_NUM") + throw RepositoryError.invalidSecretKey("PORT_NUM") } return "\(scheme)://\(host):\(port)" } diff --git a/Sources/QappleRepository/API/QappleAPI.swift b/Sources/QappleRepository/API/QappleAPI.swift index 7526ee7..e8bc2da 100644 --- a/Sources/QappleRepository/API/QappleAPI.swift +++ b/Sources/QappleRepository/API/QappleAPI.swift @@ -91,6 +91,12 @@ enum QappleAPI { /// νšŒμ›κ°€μž… case signUp + /// μ‚¬μš©μž 자격 λ³€κ²½ + case roleChange(role: String) + + /// 메일 인증 ν™”μ΄νŠΈ 리슀트 등둝 + case emailWhitelist(mail: String, whitelistDurationMinutes: Int64) + var rawValue: RawValue { switch self { case let .certification(signUpToken, email): @@ -139,9 +145,19 @@ enum QappleAPI { case .signUp: appending(baseString: "sign-up") + + case let .roleChange(role): + appending(baseString: "role/change", urlQueryItems: [ + .init(key: "role", value: role) + ]) + + case let .emailWhitelist(mail, whitelistDurationMinutes): + appending(baseString: "email/whitelist/register", urlQueryItems: [ + .init(key: "mail", value: mail), + .init(key: "whitelistDurationMinutes", value: whitelistDurationMinutes) + ]) } } - } // MARK: - Board diff --git a/Sources/QappleRepository/DTO/Answer/AnswerListOfMine.swift b/Sources/QappleRepository/DTO/Answer/AnswerListOfMine.swift index b5e8e5d..0759bf3 100644 --- a/Sources/QappleRepository/DTO/Answer/AnswerListOfMine.swift +++ b/Sources/QappleRepository/DTO/Answer/AnswerListOfMine.swift @@ -20,6 +20,7 @@ public struct AnswerListOfMine: Decodable, Sendable { public let answerId: Int public let writerId: Int public let nickname: String + public let writerGeneration: String public let profileImage: String? public let content: String public let heartCount: Int diff --git a/Sources/QappleRepository/DTO/Answer/AnswerListOfQuestion.swift b/Sources/QappleRepository/DTO/Answer/AnswerListOfQuestion.swift index 6c2e07f..5497e82 100644 --- a/Sources/QappleRepository/DTO/Answer/AnswerListOfQuestion.swift +++ b/Sources/QappleRepository/DTO/Answer/AnswerListOfQuestion.swift @@ -20,6 +20,7 @@ public struct AnswerListOfQuestion: Decodable, Sendable { public let writerId: Int public let profileImage: String? public let nickname: String + public let writerGeneration: String public let content: String public let isMine: Bool public let isReported: Bool diff --git a/Sources/QappleRepository/DTO/Board/BoardList.swift b/Sources/QappleRepository/DTO/Board/BoardList.swift index bf35d0d..dcd800c 100644 --- a/Sources/QappleRepository/DTO/Board/BoardList.swift +++ b/Sources/QappleRepository/DTO/Board/BoardList.swift @@ -19,6 +19,7 @@ public struct BoardList: Decodable, Sendable { public let boardId: Int public let writerId: Int public let writerNickname: String + public let writerGeneration: String public let content: String public let heartCount: Int public let commentCount: Int diff --git a/Sources/QappleRepository/DTO/Board/SearchBoard.swift b/Sources/QappleRepository/DTO/Board/SearchBoard.swift index c7dd113..50ffd4c 100644 --- a/Sources/QappleRepository/DTO/Board/SearchBoard.swift +++ b/Sources/QappleRepository/DTO/Board/SearchBoard.swift @@ -19,6 +19,7 @@ public struct SearchBoard: Decodable, Sendable { public let boardId: Int public let writerId: Int public let writerNickname: String + public let writerGeneration: String public let content: String public let heartCount: Int public let commentCount: Int diff --git a/Sources/QappleRepository/DTO/Board/SingleBoard.swift b/Sources/QappleRepository/DTO/Board/SingleBoard.swift index 77da9ff..50eea86 100644 --- a/Sources/QappleRepository/DTO/Board/SingleBoard.swift +++ b/Sources/QappleRepository/DTO/Board/SingleBoard.swift @@ -11,6 +11,7 @@ public struct SingleBoard: Decodable, Sendable { public let boardId: Int public let writerId: Int public let writerNickname: String + public let writerGeneration: String public let content: String public let heartCount: Int public let commentCount: Int diff --git a/Sources/QappleRepository/DTO/BoardComment/BoardCommentList.swift b/Sources/QappleRepository/DTO/BoardComment/BoardCommentList.swift index 89f715b..c07ef22 100644 --- a/Sources/QappleRepository/DTO/BoardComment/BoardCommentList.swift +++ b/Sources/QappleRepository/DTO/BoardComment/BoardCommentList.swift @@ -18,6 +18,7 @@ public struct BoardCommentList: Decodable, Sendable { public struct Content: Decodable, Sendable { public let boardCommentId: Int public let writerId: Int + public let writerGeneration: String public let content: String public let heartCount: Int public let isLiked: Bool diff --git a/Sources/QappleRepository/DTO/Member/RoleChange.swift b/Sources/QappleRepository/DTO/Member/RoleChange.swift new file mode 100644 index 0000000..3c3b2c6 --- /dev/null +++ b/Sources/QappleRepository/DTO/Member/RoleChange.swift @@ -0,0 +1,13 @@ +// +// RoleChange.swift +// QappleRepository +// +// Created by Simmons on 2/12/25. +// + +import Foundation + +public struct RoleChange: Decodable, Sendable { + public let accessToken: String + public let refreshToken: String +} diff --git a/Sources/QappleRepository/DTO/Notification/NotificationList.swift b/Sources/QappleRepository/DTO/Notification/NotificationList.swift index aa9222e..2db7d76 100644 --- a/Sources/QappleRepository/DTO/Notification/NotificationList.swift +++ b/Sources/QappleRepository/DTO/Notification/NotificationList.swift @@ -21,7 +21,9 @@ public struct NotificationList: Decodable, Sendable { public let subtitle: String? public let content: String? public let boardId: String? + public let isReportedBoard: Bool? public let questionId: String? + public let isResponsedQuestion: Bool? public let boardCommentId: String? public let createdAt: String } diff --git a/Sources/QappleRepository/Entity/MemberRole.swift b/Sources/QappleRepository/Entity/MemberRole.swift new file mode 100644 index 0000000..ca852c5 --- /dev/null +++ b/Sources/QappleRepository/Entity/MemberRole.swift @@ -0,0 +1,13 @@ +// +// MemberRole.swift +// QappleRepository +// +// Created by κΉ€λ―Όμ€€ on 3/1/25. +// + +import Foundation + +public enum MemberRole: String, Sendable { + case academier = "ROLE_ACADEMIER" + case admin = "ROLE_ADMIN" +} diff --git a/Sources/QappleRepository/Entity/PaginationInfo.swift b/Sources/QappleRepository/Entity/PaginationInfo.swift deleted file mode 100644 index 2324c3b..0000000 --- a/Sources/QappleRepository/Entity/PaginationInfo.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// PaginationInfo.swift -// Qapple-Repository -// -// Created by κΉ€λ―Όμ€€ on 2/9/25. -// - -import Foundation - -/// νŽ˜μ΄μ§€λ„€μ΄μ…˜ 정보 ꡬ쑰체 -public struct PaginationInfo: Equatable, Sendable { - public var threshold: String - public var hasNext: Bool -} diff --git a/Sources/QappleRepository/Entity/RepositoryError.swift b/Sources/QappleRepository/Entity/RepositoryError.swift new file mode 100644 index 0000000..b01c59d --- /dev/null +++ b/Sources/QappleRepository/Entity/RepositoryError.swift @@ -0,0 +1,58 @@ +// +// RepositoryError.swift +// Qapple-Repository +// +// Created by κΉ€λ―Όμ€€ on 2/9/25. +// + +import Foundation + +/// Repositoryμ—μ„œ λ°œμƒν•  수 μžˆλŠ” μ—λŸ¬λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. +public enum RepositoryError: Error, Sendable { + + /// μœ νš¨ν•˜μ§€ μ•Šμ€ SecretKey μ—λŸ¬ + case invalidSecretKey(String) + + /// κΈ°λ³Έ URL μ—λŸ¬ + case invalidBaseUrl(String) + + /// URLReuqest ν•¨μˆ˜ ν˜ΈμΆœμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. + case urlRequestFailure(urlString: String) + + /// μœ νš¨ν•˜μ§€ μ•Šμ€ Responseμž…λ‹ˆλ‹€. + case invalidResponse(urlString: String, statusCode: Int, message: String) + + /// 인증에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.(403 μ—λŸ¬) + case authenticationFailed + + /// Decoding에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. + case decodingFailure(type: Decodable.Type) + + /// μ‹€νŒ¨ Response + struct FailedResponse: Decodable { + let message: String + } +} + +// MARK: - LocalizedError + +extension RepositoryError: LocalizedError { + + /// μ—λŸ¬ λ©”μ‹œμ§€ + public var errorDescription: String? { + switch self { + case let .invalidSecretKey(string): + NSLocalizedString("\(string) μ„€μ • 였λ₯˜", comment: "invalidSecretKey") + case .invalidBaseUrl: + NSLocalizedString("BaseURL μ„€μ • 였λ₯˜", comment: "invalidBaseUrl") + case .urlRequestFailure: + NSLocalizedString("λ„€νŠΈμ›Œν¬ μš”μ²­ μ‹€νŒ¨", comment: "urlRequestFailure") + case let .invalidResponse(_, statusCode, message): + NSLocalizedString("잘λͺ»λœ λ„€νŠΈμ›Œν¬ 응닡: \(statusCode) + \(message)", comment: "invalidResponse") + case .authenticationFailed: + NSLocalizedString("μ•‘μ„ΈμŠ€ 토큰 만료: 403", comment: "authenticationFailed") + case let .decodingFailure(type): + NSLocalizedString("λ””μ½”λ”© μ‹€νŒ¨: \(type)", comment: "decodingFailure") + } + } +} diff --git a/Sources/QappleRepository/NetworkService/NetworkError.swift b/Sources/QappleRepository/NetworkService/NetworkError.swift deleted file mode 100644 index 9567f27..0000000 --- a/Sources/QappleRepository/NetworkService/NetworkError.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// NetworkError.swift -// Qapple-Repository -// -// Created by κΉ€λ―Όμ€€ on 2/9/25. -// - -import Foundation - -/// λ„€νŠΈμ›Œν‚Ήμ—μ„œ λ°œμƒν•  수 μžˆλŠ” μ—λŸ¬λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. -public enum NetworkError: Error { - - /// URLReuqest ν•¨μˆ˜ ν˜ΈμΆœμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. - case urlRequestFailure(urlString: String) - - /// μœ νš¨ν•˜μ§€ μ•Šμ€ Responseμž…λ‹ˆλ‹€. - case invalidResponse(urlString: String, statusCode: Int, message: String) - - /// 인증에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.(403 μ—λŸ¬) - case authenticationFailed - - /// Decoding에 μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€. - case decodingFailure(type: Decodable.Type) - - /// μ‹€νŒ¨ Response - struct FailedResponse: Decodable { - let message: String - } -} diff --git a/Sources/QappleRepository/NetworkService/NetworkService.swift b/Sources/QappleRepository/NetworkService/NetworkService.swift index c26bfdf..80ad7f7 100644 --- a/Sources/QappleRepository/NetworkService/NetworkService.swift +++ b/Sources/QappleRepository/NetworkService/NetworkService.swift @@ -80,7 +80,7 @@ extension NetworkService { return try await URLSession.shared.data(for: request) } catch { - throw NetworkError.urlRequestFailure(urlString: url.absoluteString) + throw RepositoryError.urlRequestFailure(urlString: url.absoluteString) } } @@ -127,13 +127,13 @@ extension NetworkService { let successStatusCodeRange = 200...299 guard successStatusCodeRange.contains(statusCode) else { if statusCode == 403 { - throw NetworkError.authenticationFailed + throw RepositoryError.authenticationFailed } else { let failedResponse = try? decoder.decode( - NetworkError.FailedResponse.self, + RepositoryError.FailedResponse.self, from: data ) - throw NetworkError.invalidResponse( + throw RepositoryError.invalidResponse( urlString: response.url?.absoluteString ?? "", statusCode: statusCode, message: failedResponse?.message ?? "μ—λŸ¬ λ©”μ‹œμ§€ μ—†μŒ" @@ -156,7 +156,7 @@ extension NetworkService { ) return decodedData.result } catch { - throw NetworkError.decodingFailure(type: T.self) + throw RepositoryError.decodingFailure(type: T.self) } } } diff --git a/Sources/QappleRepository/UseCase/MemberAPI.swift b/Sources/QappleRepository/UseCase/MemberAPI.swift index 1b2c10a..fd52205 100644 --- a/Sources/QappleRepository/UseCase/MemberAPI.swift +++ b/Sources/QappleRepository/UseCase/MemberAPI.swift @@ -158,4 +158,28 @@ public enum MemberAPI: Sendable { accessToken: accessToken ) } + + /// μ‚¬μš©μž 자격 λ³€κ²½ API μž…λ‹ˆλ‹€. + public static func roleChange( + role: MemberRole, + server: Server, + accessToken: String + ) async throws -> RoleChange { + let url = try QappleAPI.Member.roleChange(role: role.rawValue).url(from: server) + return try await NetworkService.get(url: url, accessToken: accessToken) + } + + /// 메일 인증 ν™”μ΄νŠΈ 리슀트 등둝 API μž…λ‹ˆλ‹€. + public static func emailWhitelist( + email: String, + durationMinutes: Int, + server: Server, + accessToken: String + ) async throws -> Bool { + let url = try QappleAPI.Member.emailWhitelist( + mail: email, + whitelistDurationMinutes: Int64(durationMinutes) + ).url(from: server) + return try await NetworkService.get(url: url, accessToken: accessToken) + } } diff --git a/Tests/QappleRepositoryTests/AdminQuestionAPITests.swift b/Tests/QappleRepositoryTests/AdminQuestionAPITests.swift index 54dbed8..8713b73 100644 --- a/Tests/QappleRepositoryTests/AdminQuestionAPITests.swift +++ b/Tests/QappleRepositoryTests/AdminQuestionAPITests.swift @@ -12,35 +12,25 @@ import Testing struct AdminQuestionAPITests { - @Test("Admin 질문 생성 ν…ŒμŠ€νŠΈ") + @Test("Admin 질문 생성 및 μ‚­μ œ ν…ŒμŠ€νŠΈ") func createQuestion() async throws { - let response = try await AdminQuestionAPI.createQuestion( - questionStatus: .pending, - content: "ν…ŒμŠ€νŠΈ μ§ˆλ¬Έμž…λ‹ˆλ‹€!", + let token = try await TestHelper.accessToken() + let _ = try await MemberAPI.roleChange( + role: .admin, server: .test, - accessToken: TestHelper.accessToken() + accessToken: token ) - - dump(response) - } - - @Test("Admin 질문 μ‚­μ œ ν…ŒμŠ€νŠΈ") - func deleteQuestion() async throws { - let token = try await TestHelper.accessToken() - let createResponse = try await AdminQuestionAPI.createQuestion( + let createQuestion = try await AdminQuestionAPI.createQuestion( questionStatus: .pending, content: "ν…ŒμŠ€νŠΈ μ§ˆλ¬Έμž…λ‹ˆλ‹€!", server: .test, accessToken: token ) - - let deleteResponse = try await AdminQuestionAPI.deleteQuestion( - questionId: createResponse.questionId, + let _ = try await AdminQuestionAPI.deleteQuestion( + questionId: createQuestion.questionId, server: .test, accessToken: token ) - - dump(deleteResponse) } // @Test("Admin 질문 CSV 파일 μ—…λ‘œλ“œ ν…ŒμŠ€νŠΈ") diff --git a/Tests/QappleRepositoryTests/AnswerAPITests.swift b/Tests/QappleRepositoryTests/AnswerAPITests.swift index d0bf391..0b22c47 100644 --- a/Tests/QappleRepositoryTests/AnswerAPITests.swift +++ b/Tests/QappleRepositoryTests/AnswerAPITests.swift @@ -11,61 +11,36 @@ import Foundation struct AnswerAPITests { - @Test("λ‚΄κ°€ μž‘μ„±ν•œ λ‹΅λ³€ 리슀트 ν…ŒμŠ€νŠΈ") + @Test("λ‹΅λ³€ 생성, λ‚΄ λ‹΅λ³€ 확인, λ‹΅λ³€ μ‚­μ œ ν…ŒμŠ€νŠΈ") func fetchListOfMine() async throws { let accessToken = try await TestHelper.accessToken() - let _ = try await AnswerAPI.create( + let create = try await AnswerAPI.create( content: "ν…ŒμŠ€νŠΈ λ‹΅λ³€", questionId: 1, server: .test, accessToken: accessToken ) - let response = try await AnswerAPI.fetchListOfMine( + let _ = try await AnswerAPI.fetchListOfMine( threshold: nil, pageSize: 30, server: .test, accessToken: accessToken ) - dump(response) - } - - @Test("λ‹΅λ³€ μ‚­μ œ ν…ŒμŠ€νŠΈ") - func delete() async throws { - let accessToken = try await TestHelper.accessToken() - let createAnswer = try await AnswerAPI.create( - content: "ν…ŒμŠ€νŠΈ λ‹΅λ³€", - questionId: 1, - server: .test, - accessToken: accessToken - ) - let deleteAnswer = try await AnswerAPI.delete( - answerId: createAnswer.answerId, + let _ = try await AnswerAPI.delete( + answerId: create.answerId, server: .test, accessToken: accessToken ) - dump(deleteAnswer) } @Test("μ§ˆλ¬Έμ— λŒ€ν•œ λ‹΅λ³€ 리슀트 ν…ŒμŠ€νŠΈ") func fetchListOfQuestion() async throws { - let response = try await AnswerAPI.fetchListOfQuestion( + let _ = try await AnswerAPI.fetchListOfQuestion( questionId: 1, threshold: nil, pageSize: 30, server: .test, accessToken: TestHelper.accessToken() ) - dump(response) - } - - @Test("λ‹΅λ³€ 생성 ν…ŒμŠ€νŠΈ") - func create() async throws { - let response = try await AnswerAPI.create( - content: "ν…ŒμŠ€νŠΈ λ‹΅λ³€", - questionId: 1, - server: .test, - accessToken: TestHelper.accessToken() - ) - dump(response) } } diff --git a/Tests/QappleRepositoryTests/BoardAPITests.swift b/Tests/QappleRepositoryTests/BoardAPITests.swift index 69777ec..e85b9fe 100644 --- a/Tests/QappleRepositoryTests/BoardAPITests.swift +++ b/Tests/QappleRepositoryTests/BoardAPITests.swift @@ -14,76 +14,58 @@ struct BoardAPITests { @Test("κ²Œμ‹œκΈ€ 리슀트 쑰회 ν…ŒμŠ€νŠΈ") func fetchList() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await BoardAPI.fetchList( + let _ = try await BoardAPI.fetchList( threshold: nil, pageSize: 30, server: .test, accessToken: accessToken ) - dump(response) - } - - @Test("κ²Œμ‹œκΈ€ 생성 ν…ŒμŠ€νŠΈ") - func create() async throws { - let accessToken = try await TestHelper.accessToken() - let response = try await BoardAPI.create( - content: "ν…ŒμŠ€νŠΈ κ²Œμ‹œκΈ€", - boardType: .freeBoard, - server: .test, - accessToken: accessToken - ) - dump(response) } @Test("κ²Œμ‹œκΈ€ 단건 쑰회 ν…ŒμŠ€νŠΈ") func fetchSingle() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await BoardAPI.fetchSingle( + let _ = try await BoardAPI.fetchSingle( boardId: 1, server: .test, accessToken: accessToken ) - dump(response) } - @Test("κ²Œμ‹œκΈ€ μ‚­μ œ ν…ŒμŠ€νŠΈ") - func delete() async throws { + @Test("κ²Œμ‹œκΈ€ 생성, μ’‹μ•„μš” 및 μ·¨μ†Œ, 검색, μ‚­μ œ ν…ŒμŠ€νŠΈ") + func create() async throws { let accessToken = try await TestHelper.accessToken() + let title = "ν…ŒμŠ€νŠΈ κ²Œμ‹œκΈ€" let create = try await BoardAPI.create( - content: "ν…ŒμŠ€νŠΈ κ²Œμ‹œκΈ€", + content: title, boardType: .freeBoard, server: .test, accessToken: accessToken ) - let delete = try await BoardAPI.fetchSingle( + let like = try await BoardAPI.like( boardId: create.boardId, server: .test, accessToken: accessToken ) - dump(delete) - } - - @Test("κ²Œμ‹œκΈ€ μ’‹μ•„μš” 및 μ·¨μ†Œ ν…ŒμŠ€νŠΈ") - func like() async throws { - let accessToken = try await TestHelper.accessToken() - let response = try await BoardAPI.like( - boardId: 1, + #expect(like.isLiked) + let disLike = try await BoardAPI.like( + boardId: create.boardId, server: .test, accessToken: accessToken ) - dump(response) - } - - @Test("κ²Œμ‹œκΈ€ 검색 ν…ŒμŠ€νŠΈ") - func search() async throws { - let accessToken = try await TestHelper.accessToken() - let response = try await BoardAPI.search( - keyword: "γ…‡", + #expect(!disLike.isLiked) + let search = try await BoardAPI.search( + keyword: title, threshold: nil, pageSize: 30, server: .test, accessToken: accessToken ) - dump(response) + #expect(!search.content.isEmpty) + let _ = try await BoardAPI.fetchSingle( + boardId: create.boardId, + server: .test, + accessToken: accessToken + ) } } diff --git a/Tests/QappleRepositoryTests/BoardCommentAPITests.swift b/Tests/QappleRepositoryTests/BoardCommentAPITests.swift index ae1d46d..1d7d667 100644 --- a/Tests/QappleRepositoryTests/BoardCommentAPITests.swift +++ b/Tests/QappleRepositoryTests/BoardCommentAPITests.swift @@ -14,63 +14,40 @@ struct BoardCommentAPITests { @Test("κ²Œμ‹œκΈ€ λŒ“κΈ€ 리슀트 쑰회 ν…ŒμŠ€νŠΈ") func fetchList() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await BoardCommentAPI.fetchList( + let _ = try await BoardCommentAPI.fetchList( boardId: 1, threshold: nil, pageSize: 30, server: .test, accessToken: accessToken ) - dump(response) } - @Test("κ²Œμ‹œκΈ€ λŒ“κΈ€ 생성 ν…ŒμŠ€νŠΈ") + @Test("κ²Œμ‹œκΈ€ λŒ“κΈ€ 생성, μ’‹μ•„μš” 및 μ·¨μ†Œ, μ‚­μ œ ν…ŒμŠ€νŠΈ") func create() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await BoardCommentAPI.create( + let create = try await BoardCommentAPI.create( boardId: 1, content: "ν…ŒμŠ€νŠΈ λŒ“κΈ€", server: .test, accessToken: accessToken ) - dump(response) - } - - @Test("κ²Œμ‹œκΈ€ λŒ“κΈ€ μ‚­μ œ ν…ŒμŠ€νŠΈ") - func delete() async throws { - let accessToken = try await TestHelper.accessToken() - // λŒ“κΈ€ 생성 - let createComment = try await BoardCommentAPI.create( - boardId: 1, - content: "μ‚­μ œν•  λŒ“κΈ€", + let like = try await BoardCommentAPI.like( + commentId: create.boardCommentId, server: .test, accessToken: accessToken ) - // μƒμ„±ν•œ λŒ“κΈ€ μ‚­μ œ - let deleteComment = try await BoardCommentAPI.delete( - commentId: createComment.boardCommentId, - server: .test, - accessToken: accessToken - ) - dump(deleteComment) - } - - @Test("κ²Œμ‹œκΈ€ λŒ“κΈ€ μ’‹μ•„μš” 및 μ·¨μ†Œ ν…ŒμŠ€νŠΈ") - func like() async throws { - let accessToken = try await TestHelper.accessToken() - // λŒ“κΈ€ 생성 - let createComment = try await BoardCommentAPI.create( - boardId: 1, - content: "μ’‹μ•„μš” ν…ŒμŠ€νŠΈ λŒ“κΈ€", + #expect(like.isLiked) + let disLike = try await BoardCommentAPI.like( + commentId: create.boardCommentId, server: .test, accessToken: accessToken ) - // μƒμ„±ν•œ λŒ“κΈ€μ— μ’‹μ•„μš” - let likeComment = try await BoardCommentAPI.like( - commentId: createComment.boardCommentId, + #expect(!disLike.isLiked) + let _ = try await BoardCommentAPI.delete( + commentId: create.boardCommentId, server: .test, accessToken: accessToken ) - dump(likeComment) } } diff --git a/Tests/QappleRepositoryTests/MemberAPITests.swift b/Tests/QappleRepositoryTests/MemberAPITests.swift index a43e394..771f573 100644 --- a/Tests/QappleRepositoryTests/MemberAPITests.swift +++ b/Tests/QappleRepositoryTests/MemberAPITests.swift @@ -11,104 +11,119 @@ import Foundation struct MemberAPITests { - @Test + @Test("ν…ŒμŠ€νŠΈμš© 둜컬 둜그인") func localSignIn() async throws { - let response = try await MemberAPI.localSignIn( - testId: "TEST_ID_\(Date.now.description)", + let _ = try await MemberAPI.localSignIn( + testId: "TEST_ID_\(UUID())", deviceToken: "TEST_DEVICE_TOKEN", server: .test ) - dump(response) } - @Test + @Test("ν…ŒμŠ€νŠΈμš© 둜컬 νšŒμ›κ°€μž…") func localSignUp() async throws { let refreshToken = try await MemberAPI.localSignIn( - testId: "TEST_ID_\(Date.now.description)", + testId: "TEST_ID_\(UUID())", deviceToken: "TEST_DEVICE_TOKEN", server: .test ).refreshToken - let response = try await MemberAPI.signUp( + let _ = try await MemberAPI.signUp( signUpToken: refreshToken, email: "email", nickname: "nickname", deviceToken: "deviceToken", server: .test ) - dump(response) } @Test("νšŒμ›κ°€μž… 인증메일 λ°œμ†‘ ν…ŒμŠ€νŠΈ") func sendCertificationEmail() async throws { let refreshToken = try await MemberAPI.localSignIn( - testId: "TEST_ID_\(Date.now.description)", + testId: "TEST_ID_\(UUID())", deviceToken: "TEST_DEVICE_TOKEN", server: .test ).refreshToken - let response = try await MemberAPI.sendCertificationEmail( + let _ = try await MemberAPI.sendCertificationEmail( signUpToken: refreshToken, email: "test@pos.idserve.net", server: .test ) - dump(response) } @Test("νšŒμ›κ°€μž… μΈμ¦μ½”λ“œ 확인 ν…ŒμŠ€νŠΈ") func checkAuthCode() async throws { - let refreshToken = try await MemberAPI.localSignIn( - testId: "TEST_ID_\(Date.now.description)", - deviceToken: "TEST_DEVICE_TOKEN", + let uuid = UUID() + let testEmail = "test\(uuid)date25@pos.idserve.net" + + let accessToken = try await TestHelper.accessToken() + let _ = try await MemberAPI.roleChange( + role: .admin, + server: .test, + accessToken: accessToken + ) + let _ = try await MemberAPI.emailWhitelist( + email: testEmail, + durationMinutes: 10, + server: .test, + accessToken: accessToken + ) + let localSignUp = try await MemberAPI.localSignIn( + testId: "TEST_ID_\(uuid)", + deviceToken: "TEST_DEVICE_TOKEN_\(uuid)", server: .test - ).refreshToken - let response = try await MemberAPI.checkAuthCode( - signUpToken: refreshToken, - email: "test@pos.idserve.net", - certCode: "123456", + ) + let _ = try await MemberAPI.sendCertificationEmail( + signUpToken: localSignUp.refreshToken, + email: testEmail, + server: .test + ) + guard let certCode = ProcessInfo.processInfo.environment["CERT_CODE"] else { + throw RepositoryError.invalidSecretKey("CERT_CODE") + } + let _ = try await MemberAPI.checkAuthCode( + signUpToken: localSignUp.refreshToken, + email: testEmail, + certCode: certCode, server: .test ) - dump(response) } @Test("λ‹‰λ„€μž„ 쀑볡 확인 ν…ŒμŠ€νŠΈ") func checkNicknameDuplicate() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await MemberAPI.checkNicknameDuplicate( + let _ = try await MemberAPI.checkNicknameDuplicate( nickname: "TestUser", server: .test, accessToken: accessToken ) - dump(response) } @Test("ν”„λ‘œν•„ 쑰회 ν…ŒμŠ€νŠΈ") func fetchProfile() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await MemberAPI.fetchProfile( + let _ = try await MemberAPI.fetchProfile( server: .test, accessToken: accessToken ) - dump(response) } @Test("ν”„λ‘œν•„ μˆ˜μ • ν…ŒμŠ€νŠΈ") func updateProfile() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await MemberAPI.updateProfile( - nickname: "User\(Int.random(in: 1...100))", + let _ = try await MemberAPI.updateProfile( + nickname: "User\(Int.random(in: 0...99999))", profileImage: nil, server: .test, accessToken: accessToken ) - dump(response) } @Test("νšŒμ›νƒˆν‡΄ ν…ŒμŠ€νŠΈ") func resign() async throws { let accessToken = try await TestHelper.accessToken() - let response = try await MemberAPI.resign( + let _ = try await MemberAPI.resign( server: .test, accessToken: accessToken ) - dump(response) } } diff --git a/Tests/QappleRepositoryTests/NotificationAPITests.swift b/Tests/QappleRepositoryTests/NotificationAPITests.swift index a275b01..a9660be 100644 --- a/Tests/QappleRepositoryTests/NotificationAPITests.swift +++ b/Tests/QappleRepositoryTests/NotificationAPITests.swift @@ -14,13 +14,11 @@ struct NotificationAPITests { @Test("μ•Œλ¦Ό 리슀트 쑰회 ν…ŒμŠ€νŠΈ") func fetchNotifications() async throws { - let response = try await NotificationAPI.fetchNotifications( + let _ = try await NotificationAPI.fetchNotifications( threshold: nil, pageSize: 25, server: .test, accessToken: TestHelper.accessToken() ) - - dump(response) } } diff --git a/Tests/QappleRepositoryTests/QuestionAPITests.swift b/Tests/QappleRepositoryTests/QuestionAPITests.swift index 170c0e2..375d80b 100644 --- a/Tests/QappleRepositoryTests/QuestionAPITests.swift +++ b/Tests/QappleRepositoryTests/QuestionAPITests.swift @@ -14,11 +14,12 @@ struct QuestionAPITests { @Test("질문 리슀트 쑰회 ν…ŒμŠ€νŠΈ") func fetchQuestions() async throws { + let accessToken = try await TestHelper.accessToken() let response = try await QuestionAPI.fetchQuestionList( threshold: nil, pageSize: 25, server: .test, - accessToken: TestHelper.accessToken() + accessToken: accessToken ) if response.hasNext { @@ -26,22 +27,18 @@ struct QuestionAPITests { threshold: response.threshold, pageSize: 25, server: .test, - accessToken: TestHelper.accessToken() + accessToken: accessToken ) #expect(response.content.first!.questionId != pagination.content.first!.questionId) } - - dump(response) } @Test("메인 질문 쑰회 ν…ŒμŠ€νŠΈ") func fetchMainQuestion() async throws { - let response = try await QuestionAPI.fetchMainQuestion( + let _ = try await QuestionAPI.fetchMainQuestion( server: .test, accessToken: TestHelper.accessToken() ) - - dump(response) } } diff --git a/Tests/QappleRepositoryTests/ReportTests.swift b/Tests/QappleRepositoryTests/ReportTests.swift index f6a5f47..bf2d8f4 100644 --- a/Tests/QappleRepositoryTests/ReportTests.swift +++ b/Tests/QappleRepositoryTests/ReportTests.swift @@ -20,13 +20,12 @@ struct ReportTests { server: .test, accessToken: accessToken ) - let response = try await ReportAPI.reportAnswer( + let _ = try await ReportAPI.reportAnswer( answerId: createAnswer.answerId, reportType: .ABUSIVE_LANGUAGE_AND_DISPARAGEMENT, server: .test, accessToken: accessToken ) - dump(response) } @Test("κ²Œμ‹œκΈ€ μ‹ κ³  ν…ŒμŠ€νŠΈ") @@ -38,25 +37,28 @@ struct ReportTests { server: .test, accessToken: accessToken ) - let response = try await ReportAPI.reportBoard( + let _ = try await ReportAPI.reportBoard( boardId: createBoard.boardId, reportType: .COMMERCIAL_ADVERTISING_AND_SALES, server: .test, accessToken: accessToken ) - dump(response) } @Test("κ²Œμ‹œκΈ€ λŒ“κΈ€ μ‹ κ³  ν…ŒμŠ€νŠΈ") func reportBoardComment() async throws { let accessToken = try await TestHelper.accessToken() - // TODO: ν…ŒμŠ€νŠΈ λŒ“κΈ€ API μ—°κ²° ν•„μš” - let response = try await ReportAPI.reportBoardComment( - boardCommentId: 0, + let _ = try await BoardCommentAPI.create( + boardId: 1, + content: "ν…ŒμŠ€νŠΈ λŒ“κΈ€", + server: .test, + accessToken: accessToken + ) + let _ = try await ReportAPI.reportBoardComment( + boardCommentId: 1, reportType: .DISTRIBUTION_OF_ILLEGAL_PHOTOGRAPHS, server: .test, accessToken: accessToken ) - dump(response) } } diff --git a/Tests/QappleRepositoryTests/TestHelper+.swift b/Tests/QappleRepositoryTests/TestHelper+.swift index b29540a..5063526 100644 --- a/Tests/QappleRepositoryTests/TestHelper+.swift +++ b/Tests/QappleRepositoryTests/TestHelper+.swift @@ -13,7 +13,7 @@ enum TestHelper { /// ν…ŒμŠ€νŠΈ 토큰을 λ°œκΈ‰ν•©λ‹ˆλ‹€. static func accessToken() async throws -> String { let localSignIn = try await MemberAPI.localSignIn( - testId: "TEST_ID_\(Date.now.description)", + testId: "TEST_ID_\(UUID())", deviceToken: "TEST_DEVICE_TOKEN", server: .test )