/*
* SonarQube
* Copyright (C) 2009-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { cloneDeep, times } from 'lodash';
import {
mockHotspot,
mockHotspotComment,
mockHotspotRule,
mockRawHotspot,
mockStandards,
} from '../../helpers/mocks/security-hotspots';
import { mockSourceLine } from '../../helpers/mocks/sources';
import { getStandards } from '../../helpers/security-standard';
import { mockPaging, mockUser } from '../../helpers/testMocks';
import {
Hotspot,
HotspotAssignRequest,
HotspotComment,
HotspotResolution,
HotspotStatus,
} from '../../types/security-hotspots';
import { getSources } from '../components';
import { getMeasures } from '../measures';
import {
assignSecurityHotspot,
commentSecurityHotspot,
deleteSecurityHotspotComment,
editSecurityHotspotComment,
getSecurityHotspotDetails,
getSecurityHotspotList,
getSecurityHotspots,
setSecurityHotspotStatus,
} from '../security-hotspots';
import { searchUsers } from '../users';
const NUMBER_OF_LINES = 20;
export default class SecurityHotspotServiceMock {
hotspots: Hotspot[] = [];
nextAssignee: string | undefined;
canChangeStatus: boolean = true;
hotspotsComments: HotspotComment[] = [];
constructor() {
this.reset();
jest.mocked(getMeasures).mockImplementation(this.handleGetMeasures);
jest.mocked(getSecurityHotspots).mockImplementation(this.handleGetSecurityHotspots);
jest.mocked(getSecurityHotspotDetails).mockImplementation(this.handleGetSecurityHotspotDetails);
jest.mocked(getSecurityHotspotList).mockImplementation(this.handleGetSecurityHotspotList);
jest.mocked(assignSecurityHotspot).mockImplementation(this.handleAssignSecurityHotspot);
jest.mocked(setSecurityHotspotStatus).mockImplementation(this.handleSetSecurityHotspotStatus);
jest.mocked(searchUsers).mockImplementation(this.handleSearchUsers);
jest.mocked(getSources).mockResolvedValue(
times(NUMBER_OF_LINES, (n) =>
mockSourceLine({
line: n,
code: ' symbole',
})
)
);
jest.mocked(commentSecurityHotspot).mockImplementation(this.handleCommentSecurityHotspot);
jest
.mocked(deleteSecurityHotspotComment)
.mockImplementation(this.handleDeleteSecurityHotspotComment);
jest
.mocked(editSecurityHotspotComment)
.mockImplementation(this.handleEditSecurityHotspotComment);
jest.mocked(getStandards).mockImplementation(this.handleGetStandards);
}
handleCommentSecurityHotspot = () => {
this.hotspotsComments = [
mockHotspotComment({
htmlText: 'This is a comment from john doe',
markdown: 'This is a comment from john doe',
updatable: true,
}),
];
return Promise.resolve();
};
handleDeleteSecurityHotspotComment = () => {
this.hotspotsComments = [];
return Promise.resolve();
};
handleEditSecurityHotspotComment = () => {
const response = mockHotspotComment({
htmlText: 'This is a comment from john doe test',
markdown: 'This is a comment from john doe test',
updatable: true,
});
this.hotspotsComments = [response];
return Promise.resolve(response);
};
handleGetStandards = () => {
return Promise.resolve(mockStandards());
};
handleSetSecurityHotspotStatus = () => {
return Promise.resolve();
};
handleSearchUsers = () => {
return this.reply({
users: [
mockUser({ name: 'User John', login: 'user.john' }),
mockUser({ name: 'User Doe', login: 'user.doe' }),
mockUser({ name: 'User Foo', login: 'user.foo' }),
],
paging: mockPaging(),
});
};
handleGetSecurityHotspotList = (
hotspotKeys: string[],
data: {
projectKey: string;
branch?: string;
}
) => {
if (data?.branch === 'b1') {
return this.reply({
paging: mockPaging(),
hotspots: [
mockRawHotspot({
assignee: 'John Doe',
key: 'b1-test-1',
message: "'F' is a magic number.",
}),
mockRawHotspot({ assignee: 'John Doe', key: 'b1-test-2' }),
].filter((h) => hotspotKeys.includes(h.key) || hotspotKeys.length === 0),
components: [
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed:index.php',
qualifier: 'FIL',
name: 'index.php',
longName: 'index.php',
path: 'index.php',
},
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed',
qualifier: 'TRK',
name: 'benflix',
longName: 'benflix',
},
],
});
}
return this.reply({
paging: mockPaging(),
hotspots: this.mockRawHotspots(false).filter(
(h) => hotspotKeys.includes(h.key) || hotspotKeys.length === 0
),
components: [
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed:index.php',
qualifier: 'FIL',
name: 'index.php',
longName: 'index.php',
path: 'index.php',
},
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed',
qualifier: 'TRK',
name: 'benflix',
longName: 'benflix',
},
],
});
};
handleGetSecurityHotspots = (data: {
projectKey: string;
p: number;
ps: number;
status?: HotspotStatus;
resolution?: HotspotResolution;
onlyMine?: boolean;
inNewCodePeriod?: boolean;
branch?: string;
}) => {
if (data?.branch === 'b1') {
return this.reply({
paging: mockPaging({ pageIndex: 1, pageSize: data.ps, total: 2 }),
hotspots: [
mockRawHotspot({
assignee: 'John Doe',
key: 'b1-test-1',
message: "'F' is a magic number.",
}),
mockRawHotspot({ assignee: 'John Doe', key: 'b1-test-2' }),
],
components: [
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed:index.php',
qualifier: 'FIL',
name: 'index.php',
longName: 'index.php',
path: 'index.php',
},
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed',
qualifier: 'TRK',
name: 'benflix',
longName: 'benflix',
},
],
});
}
return this.reply({
paging: mockPaging({ pageIndex: 1, pageSize: data.ps, total: this.hotspots.length }),
hotspots: this.mockRawHotspots(data.onlyMine),
components: [
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed:index.php',
qualifier: 'FIL',
name: 'index.php',
longName: 'index.php',
path: 'index.php',
},
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed',
qualifier: 'TRK',
name: 'benflix',
longName: 'benflix',
},
],
});
};
mockRawHotspots = (onlyMine: boolean | undefined) => {
if (onlyMine) {
return [];
}
return [
mockRawHotspot({ assignee: 'John Doe', key: 'test-1' }),
mockRawHotspot({ assignee: 'John Doe', key: 'test-2' }),
];
};
handleGetSecurityHotspotDetails = (securityHotspotKey: string) => {
const hotspot = this.hotspots.find((h) => h.key === securityHotspotKey);
if (hotspot === undefined) {
return Promise.reject({
errors: [{ msg: `No security hotspot for key ${securityHotspotKey}` }],
});
}
if (this.nextAssignee !== undefined) {
hotspot.assigneeUser = {
...hotspot.assigneeUser,
login: this.nextAssignee,
name: this.nextAssignee,
};
this.nextAssignee = undefined;
}
hotspot.canChangeStatus = this.canChangeStatus;
hotspot.comment = this.hotspotsComments;
return this.reply(hotspot);
};
handleGetMeasures = () => {
return this.reply([
{
key: 'guillaume-peoch-sonarsource_benflix_AYGpXq2bd8qy4i0eO9ed',
name: 'benflix',
qualifier: 'TRK',
metric: 'security_hotspots_reviewed',
measures: [{ metric: 'security_hotspots_reviewed', value: '0.0', bestValue: false }],
},
]);
};
handleAssignSecurityHotspot = (_: string, data: HotspotAssignRequest) => {
this.nextAssignee = data.assignee;
return Promise.resolve();
};
setHotspotChangeStatusPermission = (value: boolean) => (this.canChangeStatus = value);
reply(response: T): Promise {
return Promise.resolve(cloneDeep(response));
}
reset = () => {
this.hotspots = [
mockHotspot({
rule: mockHotspotRule({ key: 'rule2' }),
assignee: 'John Doe',
key: 'b1-test-1',
message: "'F' is a magic number.",
}),
mockHotspot({
rule: mockHotspotRule({ key: 'rule2' }),
assignee: 'John Doe',
key: 'b1-test-2',
}),
mockHotspot({
rule: mockHotspotRule({ key: 'rule2' }),
key: 'test-1',
status: HotspotStatus.TO_REVIEW,
}),
mockHotspot({
rule: mockHotspotRule({ key: 'rule2' }),
key: 'test-2',
status: HotspotStatus.TO_REVIEW,
message: "'2' is a magic number.",
codeVariants: ['variant 1', 'variant 2'],
}),
];
this.canChangeStatus = true;
};
}