-
Notifications
You must be signed in to change notification settings - Fork 2.2k
[App Configuration] Add test case for feature management lib #41498
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
bd6c6be
add feature management common test cases
ivywei0125 7a582c4
add test case for no filters
ivywei0125 73b7702
initialize feature manager and call `featureManager.isEnabled()` to c…
ivywei0125 6c80093
update the schema of test case
ivywei0125 f9c7db7
get sample file and tests file by listFiles api
ivywei0125 745943e
update the todo comment.
ivywei0125 3208660
use `getContextClassLoader().getResource` to get the resource folder …
ivywei0125 9f90610
address comment: typo wording error fix, add "@SuppressWarnings"
ivywei0125 9cff351
address comment: typo wording error fix
ivywei0125 9a7c544
address comment: update member name
ivywei0125 f4e8833
add running log
ivywei0125 b23e1af
use contains to filter out the "TargetingFilter.sample.json"
ivywei0125 a7ffe46
add some info log
ivywei0125 8234479
sort the file list
ivywei0125 94ceef2
move to another the package to avoid making feature manager construct…
ivywei0125 c50b3a6
address comment: use logging, remove else
ivywei0125 df174c1
address comment: throw exception when empty "feature_management" sect…
ivywei0125 1117374
address comment: rename symbol
ivywei0125 7d77d46
Update ValidationsTest.java
mrm9084 0f96591
fixing linting
mrm9084 7eccbdc
address comment: ignore both targeting filter test case
ivywei0125 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
174 changes: 174 additions & 0 deletions
174
...e-management/src/test/java/com/azure/spring/cloud/feature/management/ValidationsTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
| package com.azure.spring.cloud.feature.management; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertEquals; | ||
| import static org.junit.jupiter.api.Assertions.assertNull; | ||
| import static org.mockito.Mockito.when; | ||
|
|
||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.net.URL; | ||
| import java.nio.file.Files; | ||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.Comparator; | ||
| import java.util.LinkedHashMap; | ||
| import java.util.List; | ||
| import java.util.stream.Stream; | ||
|
|
||
| import org.junit.jupiter.api.AfterEach; | ||
| import org.junit.jupiter.api.BeforeEach; | ||
| import org.junit.jupiter.api.extension.ExtendWith; | ||
| import org.junit.jupiter.params.ParameterizedTest; | ||
| import org.junit.jupiter.params.provider.Arguments; | ||
| import org.junit.jupiter.params.provider.MethodSource; | ||
| import org.mockito.Mock; | ||
| import org.mockito.Mockito; | ||
| import org.mockito.MockitoAnnotations; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.context.ApplicationContext; | ||
| import org.springframework.test.context.junit.jupiter.SpringExtension; | ||
|
|
||
| import com.azure.spring.cloud.feature.management.filters.TargetingFilter; | ||
| import com.azure.spring.cloud.feature.management.filters.TargetingFilterTestContextAccessor; | ||
| import com.azure.spring.cloud.feature.management.filters.TimeWindowFilter; | ||
| import com.azure.spring.cloud.feature.management.implementation.FeatureManagementConfigProperties; | ||
| import com.azure.spring.cloud.feature.management.implementation.FeatureManagementProperties; | ||
| import com.azure.spring.cloud.feature.management.validationstests.models.ValidationTestCase; | ||
| import com.fasterxml.jackson.core.type.TypeReference; | ||
| import com.fasterxml.jackson.databind.MapperFeature; | ||
| import com.fasterxml.jackson.databind.ObjectMapper; | ||
| import com.fasterxml.jackson.databind.json.JsonMapper; | ||
| import com.fasterxml.jackson.databind.type.CollectionType; | ||
| import com.fasterxml.jackson.databind.type.TypeFactory; | ||
|
|
||
| @ExtendWith(SpringExtension.class) | ||
| public class ValidationsTest { | ||
| @Mock | ||
| private ApplicationContext context; | ||
|
|
||
| @Mock | ||
| private FeatureManagementConfigProperties configProperties; | ||
|
|
||
| private static final Logger LOGGER = LoggerFactory.getLogger(ValidationsTest.class); | ||
|
|
||
| private static final ObjectMapper OBJECT_MAPPER = JsonMapper.builder() | ||
| .configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true).build(); | ||
|
|
||
| private static final String TEST_CASE_FOLDER_PATH = "validations-tests"; | ||
|
|
||
| private final String inputsUser = "user"; | ||
|
|
||
| private final String inputsGroups = "groups"; | ||
|
|
||
| private static final String SAMPLE_FILE_NAME_FILTER = "sample"; | ||
|
|
||
| private static final String TESTS_FILE_NAME_FILTER = "tests"; | ||
|
|
||
| @BeforeEach | ||
| public void setup() { | ||
| MockitoAnnotations.openMocks(this); | ||
| when(configProperties.isFailFast()).thenReturn(true); | ||
| when(context.getBean(Mockito.contains("TimeWindow"))).thenReturn(new TimeWindowFilter()); | ||
| } | ||
|
|
||
| @AfterEach | ||
| public void cleanup() throws Exception { | ||
| MockitoAnnotations.openMocks(this).close(); | ||
| } | ||
|
|
||
| private boolean hasException(ValidationTestCase testCase) { | ||
| final String exceptionStr = testCase.getIsEnabled().getException(); | ||
| return exceptionStr != null && !exceptionStr.isEmpty(); | ||
| } | ||
|
|
||
| private boolean hasInput(ValidationTestCase testCase) { | ||
| final LinkedHashMap<String, Object> inputsMap = testCase.getInputs(); | ||
| return inputsMap != null && !inputsMap.isEmpty(); | ||
| } | ||
|
|
||
| private static File[] getFileList(String fileNameFilter) { | ||
| final URL folderUrl = Thread.currentThread().getContextClassLoader().getResource(TEST_CASE_FOLDER_PATH); | ||
| assert folderUrl != null; | ||
|
|
||
| final File folderFile = new File(folderUrl.getFile()); | ||
| final File[] filteredFiles = folderFile | ||
| .listFiles(pathname -> pathname.getName().toLowerCase().contains(fileNameFilter)); | ||
| assert filteredFiles != null; | ||
|
|
||
| Arrays.sort(filteredFiles, Comparator.comparing(File::getName)); | ||
| return filteredFiles; | ||
| } | ||
|
|
||
| private List<ValidationTestCase> readTestcasesFromFile(File testFile) throws IOException { | ||
| final String jsonString = Files.readString(testFile.toPath()); | ||
| final CollectionType typeReference = TypeFactory.defaultInstance().constructCollectionType(List.class, | ||
| ValidationTestCase.class); | ||
| return OBJECT_MAPPER.readValue(jsonString, typeReference); | ||
| } | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| private static LinkedHashMap<String, Object> readConfigurationFromFile(File sampleFile) throws IOException { | ||
| final String jsonString = Files.readString(sampleFile.toPath()); | ||
| final LinkedHashMap<String, Object> configurations = OBJECT_MAPPER.readValue(jsonString, new TypeReference<>() { | ||
| }); | ||
| final Object featureManagementSection = configurations.get("feature_management"); | ||
| if (featureManagementSection.getClass().isAssignableFrom(LinkedHashMap.class)) { | ||
| return (LinkedHashMap<String, Object>) featureManagementSection; | ||
| } | ||
| throw new IllegalArgumentException("feature_management part is not a map"); | ||
| } | ||
|
|
||
| static Stream<Arguments> testProvider() throws IOException { | ||
| List<Arguments> arguments = new ArrayList<>(); | ||
| File[] files = getFileList(TESTS_FILE_NAME_FILTER); | ||
|
|
||
| final File[] sampleFiles = getFileList(SAMPLE_FILE_NAME_FILTER); | ||
| List<FeatureManagementProperties> properties = new ArrayList<>(); | ||
| for (File sampleFile : sampleFiles) { | ||
| final FeatureManagementProperties managementProperties = new FeatureManagementProperties(); | ||
| managementProperties.putAll(readConfigurationFromFile(sampleFile)); | ||
| properties.add(managementProperties); | ||
| } | ||
|
|
||
| for (int i = 0; i < files.length; i++) { | ||
| if (files[i].getName().contains(("TargetingFilter"))) { | ||
| continue; // TODO(mametcal). Not run the test case until we release the little endian fix | ||
| } | ||
| arguments.add(Arguments.of(files[i].getName(), files[i], properties.get(i))); | ||
| } | ||
|
|
||
| return arguments.stream(); | ||
| } | ||
|
|
||
| @ParameterizedTest(name = "{0}") | ||
| @MethodSource("testProvider") | ||
| void validationTest(String name, File testsFile, FeatureManagementProperties managementProperties) | ||
| throws IOException { | ||
| LOGGER.debug("Running test case from file: " + name); | ||
| final FeatureManager featureManager = new FeatureManager(context, managementProperties, configProperties); | ||
| List<ValidationTestCase> testCases = readTestcasesFromFile(testsFile); | ||
| for (ValidationTestCase testCase : testCases) { | ||
| LOGGER.debug("Test case : " + testCase.getDescription()); | ||
| if (hasException(testCase)) { // TODO(mametcal). Currently we didn't throw the exception when parameter is | ||
| // invalid | ||
| assertNull(managementProperties.getOnOff().get(testCase.getFeatureFlagName())); | ||
| continue; | ||
| } | ||
| if (hasInput(testCase)) { // Set inputs | ||
| final Object userObj = testCase.getInputs().get(inputsUser); | ||
| final Object groupsObj = testCase.getInputs().get(inputsGroups); | ||
| final String user = userObj != null ? userObj.toString() : null; | ||
| @SuppressWarnings("unchecked") | ||
| final List<String> groups = groupsObj != null ? (List<String>) groupsObj : null; | ||
| when(context.getBean(Mockito.contains("Targeting"))) | ||
| .thenReturn(new TargetingFilter(new TargetingFilterTestContextAccessor(user, groups))); | ||
| } | ||
|
|
||
| final Boolean result = featureManager.isEnabled(testCase.getFeatureFlagName()); | ||
| assertEquals(result.toString(), testCase.getIsEnabled().getResult()); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
...com/azure/spring/cloud/feature/management/filters/TargetingFilterTestContextAccessor.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
| package com.azure.spring.cloud.feature.management.filters; | ||
|
|
||
| import com.azure.spring.cloud.feature.management.targeting.TargetingContext; | ||
| import com.azure.spring.cloud.feature.management.targeting.TargetingContextAccessor; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public class TargetingFilterTestContextAccessor implements TargetingContextAccessor { | ||
|
|
||
| private String user; | ||
|
|
||
| private List<String> groups; | ||
|
|
||
| public TargetingFilterTestContextAccessor(String user, List<String> groups) { | ||
| this.user = user; | ||
| this.groups = groups; | ||
| } | ||
|
|
||
| @Override | ||
| public void configureTargetingContext(TargetingContext context) { | ||
| context.setUserId(user); | ||
| context.setGroups(groups); | ||
| } | ||
|
|
||
| } |
36 changes: 36 additions & 0 deletions
36
...est/java/com/azure/spring/cloud/feature/management/validationstests/models/IsEnabled.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| // Copyright (c) Microsoft Corporation. All rights reserved. | ||
| // Licensed under the MIT License. | ||
| package com.azure.spring.cloud.feature.management.validationstests.models; | ||
|
|
||
| public class IsEnabled { | ||
| private String result; | ||
| private String exception; | ||
|
|
||
| /** | ||
| * @return result | ||
| * */ | ||
| public String getResult() { | ||
| return result; | ||
| } | ||
|
|
||
| /** | ||
| * @param result the result of validation test case | ||
| * */ | ||
| public void setResult(String result) { | ||
| this.result = result; | ||
| } | ||
|
|
||
| /** | ||
| * @return exception | ||
| * */ | ||
| public String getException() { | ||
| return exception; | ||
| } | ||
|
|
||
| /** | ||
| * @param exception the exception message throws when run test case | ||
| * */ | ||
| public void setException(String exception) { | ||
| this.exception = exception; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.