You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LinesActionIT.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2023 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.source.ws;
  21. import org.junit.Before;
  22. import org.junit.Rule;
  23. import org.junit.Test;
  24. import org.mockito.stubbing.Answer;
  25. import org.sonar.api.utils.System2;
  26. import org.sonar.api.web.UserRole;
  27. import org.sonar.core.util.Uuids;
  28. import org.sonar.db.DbTester;
  29. import org.sonar.db.audit.NoOpAuditPersister;
  30. import org.sonar.db.component.BranchDto;
  31. import org.sonar.db.component.ComponentDao;
  32. import org.sonar.db.component.ComponentDto;
  33. import org.sonar.db.component.ProjectData;
  34. import org.sonar.db.component.SnapshotDao;
  35. import org.sonar.db.component.SnapshotDto;
  36. import org.sonar.db.project.ProjectDto;
  37. import org.sonar.db.protobuf.DbFileSources;
  38. import org.sonar.db.source.FileSourceDto;
  39. import org.sonar.db.user.UserDto;
  40. import org.sonar.server.component.TestComponentFinder;
  41. import org.sonar.server.exceptions.ForbiddenException;
  42. import org.sonar.server.exceptions.NotFoundException;
  43. import org.sonar.server.source.HtmlSourceDecorator;
  44. import org.sonar.server.source.SourceService;
  45. import org.sonar.server.source.index.FileSourceTesting;
  46. import org.sonar.server.tester.UserSessionRule;
  47. import org.sonar.server.ws.TestRequest;
  48. import org.sonar.server.ws.TestResponse;
  49. import org.sonar.server.ws.WsActionTester;
  50. import static java.lang.String.format;
  51. import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
  52. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  53. import static org.mockito.ArgumentMatchers.anyString;
  54. import static org.mockito.Mockito.mock;
  55. import static org.mockito.Mockito.when;
  56. import static org.sonar.db.component.BranchType.PULL_REQUEST;
  57. import static org.sonar.db.component.ComponentTesting.newFileDto;
  58. public class LinesActionIT {
  59. @Rule
  60. public DbTester db = DbTester.create(System2.INSTANCE);
  61. @Rule
  62. public UserSessionRule userSession = UserSessionRule.standalone();
  63. private final ComponentDao componentDao = new ComponentDao(new NoOpAuditPersister());
  64. private final SnapshotDao snapshotDao = new SnapshotDao();
  65. private final HtmlSourceDecorator htmlSourceDecorator = mock(HtmlSourceDecorator.class);
  66. private final SourceService sourceService = new SourceService(db.getDbClient(), htmlSourceDecorator);
  67. private final LinesJsonWriter linesJsonWriter = new LinesJsonWriter(htmlSourceDecorator);
  68. private final LinesAction underTest = new LinesAction(TestComponentFinder.from(db), db.getDbClient(), sourceService, linesJsonWriter, userSession);
  69. private final WsActionTester tester = new WsActionTester(underTest);
  70. @Before
  71. public void setUp() {
  72. when(htmlSourceDecorator.getDecoratedSourceAsHtml(anyString(), anyString(), anyString()))
  73. .then((Answer<String>) invocationOnMock -> "<p>" + invocationOnMock.getArguments()[0] + "</p>");
  74. }
  75. @Test
  76. public void show_source() {
  77. ProjectData privateProject = db.components().insertPrivateProject();
  78. ComponentDto file = insertFileWithData(FileSourceTesting.newFakeData(3).build(), privateProject.getMainBranchComponent());
  79. setUserWithValidPermission(privateProject);
  80. TestResponse response = tester.newRequest()
  81. .setParam("uuid", file.uuid())
  82. .execute();
  83. response.assertJson(getClass(), "show_source.json");
  84. }
  85. @Test
  86. public void fail_to_show_source_if_no_source_found() {
  87. ProjectData privateProject = db.components().insertPrivateProject();
  88. ComponentDto file = insertFile(privateProject.getMainBranchComponent());
  89. setUserWithValidPermission(privateProject);
  90. TestRequest request = tester.newRequest()
  91. .setParam("uuid", file.uuid());
  92. assertThatThrownBy(() -> request.execute())
  93. .isInstanceOf(NotFoundException.class);
  94. }
  95. @Test
  96. public void show_paginated_lines() {
  97. ProjectData privateProject = db.components().insertPrivateProject();
  98. ComponentDto file = insertFileWithData(FileSourceTesting.newFakeData(3).build(), privateProject.getMainBranchComponent());
  99. setUserWithValidPermission(privateProject);
  100. tester
  101. .newRequest()
  102. .setParam("uuid", file.uuid())
  103. .setParam("from", "3")
  104. .setParam("to", "3")
  105. .execute()
  106. .assertJson(getClass(), "show_paginated_lines.json");
  107. }
  108. @Test
  109. public void branch() {
  110. ProjectData project = db.components().insertPrivateProject();
  111. String branchName = randomAlphanumeric(248);
  112. ComponentDto branch = db.components().insertProjectBranch(project.getMainBranchComponent(), b -> b.setKey(branchName));
  113. ComponentDto file = db.components().insertComponent(newFileDto(branch, project.mainBranchUuid()));
  114. db.getDbClient().fileSourceDao().insert(db.getSession(), new FileSourceDto()
  115. .setUuid(Uuids.createFast())
  116. .setProjectUuid(branch.uuid())
  117. .setFileUuid(file.uuid())
  118. .setSourceData(FileSourceTesting.newFakeData(3).build()));
  119. db.commit();
  120. userSession.logIn("login")
  121. .addProjectPermission(UserRole.USER, project.getProjectDto())
  122. .addProjectBranchMapping(project.projectUuid(), branch)
  123. .addProjectPermission(UserRole.CODEVIEWER, project.getProjectDto());
  124. tester.newRequest()
  125. .setParam("key", file.getKey())
  126. .setParam("branch", branchName)
  127. .execute()
  128. .assertJson(getClass(), "show_source.json");
  129. }
  130. @Test
  131. public void pull_request() {
  132. ProjectData projectData = db.components().insertPrivateProject();
  133. ComponentDto mainBranch = projectData.getMainBranchComponent();
  134. String pullRequestKey = randomAlphanumeric(100);
  135. ComponentDto branch = db.components().insertProjectBranch(mainBranch, b -> b.setBranchType(PULL_REQUEST).setKey(pullRequestKey));
  136. ComponentDto file = db.components().insertComponent(newFileDto(branch, mainBranch.uuid()));
  137. db.getDbClient().fileSourceDao().insert(db.getSession(), new FileSourceDto()
  138. .setUuid(Uuids.createFast())
  139. .setProjectUuid(branch.uuid())
  140. .setFileUuid(file.uuid())
  141. .setSourceData(FileSourceTesting.newFakeData(3).build()));
  142. db.commit();
  143. userSession.logIn("login")
  144. .addProjectPermission(UserRole.USER, projectData.getProjectDto())
  145. .addProjectPermission(UserRole.CODEVIEWER, projectData.getProjectDto())
  146. .addProjectBranchMapping(projectData.projectUuid(), branch);
  147. tester.newRequest()
  148. .setParam("key", file.getKey())
  149. .setParam("pullRequest", pullRequestKey)
  150. .execute()
  151. .assertJson(getClass(), "show_source.json");
  152. }
  153. @Test
  154. public void fail_when_no_uuid_or_key_param() {
  155. assertThatThrownBy(() -> tester.newRequest().execute())
  156. .isInstanceOf(IllegalArgumentException.class)
  157. .hasMessageContaining("Either 'uuid' or 'key' must be provided");
  158. }
  159. @Test
  160. public void fail_when_file_key_does_not_exist() {
  161. assertThatThrownBy(() -> tester.newRequest().setParam("key", "Foo.java").execute())
  162. .isInstanceOf(NotFoundException.class)
  163. .hasMessageContaining("Component key 'Foo.java' not found");
  164. }
  165. @Test
  166. public void fail_when_file_uuid_does_not_exist() {
  167. assertThatThrownBy(() -> tester.newRequest().setParam("uuid", "ABCD").execute())
  168. .isInstanceOf(NotFoundException.class)
  169. .hasMessageContaining("Component id 'ABCD' not found");
  170. }
  171. @Test
  172. public void fail_when_file_is_removed() {
  173. ProjectData privateProject = db.components().insertPrivateProject();
  174. ComponentDto file = newFileDto(privateProject.getMainBranchComponent()).setKey("file-key").setEnabled(false);
  175. db.components().insertComponents(file);
  176. setUserWithValidPermission(privateProject);
  177. assertThatThrownBy(() -> tester.newRequest().setParam("key", "file-key").execute())
  178. .isInstanceOf(NotFoundException.class)
  179. .hasMessageContaining("Component key 'file-key' not found");
  180. }
  181. @Test
  182. public void check_permission() {
  183. ComponentDto privateProject = db.components().insertPrivateProject().getMainBranchComponent();
  184. ComponentDto file = insertFileWithData(FileSourceTesting.newFakeData(1).build(), privateProject);
  185. userSession.logIn("login");
  186. assertThatThrownBy(() -> {
  187. tester.newRequest()
  188. .setParam("uuid", file.uuid())
  189. .execute();
  190. })
  191. .isInstanceOf(ForbiddenException.class);
  192. }
  193. @Test
  194. public void display_deprecated_fields() {
  195. ProjectData privateProject = db.components().insertPrivateProject();
  196. ComponentDto file = insertFileWithData(FileSourceTesting.newFakeData(1).build(), privateProject.getMainBranchComponent());
  197. setUserWithValidPermission(privateProject);
  198. tester.newRequest()
  199. .setParam("uuid", file.uuid())
  200. .execute()
  201. .assertJson(getClass(), "display_deprecated_fields.json");
  202. }
  203. @Test
  204. public void use_period_date_if_new_line_not_yet_available_in_db() {
  205. DbFileSources.Data.Builder dataBuilder = DbFileSources.Data.newBuilder();
  206. dataBuilder.addLines(DbFileSources.Line.newBuilder().setLine(1).setScmDate(1000L).build());
  207. dataBuilder.addLines(DbFileSources.Line.newBuilder().setLine(2).setScmDate(2000L).build());
  208. // only this line should be considered as new
  209. dataBuilder.addLines(DbFileSources.Line.newBuilder().setLine(3).setScmDate(3000L).build());
  210. ProjectData project = db.components().insertPrivateProject();
  211. insertPeriod(project.getMainBranchComponent(), 2000L);
  212. ComponentDto file = insertFileWithData(dataBuilder.build(), project.getMainBranchComponent());
  213. setUserWithValidPermission(project);
  214. tester.newRequest()
  215. .setParam("uuid", file.uuid())
  216. .execute()
  217. .assertJson(getClass(), "generated_isNew.json");
  218. }
  219. @Test
  220. public void use_deprecated_overall_coverage_fields_if_exists() {
  221. ProjectData privateProject = db.components().insertPrivateProject();
  222. DbFileSources.Data.Builder dataBuilder = DbFileSources.Data.newBuilder();
  223. ComponentDto file = insertFileWithData(dataBuilder.addLines(newLineBuilder()
  224. .setDeprecatedOverallLineHits(1)
  225. .setDeprecatedOverallConditions(2)
  226. .setDeprecatedOverallCoveredConditions(3)
  227. .setDeprecatedUtLineHits(1)
  228. .setDeprecatedUtConditions(2)
  229. .setDeprecatedUtCoveredConditions(3)
  230. .setDeprecatedItLineHits(1)
  231. .setDeprecatedItConditions(2)
  232. .setDeprecatedItCoveredConditions(3)).build(), privateProject.getMainBranchComponent());
  233. setUserWithValidPermission(privateProject);
  234. tester.newRequest()
  235. .setParam("uuid", file.uuid())
  236. .execute()
  237. .assertJson(getClass(), "convert_deprecated_data.json");
  238. }
  239. @Test
  240. public void use_deprecated_ut_coverage_fields_if_exists() {
  241. ProjectData privateProject = db.components().insertPrivateProject();
  242. DbFileSources.Data.Builder dataBuilder = DbFileSources.Data.newBuilder();
  243. ComponentDto file = insertFileWithData(dataBuilder.addLines(newLineBuilder()
  244. .setDeprecatedUtLineHits(1)
  245. .setDeprecatedUtConditions(2)
  246. .setDeprecatedUtCoveredConditions(3)
  247. .setDeprecatedItLineHits(1)
  248. .setDeprecatedItConditions(2)
  249. .setDeprecatedItCoveredConditions(3)).build(), privateProject.getMainBranchComponent());
  250. setUserWithValidPermission(privateProject);
  251. tester.newRequest()
  252. .setParam("uuid", file.uuid())
  253. .execute()
  254. .assertJson(getClass(), "convert_deprecated_data.json");
  255. }
  256. @Test
  257. public void use_deprecated_it_coverage_fields_if_exists() {
  258. ProjectData privateProject = db.components().insertPrivateProject();
  259. DbFileSources.Data.Builder dataBuilder = DbFileSources.Data.newBuilder();
  260. ComponentDto file = insertFileWithData(dataBuilder.addLines(newLineBuilder()
  261. .setDeprecatedItLineHits(1)
  262. .setDeprecatedItConditions(2)
  263. .setDeprecatedItCoveredConditions(3)).build(), privateProject.getMainBranchComponent());
  264. setUserWithValidPermission(privateProject);
  265. tester.newRequest()
  266. .setParam("uuid", file.uuid())
  267. .execute()
  268. .assertJson(getClass(), "convert_deprecated_data.json");
  269. }
  270. @Test
  271. public void fail_if_branch_does_not_exist() {
  272. ProjectData project = db.components().insertPrivateProject();
  273. ComponentDto file = db.components().insertComponent(newFileDto(project.getMainBranchComponent()));
  274. userSession.addProjectPermission(UserRole.USER, project.getProjectDto());
  275. db.components().insertProjectBranch(project.getProjectDto(), b -> b.setKey("my_branch"));
  276. assertThatThrownBy(() -> tester.newRequest()
  277. .setParam("key", file.getKey())
  278. .setParam("branch", "another_branch")
  279. .execute())
  280. .isInstanceOf(NotFoundException.class)
  281. .hasMessageContaining(String.format("Component '%s' on branch '%s' not found", file.getKey(), "another_branch"));
  282. }
  283. @Test
  284. public void fail_when_uuid_and_branch_params_are_used_together() {
  285. ProjectData project = db.components().insertPrivateProject();
  286. ComponentDto file = db.components().insertComponent(newFileDto(project.getMainBranchComponent()));
  287. userSession.addProjectPermission(UserRole.USER, project.getProjectDto());
  288. db.components().insertProjectBranch(project.getProjectDto(), b -> b.setKey("my_branch"));
  289. assertThatThrownBy(() -> tester.newRequest()
  290. .setParam("uuid", file.uuid())
  291. .setParam("branch", "another_branch")
  292. .execute())
  293. .isInstanceOf(IllegalArgumentException.class)
  294. .hasMessageContaining("Parameter 'uuid' cannot be used at the same time as 'branch' or 'pullRequest'");
  295. }
  296. @Test
  297. public void fail_when_using_branch_uuid() {
  298. ProjectData project = db.components().insertPrivateProject();
  299. BranchDto branch = db.components().insertProjectBranch(project.getProjectDto());
  300. userSession.addProjectPermission(UserRole.USER, project.getProjectDto());
  301. assertThatThrownBy(() -> tester.newRequest()
  302. .setParam("uuid", branch.getUuid())
  303. .execute())
  304. .isInstanceOf(NotFoundException.class)
  305. .hasMessageContaining(format("Component id '%s' not found", branch.getUuid()));
  306. }
  307. @Test
  308. public void hide_scmAuthors() {
  309. ProjectData projectData = db.components().insertPublicProject();
  310. ComponentDto mainBranch = projectData.getMainBranchComponent();
  311. userSession.registerProjects(projectData.getProjectDto());
  312. userSession.addProjectBranchMapping(projectData.projectUuid(), mainBranch);
  313. DbFileSources.Data data = DbFileSources.Data.newBuilder()
  314. .addLines(newLineBuilder().setScmAuthor("isaac@asimov.com"))
  315. .build();
  316. ComponentDto file = insertFileWithData(data, mainBranch);
  317. tester.newRequest()
  318. .setParam("uuid", file.uuid())
  319. .execute()
  320. .assertJson(getClass(), "hide_scmAuthors.json");
  321. }
  322. @Test
  323. public void show_scmAuthors() {
  324. ProjectData projectData = db.components().insertPublicProject();
  325. ComponentDto mainBranch = projectData.getMainBranchComponent();
  326. UserDto user = db.users().insertUser();
  327. userSession.logIn(user).registerProjects(projectData.getProjectDto());
  328. userSession.addProjectBranchMapping(projectData.projectUuid(), mainBranch);
  329. DbFileSources.Data data = DbFileSources.Data.newBuilder()
  330. .addLines(newLineBuilder().setScmAuthor("isaac@asimov.com"))
  331. .build();
  332. ComponentDto file = insertFileWithData(data, mainBranch);
  333. tester.newRequest()
  334. .setParam("uuid", file.uuid())
  335. .execute()
  336. .assertJson(getClass(), "show_scmAuthors.json");
  337. }
  338. private ComponentDto insertFileWithData(DbFileSources.Data fileData, ComponentDto project) {
  339. ComponentDto file = insertFile(project);
  340. db.getDbClient().fileSourceDao().insert(db.getSession(), new FileSourceDto()
  341. .setUuid(Uuids.createFast())
  342. .setProjectUuid(project.branchUuid())
  343. .setFileUuid(file.uuid())
  344. .setSourceData(fileData));
  345. db.commit();
  346. return file;
  347. }
  348. private void setUserWithValidPermission(ProjectData privateProject) {
  349. userSession.logIn("login")
  350. .addProjectPermission(UserRole.CODEVIEWER, privateProject.getProjectDto())
  351. .registerBranches(privateProject.getMainBranchDto());
  352. }
  353. private ComponentDto insertFile(ComponentDto project) {
  354. ComponentDto file = newFileDto(project);
  355. componentDao.insertOnMainBranch(db.getSession(), file);
  356. db.getSession().commit();
  357. return file;
  358. }
  359. private DbFileSources.Line.Builder newLineBuilder() {
  360. return DbFileSources.Line.newBuilder()
  361. .setLine(1)
  362. .setScmRevision("REVISION_" + 1)
  363. .setScmAuthor("AUTHOR_" + 1)
  364. .setScmDate(1_500_000_000_00L)
  365. .setSource("SOURCE_" + 1);
  366. }
  367. private void insertPeriod(ComponentDto componentDto, long date) {
  368. SnapshotDto dto = new SnapshotDto();
  369. dto.setUuid("uuid");
  370. dto.setLast(true);
  371. dto.setPeriodDate(date);
  372. dto.setRootComponentUuid(componentDto.uuid());
  373. snapshotDao.insert(db.getSession(), dto);
  374. }
  375. }