// // CooperationOrderAPITests.swift // suixinkanTests // // Created by Codex on 2026/6/29. // import XCTest @testable import suixinkan @MainActor /// 合作订单 API 测试,覆盖解码与请求参数。 final class CooperationOrderAPITests: XCTestCase { /// 测试合作获客员列表解码与查询参数。 func testCooperativeSalersDecodesFixtureAndUsesExpectedQuery() async throws { let session = CooperationRecordingURLSession(data: try TestFixture.data(named: "cooperative_salers_success")) let api = CooperationOrderAPI(client: APIClient(session: session)) let response = try await api.cooperativeSalers(storeId: 66, page: 0, pageSize: 0) XCTAssertEqual(response.total, 1) XCTAssertEqual(response.page, 1) XCTAssertEqual(response.pageSize, 50) XCTAssertEqual(response.list.first?.displayId, 101) XCTAssertEqual(response.list.first?.displayName, "张哥") XCTAssertEqual(response.list.first?.photogRemarkName, "张哥") XCTAssertEqual(response.list.first?.salerRealName, "管铜彬") XCTAssertEqual(response.list.first?.displayPhone, "19281871413") XCTAssertEqual(response.list.first?.bindingId, 5) XCTAssertEqual(response.list.first?.shareCode, "INV-000101") XCTAssertEqual(response.list.first?.commissionRate, 15) XCTAssertEqual(response.list.first?.commissionRateLabel, "15%") XCTAssertEqual(response.list.first?.displayCommissionRate, "15%") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.httpMethod, "GET") XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/cooperative-salers") let query = cooperationQueryItems(from: request) XCTAssertEqual(query["store_id"], "66") XCTAssertEqual(query["page"], "1") XCTAssertEqual(query["page_size"], "1") } /// 测试获客员邀请信息解码。 func testSaleUserInviteInfoDecodesFixture() async throws { let session = CooperationRecordingURLSession(data: try TestFixture.data(named: "sale_user_invite_info_success")) let api = CooperationOrderAPI(client: APIClient(session: session)) let info = try await api.saleUserInviteInfo(saleUserId: 101) XCTAssertEqual(info.saleUserId, 101) XCTAssertFalse(info.isBound) XCTAssertEqual(info.photographerName, "陆久银") XCTAssertEqual(info.photographerPhone, "18651857230") XCTAssertEqual(info.salerName, "管铜彬") XCTAssertEqual(info.salerPhone, "13222319413") XCTAssertEqual(info.shareCode, "INV-000101") XCTAssertEqual(info.subtitle, "确认与获客员绑定,可以绑定多个获客员") XCTAssertEqual(info.title, "绑定获客员") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/invite-info") XCTAssertEqual(cooperationQueryItems(from: request)["sale_user_id"], "101") } /// 测试获客线索列表解码。 func testSaleUserReferralLeadsDecodesFixture() async throws { let session = CooperationRecordingURLSession(data: try TestFixture.data(named: "sale_user_referral_leads_success")) let api = CooperationOrderAPI(client: APIClient(session: session)) let response = try await api.saleUserReferralLeads(page: 2, pageSize: 20) XCTAssertEqual(response.total, 1) XCTAssertEqual(response.list.first?.displayId, "501") XCTAssertEqual(response.list.first?.displayPhone, "13900000002") XCTAssertEqual(response.list.first?.displaySalerName, "张哥") XCTAssertEqual(response.list.first?.remark, "景区门口获客") XCTAssertEqual(response.list.first?.displayImages.first, "https://example.com/lead.jpg") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/referral-leads") let query = cooperationQueryItems(from: request) XCTAssertEqual(query["page"], "2") XCTAssertEqual(query["page_size"], "20") } /// 测试获客订单列表 keyword 查询。 func testSaleUserAcquisitionOrdersDecodesFixtureWithKeyword() async throws { let session = CooperationRecordingURLSession(data: try TestFixture.data(named: "sale_user_acquisition_orders_success")) let api = CooperationOrderAPI(client: APIClient(session: session)) let response = try await api.saleUserAcquisitionOrders(keyword: "138", page: 1, pageSize: 10) XCTAssertEqual(response.total, 1) XCTAssertEqual(response.list.first?.orderNumber, "ACQ001") XCTAssertEqual(response.list.first?.displayAmount, "299.00") XCTAssertEqual(response.list.first?.displayCustomerPhone, "13700000003") XCTAssertEqual(response.list.first?.displayPartner, "张三") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/acquisition-orders") let query = cooperationQueryItems(from: request) XCTAssertEqual(query["keyword"], "138") } /// 测试可绑定带客单列表解码。 func testSaleUserReferralOrdersDecodesFixture() async throws { let session = CooperationRecordingURLSession(data: try TestFixture.data(named: "sale_user_referral_orders_success")) let api = CooperationOrderAPI(client: APIClient(session: session)) let response = try await api.saleUserReferralOrders(saleUserId: 101, page: 1, pageSize: 10) XCTAssertEqual(response.total, 1) XCTAssertEqual(response.page, 1) XCTAssertEqual(response.pageSize, 50) XCTAssertEqual(response.saler?.saleUserId, 101) XCTAssertEqual(response.saler?.salerName, "管铜彬") XCTAssertEqual(response.list.first?.id, 54) XCTAssertEqual(response.list.first?.displayReferralOrderNo, "LD202606250009") XCTAssertEqual(response.list.first?.userMobile, "13322222223") XCTAssertEqual(response.list.first?.remark, "带客备注") XCTAssertEqual(response.list.first?.statusLabel, "未完成") XCTAssertEqual(response.list.first?.images.count, 1) let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/referral-orders") let query = cooperationQueryItems(from: request) XCTAssertEqual(query["sale_user_id"], "101") } /// 测试接受绑定、绑定带客单与更新状态请求。 func testSaleUserAcceptBindAndBindReferralOrderUsePostQuery() async throws { let emptyData = try TestFixture.data(named: "empty_success") let acceptSession = CooperationRecordingURLSession(data: emptyData) let acceptAPI = CooperationOrderAPI(client: APIClient(session: acceptSession)) try await acceptAPI.saleUserAcceptBind(saleUserId: 101, commissionRate: 20) let acceptRequest = try XCTUnwrap(acceptSession.requests.last) XCTAssertEqual(acceptRequest.httpMethod, "POST") XCTAssertEqual(acceptRequest.url?.path, "/api/yf-handset-app/photog/sale-user/accept-bind") let acceptQuery = cooperationQueryItems(from: acceptRequest) XCTAssertEqual(acceptQuery["sale_user_id"], "101") XCTAssertEqual(acceptQuery["commission_rate"], "20") XCTAssertNil(acceptRequest.httpBody) let bindSession = CooperationRecordingURLSession(data: emptyData) let bindAPI = CooperationOrderAPI(client: APIClient(session: bindSession)) try await bindAPI.saleUserBindReferralOrder(orderNumber: "ORD001", referralOrderNo: "REF001") let bindRequest = try XCTUnwrap(bindSession.requests.last) XCTAssertEqual(bindRequest.httpMethod, "POST") XCTAssertEqual(bindRequest.url?.path, "/api/yf-handset-app/photog/sale-user/bind-referral-order") let bindQuery = cooperationQueryItems(from: bindRequest) XCTAssertEqual(bindQuery["order_number"], "ORD001") XCTAssertEqual(bindQuery["referral_order_no"], "REF001") XCTAssertNil(bindRequest.httpBody) let statusSession = CooperationRecordingURLSession(data: emptyData) let statusAPI = CooperationOrderAPI(client: APIClient(session: statusSession)) try await statusAPI.saleUserUpdateReferralOrderStatus(id: 15, status: 3) let statusRequest = try XCTUnwrap(statusSession.requests.last) XCTAssertEqual(statusRequest.httpMethod, "POST") XCTAssertEqual( statusRequest.url?.path, "/api/yf-handset-app/photog/sale-user/referral-orders/15/status" ) let statusBody = try XCTUnwrap(statusRequest.httpBody) let statusJSON = try XCTUnwrap(JSONSerialization.jsonObject(with: statusBody) as? [String: Any]) XCTAssertEqual(statusJSON["status"] as? Int, 3) } /// 测试更新合作获客员备注名请求体。 func testSaleUserUpdateRemarkUsesPostBody() async throws { let emptyData = try TestFixture.data(named: "empty_success") let session = CooperationRecordingURLSession(data: emptyData) let api = CooperationOrderAPI(client: APIClient(session: session)) try await api.saleUserUpdateRemark(saleUserId: 123, photogRemarkName: "张哥") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.httpMethod, "POST") XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/update-remark") let body = try XCTUnwrap(request.httpBody) let json = try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any]) XCTAssertEqual(json["sale_user_id"] as? Int, 123) XCTAssertEqual(json["photog_remark_name"] as? String, "张哥") } /// 测试发送修改分成比例验证码请求体。 func testSaleUserCommissionRateSendSmsCodeUsesPostBody() async throws { let emptyData = try TestFixture.data(named: "empty_success") let session = CooperationRecordingURLSession(data: emptyData) let api = CooperationOrderAPI(client: APIClient(session: session)) try await api.saleUserCommissionRateSendSmsCode(phone: "19281871413") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.httpMethod, "POST") XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/commission-rate/send-sms-code") let body = try XCTUnwrap(request.httpBody) let json = try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any]) XCTAssertEqual(json["phone"] as? String, "19281871413") } /// 测试更新合作获客员分成比例请求体与响应解码。 func testSaleUserUpdateCommissionRateUsesPostBodyAndDecodesResponse() async throws { let responseData = Data( """ { "code": 100000, "msg": "success", "data": { "binding_id": 5, "sale_user_id": 101, "share_code": "INV-000101", "name": "管铜彬", "saler_name": "管铜彬", "phone": "19281871413", "phone_masked": "192****1413", "saler_phone": "19281871413", "photog_commission_rate": 20, "commission_rate": 20, "commission_rate_label": "20%", "bind_time": "2026-06-25 17:11:01", "bound_at": "2026-06-25 17:11:01", "store_id": 37 } } """.utf8 ) let session = CooperationRecordingURLSession(data: responseData) let api = CooperationOrderAPI(client: APIClient(session: session)) let updated = try await api.saleUserUpdateCommissionRate( saleUserId: 101, commissionRate: 20, smsCode: "889900" ) XCTAssertEqual(updated.saleUserId, 101) XCTAssertEqual(updated.commissionRate, 20) XCTAssertEqual(updated.commissionRateLabel, "20%") XCTAssertEqual(updated.displayCommissionRate, "20%") let request = try XCTUnwrap(session.requests.last) XCTAssertEqual(request.httpMethod, "POST") XCTAssertEqual(request.url?.path, "/api/yf-handset-app/photog/sale-user/commission-rate") let body = try XCTUnwrap(request.httpBody) let json = try XCTUnwrap(JSONSerialization.jsonObject(with: body) as? [String: Any]) XCTAssertEqual(json["sale_user_id"] as? Int, 101) XCTAssertEqual(json["commission_rate"] as? Int, 20) XCTAssertEqual(json["sms_code"] as? String, "889900") } } /// URLSession 测试替身,记录合作订单 API 请求。 private final class CooperationRecordingURLSession: URLSessionProtocol { let data: Data var statusCode: Int private(set) var requests: [URLRequest] = [] init(data: Data, statusCode: Int = 200) { self.data = data self.statusCode = statusCode } /// 返回固定数据和 HTTP 响应。 func data(for request: URLRequest) async throws -> (Data, URLResponse) { requests.append(request) return ( data, HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil)! ) } } private func cooperationQueryItems(from request: URLRequest) -> [String: String] { guard let url = request.url, let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return [:] } return Dictionary(uniqueKeysWithValues: (components.queryItems ?? []).compactMap { item in item.value.map { (item.name, $0) } }) }