修复样片详情解析并优化列表上下架交互。
兼容 Android 媒体项 size 字段格式,增强灵活解码容错,并统一样片列表开关确认逻辑与安全区布局。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -70,6 +70,26 @@ final class SampleManagementAPITests: XCTestCase {
|
||||
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))
|
||||
|
||||
@ -187,6 +187,19 @@ final class SampleManagementViewModelTests: XCTestCase {
|
||||
XCTAssertNil(viewModel.uploadDialogState)
|
||||
}
|
||||
|
||||
func testLateProgressCallbackCannotReopenCompletedUploadDialog() async {
|
||||
let viewModel = UploadSampleViewModel(currentScenicIdProvider: { 3 })
|
||||
let uploader = MockSampleUploader(urls: ["https://cdn/a.jpg"])
|
||||
uploader.emitLateProgress = true
|
||||
|
||||
await viewModel.addLocalMedia([makeUploadItem(fileName: "a.jpg")], uploader: uploader)
|
||||
try? await Task.sleep(nanoseconds: 1_000_000)
|
||||
|
||||
XCTAssertEqual(viewModel.uploadedMediaList.first?.ossUrl, "https://cdn/a.jpg")
|
||||
XCTAssertFalse(viewModel.uploadedMediaList.first?.isUploading ?? true)
|
||||
XCTAssertNil(viewModel.uploadDialogState)
|
||||
}
|
||||
|
||||
func testUploadValidationMessages() async {
|
||||
let api = MockSampleAPI()
|
||||
let viewModel = UploadSampleViewModel(
|
||||
@ -324,6 +337,7 @@ private final class MockSampleAPI: SampleManagementServing {
|
||||
@MainActor
|
||||
private final class MockSampleUploader: SampleOSSUploading {
|
||||
private var results: [Result<String, Error>]
|
||||
var emitLateProgress = false
|
||||
|
||||
init(urls: [String]) {
|
||||
results = urls.map(Result.success)
|
||||
@ -340,7 +354,15 @@ private final class MockSampleUploader: SampleOSSUploading {
|
||||
scenicId: Int,
|
||||
onProgress: @escaping (Int) -> Void
|
||||
) async throws -> String {
|
||||
onProgress(100)
|
||||
if emitLateProgress {
|
||||
onProgress(20)
|
||||
Task { @MainActor in
|
||||
await Task.yield()
|
||||
onProgress(100)
|
||||
}
|
||||
} else {
|
||||
onProgress(100)
|
||||
}
|
||||
guard !results.isEmpty else { return "https://cdn/default.jpg" }
|
||||
return try results.removeFirst().get()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user