blob: f547a8edb31827898e18630aa540a5e07424d0b1 [file] [log] [blame]
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.project}~${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.project}~${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.project}~${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);
});
});
});