按账号与相册隔离相机下载目录,并同步更新传输管道与测试。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@ -173,9 +173,10 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
),
|
||||
])
|
||||
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "DSC_PROGRESS.JPG")
|
||||
let scope = downloadScope(account: account, albumID: context.albumId)
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "DSC_PROGRESS.JPG", scope: scope)
|
||||
try Data(repeating: 0xAB, count: 16).write(to: fileURL)
|
||||
defer { try? FileManager.default.removeItem(at: fileURL) }
|
||||
defer { CameraDownloadStorage.removeScopeDirectory(for: scope) }
|
||||
|
||||
camera.downloadURL = fileURL
|
||||
camera.onNewAsset?(CameraAsset(id: "ptp_progress", filename: "DSC_PROGRESS.JPG", fileSize: 16))
|
||||
@ -253,9 +254,10 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
let account = AccountContext()
|
||||
account.applyLogin(profile: AccountProfile(userId: "101", displayName: "测试"))
|
||||
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "retry_failed.JPG")
|
||||
let scope = downloadScope(account: account, albumID: context.albumId)
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "retry_failed.JPG", scope: scope)
|
||||
try Data(repeating: 0xCD, count: 32).write(to: fileURL)
|
||||
defer { try? FileManager.default.removeItem(at: fileURL) }
|
||||
defer { CameraDownloadStorage.removeScopeDirectory(for: scope) }
|
||||
|
||||
let photoID = "persist_failed_001"
|
||||
let record = WiredTransferPhotoRecord(
|
||||
@ -304,12 +306,13 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
account.applyLogin(profile: AccountProfile(userId: userID, displayName: "测试"))
|
||||
|
||||
let fileURLs = try (1 ... 5).map { index in
|
||||
let url = CameraDownloadStorage.uniqueLocalURL(for: "batch_persist_\(index).JPG")
|
||||
let scope = downloadScope(account: account, albumID: albumID)
|
||||
let url = CameraDownloadStorage.uniqueLocalURL(for: "batch_persist_\(index).JPG", scope: scope)
|
||||
try Data(repeating: UInt8(0xA0 + index), count: 32).write(to: url)
|
||||
return url
|
||||
}
|
||||
defer {
|
||||
fileURLs.forEach { try? FileManager.default.removeItem(at: $0) }
|
||||
CameraDownloadStorage.removeScopeDirectory(for: downloadScope(account: account, albumID: albumID))
|
||||
}
|
||||
|
||||
let records = fileURLs.enumerated().map { offset, url in
|
||||
@ -410,18 +413,19 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
accountContext: account
|
||||
)
|
||||
|
||||
let imageURL = CameraDownloadStorage.uniqueLocalURL(for: "thumb_source.JPG")
|
||||
let scope = downloadScope(account: account, albumID: albumID)
|
||||
let imageURL = CameraDownloadStorage.uniqueLocalURL(for: "thumb_source.JPG", scope: scope)
|
||||
try makeJPEGData().write(to: imageURL)
|
||||
defer { try? FileManager.default.removeItem(at: imageURL) }
|
||||
defer { CameraDownloadStorage.removeScopeDirectory(for: scope) }
|
||||
|
||||
camera.downloadURL = imageURL
|
||||
camera.onNewAsset?(CameraAsset(id: "thumb_asset_001", filename: "thumb_source.JPG", fileSize: 128))
|
||||
try await Task.sleep(nanoseconds: 900_000_000)
|
||||
|
||||
let photo = try XCTUnwrap(viewModel.photos.first { $0.id == "thumb_asset_001" })
|
||||
XCTAssertTrue(photo.thumbnailURL.contains("/CameraDownloads/Thumbnails/"))
|
||||
XCTAssertTrue(photo.thumbnailURL.contains("/CameraDownloads/\(scope.accountDirectoryName)/\(albumID)/thumbnails/"))
|
||||
XCTAssertTrue(photo.previewURL.contains("/CameraDownloads/"))
|
||||
XCTAssertFalse(photo.previewURL.contains("/Thumbnails/"))
|
||||
XCTAssertFalse(photo.previewURL.contains("/thumbnails/"))
|
||||
}
|
||||
|
||||
/// 测试旧记录没有缩略图时列表先占位,但仍保留原图预览能力。
|
||||
@ -429,9 +433,10 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
let albumID = 6602
|
||||
let account = AccountContext()
|
||||
account.applyLogin(profile: AccountProfile(userId: "legacy-user", displayName: "测试"))
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "legacy_only.JPG")
|
||||
let scope = downloadScope(account: account, albumID: albumID)
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "legacy_only.JPG", scope: scope)
|
||||
try makeJPEGData().write(to: fileURL)
|
||||
defer { try? FileManager.default.removeItem(at: fileURL) }
|
||||
defer { CameraDownloadStorage.removeScopeDirectory(for: scope) }
|
||||
|
||||
let record = WiredTransferPhotoRecord(
|
||||
id: "legacy_photo_001",
|
||||
@ -471,6 +476,114 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
XCTAssertFalse(photo.previewURL.isEmpty)
|
||||
}
|
||||
|
||||
/// 测试清空相册时只删除当前账号当前相册的物理目录。
|
||||
func testClearAlbumRemovesOnlyCurrentAlbumScopedDirectory() throws {
|
||||
let userID = "clear-scope-user"
|
||||
let account = AccountContext()
|
||||
account.applyLogin(profile: AccountProfile(userId: userID, displayName: "测试"), accountType: "store")
|
||||
let albumID = 8801
|
||||
let otherAlbumID = 8802
|
||||
let scope = downloadScope(account: account, albumID: albumID)
|
||||
let otherScope = downloadScope(account: account, albumID: otherAlbumID)
|
||||
let scopeDirectoryPath = CameraDownloadStorage.scopeDirectory(for: scope).path
|
||||
let otherScopeDirectoryPath = CameraDownloadStorage.scopeDirectory(for: otherScope).path
|
||||
defer {
|
||||
CameraDownloadStorage.removeScopeDirectory(for: scope)
|
||||
CameraDownloadStorage.removeScopeDirectory(for: otherScope)
|
||||
}
|
||||
|
||||
let fileURL = CameraDownloadStorage.uniqueLocalURL(for: "clear_target.JPG", scope: scope)
|
||||
let otherFileURL = CameraDownloadStorage.uniqueLocalURL(for: "clear_other.JPG", scope: otherScope)
|
||||
try Data(repeating: 0x01, count: 8).write(to: fileURL)
|
||||
try Data(repeating: 0x02, count: 8).write(to: otherFileURL)
|
||||
|
||||
let store = WiredTransferPhotoStore(
|
||||
accountPrefixProvider: { account.accountCachePrefix ?? "guest_" },
|
||||
userIDProvider: { userID }
|
||||
)
|
||||
store.save(albumID: albumID, records: [
|
||||
makePhotoRecord(
|
||||
id: "clear_target",
|
||||
fileName: "clear_target.JPG",
|
||||
localPath: CameraDownloadStorage.relativePath(for: fileURL),
|
||||
albumID: albumID,
|
||||
userID: userID
|
||||
)
|
||||
])
|
||||
store.save(albumID: otherAlbumID, records: [
|
||||
makePhotoRecord(
|
||||
id: "clear_other",
|
||||
fileName: "clear_other.JPG",
|
||||
localPath: CameraDownloadStorage.relativePath(for: otherFileURL),
|
||||
albumID: otherAlbumID,
|
||||
userID: userID
|
||||
)
|
||||
])
|
||||
|
||||
let deletedIDs = store.clearAlbum(albumID: albumID)
|
||||
|
||||
XCTAssertEqual(deletedIDs, Set(["clear_target"]))
|
||||
XCTAssertFalse(FileManager.default.fileExists(atPath: fileURL.path))
|
||||
XCTAssertFalse(FileManager.default.fileExists(atPath: scopeDirectoryPath))
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: otherFileURL.path))
|
||||
XCTAssertTrue(FileManager.default.fileExists(atPath: otherScopeDirectoryPath))
|
||||
}
|
||||
|
||||
/// 测试同一用户 ID 在不同账号类型下的有线传图记录与物理目录隔离。
|
||||
func testSameUserIDDifferentAccountTypesAreIsolated() throws {
|
||||
let userID = "multi-account-user"
|
||||
let albumID = 8803
|
||||
let storeAccount = AccountContext()
|
||||
storeAccount.applyLogin(profile: AccountProfile(userId: userID, displayName: "门店"), accountType: "store")
|
||||
let photographerAccount = AccountContext()
|
||||
photographerAccount.applyLogin(profile: AccountProfile(userId: userID, displayName: "摄影师"), accountType: "photographer")
|
||||
let storeScope = downloadScope(account: storeAccount, albumID: albumID)
|
||||
let photographerScope = downloadScope(account: photographerAccount, albumID: albumID)
|
||||
defer {
|
||||
CameraDownloadStorage.removeScopeDirectory(for: storeScope)
|
||||
CameraDownloadStorage.removeScopeDirectory(for: photographerScope)
|
||||
}
|
||||
|
||||
let storeURL = CameraDownloadStorage.uniqueLocalURL(for: "store_photo.JPG", scope: storeScope)
|
||||
let photographerURL = CameraDownloadStorage.uniqueLocalURL(for: "photographer_photo.JPG", scope: photographerScope)
|
||||
try Data(repeating: 0x03, count: 8).write(to: storeURL)
|
||||
try Data(repeating: 0x04, count: 8).write(to: photographerURL)
|
||||
|
||||
let store = WiredTransferPhotoStore(
|
||||
accountPrefixProvider: { storeAccount.accountCachePrefix ?? "guest_" },
|
||||
userIDProvider: { userID }
|
||||
)
|
||||
let photographerStore = WiredTransferPhotoStore(
|
||||
accountPrefixProvider: { photographerAccount.accountCachePrefix ?? "guest_" },
|
||||
userIDProvider: { userID }
|
||||
)
|
||||
store.save(albumID: albumID, records: [
|
||||
makePhotoRecord(
|
||||
id: "store_photo",
|
||||
fileName: "store_photo.JPG",
|
||||
localPath: CameraDownloadStorage.relativePath(for: storeURL),
|
||||
albumID: albumID,
|
||||
userID: userID
|
||||
)
|
||||
])
|
||||
photographerStore.save(albumID: albumID, records: [
|
||||
makePhotoRecord(
|
||||
id: "photographer_photo",
|
||||
fileName: "photographer_photo.JPG",
|
||||
localPath: CameraDownloadStorage.relativePath(for: photographerURL),
|
||||
albumID: albumID,
|
||||
userID: userID
|
||||
)
|
||||
])
|
||||
|
||||
XCTAssertEqual(store.load(albumID: albumID).map(\.id), ["store_photo"])
|
||||
XCTAssertEqual(photographerStore.load(albumID: albumID).map(\.id), ["photographer_photo"])
|
||||
XCTAssertNotEqual(
|
||||
CameraDownloadStorage.scopeDirectory(for: storeScope).path,
|
||||
CameraDownloadStorage.scopeDirectory(for: photographerScope).path
|
||||
)
|
||||
}
|
||||
|
||||
private func makeViewModelWithPendingPhotos() -> WiredCameraTransferViewModel {
|
||||
let viewModel = WiredCameraTransferViewModel(
|
||||
context: WiredTransferContext(albumId: 1, albumName: "相册", phone: "", orderNumber: ""),
|
||||
@ -536,6 +649,10 @@ final class WiredCameraTransferViewModelTests: XCTestCase {
|
||||
context.fill(CGRect(x: 0, y: 0, width: 8, height: 8))
|
||||
}
|
||||
}
|
||||
|
||||
private func downloadScope(account: AccountContext, albumID: Int) -> CameraDownloadStorage.Scope {
|
||||
CameraDownloadStorage.Scope(accountKey: account.accountCachePrefix ?? "guest_", albumID: albumID)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
||||
Reference in New Issue
Block a user