| import {CodeOwnerService} from './code-owners-service.js'; |
| import { |
| RequestPayload, |
| RestPluginApi, |
| } from '@gerritcodereview/typescript-api/rest.js'; |
| import { |
| ChangeInfo, |
| HttpMethod, |
| } from '@gerritcodereview/typescript-api/rest-api.js'; |
| import {SuggestionsType} from './code-owners-model.js'; |
| import {assert} from '@open-wc/testing'; |
| |
| function flush() { |
| return new Promise((resolve, _reject) => { |
| setTimeout(resolve, 0); |
| }); |
| } |
| |
| suite('code owners service tests', () => { |
| let codeOwnersService: CodeOwnerService; |
| const fakeRestApi = { |
| send( |
| _method: HttpMethod, |
| _url: string, |
| _payload?: RequestPayload, |
| _errFn?: ErrorCallback, |
| _contentType?: string |
| ) { |
| return Promise.resolve({}); |
| }, |
| getLoggedIn() { |
| return Promise.resolve(true); |
| }, |
| getAccount() { |
| return Promise.resolve({email: 'someone@google.com'}); |
| }, |
| } as unknown as RestPluginApi; |
| |
| const fakeStatus = { |
| // fake data with fake files |
| patch_set_number: 1, |
| file_code_owner_statuses: [ |
| { |
| new_path_status: { |
| path: 'a.js', |
| status: 'INSUFFICIENT_REVIEWERS', |
| }, |
| }, |
| { |
| new_path_status: { |
| path: 'b.js', |
| status: 'INSUFFICIENT_REVIEWERS', |
| }, |
| }, |
| { |
| new_path_status: { |
| path: 'c.ts', |
| status: 'INSUFFICIENT_REVIEWERS', |
| }, |
| }, |
| { |
| change_type: 'RENAMED', |
| old_path_status: { |
| path: 'd.ts', |
| status: 'INSUFFICIENT_REVIEWERS', |
| }, |
| new_path_status: { |
| path: 'd_edit.ts', |
| status: 'INSUFFICIENT_REVIEWERS', |
| }, |
| }, |
| { |
| new_path_status: { |
| path: 'e.ts', |
| status: 'PENDING', |
| }, |
| }, |
| { |
| new_path_status: { |
| path: 'f.ts', |
| status: 'PENDING', |
| }, |
| }, |
| { |
| new_path_status: { |
| path: 'g.ts', |
| status: 'PENDING', |
| }, |
| }, |
| ], |
| }; |
| const fakeChange = { |
| project: 'test', |
| branch: 'main', |
| _number: '123', |
| revisions: { |
| a: { |
| _number: 1, |
| }, |
| }, |
| current_revision: 'a', |
| } as unknown as ChangeInfo; |
| |
| let getApiStub: sinon.SinonStub; |
| |
| setup(() => { |
| getApiStub = sinon.stub(fakeRestApi, 'send'); |
| getApiStub.returns(Promise.resolve({})); |
| }); |
| |
| teardown(() => { |
| getApiStub.reset(); |
| CodeOwnerService.reset(); |
| sinon.restore(); |
| }); |
| |
| suite('basic api request tests', () => { |
| setup(async () => { |
| getApiStub |
| .withArgs( |
| sinon.match.any, |
| `/changes/${fakeChange._number}/code_owners.status?limit=100000`, |
| sinon.match.any, |
| sinon.match.any |
| ) |
| .returns(Promise.resolve(fakeStatus)); |
| codeOwnersService = CodeOwnerService.getOwnerService(fakeRestApi, { |
| ...fakeChange, |
| }); |
| await flush(); |
| }); |
| |
| test('getOwnerService - same change returns the same instance', () => { |
| assert.equal( |
| CodeOwnerService.getOwnerService(fakeRestApi, fakeChange), |
| CodeOwnerService.getOwnerService(fakeRestApi, fakeChange) |
| ); |
| }); |
| |
| test('return a new instance when change changed', () => { |
| assert.notEqual( |
| CodeOwnerService.getOwnerService(fakeRestApi, {...fakeChange}), |
| CodeOwnerService.getOwnerService(fakeRestApi, {...fakeChange}) |
| ); |
| }); |
| |
| test('should fetch status after init', () => { |
| assert.isTrue(getApiStub.called); |
| assert.equal( |
| getApiStub.lastCall.args[1], |
| `/changes/${fakeChange._number}/code_owners.status?limit=100000` |
| ); |
| }); |
| |
| test('getSuggestion should kickoff the fetch', async () => { |
| assert.equal(getApiStub.callCount, 2); |
| getApiStub.resetHistory(); |
| await codeOwnersService.getSuggestedOwners( |
| SuggestionsType.ALL_SUGGESTIONS |
| ); |
| // 8 requests for getting file owners |
| assert.equal(getApiStub.callCount, 8); |
| }); |
| |
| test('approved status calculation', async () => { |
| const approved = await codeOwnersService.areAllFilesApproved(); |
| assert.equal(approved, false); |
| }); |
| }); |
| |
| suite('getOwnedPaths', () => { |
| async function setupBranchConfig(disabled: boolean | undefined) { |
| getApiStub |
| .withArgs( |
| sinon.match.any, |
| `/projects/${fakeChange.project}/branches/` + |
| `${fakeChange.branch}/code_owners.branch_config`, |
| sinon.match.any, |
| sinon.match.any |
| ) |
| .returns(Promise.resolve({disabled})); |
| codeOwnersService = CodeOwnerService.getOwnerService(fakeRestApi, { |
| ...fakeChange, |
| }); |
| await flush(); |
| getApiStub.resetHistory(); |
| } |
| test('should not fetch if disabled', async () => { |
| await setupBranchConfig(true); |
| |
| await codeOwnersService.getOwnedPaths(); |
| assert.equal(getApiStub.callCount, 0); |
| }); |
| |
| test('should fetch if enabled', async () => { |
| await setupBranchConfig(false); |
| |
| await codeOwnersService.getOwnedPaths(); |
| assert.equal(getApiStub.callCount, 1); |
| }); |
| |
| test('should fetch if enabled by default', async () => { |
| await setupBranchConfig(undefined); |
| |
| await codeOwnersService.getOwnedPaths(); |
| assert.equal(getApiStub.callCount, 1); |
| }); |
| }); |
| |
| suite('all approved case', () => { |
| setup(async () => { |
| getApiStub |
| .withArgs( |
| sinon.match.any, |
| `/changes/${fakeChange._number}/code_owners.status?limit=100000`, |
| sinon.match.any, |
| sinon.match.any |
| ) |
| .returns( |
| Promise.resolve({ |
| // fake data with fake files |
| patch_set_number: 1, |
| file_code_owner_statuses: [ |
| { |
| new_path_status: { |
| path: 'a.js', |
| status: 'APPROVED', |
| }, |
| }, |
| { |
| new_path_status: { |
| path: 'b.js', |
| status: 'APPROVED', |
| }, |
| }, |
| { |
| old_path_status: { |
| path: 'd.js', |
| status: 'APPROVED', |
| }, |
| change_type: 'DELETED', |
| }, |
| ], |
| }) |
| ); |
| |
| codeOwnersService = CodeOwnerService.getOwnerService(fakeRestApi, { |
| ...fakeChange, |
| }); |
| await flush(); |
| }); |
| |
| test('approved status calculation', async () => { |
| const approved = await codeOwnersService.areAllFilesApproved(); |
| assert.equal(approved, true); |
| }); |
| }); |
| }); |