TF-4236 Fix partial-update validation for thread label ops.

This commit is contained in:
dab246
2026-01-26 10:22:39 +07:00
committed by Dat H. Pham
parent b633bcf709
commit a05a28700a
4 changed files with 22 additions and 11 deletions
@@ -19,6 +19,7 @@ class RemoveALabelFromEmailScenario extends BaseTestScenario
@override @override
Future<void> runTestLogic() async { Future<void> runTestLogic() async {
const emailUser = String.fromEnvironment('BASIC_AUTH_EMAIL'); const emailUser = String.fromEnvironment('BASIC_AUTH_EMAIL');
expect(emailUser, isNotEmpty, reason: 'BASIC_AUTH_EMAIL must be set');
final threadRobot = ThreadRobot($); final threadRobot = ThreadRobot($);
final emailRobot = EmailRobot($); final emailRobot = EmailRobot($);
+15 -7
View File
@@ -971,6 +971,10 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
List<EmailId> emailIds, List<EmailId> emailIds,
KeyWordIdentifier labelKeyword, KeyWordIdentifier labelKeyword,
) async { ) async {
if (emailIds.isEmpty) {
throw ArgumentError.value(emailIds, 'emailIds', 'must not be empty');
}
final method = SetEmailMethod(accountId) final method = SetEmailMethod(accountId)
..addUpdates(emailIds.generateMapUpdateObjectLabel(labelKeyword)); ..addUpdates(emailIds.generateMapUpdateObjectLabel(labelKeyword));
@@ -987,8 +991,9 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
); );
final emailIdsUpdated = response?.updated?.keys ?? <Id>[]; final emailIdsUpdated = response?.updated?.keys ?? <Id>[];
final ids = emailIds.map((emailId) => emailId.id); final ids = emailIds.toSetIds();
final isUpdated = emailIdsUpdated.every(ids.contains); final isUpdated = ids.every(emailIdsUpdated.contains) &&
emailIdsUpdated.length == ids.length;
if (emailIdsUpdated.isEmpty || !isUpdated) { if (emailIdsUpdated.isEmpty || !isUpdated) {
throw parseErrorForSetResponse(response, emailIds.first.id); throw parseErrorForSetResponse(response, emailIds.first.id);
@@ -1031,6 +1036,10 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
List<EmailId> emailIds, List<EmailId> emailIds,
KeyWordIdentifier labelKeyword, KeyWordIdentifier labelKeyword,
) async { ) async {
if (emailIds.isEmpty) {
throw ArgumentError.value(emailIds, 'emailIds', 'must not be empty');
}
final method = SetEmailMethod(accountId) final method = SetEmailMethod(accountId)
..addUpdates(emailIds.generateMapUpdateObjectLabel( ..addUpdates(emailIds.generateMapUpdateObjectLabel(
labelKeyword, labelKeyword,
@@ -1050,13 +1059,12 @@ class EmailAPI with HandleSetErrorMixin, MailAPIMixin {
); );
final emailIdsUpdated = response?.updated?.keys ?? <Id>[]; final emailIdsUpdated = response?.updated?.keys ?? <Id>[];
final ids = emailIds.map((emailId) => emailId.id); final ids = emailIds.toSetIds();
final isUpdated = emailIdsUpdated.every(ids.contains); final isUpdated = emailIdsUpdated.every(ids.contains) &&
emailIdsUpdated.length == ids.length;
if (emailIdsUpdated.isEmpty || !isUpdated) { if (emailIdsUpdated.isEmpty || !isUpdated) {
for (var id in emailIds) { throw parseErrorForSetResponse(response, emailIds.first.id);
throw parseErrorForSetResponse(response, id.id);
}
} }
} }
} }
@@ -32,11 +32,11 @@ extension MapKeywordsExtension on Map<KeyWordIdentifier, bool>? {
this?.remove(keyword); this?.remove(keyword);
} }
void toggleKeyword(KeyWordIdentifier keyword, bool remove) { void toggleKeyword(KeyWordIdentifier keyword, bool shouldRemove) {
if (remove) { if (shouldRemove) {
this?.remove(keyword); removeKeyword(keyword);
} else { } else {
this?[keyword] = true; addKeyword(keyword);
} }
} }
} }
@@ -9,6 +9,8 @@ extension ListEmailIdExtension on List<EmailId> {
List<Id> toIds() => map((emailId) => emailId.id).toList(); List<Id> toIds() => map((emailId) => emailId.id).toList();
Set<Id> toSetIds() => map((emailId) => emailId.id).toSet();
Map<Id, PatchObject> generateMapUpdateObjectMarkAsRead(ReadActions readActions) { Map<Id, PatchObject> generateMapUpdateObjectMarkAsRead(ReadActions readActions) {
return { return {
for (var emailId in this) for (var emailId in this)