-
Notifications
You must be signed in to change notification settings - Fork 115
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Codable Unit Tests for ProductImageStatus
, ProductImageAssetType
and ProductOrVariationID
#15253
base: feat/save-product-image-upload-statuses-in-user-defaults
Are you sure you want to change the base?
Add Codable Unit Tests for ProductImageStatus
, ProductImageAssetType
and ProductOrVariationID
#15253
Changes from all commits
0094572
0a5c32b
8c048aa
c2bcc6b
8d8f36c
0d1c2e2
57b3fd6
5dc5a42
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,86 @@ | ||||||
import XCTest | ||||||
import Photos | ||||||
@testable import Networking | ||||||
|
||||||
final class ProductImageAssetTypeCodableTests: XCTestCase { | ||||||
|
||||||
func test_uiImage_encodes_and_decodes_correctly() throws { | ||||||
// Given | ||||||
let assetType = ProductImageAssetType.uiImage( | ||||||
image: UIImage.checkmark, | ||||||
filename: "test_image.png", | ||||||
altText: "Test Alt Text" | ||||||
) | ||||||
|
||||||
// When | ||||||
let data = try JSONEncoder().encode(assetType) | ||||||
let decodedAssetType = try JSONDecoder().decode(ProductImageAssetType.self, from: data) | ||||||
|
||||||
// Then | ||||||
if case let .uiImage(decodedImage, decodedFilename, decodedAltText) = decodedAssetType { | ||||||
XCTAssertNotNil(decodedImage.pngData(), "Decoded image data should not be nil") | ||||||
XCTAssertEqual(decodedFilename, "test_image.png") | ||||||
XCTAssertEqual(decodedAltText, "Test Alt Text") | ||||||
} else { | ||||||
XCTFail("Decoded asset type should be .uiImage") | ||||||
} | ||||||
} | ||||||
|
||||||
func test_phAsset_decoding_fails_with_expected_error() throws { | ||||||
// Given | ||||||
let fakeLocalIdentifier = "this_is_a_fake_local_identifier" | ||||||
let mockAsset = PHAssetMock(localIdentifier: fakeLocalIdentifier) | ||||||
let assetType = ProductImageAssetType.phAsset(asset: mockAsset) | ||||||
|
||||||
// Then | ||||||
XCTAssertThrowsError(try { | ||||||
let data = try JSONEncoder().encode(assetType) | ||||||
_ = try JSONDecoder().decode(ProductImageAssetType.self, from: data) | ||||||
}()) { error in | ||||||
if case let DecodingError.dataCorrupted(context) = error { | ||||||
XCTAssertEqual(context.debugDescription, "No PHAsset found with localIdentifier \(fakeLocalIdentifier)") | ||||||
} else { | ||||||
XCTFail("Expected dataCorrupted error, got \(error)") | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
func test_decoding_unknown_asset_type_throwsError() throws { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// Given | ||||||
let jsonData = """ | ||||||
{ | ||||||
"type": "unknownType" | ||||||
} | ||||||
""".data(using: .utf8)! | ||||||
|
||||||
// Then | ||||||
XCTAssertThrowsError(try JSONDecoder().decode(ProductImageAssetType.self, from: jsonData)) { error in | ||||||
if case let DecodingError.dataCorrupted(context) = error { | ||||||
XCTAssertTrue(context.debugDescription.contains("Unknown type unknownType")) | ||||||
} else { | ||||||
XCTFail("Expected dataCorrupted error, but got: \(error)") | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
func test_invalid_Base64Data_for_UIImage_throwsError() throws { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// Given | ||||||
let jsonData = """ | ||||||
{ | ||||||
"type": "uiImage", | ||||||
"imageData": "INVALID_BASE64_DATA", | ||||||
"filename": "test.png", | ||||||
"altText": "Test Alt" | ||||||
} | ||||||
""".data(using: .utf8)! | ||||||
|
||||||
// Then | ||||||
XCTAssertThrowsError(try JSONDecoder().decode(ProductImageAssetType.self, from: jsonData)) { error in | ||||||
if case let DecodingError.dataCorrupted(context) = error { | ||||||
XCTAssertTrue(context.debugDescription.contains("Invalid image data")) | ||||||
} else { | ||||||
XCTFail("Expected dataCorrupted error, got \(error)") | ||||||
} | ||||||
} | ||||||
} | ||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,169 @@ | ||||||
// Start of Selection | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be deleted. |
||||||
import XCTest | ||||||
import Photos | ||||||
@testable import Networking | ||||||
|
||||||
final class ProductImageStatusCodableTests: XCTestCase { | ||||||
|
||||||
func test_uploading_encodes_and_decodes_correctly() throws { | ||||||
// Given | ||||||
let status = ProductImageStatus.uploading( | ||||||
asset: .uiImage(image: UIImage.checkmark, filename: "test.png", altText: "Test Alt"), | ||||||
siteID: 12345, | ||||||
productID: .product(id: 999) | ||||||
) | ||||||
|
||||||
// When | ||||||
let encodedData = try JSONEncoder().encode(status) | ||||||
let decodedStatus = try JSONDecoder().decode(ProductImageStatus.self, from: encodedData) | ||||||
|
||||||
// Then | ||||||
guard case let .uploading(.uiImage(decodedImage, decodedFilename, decodedAltText), siteID, productID) = decodedStatus else { | ||||||
return XCTFail("Decoded status should be .uploading with .uiImage asset") | ||||||
} | ||||||
|
||||||
XCTAssertNotNil(decodedImage.pngData(), "Decoded image data should not be nil") | ||||||
XCTAssertEqual(decodedFilename, "test.png") | ||||||
XCTAssertEqual(decodedAltText, "Test Alt") | ||||||
XCTAssertEqual(siteID, 12345) | ||||||
XCTAssertEqual(productID, .product(id: 999)) | ||||||
} | ||||||
|
||||||
func test_uploading_phAsset_decoding_fails_with_expected_error() throws { | ||||||
// Given | ||||||
let fakeLocalIdentifier = "4321" | ||||||
let mockAsset = PHAssetMock(localIdentifier: fakeLocalIdentifier) | ||||||
let status = ProductImageStatus.uploading( | ||||||
asset: .phAsset(asset: mockAsset), | ||||||
siteID: 11111, | ||||||
productID: .variation(productID: 43, variationID: 55) | ||||||
) | ||||||
|
||||||
// When | ||||||
let encodedData = try JSONEncoder().encode(status) | ||||||
|
||||||
// Then | ||||||
XCTAssertThrowsError(try JSONDecoder().decode(ProductImageStatus.self, from: encodedData)) { error in | ||||||
if case let DecodingError.dataCorrupted(context) = error { | ||||||
XCTAssertEqual(context.debugDescription, "No PHAsset found with localIdentifier \(fakeLocalIdentifier)") | ||||||
} else { | ||||||
XCTFail("Expected dataCorrupted error, but got: \(error)") | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
func test_remote_encodes_and_decodes_correctly() throws { | ||||||
// Given | ||||||
let testImage = ProductImage( | ||||||
imageID: 111, | ||||||
dateCreated: Date(), | ||||||
dateModified: nil, | ||||||
src: "https://example.com/image.png", | ||||||
name: "Example Image", | ||||||
alt: "Example Alt" | ||||||
) | ||||||
let status = ProductImageStatus.remote( | ||||||
image: testImage, | ||||||
siteID: 98765, | ||||||
productID: .variation(productID: 1, variationID: 2) | ||||||
) | ||||||
|
||||||
// When | ||||||
let encodedData = try JSONEncoder().encode(status) | ||||||
let decodedStatus = try JSONDecoder().decode(ProductImageStatus.self, from: encodedData) | ||||||
|
||||||
// Then | ||||||
guard case let .remote(image, siteID, productID) = decodedStatus else { | ||||||
return XCTFail("Decoded status should be .remote") | ||||||
} | ||||||
|
||||||
XCTAssertEqual(image.imageID, 111) | ||||||
XCTAssertEqual(image.src, "https://example.com/image.png") | ||||||
XCTAssertEqual(image.name, "Example Image") | ||||||
XCTAssertEqual(image.alt, "Example Alt") | ||||||
XCTAssertEqual(siteID, 98765) | ||||||
XCTAssertEqual(productID, .variation(productID: 1, variationID: 2)) | ||||||
} | ||||||
|
||||||
func test_uploadFailure_encodes_and_decodes_correctly() throws { | ||||||
// Given | ||||||
let error = NSError(domain: "TestErrorDomain", code: 404, userInfo: ["info": "not found"]) | ||||||
let status = ProductImageStatus.uploadFailure( | ||||||
asset: .uiImage(image: UIImage.checkmark, filename: "failure.png", altText: nil), | ||||||
error: error, | ||||||
siteID: 55555, | ||||||
productID: .product(id: 123) | ||||||
) | ||||||
|
||||||
// When | ||||||
let encodedData = try JSONEncoder().encode(status) | ||||||
let decodedStatus = try JSONDecoder().decode(ProductImageStatus.self, from: encodedData) | ||||||
|
||||||
// Then | ||||||
guard case let .uploadFailure(.uiImage(_, decodedFilename, decodedAltText), decodedError as NSError, siteID, productID) = decodedStatus else { | ||||||
return XCTFail("Decoded status should be .uploadFailure with .uiImage asset") | ||||||
} | ||||||
|
||||||
XCTAssertEqual(decodedFilename, "failure.png") | ||||||
XCTAssertNil(decodedAltText) | ||||||
XCTAssertEqual(decodedError.domain, "TestErrorDomain") | ||||||
XCTAssertEqual(decodedError.code, 404) | ||||||
XCTAssertEqual(decodedError.userInfo["info"] as? String, "not found") | ||||||
XCTAssertEqual(siteID, 55555) | ||||||
XCTAssertEqual(productID, .product(id: 123)) | ||||||
} | ||||||
|
||||||
func test_decoding_unknown_status_type_throwsError() throws { | ||||||
// Given | ||||||
let jsonString = """ | ||||||
{ | ||||||
"type": "unknownType", | ||||||
"siteID": 1234, | ||||||
"productID": { | ||||||
"type": "product", | ||||||
"id": 999 | ||||||
} | ||||||
} | ||||||
""" | ||||||
let data = jsonString.data(using: .utf8)! | ||||||
|
||||||
// Then | ||||||
XCTAssertThrowsError(try JSONDecoder().decode(ProductImageStatus.self, from: data)) { error in | ||||||
if case let DecodingError.dataCorrupted(context) = error { | ||||||
XCTAssertTrue(context.debugDescription.contains("Invalid type value")) | ||||||
} else { | ||||||
XCTFail("Expected dataCorrupted error, but got: \(error)") | ||||||
} | ||||||
} | ||||||
} | ||||||
|
||||||
func test_invalid_Base64Data_for_UIImage_throwsError() throws { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
// Given: | ||||||
let jsonString = """ | ||||||
{ | ||||||
"type": "uploading", | ||||||
"asset": { | ||||||
"type": "uiImage", | ||||||
"imageData": "INVALID_BASE64_DATA", | ||||||
"filename": "base64", | ||||||
"altText": "BASE64" | ||||||
}, | ||||||
"siteID": 111, | ||||||
"productID": { | ||||||
"type": "product", | ||||||
"id": 1 | ||||||
} | ||||||
} | ||||||
""" | ||||||
let data = jsonString.data(using: .utf8)! | ||||||
|
||||||
// When / Then | ||||||
XCTAssertThrowsError(try JSONDecoder().decode(ProductImageStatus.self, from: data)) { error in | ||||||
if case let DecodingError.dataCorrupted(context) = error { | ||||||
XCTAssertTrue(context.debugDescription.contains("Invalid image data")) | ||||||
} else { | ||||||
XCTFail("Expected dataCorrupted error, got \(error)") | ||||||
} | ||||||
} | ||||||
} | ||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to this PR: We should move the key string values
phAsset
anduiImage
fromProductImageAssetType
into a constant and reuse it while encoding and decoding. This will help avoid mistakingly editing these key values.The same comment applies for
ProductImageStatus
andProductOrVariationID
as well.