// // SampleManagementAPITests.swift // suixinkanTests // import XCTest @testable import suixinkan /// 样片管理 API 测试。 @MainActor final class SampleManagementAPITests: XCTestCase { func testListBuildsAndroidQuery() async throws { let session = MockURLSession(responses: [envelopeJSON(#"{"total":0,"list":[],"order":null}"#)]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) _ = try await api.list(status: 4, keyword: "雪山", page: 2, pageSize: 10) let request = try XCTUnwrap(session.requests.first) XCTAssertEqual(request.httpMethod, "GET") XCTAssertEqual(request.url?.path, "/api/app/media-album/list") let query = URLComponents(url: try XCTUnwrap(request.url), resolvingAgainstBaseURL: false)?.queryItems XCTAssertEqual(query?.first { $0.name == "status" }?.value, "4") XCTAssertEqual(query?.first { $0.name == "keyword" }?.value, "雪山") XCTAssertEqual(query?.first { $0.name == "type" }?.value, "2") XCTAssertEqual(query?.first { $0.name == "page" }?.value, "2") XCTAssertEqual(query?.first { $0.name == "page_size" }?.value, "10") } func testListDecodesNullableOrderAmounts() async throws { let session = MockURLSession(responses: [envelopeJSON(#"{"total":0,"list":[],"order":{"avg_change":-1025,"avg_order_amount":null,"refund_total":null,"total_num":0}}"#)]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) let response = try await api.list(status: 4, keyword: nil, page: 1, pageSize: 10) XCTAssertEqual(response.order?.avgOrderAmount, "0") XCTAssertEqual(response.order?.refundTotal, "0") XCTAssertEqual(response.order?.avgChange, -1025) XCTAssertEqual(response.order?.totalNum, 0) } func testDetailAndOperationUseSampleType() async throws { let detail = envelopeJSON(#"{"id":8,"status":1,"listing_status":1}"#) let empty = envelopeJSON("{}") let session = MockURLSession(responses: [detail, empty]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) _ = try await api.detail(id: 8) try await api.setListingStatus(id: 8, listingStatus: 0) XCTAssertEqual(session.requests[0].url?.path, "/api/app/media-album/detail") let detailQuery = URLComponents(url: session.requests[0].url!, resolvingAgainstBaseURL: false)?.queryItems XCTAssertEqual(detailQuery?.first { $0.name == "id" }?.value, "8") XCTAssertEqual(detailQuery?.first { $0.name == "type" }?.value, "2") XCTAssertEqual(session.requests[1].httpMethod, "POST") XCTAssertEqual(session.requests[1].url?.path, "/api/app/media-album/operation") let operationQuery = URLComponents(url: session.requests[1].url!, resolvingAgainstBaseURL: false)?.queryItems XCTAssertEqual(operationQuery?.first { $0.name == "id" }?.value, "8") XCTAssertEqual(operationQuery?.first { $0.name == "listing_status" }?.value, "0") XCTAssertEqual(operationQuery?.first { $0.name == "type" }?.value, "2") } func testDetailMediaTypeZeroFallsBackToAlbumType() async throws { let detail = envelopeJSON(#"{"id":8,"type":2,"status":1,"listing_status":1,"media_list":[{"id":1,"type":0,"oss_url":"https://cdn/video.mp4"}]}"#) let session = MockURLSession(responses: [detail]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) let response = try await api.detail(id: 8) XCTAssertEqual(response.mediaList?.first?.resolvedType(fallback: response.type ?? 1), 2) } func testDetailDecodesUploadedAlbumMediaWithoutItemTypeOrCounters() async throws { let detail = envelopeJSON( #"{"id":113,"name":"样片相册iOS测试","type":1,"status":0,"listing_status":0,"project_price":0.01,"project_price_deposit":"0.00","media_list":[{"id":392,"original_name":"IMG_2741.jpeg","oss_url":"https://cdn/IMG_2741.jpeg","show_url":"https://cdn/IMG_274_cover.jpeg","thumbnail_url":"https://cdn/thumb.jpeg","size":285534,"width":828,"height":1792,"created_at":"2026-07-13 15:06:26","updated_at":"2026-07-13 15:06:26"}]}"# ) let session = MockURLSession(responses: [detail]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) let response = try await api.detail(id: 113) let media = try XCTUnwrap(response.mediaList?.first) XCTAssertEqual(response.projectPrice, 0.01) XCTAssertEqual(media.type, 0) XCTAssertEqual(media.likesCount, 0) XCTAssertEqual(media.downloadCount, 0) XCTAssertEqual(media.size, 285_534) XCTAssertEqual(media.width, 828) XCTAssertEqual(media.height, 1_792) XCTAssertEqual(media.resolvedType(fallback: response.type ?? 1), 1) } func testUploadEncodesAndroidBodyKeys() async throws { let session = MockURLSession(responses: [envelopeJSON("{}")]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) let request = SampleUploadMaterialRequest( name: "样片A", mediaType: .image, mediaList: [ SampleUploadMediaItem( originalName: "a.jpg", ossUrl: "https://cdn/a.jpg", size: 1024, fileWidthSize: SampleUploadFileSize(width: 1920, height: 1080) ), ], coverUrl: "https://cdn/c.jpg", coverSize: "512", scenicSpotId: 9, projectId: 7 ) try await api.upload(request) let urlRequest = try XCTUnwrap(session.requests.first) XCTAssertEqual(urlRequest.httpMethod, "POST") XCTAssertEqual(urlRequest.url?.path, "/api/app/media-album/upload") let body = try JSONSerialization.jsonObject(with: try XCTUnwrap(urlRequest.httpBody)) as? [String: Any] XCTAssertEqual(body?["name"] as? String, "样片A") XCTAssertEqual(body?["type"] as? String, "2") XCTAssertEqual(body?["media_type"] as? String, "1") XCTAssertEqual(body?["cover_url"] as? String, "https://cdn/c.jpg") XCTAssertEqual(body?["cover_size"] as? String, "512") XCTAssertEqual(body?["scenic_spot_id"] as? Int, 9) XCTAssertEqual(body?["material_tag"] as? String, "") XCTAssertEqual(body?["project_id"] as? Int, 7) let media = (body?["media_list"] as? [[String: Any]])?.first XCTAssertEqual(media?["original_name"] as? String, "a.jpg") XCTAssertEqual(media?["oss_url"] as? String, "https://cdn/a.jpg") XCTAssertEqual(media?["size"] as? Int, 1024) let fileSize = media?["file_width_size"] as? [String: Any] XCTAssertEqual(fileSize?["width"] as? Int, 1920) XCTAssertEqual(fileSize?["height"] as? Int, 1080) } func testUploadEncodesVideoMetadata() async throws { let session = MockURLSession(responses: [envelopeJSON("{}")]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) let request = SampleUploadMaterialRequest( name: "视频样片", mediaType: .video, mediaList: [ SampleUploadMediaItem( originalName: "rotated.mp4", ossUrl: "https://cdn/rotated.mp4", size: 4_096, fileWidthSize: SampleUploadFileSize(width: 1080, height: 1920) ), ], coverUrl: "https://cdn/cover.jpg", coverSize: "512", scenicSpotId: 9, projectId: 7 ) try await api.upload(request) let body = try JSONSerialization.jsonObject(with: try XCTUnwrap(session.requests.first?.httpBody)) as? [String: Any] XCTAssertEqual(body?["type"] as? String, "2") XCTAssertEqual(body?["media_type"] as? String, "2") let media = (body?["media_list"] as? [[String: Any]])?.first XCTAssertEqual(media?["original_name"] as? String, "rotated.mp4") XCTAssertEqual(media?["size"] as? Int, 4_096) let fileSize = media?["file_width_size"] as? [String: Any] XCTAssertEqual(fileSize?["width"] as? Int, 1080) XCTAssertEqual(fileSize?["height"] as? Int, 1920) } func testProjectAndScenicSpotPaths() async throws { let spots = envelopeJSON(#"{"total":1,"list":[]}"#) let projects = envelopeJSON(#"{"total":1,"list":[]}"#) let session = MockURLSession(responses: [spots, projects]) let api = SampleManagementAPI(client: APIClient(environment: .testing, session: session)) _ = try await api.scenicSpotListAll(scenicId: "12") _ = try await api.projectList(scenicId: "12", name: nil, page: 3, pageSize: 10) XCTAssertEqual(session.requests[0].url?.path, "/api/yf-handset-app/photog/scenic-spot/list-all") XCTAssertEqual(URLComponents(url: session.requests[0].url!, resolvingAgainstBaseURL: false)?.queryItems?.first { $0.name == "scenic_id" }?.value, "12") XCTAssertEqual(session.requests[1].url?.path, "/api/yf-handset-app/photog/project/list") let query = URLComponents(url: session.requests[1].url!, resolvingAgainstBaseURL: false)?.queryItems XCTAssertEqual(query?.first { $0.name == "scenic_id" }?.value, "12") XCTAssertEqual(query?.first { $0.name == "page" }?.value, "3") XCTAssertEqual(query?.first { $0.name == "page_size" }?.value, "10") } private func envelopeJSON(_ dataJSON: String) -> Data { """ {"code":100000,"msg":"success","data":\(dataJSON)} """.data(using: .utf8)! } }