/*
 * Copyright 2002-2017 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
 *
 *      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 org.springframework.test.web.reactive.server.samples.bind;

import java.security.Principal;
import java.util.function.UnaryOperator;

import org.junit.Test;
import reactor.core.publisher.Mono;

import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;

import static org.junit.Assert.assertEquals;

/**
 * Bind to annotated controllers.
 *
 * @author Rossen Stoyanchev
 * @since 5.0
 */
public class ControllerTests {

	private final WebTestClient client = WebTestClient.bindToController(new TestController())
			.exchangeMutator(principal("Pablo"))
			.webFilter(prefixFilter("Mr."))
			.build();


	@Test
	public void bodyContent() throws Exception {
		this.client.get().uri("/principal")
				.exchange()
				.expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello Mr. Pablo!");
	}

	@Test
	public void bodyContentWithConsumer() throws Exception {
		this.client.get().uri("/principal")
				.exchange()
				.expectStatus().isOk()
				.expectBody().consumeAsStringWith(body -> assertEquals("Hello Mr. Pablo!", body));
	}

	@Test
	public void perRequestExchangeMutator() throws Exception {
		this.client.exchangeMutator(principal("Giovanni"))
				.get().uri("/principal")
				.exchange()
				.expectStatus().isOk()
				.expectBody(String.class).isEqualTo("Hello Mr. Giovanni!");
	}

	@Test
	public void perRequestMultipleExchangeMutators() throws Exception {
		this.client
				.exchangeMutator(attribute("attr1", "foo"))
				.exchangeMutator(attribute("attr2", "bar"))
				.get().uri("/attributes")
				.exchange()
				.expectStatus().isOk()
				.expectBody(String.class).isEqualTo("foo+bar");
	}


	private UnaryOperator<ServerWebExchange> principal(String userName) {
		return exchange -> exchange.mutate().principal(Mono.just(new TestUser(userName))).build();
	}

	private WebFilter prefixFilter(String prefix) {
		return (exchange, chain) -> {
			Mono<Principal> user = exchange.getPrincipal().map(p -> new TestUser(prefix + " " + p.getName()));
			return chain.filter(exchange.mutate().principal(user).build());
		};
	}

	private UnaryOperator<ServerWebExchange> attribute(String attrName, String attrValue) {
		return exchange -> {
			exchange.getAttributes().put(attrName, attrValue);
			return exchange;
		};
	}


	@RestController
	static class TestController {

		@GetMapping("/principal")
		public String handle(Principal principal) {
			return "Hello " + principal.getName() + "!";
		}

		@GetMapping("/attributes")
		public String handle(@RequestAttribute String attr1, @RequestAttribute String attr2) {
			return attr1 + "+" + attr2;
		}
	}

	private static class TestUser implements Principal {

		private final String name;

		TestUser(String name) {
			this.name = name;
		}

		@Override
		public String getName() {
			return this.name;
		}
	}

}
