/* * 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. */ package org.sonar.core.sarif; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.junit.MockitoJUnitRunner; import static java.lang.String.format; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.fail; import static org.sonar.core.sarif.SarifVersionValidator.UNSUPPORTED_VERSION_MESSAGE_TEMPLATE; @RunWith(MockitoJUnitRunner.class) public class SarifSerializerImplTest { private static final String SARIF_JSON = "{\"version\":\"2.1.0\",\"$schema\":\"http://json.schemastore.org/sarif-2.1.0-rtm.4\",\"runs\":[{\"results\":[]}]}"; private final SarifSerializerImpl serializer = new SarifSerializerImpl(); @Test public void serialize() { Run.builder().results(Set.of()).build(); Sarif210 sarif210 = new Sarif210("http://json.schemastore.org/sarif-2.1.0-rtm.4", Run.builder().results(Set.of()).build()); String result = serializer.serialize(sarif210); assertThat(result).isEqualTo(SARIF_JSON); } @Test public void deserialize() throws URISyntaxException, NoSuchFileException { URL sarifResource = requireNonNull(getClass().getResource("eslint-sarif210.json")); Path sarif = Paths.get(sarifResource.toURI()); Sarif210 deserializationResult = serializer.deserialize(sarif); verifySarif(deserializationResult); } @Test public void deserialize_shouldFail_whenFileCantBeFound() { String file = "wrongPathToFile"; Path sarif = Paths.get(file); assertThatThrownBy(() -> serializer.deserialize(sarif)) .isInstanceOf(NoSuchFileException.class); } @Test public void deserialize_shouldFail_whenJsonSyntaxIsIncorrect() throws URISyntaxException { URL sarifResource = requireNonNull(getClass().getResource("invalid-json-syntax.json")); Path sarif = Paths.get(sarifResource.toURI()); assertThatThrownBy(() -> serializer.deserialize(sarif)) .isInstanceOf(IllegalStateException.class) .hasMessage(format("Failed to read SARIF report at '%s': invalid JSON syntax or file is not UTF-8 encoded", sarif)); } @Test public void deserialize_whenFileIsNotUtf8encoded_shouldFail() throws URISyntaxException { URL sarifResource = requireNonNull(getClass().getResource("sarif210-nonUtf8.json")); Path sarif = Paths.get(sarifResource.toURI()); assertThatThrownBy(() -> serializer.deserialize(sarif)) .isInstanceOf(IllegalStateException.class) .hasMessage(format("Failed to read SARIF report at '%s': invalid JSON syntax or file is not UTF-8 encoded", sarif)); } @Test public void deserialize_shouldFail_whenSarifVersionIsNotSupported() throws URISyntaxException { URL sarifResource = requireNonNull(getClass().getResource("unsupported-sarif-version-abc.json")); Path sarif = Paths.get(sarifResource.toURI()); assertThatThrownBy(() -> serializer.deserialize(sarif)) .isInstanceOf(IllegalStateException.class) .hasMessage(format(UNSUPPORTED_VERSION_MESSAGE_TEMPLATE, "A.B.C")); } private void verifySarif(Sarif210 deserializationResult) { Sarif210 expected = buildExpectedSarif210(); assertThat(deserializationResult).isNotNull(); assertThat(deserializationResult).usingRecursiveComparison().ignoringFields("runs").isEqualTo(expected); Run run = getRun(deserializationResult); Run expectedRun = getRun(expected); assertThat(run).usingRecursiveComparison().ignoringFields("results", "tool.driver.rules").isEqualTo(expectedRun); Result result = getResult(run); Result expectedResult = getResult(expectedRun); assertThat(result).usingRecursiveComparison().isEqualTo(expectedResult); Rule rule = getRule(run); Rule expectedRule = getRule(expectedRun); assertThat(rule).usingRecursiveComparison().ignoringFields("properties").isEqualTo(expectedRule); } private static Sarif210 buildExpectedSarif210() { return new Sarif210("http://json.schemastore.org/sarif-2.1.0-rtm.4", buildExpectedRun()); } private static Run buildExpectedRun() { Tool tool = new Tool(buildExpectedDriver()); return Run.builder() .tool(tool) .results(Set.of(buildExpectedResult())).build(); } private static Driver buildExpectedDriver() { return Driver.builder() .name("ESLint") .rules(Set.of(buildExpectedRule())) .build(); } private static Rule buildExpectedRule() { return Rule.builder() .id("no-unused-vars") .shortDescription("disallow unused variables") .build(); } private static Result buildExpectedResult() { return Result.builder() .ruleId("no-unused-vars") .locations(Set.of(buildExpectedLocation())) .message("'x' is assigned a value but never used.") .level("error") .build(); } private static Location buildExpectedLocation() { ArtifactLocation artifactLocation = new ArtifactLocation(null, "file:///C:/dev/sarif/sarif-tutorials/samples/Introduction/simple-example.js"); PhysicalLocation physicalLocation = PhysicalLocation.of(artifactLocation, buildExpectedRegion()); return Location.of(physicalLocation); } private static Region buildExpectedRegion() { return Region.builder() .startLine(1) .startColumn(5) .build(); } private static Run getRun(Sarif210 sarif210) { return sarif210.getRuns().stream().findFirst().orElseGet(() -> fail("runs property is missing")); } private static Result getResult(Run run) { return run.getResults().stream().findFirst().orElseGet(() -> fail("results property is missing")); } private static Rule getRule(Run run) { return run.getTool().getDriver().getRules().stream().findFirst().orElseGet(() -> fail("rules property is missing")); } }