/*
 * Copyright 2012-2021 the original author or authors.
 *
 * 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
 *
 *      https://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 org.springframework.boot.validation;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import javax.validation.constraints.NotNull;

import org.junit.jupiter.api.Test;

import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

/**
 * Integration tests for {@link MessageSourceMessageInterpolator}.
 *
 * @author Dmytro Nosan
 */
class MessageSourceMessageInterpolatorIntegrationTests {

	private final Validator validator = buildValidator();

	@NotNull
	private String defaultMessage;

	@NotNull(message = "{null}")
	private String nullable;

	@NotNull(message = "{blank}")
	private String blank;

	@NotNull(message = "{unknown}")
	private String unknown;

	@NotNull(message = "{recursion}")
	private String recursion;

	@NotNull(message = "\\{null}")
	private String escapePrefix;

	@NotNull(message = "{null\\}")
	private String escapeSuffix;

	@NotNull(message = "\\{null\\}")
	private String escapePrefixSuffix;

	@NotNull(message = "\\\\{null}")
	private String escapeEscape;

	@Test
	void defaultMessage() {
		assertThat(validate("defaultMessage")).containsExactly("must not be null");
	}

	@Test
	void nullable() {
		assertThat(validate("nullable")).containsExactly("must not be null");
	}

	@Test
	void blank() {
		assertThat(validate("blank")).containsExactly("must not be null or must not be blank");
	}

	@Test
	void recursion() {
		assertThatThrownBy(() -> validate("recursion"))
				.hasStackTraceContaining("Circular reference '{recursion -> middle -> recursion}'");
	}

	@Test
	void unknown() {
		assertThat(validate("unknown")).containsExactly("{unknown}");
	}

	@Test
	void escapePrefix() {
		assertThat(validate("escapePrefix")).containsExactly("\\{null}");
	}

	@Test
	void escapeSuffix() {
		assertThat(validate("escapeSuffix")).containsExactly("{null\\}");
	}

	@Test
	void escapePrefixSuffix() {
		assertThat(validate("escapePrefixSuffix")).containsExactly("{null}");
	}

	@Test
	void escapeEscape() {
		assertThat(validate("escapeEscape")).containsExactly("\\must not be null");
	}

	private List<String> validate(String property) {
		List<String> messages = new ArrayList<>();
		Set<ConstraintViolation<Object>> constraints = this.validator.validateProperty(this, property);
		for (ConstraintViolation<Object> constraint : constraints) {
			messages.add(constraint.getMessage());
		}
		return messages;
	}

	private static Validator buildValidator() {
		Locale locale = LocaleContextHolder.getLocale();
		StaticMessageSource messageSource = new StaticMessageSource();
		messageSource.addMessage("blank", locale, "{null} or {javax.validation.constraints.NotBlank.message}");
		messageSource.addMessage("null", locale, "{javax.validation.constraints.NotNull.message}");
		messageSource.addMessage("recursion", locale, "{middle}");
		messageSource.addMessage("middle", locale, "{recursion}");
		MessageInterpolatorFactory messageInterpolatorFactory = new MessageInterpolatorFactory(messageSource);
		try (LocalValidatorFactoryBean validatorFactory = new LocalValidatorFactoryBean()) {
			validatorFactory.setMessageInterpolator(messageInterpolatorFactory.getObject());
			validatorFactory.afterPropertiesSet();
			return validatorFactory.getValidator();
		}
	}

}

@@ -887,16 +887,97 @@
 tNull;%0A%0A
+import org.junit.jupiter.api.AfterEach;%0Aimport org.junit.jupiter.api.BeforeEach;%0A
 import o
@@ -1003,16 +1003,16 @@
 i.Test;%0A
-
 %0Aimport 
@@ -1499,64 +1499,8 @@
  %7B%0A%0A
-%09private final Validator validator = buildValidator();%0A%0A
 %09@No
@@ -1995,32 +1995,32 @@
 = %22%5C%5C%5C%5C%7Bnull%7D%22)%0A
-
 %09private String 
@@ -2034,16 +2034,310 @@
 scape;%0A%0A
+%09private Locale defaultLocale;%0A%0A%09private Validator validator;%0A%0A%09@BeforeEach%0A%09void init() %7B%0A%09%09this.defaultLocale = Locale.getDefault();%0A%09%09Locale.setDefault(Locale.ENGLISH);%0A%09%09this.validator = buildValidator();%0A%09%7D%0A%0A%09@AfterEach%0A%09void restoreLocale() %7B%0A%09%09Locale.setDefault(this.defaultLocale);%0A%09%7D%0A%0A
 %09@Test%0A%09

Fix tests on non English systems