All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.liftwizard.junit.extension.match.json.JsonMatchExtension Maven / Gradle / Ivy

There is a newer version: 2.1.21
Show newest version
/*
 * Copyright 2024 Craig Motlin
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.liftwizard.junit.extension.match.json;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Objects;
import java.util.Optional;

import javax.annotation.Nonnull;

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.dropwizard.jackson.Jackson;
import io.liftwizard.junit.extension.match.AbstractMatchExtension;
import io.liftwizard.junit.extension.match.FileSlurper;
import io.liftwizard.serialization.jackson.config.ObjectMapperConfig;
import org.json.JSONException;
import org.skyscreamer.jsonassert.JSONCompare;
import org.skyscreamer.jsonassert.JSONCompareMode;
import org.skyscreamer.jsonassert.JSONCompareResult;

public class JsonMatchExtension
        extends AbstractMatchExtension
{
    private final ObjectMapper objectMapper;

    public JsonMatchExtension(@Nonnull Class callingClass)
    {
        this(callingClass, JsonMatchExtension.newObjectMapper());
    }

    public JsonMatchExtension(@Nonnull Class callingClass, ObjectMapper objectMapper)
    {
        super(callingClass);
        this.objectMapper = Objects.requireNonNull(objectMapper);
    }

    private static ObjectMapper newObjectMapper()
    {
        ObjectMapper objectMapper = Jackson.newObjectMapper();
        ObjectMapperConfig.configure(objectMapper);
        return objectMapper;
    }

    @Override
    protected void assertFileContentsOrThrow(
            @Nonnull String resourceClassPathLocation,
            @Nonnull String actualString)
            throws IOException, URISyntaxException
    {
        if (this.resourceRerecorderExtension.mustRerecord(resourceClassPathLocation))
        {
            String prettyPrintedString = this.getPrettyPrintedString(actualString);

            Path packagePath = this.resourceRerecorderExtension.getPackagePath();
            File resourceFile = packagePath.resolve(resourceClassPathLocation).toFile();

            this.resourceRerecorderExtension.writeStringToFile(
                    resourceClassPathLocation,
                    prettyPrintedString,
                    resourceFile);
            if (!this.rerecordEnabled)
            {
                String detailMessage = resourceClassPathLocation + " did not exist. Created it.";
                this.errorCollectorExtension.addError(new AssertionError(detailMessage));
            }
        }
        else
        {
            InputStream inputStream = this.callingClass.getResourceAsStream(resourceClassPathLocation);
            Objects.requireNonNull(inputStream, () -> resourceClassPathLocation + " not found.");
            String expectedStringFromFile = FileSlurper.slurp(inputStream, StandardCharsets.UTF_8);

            URL resource = Objects.requireNonNull(this.callingClass.getResource(resourceClassPathLocation));
            URI uri = resource.toURI();

            if (!this.validateExpectedStringFromFile(expectedStringFromFile, uri))
            {
                return;
            }

            String fileContents = this.getPrettyPrintedString(actualString);
            Optional message = this.compareAndGetDiff(fileContents, expectedStringFromFile);
            if (message.isPresent())
            {
                String detailMessage = this.resourceRerecorderExtension.handleMismatch(
                        resourceClassPathLocation,
                        fileContents);
                AssertionError assertionError = new AssertionError(detailMessage);
                this.errorCollectorExtension.addError(assertionError);
            }
        }
    }

    protected Optional compareAndGetDiff(@Nonnull String actualString, String expectedStringFromFile)
    {
        try
        {
            JSONCompareResult result = JSONCompare.compareJSON(
                    expectedStringFromFile,
                    actualString,
                    JSONCompareMode.STRICT);
            if (result.passed())
            {
                return Optional.empty();
            }

            if (result.failed())
            {
                String message = result.getMessage();
                return Optional.of(message);
            }

            throw new AssertionError(result);
        }
        catch (JSONException e)
        {
            throw new RuntimeException(e);
        }
    }

    protected boolean validateExpectedStringFromFile(String expectedStringFromFile, URI uri)
    {
        try
        {
            this.objectMapper.readTree(expectedStringFromFile);
            return true;
        }
        catch (JacksonException e)
        {
            String detailMessage = "Invalid JSON in %s:%n%s".formatted(
                    uri,
                    expectedStringFromFile);
            AssertionError assertionError = new AssertionError(detailMessage, e);
            this.errorCollectorExtension.addError(assertionError);
            return false;
        }
    }

    @Override
    protected String getPrettyPrintedString(@Nonnull String string)
    {
        try
        {
            JsonNode jsonNode = this.objectMapper.readTree(string);
            String prettyPrintedString = this.objectMapper.writeValueAsString(jsonNode);
            return prettyPrintedString;
        }
        catch (JsonProcessingException e)
        {
            throw new RuntimeException(e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy