blob: 37b4ce824cb2a511cf1609e3f6ba9545ef78dd24 [file] [log] [blame]
// Copyright 2023 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:auto_submit/configuration/repository_configuration.dart';
import 'package:auto_submit/configuration/repository_configuration_manager.dart';
import 'package:github/github.dart';
import 'package:neat_cache/cache_provider.dart';
import 'package:neat_cache/neat_cache.dart';
import 'package:test/test.dart';
import '../src/service/fake_config.dart';
import '../src/service/fake_github_service.dart';
import 'repository_configuration_data.dart';
void main() {
late RepositoryConfigurationManager repositoryConfigurationManager;
late CacheProvider cacheProvider;
late Cache cache;
late FakeGithubService githubService;
late FakeConfig config;
setUp(() {
cacheProvider = Cache.inMemoryCacheProvider(5);
cache = Cache<dynamic>(cacheProvider).withPrefix('config');
githubService = FakeGithubService();
config = FakeConfig(githubService: githubService);
repositoryConfigurationManager = RepositoryConfigurationManager(config, cache);
});
test('Verify cache storage', () async {
const String sampleConfig = '''
default_branch: main
auto_approval_accounts:
- dependabot[bot]
- dependabot
- DartDevtoolWorkflowBot
approving_reviews: 2
approval_group: flutter-hackers
run_ci: true
support_no_review_revert: true
required_checkruns_on_revert:
- ci.yaml validation
- Google-testing
- test (ubuntu-latest, 2.18.0)
- cla/google
''';
githubService.fileContentsMockList.add(sampleConfig);
final RepositoryConfiguration repositoryConfiguration =
await repositoryConfigurationManager.readRepositoryConfiguration(
RepositorySlug('flutter', 'cocoon'),
);
expect(repositoryConfiguration.allowConfigOverride, isFalse);
expect(repositoryConfiguration.defaultBranch, 'main');
expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
expect(repositoryConfiguration.approvingReviews, 2);
expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
expect(repositoryConfiguration.runCi, isTrue);
expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 4);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('test (ubuntu-latest, 2.18.0)'), isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('cla/google'), isTrue);
});
test('Omitted issues_repository assumes provided slug is for issues', () async {
const String sampleConfig = '''
default_branch: main
auto_approval_accounts:
- dependabot[bot]
- dependabot
- DartDevtoolWorkflowBot
approving_reviews: 2
approval_group: flutter-hackers
run_ci: true
support_no_review_revert: true
required_checkruns_on_revert:
- ci.yaml validation
- Google-testing
''';
githubService.fileContentsMockList.add(sampleConfig);
final RepositoryConfiguration repositoryConfiguration =
await repositoryConfigurationManager.readRepositoryConfiguration(
RepositorySlug('flutter', 'cocoon'),
);
expect(repositoryConfiguration.allowConfigOverride, isFalse);
expect(repositoryConfiguration.defaultBranch, 'main');
expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
expect(repositoryConfiguration.approvingReviews, 2);
expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
expect(repositoryConfiguration.runCi, isTrue);
expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
});
test('Default branch collected if omitted master', () async {
const String sampleConfig = '''
auto_approval_accounts:
- dependabot[bot]
- dependabot
- DartDevtoolWorkflowBot
approving_reviews: 2
approval_group: flutter-hackers
run_ci: true
support_no_review_revert: true
required_checkruns_on_revert:
- ci.yaml validation
- Google-testing
''';
githubService.fileContentsMockList.add(sampleConfig);
githubService.defaultBranch = 'master';
final RepositoryConfiguration repositoryConfiguration =
await repositoryConfigurationManager.readRepositoryConfiguration(
RepositorySlug('flutter', 'flutter'),
);
expect(repositoryConfiguration.allowConfigOverride, isFalse);
expect(repositoryConfiguration.defaultBranch, 'master');
expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
expect(repositoryConfiguration.approvingReviews, 2);
expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
expect(repositoryConfiguration.runCi, isTrue);
expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
});
test('Default branch collected if omitted main', () async {
const String sampleConfig = '''
auto_approval_accounts:
- dependabot[bot]
- dependabot
- DartDevtoolWorkflowBot
approving_reviews: 2
approval_group: flutter-hackers
run_ci: true
support_no_review_revert: true
required_checkruns_on_revert:
- ci.yaml validation
- Google-testing
''';
githubService.fileContentsMockList.add(sampleConfig);
githubService.defaultBranch = 'main';
final RepositoryConfiguration repositoryConfiguration =
await repositoryConfigurationManager.readRepositoryConfiguration(
RepositorySlug('flutter', 'flutter'),
);
expect(repositoryConfiguration.allowConfigOverride, isFalse);
expect(repositoryConfiguration.defaultBranch, 'main');
expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue);
expect(repositoryConfiguration.autoApprovalAccounts.length, 3);
expect(repositoryConfiguration.approvingReviews, 2);
expect(repositoryConfiguration.approvalGroup, 'flutter-hackers');
expect(repositoryConfiguration.runCi, isTrue);
expect(repositoryConfiguration.supportNoReviewReverts, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue);
});
group('Merging configurations tests', () {
// Sample configuration being used for these tests
/*
default_branch: main
allow_config_override: true
auto_approval_accounts:
- dependabot[bot]
- dependabot
- DartDevtoolWorkflowBot
approving_reviews: 2
approval_group: flutter-hackers
run_ci: true
support_no_review_revert: true
required_checkruns_on_revert:
- ci.yaml validation
*/
test('Global config merged with default local config', () {
final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration();
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(mergedRepositoryConfiguration.defaultBranch, 'main');
expect(mergedRepositoryConfiguration.allowConfigOverride, isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 3);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue);
expect(mergedRepositoryConfiguration.approvingReviews, 2);
expect(mergedRepositoryConfiguration.runCi, isTrue);
expect(mergedRepositoryConfiguration.supportNoReviewReverts, isTrue);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 1);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
});
test('Auto approval accounts is additive, they cannot be removed', () {
const String expectedAddedApprovalAccount = 'flutter-roller-account';
const Set<String> localAutoApprovalAccounts = {expectedAddedApprovalAccount};
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(autoApprovalAccounts: localAutoApprovalAccounts);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 4);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains(expectedAddedApprovalAccount), isTrue);
});
test('Duplicate auto approval account is not added', () {
const String expectedAddedApprovalAccount = 'DartDevtoolWorkflowBot';
const Set<String> localAutoApprovalAccounts = {expectedAddedApprovalAccount};
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(autoApprovalAccounts: localAutoApprovalAccounts);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 3);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue);
expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue);
});
test('Approving reviews is overridden by local config', () {
const int expectedApprovingReviews = 3;
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(approvingReviews: expectedApprovingReviews);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(globalRepositoryConfiguration.approvingReviews != mergedRepositoryConfiguration.approvingReviews, isTrue);
expect(mergedRepositoryConfiguration.approvingReviews, expectedApprovingReviews);
});
test('Approving reviews is not overridden if less than global config', () {
const int expectedApprovingReviews = 2;
final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(approvingReviews: 1);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(globalRepositoryConfiguration.approvingReviews == mergedRepositoryConfiguration.approvingReviews, isTrue);
expect(mergedRepositoryConfiguration.approvingReviews, expectedApprovingReviews);
});
test('Approval group is overridden if defined', () {
const String expectedApprovalGroup = 'flutter-devs';
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(approvalGroup: expectedApprovalGroup);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(mergedRepositoryConfiguration.approvalGroup, expectedApprovalGroup);
});
test('RunCi is updated if it differs from global config', () {
const bool expectedRunCi = false;
final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(runCi: expectedRunCi);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(globalRepositoryConfiguration.runCi != mergedRepositoryConfiguration.runCi, isTrue);
expect(mergedRepositoryConfiguration.runCi, expectedRunCi);
});
test('Support no review reverts is updated if it differs from global config', () {
const bool expectedSupportNoReviewReverts = false;
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(supportNoReviewReverts: expectedSupportNoReviewReverts);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(
globalRepositoryConfiguration.supportNoReviewReverts != mergedRepositoryConfiguration.supportNoReviewReverts,
isTrue,
);
expect(mergedRepositoryConfiguration.supportNoReviewReverts, expectedSupportNoReviewReverts);
});
test('Required check runs on revert is additive, they cannot be removed', () {
const String expectedRequiredCheckRun = 'Linux Device Doctor Validator';
const Set<String> localrequiredCheckRunsOnRevert = {expectedRequiredCheckRun};
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(requiredCheckRunsOnRevert: localrequiredCheckRunsOnRevert);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 2);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains(expectedRequiredCheckRun), isTrue);
});
test('Duplicate required check run on revert is not added', () {
const String expectedRequiredCheckRun = 'ci.yaml validation';
const Set<String> localRequiredCheckRunsOnRevert = {expectedRequiredCheckRun};
final RepositoryConfiguration localRepositoryConfiguration =
RepositoryConfiguration(requiredCheckRunsOnRevert: localRequiredCheckRunsOnRevert);
final RepositoryConfiguration globalRepositoryConfiguration =
RepositoryConfiguration.fromYaml(sampleConfigWithOverride);
final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations(
globalRepositoryConfiguration,
localRepositoryConfiguration,
);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 1);
expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains(expectedRequiredCheckRun), isTrue);
});
});
}