// // TravelAlbumAPITests.swift // suixinkanTests // // Created by Codex on 2026/6/29. // import XCTest @testable import suixinkan @MainActor /// 旅拍相册 API 测试,覆盖 path、query 与 body。 final class TravelAlbumAPITests: XCTestCase { /// 测试列表与创建接口 path。 func testAlbumListAndCreateUseExpectedPaths() async throws { let session = TravelAlbumRecordingURLSession(responses: [Self.listResponse, Self.createResponse]) let api = TravelAlbumAPI(client: APIClient(session: session)) let list = try await api.albumList(page: 1, pageSize: 20) _ = try await api.createAlbum( TravelAlbumCreateRequest( name: "2026-06-29-001", type: 1, orderNumber: nil, materialNum: 3, materialPrice: 9.9, materialPackagePrice: 99, photoPrice: 0 ) ) XCTAssertEqual(session.requests.map { $0.url?.path }, [ "/api/yf-handset-app/photog/travel-album/list", "/api/yf-handset-app/photog/travel-album/create" ]) XCTAssertEqual(list.total, 1) XCTAssertEqual(list.list.first?.name, "2026-06-29-001") let createBody = try bodyObject(from: session.requests[1]) XCTAssertEqual(createBody["name"] as? String, "2026-06-29-001") XCTAssertEqual(createBody["type"] as? Int, 1) } /// 测试上传素材登记接口 body。 func testUploadMaterialUsesExpectedBody() async throws { let session = TravelAlbumRecordingURLSession(data: Self.uploadMaterialResponse) let api = TravelAlbumAPI(client: APIClient(session: session)) _ = try await api.uploadMaterial( TravelAlbumUploadMaterialRequest( userEquityTravelId: 8, fileName: "DSC_001.JPG", fileURL: "https://cdn/a.jpg" ) ) XCTAssertEqual(session.requests.first?.url?.path, "/api/yf-handset-app/photog/travel-album/upload-material") let body = try bodyObject(from: try XCTUnwrap(session.requests.first)) XCTAssertEqual(body["user_equity_travel_id"] as? Int, 8) XCTAssertEqual(body["file_name"] as? String, "DSC_001.JPG") XCTAssertEqual(body["file_url"] as? String, "https://cdn/a.jpg") } fileprivate static let listResponse = Data( #"{"code":100000,"msg":"success","data":{"total":"1","list":[{"id":"8","store_user_id":"1","name":"2026-06-29-001","type":"1","order_number":"","material_num":"3","material_price":"990","material_package_price":"9900","photo_price":"0","cover_url":"","user_id":"100","status":"1","created_at":"2026","updated_at":"2026","user":{"id":"200","phone":"13800138000"}}]}}"#.utf8 ) fileprivate static let createResponse = Data( #"{"code":100000,"msg":"success","data":{"id":"9"}}"#.utf8 ) fileprivate static let uploadMaterialResponse = Data( #"{"code":100000,"msg":"success","data":{"id":"11","user_equity_travel_id":"8","status":"1","order_number":"","user_id":"100","file_name":"DSC_001.JPG","file_type":"2","file_url":"https://cdn/a.jpg","file_size":"2048","cover_url":"","is_purchased":"0","created_at":"2026","updated_at":"2026"}}"#.utf8 ) } private final class TravelAlbumRecordingURLSession: URLSessionProtocol { private var responses: [Data] private(set) var requests: [URLRequest] = [] init(data: Data) { responses = [data] } init(responses: [Data]) { self.responses = responses } func data(for request: URLRequest) async throws -> (Data, URLResponse) { requests.append(request) let data = responses.isEmpty ? TravelAlbumAPITests.listResponse : responses.removeFirst() let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: nil, headerFields: nil)! return (data, response) } } private func bodyObject(from request: URLRequest) throws -> [String: Any] { let data = try XCTUnwrap(request.httpBody) return try XCTUnwrap(JSONSerialization.jsonObject(with: data) as? [String: Any]) }