001/**
002 * Copyright (C) 2006-2021 Talend Inc. - www.talend.com
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.talend.sdk.component.junit.http.internal.junit5;
017
018import java.lang.annotation.Annotation;
019import java.lang.reflect.InvocationTargetException;
020import java.util.Optional;
021import java.util.concurrent.Executor;
022import java.util.function.Predicate;
023import java.util.function.Supplier;
024
025import javax.net.ssl.SSLContext;
026
027import org.junit.jupiter.api.extension.AfterAllCallback;
028import org.junit.jupiter.api.extension.AfterEachCallback;
029import org.junit.jupiter.api.extension.BeforeAllCallback;
030import org.junit.jupiter.api.extension.BeforeEachCallback;
031import org.junit.jupiter.api.extension.ExtensionContext;
032import org.junit.platform.commons.util.AnnotationUtils;
033import org.talend.sdk.component.junit.base.junit5.JUnit5InjectionSupport;
034import org.talend.sdk.component.junit.http.api.HttpApiHandler;
035import org.talend.sdk.component.junit.http.api.ResponseLocator;
036import org.talend.sdk.component.junit.http.internal.impl.DefaultResponseLocator;
037import org.talend.sdk.component.junit.http.internal.impl.HandlerImpl;
038import org.talend.sdk.component.junit.http.internal.impl.Handlers;
039import org.talend.sdk.component.junit.http.junit5.HttpApi;
040import org.talend.sdk.component.junit.http.junit5.HttpApiInject;
041import org.talend.sdk.component.junit.http.junit5.HttpApiName;
042
043public class JUnit5HttpApi extends HttpApiHandler<JUnit5HttpApi>
044        implements BeforeAllCallback, AfterAllCallback, JUnit5InjectionSupport, AfterEachCallback, BeforeEachCallback {
045
046    private static final ExtensionContext.Namespace NAMESPACE =
047            ExtensionContext.Namespace.create(JUnit5HttpApi.class.getName());
048
049    @Override
050    public void beforeAll(final ExtensionContext extensionContext) {
051        final HttpApi config =
052                AnnotationUtils.findAnnotation(extensionContext.getElement(), HttpApi.class).orElse(null);
053        if (config != null) {
054            setGlobalProxyConfiguration(config.globalProxyConfiguration());
055            setLogLevel(config.logLevel());
056            setPort(config.port());
057            newInstance(config.responseLocator(), ResponseLocator.class).ifPresent(this::setResponseLocator);
058            newInstance(config.headerFilter(), Predicate.class).ifPresent(this::setHeaderFilter);
059            newInstance(config.executor(), Executor.class).ifPresent(this::setExecutor);
060            newInstance(config.sslContext(), Supplier.class)
061                    .map(s -> SSLContext.class.cast(s.get()))
062                    .ifPresent(this::setSslContext);
063            setSkipProxyHeaders(config.skipProxyHeaders());
064            if (config.useSsl()) {
065                activeSsl();
066            }
067        }
068        extensionContext.getStore(NAMESPACE).put(HttpApiHandler.class.getName(), this);
069        final HandlerImpl<JUnit5HttpApi> handler = new HandlerImpl<>(this, null, null);
070        extensionContext.getStore(NAMESPACE).put(HandlerImpl.class.getName(), handler);
071        handler.start();
072    }
073
074    @Override
075    public void afterAll(final ExtensionContext extensionContext) {
076        HandlerImpl.class.cast(extensionContext.getStore(NAMESPACE).get(HandlerImpl.class.getName())).close();
077    }
078
079    @Override
080    public Class<? extends Annotation> injectionMarker() {
081        return HttpApiInject.class;
082    }
083
084    @Override
085    public void beforeEach(final ExtensionContext extensionContext) {
086        // test name
087        final ResponseLocator responseLocator = getResponseLocator();
088        if (!DefaultResponseLocator.class.isInstance(responseLocator)) {
089            return;
090        }
091        final String test = extensionContext.getTestMethod().map(m -> {
092            final String displayName = sanitizeDisplayName(extensionContext.getDisplayName());
093            return AnnotationUtils
094                    .findAnnotation(m, HttpApiName.class)
095                    .map(HttpApiName::value)
096                    .map(it -> it.replace("${class}", m.getDeclaringClass().getName()))
097                    .map(it -> it.replace("${method}", m.getName()))
098                    .map(it -> it.replace("${displayName}", displayName))
099                    .orElseGet(() -> m.getDeclaringClass().getName() + "_" + m.getName()
100                            + (displayName.equals(m.getName()) ? "" : ("_" + displayName)));
101        }).orElse(null);
102        DefaultResponseLocator.class.cast(responseLocator).setTest(test);
103    }
104
105    @Override
106    public void afterEach(final ExtensionContext extensionContext) {
107        if (Handlers.isActive("capture")) {
108            Optional
109                    .of(getResponseLocator())
110                    .filter(DefaultResponseLocator.class::isInstance)
111                    .map(DefaultResponseLocator.class::cast)
112                    .ifPresent(r -> r.flush(Handlers.getBaseCapture()));
113        }
114    }
115
116    private String sanitizeDisplayName(final String displayName) {
117        final String base = displayName.replace(" ", "_");
118        final int parenthesis = base.indexOf('(');
119        if (parenthesis > 0) {
120            return base.substring(0, parenthesis);
121        }
122        return base;
123    }
124
125    private static <T> Optional<T> newInstance(final Class<?> type, final Class<T> api) {
126        if (api == type) {
127            return Optional.empty();
128        }
129        try {
130            return Optional.of(api.cast(type.getConstructor().newInstance()));
131        } catch (final NoSuchMethodException | InstantiationException | IllegalAccessException e) {
132            throw new IllegalArgumentException(e);
133        } catch (final InvocationTargetException e) {
134            throw new IllegalStateException(e.getTargetException());
135        }
136    }
137}