/*
 * Copyright (c) 2010-2021. Axon Framework
 *
 * 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.axonframework.springboot.util;

import com.thoughtworks.xstream.XStream;
import org.junit.jupiter.api.*;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.jmx.support.RegistrationPolicy;
import org.springframework.test.context.ContextConfiguration;

import static org.mockito.AdditionalMatchers.*;
import static org.mockito.Mockito.*;

/**
 * Test class validating the {@link XStreamSecurityTypeUtility} through the {@link ApplicationContextRunner}. This
 * ensures an actual {@link org.springframework.context.ApplicationContext} is used for validating.
 *
 * @author Steven van Beelen
 */
class XStreamSecurityTypeUtilityTest {

    private ApplicationContextRunner testApplicationContext;
    private XStream testSubject;

    @BeforeEach
    void setUp() {
        testApplicationContext = new ApplicationContextRunner().withPropertyValues("axon.axonserver.enabled:false");
        testSubject = spy(new XStream());
    }

    @Test
    void testEmptyComponentScanAnnotatedBeanAllowsBeanPackageNameAsWildcard() {
        String expectedPackageWildcard = "org.axonframework.springboot.util.**";
        testApplicationContext.withUserConfiguration(TestContextWithEmptyComponentScan.class)
                              .run(context -> {
                                  XStreamSecurityTypeUtility.allowTypesFromComponentScanAnnotatedBeans(
                                          context.getSourceApplicationContext(), testSubject
                                  );
                                  verify(testSubject).allowTypesByWildcard(aryEq(new String[]{expectedPackageWildcard}));
                              });
    }

    @Test
    void testBasePackageClassesComponentScanAnnotatedBeanAllowsTypes() {
        Class<String> expectedType = String.class;
        testApplicationContext.withUserConfiguration(TestContextWithBasePackageClassesComponentScan.class)
                              .run(context -> {
                                  XStreamSecurityTypeUtility.allowTypesFromComponentScanAnnotatedBeans(
                                          context.getSourceApplicationContext(), testSubject
                                  );
                                  verify(testSubject).allowTypes(aryEq(new Class[]{expectedType}));
                              });
    }

    @Test
    void testBasePackagesComponentScanAnnotatedBeanAllowsTypeByWildcard() {
        String expectedPackageWildcard = "foo.bar.**";
        testApplicationContext.withUserConfiguration(TestContextWithBasePackagesComponentScan.class)
                              .run(context -> {
                                  XStreamSecurityTypeUtility.allowTypesFromComponentScanAnnotatedBeans(
                                          context.getSourceApplicationContext(), testSubject
                                  );
                                  verify(testSubject).allowTypesByWildcard(aryEq(new String[]{expectedPackageWildcard}));
                              });
    }

    @Test
    void testValuesComponentScanAnnotatedBeanAllowsTypeByWildcard() {
        String expectedPackageWildcard = "bar.baz.**";
        testApplicationContext.withUserConfiguration(TestContextWithValuesComponentScan.class)
                              .run(context -> {
                                  XStreamSecurityTypeUtility.allowTypesFromComponentScanAnnotatedBeans(
                                          context.getSourceApplicationContext(), testSubject
                                  );
                                  verify(testSubject).allowTypesByWildcard(aryEq(new String[]{expectedPackageWildcard}));
                              });
    }

    @Test
    void testSpringBootApplicationAnnotatedBeanAllowsBeanPackageNameAsWildcard() {
        String expectedPackageWildcard = "org.axonframework.springboot.util.**";
        testApplicationContext.withUserConfiguration(TestContextWithSpringBootApplication.class)
                              .run(context -> {
                                  XStreamSecurityTypeUtility.allowTypesFromComponentScanAnnotatedBeans(
                                          context.getSourceApplicationContext(), testSubject
                                  );
                                  verify(testSubject).allowTypesByWildcard(aryEq(new String[]{expectedPackageWildcard}));
                              });
    }

    @ContextConfiguration
    @EnableAutoConfiguration
    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
    private static class TestContextWithEmptyComponentScan {

        @Bean
        private MainClass mainClass() {
            return new MainClass();
        }

        @ComponentScan
        private static class MainClass {

        }
    }

    @ContextConfiguration
    @EnableAutoConfiguration
    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
    private static class TestContextWithBasePackageClassesComponentScan {

        @Bean
        private MainClass mainClass() {
            return new MainClass();
        }

        @ComponentScan(basePackageClasses = String.class)
        private static class MainClass {

        }
    }

    @ContextConfiguration
    @EnableAutoConfiguration
    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
    private static class TestContextWithBasePackagesComponentScan {

        @Bean
        private MainClass mainClass() {
            return new MainClass();
        }

        @ComponentScan(basePackages = "foo.bar")
        private static class MainClass {

        }
    }

    @ContextConfiguration
    @EnableAutoConfiguration
    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
    private static class TestContextWithValuesComponentScan {

        @Bean
        private MainClass mainClass() {
            return new MainClass();
        }

        @ComponentScan(value = "bar.baz")
        private static class MainClass {

        }
    }

    @ContextConfiguration
    @EnableAutoConfiguration
    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)
    private static class TestContextWithSpringBootApplication {

        @Bean
        private MainClass mainClass() {
            return new MainClass();
        }

        @SpringBootApplication
        private static class MainClass {

        }
    }
}
@@ -5357,32 +5357,1008 @@
      %7D);%0A    %7D%0A%0A
+    @Test%0A    void testBasePackagesAndClassesSpringBootApplicationAnnotatedBeanAllowsTypesAndAllowsTypesByWildcard() %7B%0A        Class%3CInteger%3E expectedType = Integer.class;%0A        String expectedPackageWildcard = %22foo.bar.baz.**%22;%0A        testApplicationContext.withUserConfiguration(%0A                                      TestContextWithBasePackageClassesAndBasePackagesSpringBootApplication.class%0A                              )%0A                              .run(context -%3E %7B%0A                                  XStreamSecurityTypeUtility.allowTypesFromComponentScanAnnotatedBeans(%0A                                          context.getSourceApplicationContext(), testSubject%0A                                  );%0A                                  verify(testSubject).allowTypes(aryEq(new Class%5B%5D%7BexpectedType%7D));%0A                                  verify(testSubject).allowTypesByWildcard(aryEq(new String%5B%5D%7BexpectedPackageWildcard%7D));%0A                              %7D);%0A    %7D%0A%0A
     @ContextConf
@@ -8298,32 +8298,524 @@
 ss MainClass %7B%0A%0A
+        %7D%0A    %7D%0A%0A    @ContextConfiguration%0A    @EnableAutoConfiguration%0A    @EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING)%0A    private static class TestContextWithBasePackageClassesAndBasePackagesSpringBootApplication %7B%0A%0A        @Bean%0A        private MainClass mainClass() %7B%0A            return new MainClass();%0A        %7D%0A%0A        @SpringBootApplication(scanBasePackageClasses = Integer.class, scanBasePackages = %22foo.bar.baz%22)%0A        private static class MainClass %7B%0A%0A
         %7D%0A    %7D%0A

Add test case