From d991c8b02454d9d0d5415bb9eae7be83ffc945ef Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Fri, 22 May 2026 17:30:28 +0000 Subject: [PATCH 1/9] Set fuzzer author as reporter for bugs from external fuzzers --- scripts/migrate_fuzzer_owners.py | 36 +++++++++++++++++++ src/appengine/handlers/fuzzers.py | 2 ++ .../components/fuzzers-page/edit-form.html | 8 +++++ .../_internal/datastore/data_types.py | 3 ++ .../_internal/issue_management/issue_filer.py | 6 +++- .../tests/appengine/libs/issue_filer_test.py | 35 ++++++++++++++++++ 6 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 scripts/migrate_fuzzer_owners.py diff --git a/scripts/migrate_fuzzer_owners.py b/scripts/migrate_fuzzer_owners.py new file mode 100644 index 00000000000..0593a382b02 --- /dev/null +++ b/scripts/migrate_fuzzer_owners.py @@ -0,0 +1,36 @@ +import argparse +import sys +import os + +# Ensure the local project root is in the path +sys.path.append(os.path.join(os.path.dirname(__file__), '../src')) + +from google.cloud import ndb +from clusterfuzz._internal.datastore import data_types +from clusterfuzz._internal.base import helpers + +def migrate(dry_run=True, default_owner='default-owner@example.com'): + """Migrates fuzzer owners.""" + query = data_types.Fuzzer.query( + ndb.OR(data_types.Fuzzer.primary_owner == None, + data_types.Fuzzer.primary_owner == '')) + + count = 0 + for fuzzer in query.fetch(): + print(f"Processing fuzzer: {fuzzer.name}") + if not dry_run: + fuzzer.primary_owner = default_owner + fuzzer.put() + helpers.log(f"Backfilled primary_owner for {fuzzer.name} to {default_owner}", helpers.MODIFY_OPERATION) + count += 1 + + mode = "DRY RUN" if dry_run else "LIVE" + print(f"{mode}: Processed {count} fuzzers.") + +if __name__ == '__main__': + parser = argparse.ArgumentParser(description='Backfill primary_owner for Fuzzers.') + parser.add_argument('--live', action='store_true', help='Perform live updates.') + parser.add_argument('--owner', default='default-owner@example.com', help='Default owner email.') + args = parser.parse_args() + + migrate(dry_run=not args.live, default_owner=args.owner) diff --git a/src/appengine/handlers/fuzzers.py b/src/appengine/handlers/fuzzers.py index e1f5e1e1466..015b0ebc10c 100644 --- a/src/appengine/handlers/fuzzers.py +++ b/src/appengine/handlers/fuzzers.py @@ -177,6 +177,7 @@ def apply_fuzzer_changes(self, fuzzer, upload_info): differential = request.get('differential', False) environment_string = request.get('additional_environment_string') data_bundle_name = request.get('data_bundle_name') + primary_owner = request.get('primary_owner') # Save the fuzzer file metadata. if upload_info: @@ -193,6 +194,7 @@ def apply_fuzzer_changes(self, fuzzer, upload_info): fuzzer.sample_testcase = None fuzzer.console_output = None fuzzer.external_contribution = bool(external_contribution) + fuzzer.primary_owner = primary_owner fuzzer.differential = bool(differential) fuzzer.additional_environment_string = environment_string fuzzer.timestamp = datetime.datetime.now(tz=datetime.timezone.utc).replace( diff --git a/src/appengine/private/components/fuzzers-page/edit-form.html b/src/appengine/private/components/fuzzers-page/edit-form.html index fb1e41886e8..9b3b8173918 100644 --- a/src/appengine/private/components/fuzzers-page/edit-form.html +++ b/src/appengine/private/components/fuzzers-page/edit-form.html @@ -112,6 +112,13 @@ title="Name of fuzzer. Allowed characters include letters, numbers, dashes and underscores."> +
+ + +
Date: Fri, 12 Jun 2026 20:07:11 +0000 Subject: [PATCH 2/9] Add license. Modify locations. --- scripts/migrate_fuzzer_owners.py | 36 ----------- .../_internal/datastore/data_types.py | 2 +- .../butler/scripts/migrate_fuzzer_owners.py | 63 +++++++++++++++++++ 3 files changed, 64 insertions(+), 37 deletions(-) delete mode 100644 scripts/migrate_fuzzer_owners.py create mode 100644 src/local/butler/scripts/migrate_fuzzer_owners.py diff --git a/scripts/migrate_fuzzer_owners.py b/scripts/migrate_fuzzer_owners.py deleted file mode 100644 index 0593a382b02..00000000000 --- a/scripts/migrate_fuzzer_owners.py +++ /dev/null @@ -1,36 +0,0 @@ -import argparse -import sys -import os - -# Ensure the local project root is in the path -sys.path.append(os.path.join(os.path.dirname(__file__), '../src')) - -from google.cloud import ndb -from clusterfuzz._internal.datastore import data_types -from clusterfuzz._internal.base import helpers - -def migrate(dry_run=True, default_owner='default-owner@example.com'): - """Migrates fuzzer owners.""" - query = data_types.Fuzzer.query( - ndb.OR(data_types.Fuzzer.primary_owner == None, - data_types.Fuzzer.primary_owner == '')) - - count = 0 - for fuzzer in query.fetch(): - print(f"Processing fuzzer: {fuzzer.name}") - if not dry_run: - fuzzer.primary_owner = default_owner - fuzzer.put() - helpers.log(f"Backfilled primary_owner for {fuzzer.name} to {default_owner}", helpers.MODIFY_OPERATION) - count += 1 - - mode = "DRY RUN" if dry_run else "LIVE" - print(f"{mode}: Processed {count} fuzzers.") - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description='Backfill primary_owner for Fuzzers.') - parser.add_argument('--live', action='store_true', help='Perform live updates.') - parser.add_argument('--owner', default='default-owner@example.com', help='Default owner email.') - args = parser.parse_args() - - migrate(dry_run=not args.live, default_owner=args.owner) diff --git a/src/clusterfuzz/_internal/datastore/data_types.py b/src/clusterfuzz/_internal/datastore/data_types.py index 9025c0b7f9f..6f61ed661f9 100644 --- a/src/clusterfuzz/_internal/datastore/data_types.py +++ b/src/clusterfuzz/_internal/datastore/data_types.py @@ -347,7 +347,7 @@ class Fuzzer(Model): # reward flags. external_contribution = ndb.BooleanProperty(default=False) - # Primary owner for VRP reasons. + # Primary owner to be reported for bugs filed by CF primary_owner = ndb.StringProperty() # Max testcases to generate for this fuzzer. diff --git a/src/local/butler/scripts/migrate_fuzzer_owners.py b/src/local/butler/scripts/migrate_fuzzer_owners.py new file mode 100644 index 00000000000..47b766d1713 --- /dev/null +++ b/src/local/butler/scripts/migrate_fuzzer_owners.py @@ -0,0 +1,63 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +"""Script to migrate fuzzer owners.""" + +import argparse +import os +import sys + +# Ensure the local project root is in the path +sys.path.append(os.path.join(os.path.dirname(__file__), '../src')) +sys.path.append(os.path.join(os.path.dirname(__file__), '../src/appengine')) + +from google.cloud import ndb + +from clusterfuzz._internal.datastore import data_types +from libs import helpers + + +def migrate(dry_run=True, default_owner='default-owner@example.com'): + """Migrates fuzzer owners.""" + query = data_types.Fuzzer.query( + ndb.OR( + data_types.Fuzzer.primary_owner == None, # pylint: disable=singleton-comparison + data_types.Fuzzer.primary_owner == '')) + + count = 0 + for fuzzer in query.fetch(): + print(f"Processing fuzzer: {fuzzer.name}") + if not dry_run: + fuzzer.primary_owner = default_owner + fuzzer.put() + helpers.log( + f"Backfilled primary_owner for {fuzzer.name} to {default_owner}", + helpers.MODIFY_OPERATION) + count += 1 + + mode = "DRY RUN" if dry_run else "LIVE" + print(f"{mode}: Processed {count} fuzzers.") + + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='Backfill primary_owner for Fuzzers.') + parser.add_argument( + '--live', action='store_true', help='Perform live updates.') + parser.add_argument( + '--owner', + default='default-owner@example.com', + help='Default owner email.') + args = parser.parse_args() + + migrate(dry_run=not args.live, default_owner=args.owner) From 62e10a8ffdeb6f5db380b0c221f831e37121d895 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Mon, 15 Jun 2026 18:47:45 +0000 Subject: [PATCH 3/9] Migrate from monorail to buganizer. --- .../tests/appengine/libs/issue_filer_test.py | 74 ++++++++++++++----- 1 file changed, 55 insertions(+), 19 deletions(-) diff --git a/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py b/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py index 6a8c3072c6f..3f7e40833e2 100644 --- a/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py +++ b/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py @@ -655,37 +655,73 @@ def test_filed_issues_oss_fuzz_disable_disclose(self): def test_filed_issues_external_fuzzer_author(self): """Tests issue filing for external fuzzer author.""" self.mock.get.return_value = CHROMIUM_POLICY - + data_types.Fuzzer( name='fuzzer', external_contribution=True, primary_owner='owner@example.com').put() - - issue_tracker = monorail.IssueTracker(IssueTrackerManager('chromium')) - + + issue_tracker = google_issue_tracker.IssueTracker('chromium', mock.Mock(), { + 'default_component_id': '123', + 'url': 'mock' + }) + + # Intercept new_issue so we can capture the saved issue object. + original_new_issue = issue_tracker.new_issue + + def mock_new_issue(): + issue = original_new_issue() + + def mock_save(*args, **kwargs): + issue_tracker.last_issue = issue + issue._data['issueId'] = 12345 + + issue.save = mock_save + return issue + + issue_tracker.new_issue = mock_new_issue + self.testcase1.security_flag = True self.testcase1.put() - - issue_filer.file_issue(self.testcase1, issue_tracker, user_email='reporter@example.com') - - self.assertEqual('owner@example.com', issue_tracker._itm.last_issue.reporter) + + issue_filer.file_issue( + self.testcase1, issue_tracker, user_email='reporter@example.com') + + self.assertEqual('owner@example.com', issue_tracker.last_issue.reporter) def test_filed_issues_external_fuzzer_no_author(self): """Tests issue filing for external fuzzer without author.""" self.mock.get.return_value = CHROMIUM_POLICY - - data_types.Fuzzer( - name='fuzzer', - external_contribution=True).put() - - issue_tracker = monorail.IssueTracker(IssueTrackerManager('chromium')) - + + data_types.Fuzzer(name='fuzzer', external_contribution=True).put() + + issue_tracker = google_issue_tracker.IssueTracker('chromium', mock.Mock(), { + 'default_component_id': '123', + 'url': 'mock' + }) + + # Intercept new_issue so we can capture the saved issue object. + original_new_issue = issue_tracker.new_issue + + def mock_new_issue(): + issue = original_new_issue() + + def mock_save(*args, **kwargs): + issue_tracker.last_issue = issue + issue._data['issueId'] = 12345 + + issue.save = mock_save + return issue + + issue_tracker.new_issue = mock_new_issue + self.testcase1.security_flag = True self.testcase1.put() - - issue_filer.file_issue(self.testcase1, issue_tracker, user_email='reporter@example.com') - - self.assertEqual('reporter@example.com', issue_tracker._itm.last_issue.reporter) + + issue_filer.file_issue( + self.testcase1, issue_tracker, user_email='reporter@example.com') + + self.assertEqual('reporter@example.com', issue_tracker.last_issue.reporter) def test_testcase_metadata_labels_and_components(self): """Tests issue filing with additional labels and components.""" From 4024e3e3dc33bc10cca3f961128d89a75340c0f9 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Thu, 16 Jul 2026 13:39:52 +0000 Subject: [PATCH 4/9] Resolve comments on issue filing - issue_filer.py Get fuzzer from DataStore query, handles cases when no `primary_owner` is found, does not consider yet `external_contribution. - migrate_fuzzer_owners.py Removes the owner --- .../_internal/issue_management/issue_filer.py | 11 +++++++---- src/local/butler/scripts/migrate_fuzzer_owners.py | 4 ---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/clusterfuzz/_internal/issue_management/issue_filer.py b/src/clusterfuzz/_internal/issue_management/issue_filer.py index 631595da45d..61d454463f0 100644 --- a/src/clusterfuzz/_internal/issue_management/issue_filer.py +++ b/src/clusterfuzz/_internal/issue_management/issue_filer.py @@ -320,7 +320,12 @@ def file_issue(testcase, logs.info(f'Filing new issue for testcase: {testcase.key.id()}.') policy = issue_tracker_policy.get(issue_tracker.project) - fuzzer = None + try: + fuzzer = data_types.Fuzzer.query( + data_types.Fuzzer.name == testcase.fuzzer_name).get() + except Exception: + fuzzer = None + is_crash = not utils.sub_string_exists_in(NON_CRASH_TYPES, testcase.crash_type) properties = policy.get_new_issue_properties( @@ -344,8 +349,6 @@ def file_issue(testcase, if issue_tracker.project in ('chromium', 'chromium-testing'): if testcase.security_flag: # Add reward labels if this is from an external fuzzer contribution. - fuzzer = data_types.Fuzzer.query( - data_types.Fuzzer.name == testcase.fuzzer_name).get() if fuzzer and fuzzer.external_contribution: issue.labels.add(policy.substitution_mapping('reward-topanel')) issue.labels.add( @@ -483,7 +486,7 @@ def file_issue(testcase, testcase.one_time_crasher_flag and policy.unreproducible_component): issue.components.add(policy.unreproducible_component) - if fuzzer and fuzzer.external_contribution and fuzzer.primary_owner: + if fuzzer and getattr(fuzzer, 'primary_owner', None): issue.reporter = fuzzer.primary_owner else: issue.reporter = user_email diff --git a/src/local/butler/scripts/migrate_fuzzer_owners.py b/src/local/butler/scripts/migrate_fuzzer_owners.py index 47b766d1713..4ee15009a41 100644 --- a/src/local/butler/scripts/migrate_fuzzer_owners.py +++ b/src/local/butler/scripts/migrate_fuzzer_owners.py @@ -54,10 +54,6 @@ def migrate(dry_run=True, default_owner='default-owner@example.com'): description='Backfill primary_owner for Fuzzers.') parser.add_argument( '--live', action='store_true', help='Perform live updates.') - parser.add_argument( - '--owner', - default='default-owner@example.com', - help='Default owner email.') args = parser.parse_args() migrate(dry_run=not args.live, default_owner=args.owner) From 5a498d20c0b9405a09e651a856f6eb77a61229a2 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Thu, 23 Jul 2026 13:33:55 +0000 Subject: [PATCH 5/9] Remove migrate and correct comment --- .../components/fuzzers-page/edit-form.html | 2 +- .../butler/scripts/migrate_fuzzer_owners.py | 59 ------------------- 2 files changed, 1 insertion(+), 60 deletions(-) delete mode 100644 src/local/butler/scripts/migrate_fuzzer_owners.py diff --git a/src/appengine/private/components/fuzzers-page/edit-form.html b/src/appengine/private/components/fuzzers-page/edit-form.html index 9b3b8173918..2be9bc73755 100644 --- a/src/appengine/private/components/fuzzers-page/edit-form.html +++ b/src/appengine/private/components/fuzzers-page/edit-form.html @@ -116,7 +116,7 @@ + title="Primary owner for VRP rewards.">
diff --git a/src/local/butler/scripts/migrate_fuzzer_owners.py b/src/local/butler/scripts/migrate_fuzzer_owners.py deleted file mode 100644 index 4ee15009a41..00000000000 --- a/src/local/butler/scripts/migrate_fuzzer_owners.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2026 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""Script to migrate fuzzer owners.""" - -import argparse -import os -import sys - -# Ensure the local project root is in the path -sys.path.append(os.path.join(os.path.dirname(__file__), '../src')) -sys.path.append(os.path.join(os.path.dirname(__file__), '../src/appengine')) - -from google.cloud import ndb - -from clusterfuzz._internal.datastore import data_types -from libs import helpers - - -def migrate(dry_run=True, default_owner='default-owner@example.com'): - """Migrates fuzzer owners.""" - query = data_types.Fuzzer.query( - ndb.OR( - data_types.Fuzzer.primary_owner == None, # pylint: disable=singleton-comparison - data_types.Fuzzer.primary_owner == '')) - - count = 0 - for fuzzer in query.fetch(): - print(f"Processing fuzzer: {fuzzer.name}") - if not dry_run: - fuzzer.primary_owner = default_owner - fuzzer.put() - helpers.log( - f"Backfilled primary_owner for {fuzzer.name} to {default_owner}", - helpers.MODIFY_OPERATION) - count += 1 - - mode = "DRY RUN" if dry_run else "LIVE" - print(f"{mode}: Processed {count} fuzzers.") - - -if __name__ == '__main__': - parser = argparse.ArgumentParser( - description='Backfill primary_owner for Fuzzers.') - parser.add_argument( - '--live', action='store_true', help='Perform live updates.') - args = parser.parse_args() - - migrate(dry_run=not args.live, default_owner=args.owner) From 751a71ea2be51ffc696fab15b0325a3ed24a11b4 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Thu, 23 Jul 2026 15:18:06 +0000 Subject: [PATCH 6/9] Remove exception and check attribute in `fuzzer` --- .../_internal/issue_management/issue_filer.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/clusterfuzz/_internal/issue_management/issue_filer.py b/src/clusterfuzz/_internal/issue_management/issue_filer.py index 61d454463f0..1c093d34075 100644 --- a/src/clusterfuzz/_internal/issue_management/issue_filer.py +++ b/src/clusterfuzz/_internal/issue_management/issue_filer.py @@ -320,11 +320,8 @@ def file_issue(testcase, logs.info(f'Filing new issue for testcase: {testcase.key.id()}.') policy = issue_tracker_policy.get(issue_tracker.project) - try: - fuzzer = data_types.Fuzzer.query( - data_types.Fuzzer.name == testcase.fuzzer_name).get() - except Exception: - fuzzer = None + fuzzer = data_types.Fuzzer.query( + data_types.Fuzzer.name == testcase.fuzzer_name).get() is_crash = not utils.sub_string_exists_in(NON_CRASH_TYPES, testcase.crash_type) @@ -486,7 +483,7 @@ def file_issue(testcase, testcase.one_time_crasher_flag and policy.unreproducible_component): issue.components.add(policy.unreproducible_component) - if fuzzer and getattr(fuzzer, 'primary_owner', None): + if fuzzer and fuzzer.primary_owner: issue.reporter = fuzzer.primary_owner else: issue.reporter = user_email From d85e77a97f6f8497f225ce721287cb233fc37534 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Thu, 23 Jul 2026 16:27:16 +0000 Subject: [PATCH 7/9] Restructure mocks for issue filer tests --- .../tests/appengine/libs/issue_filer_test.py | 58 +++++++++---------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py b/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py index 3f7e40833e2..c19c8695772 100644 --- a/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py +++ b/src/clusterfuzz/_internal/tests/appengine/libs/issue_filer_test.py @@ -661,25 +661,19 @@ def test_filed_issues_external_fuzzer_author(self): external_contribution=True, primary_owner='owner@example.com').put() - issue_tracker = google_issue_tracker.IssueTracker('chromium', mock.Mock(), { + mock_client = mock.Mock() + issue_tracker = google_issue_tracker.IssueTracker('chromium', mock_client, { 'default_component_id': '123', 'url': 'mock' }) - # Intercept new_issue so we can capture the saved issue object. - original_new_issue = issue_tracker.new_issue - - def mock_new_issue(): - issue = original_new_issue() - - def mock_save(*args, **kwargs): - issue_tracker.last_issue = issue - issue._data['issueId'] = 12345 - - issue.save = mock_save - return issue - - issue_tracker.new_issue = mock_new_issue + issue_tracker._execute = mock.Mock(return_value={ + 'issueId': 12345, + 'issueState': { + 'componentId': '123', + 'type': 'BUG', + }, + }) self.testcase1.security_flag = True self.testcase1.put() @@ -687,7 +681,10 @@ def mock_save(*args, **kwargs): issue_filer.file_issue( self.testcase1, issue_tracker, user_email='reporter@example.com') - self.assertEqual('owner@example.com', issue_tracker.last_issue.reporter) + mock_create = mock_client.issues.return_value.create + body = mock_create.call_args[1]['body'] + self.assertEqual('owner@example.com', + body['issueState']['reporter']['emailAddress']) def test_filed_issues_external_fuzzer_no_author(self): """Tests issue filing for external fuzzer without author.""" @@ -695,25 +692,19 @@ def test_filed_issues_external_fuzzer_no_author(self): data_types.Fuzzer(name='fuzzer', external_contribution=True).put() - issue_tracker = google_issue_tracker.IssueTracker('chromium', mock.Mock(), { + mock_client = mock.Mock() + issue_tracker = google_issue_tracker.IssueTracker('chromium', mock_client, { 'default_component_id': '123', 'url': 'mock' }) - # Intercept new_issue so we can capture the saved issue object. - original_new_issue = issue_tracker.new_issue - - def mock_new_issue(): - issue = original_new_issue() - - def mock_save(*args, **kwargs): - issue_tracker.last_issue = issue - issue._data['issueId'] = 12345 - - issue.save = mock_save - return issue - - issue_tracker.new_issue = mock_new_issue + issue_tracker._execute = mock.Mock(return_value={ + 'issueId': 12345, + 'issueState': { + 'componentId': '123', + 'type': 'BUG', + }, + }) self.testcase1.security_flag = True self.testcase1.put() @@ -721,7 +712,10 @@ def mock_save(*args, **kwargs): issue_filer.file_issue( self.testcase1, issue_tracker, user_email='reporter@example.com') - self.assertEqual('reporter@example.com', issue_tracker.last_issue.reporter) + mock_create = mock_client.issues.return_value.create + body = mock_create.call_args[1]['body'] + self.assertEqual('reporter@example.com', + body['issueState']['reporter']['emailAddress']) def test_testcase_metadata_labels_and_components(self): """Tests issue filing with additional labels and components.""" From 5c0f78e19e3ff21e2eddfa8ed6b7ee6eccc8e7a1 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Thu, 23 Jul 2026 16:57:31 +0000 Subject: [PATCH 8/9] consider primary owner on tests --- .../_internal/tests/appengine/handlers/fuzzers_test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/clusterfuzz/_internal/tests/appengine/handlers/fuzzers_test.py b/src/clusterfuzz/_internal/tests/appengine/handlers/fuzzers_test.py index 14268e5eecb..f579bc4d947 100644 --- a/src/clusterfuzz/_internal/tests/appengine/handlers/fuzzers_test.py +++ b/src/clusterfuzz/_internal/tests/appengine/handlers/fuzzers_test.py @@ -162,6 +162,7 @@ def test_update_fuzzer(self): 'csrf_token': form.generate_csrf_token(), 'data_bundle_name': 'test_bundle', 'external_contribution': True, + 'primary_owner': 'owner@example.com', 'jobs': [], 'key': fuzzer.key.id(), 'max_testcases': 100, @@ -179,6 +180,7 @@ def test_update_fuzzer(self): 'additional_environment_string': 'args=123', 'data_bundle_name': 'test_bundle', 'external_contribution': True, + 'primary_owner': 'owner@example.com', 'executable_path': 'executable', 'last_edited_by': 'editor@example.com', 'launcher_script': 'launcher', From cdbe9fe8c7046304d83868a56e093ca17a3cab69 Mon Sep 17 00:00:00 2001 From: Arturo Barrera Date: Thu, 23 Jul 2026 17:41:45 +0000 Subject: [PATCH 9/9] Add primary owner to data types test --- .../_internal/tests/core/datastore/data_types_test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clusterfuzz/_internal/tests/core/datastore/data_types_test.py b/src/clusterfuzz/_internal/tests/core/datastore/data_types_test.py index 489c74ce165..98499f967cd 100644 --- a/src/clusterfuzz/_internal/tests/core/datastore/data_types_test.py +++ b/src/clusterfuzz/_internal/tests/core/datastore/data_types_test.py @@ -56,6 +56,7 @@ def test_get_config_dict(self): 'revision': 3, 'source': 'author', 'last_edited_by': 'editor', + 'primary_owner': None, }, config_dict, )