Java tutorial
/* * Copyright 2019 ThoughtWorks, Inc. * * 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 com.thoughtworks.go.server.service.support.toggle; import com.google.gson.Gson; import com.thoughtworks.go.server.domain.support.toggle.FeatureToggle; import com.thoughtworks.go.server.domain.support.toggle.FeatureToggles; import com.thoughtworks.go.util.SystemEnvironment; import com.thoughtworks.go.util.TestFileUtil; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.mockito.Mock; import java.io.File; import java.net.URISyntaxException; import java.text.MessageFormat; import java.util.ArrayList; import java.util.List; import static java.nio.charset.StandardCharsets.UTF_8; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; public class FeatureToggleRepositoryTest { public static final String TEST_AVAILABLE_TOGGLES_PATH = "/available.test.toggles"; @Rule public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Mock private SystemEnvironment environment; @Before public void setUp() throws Exception { initMocks(this); } @After public void tearDown() throws Exception { FileUtils.writeStringToFile(availableTogglesFile(), "", UTF_8); } @Test public void shouldReadFeatureTogglesFromAvailableTogglesFile() throws Exception { FeatureToggle featureToggle1 = new FeatureToggle("key1", "desc1", true); FeatureToggle featureToggle2 = new FeatureToggle("key2", "desc2", false); setupAvailableToggles(featureToggle1, featureToggle2); FeatureToggleRepository repository = new FeatureToggleRepository(environment); assertThat(repository.availableToggles(), is(new FeatureToggles(featureToggle1, featureToggle2))); } @Test public void shouldNotFailWhenSpecifiedAvailableTogglesFileIsNotFound() throws Exception { setupAvailableToggleFileAs("a-non-existent-file"); FeatureToggleRepository repository = new FeatureToggleRepository(environment); assertThat(repository.availableToggles(), is(new FeatureToggles())); } @Test public void shouldNotFailWhenContentOfAvailableTogglesFileIsInvalid() throws Exception { setupAvailableToggleFileAs(TEST_AVAILABLE_TOGGLES_PATH); FileUtils.writeStringToFile(availableTogglesFile(), "SOME-INVALID-CONTENT", UTF_8); FeatureToggleRepository repository = new FeatureToggleRepository(environment); assertThat(repository.availableToggles(), is(new FeatureToggles())); } @Test public void shouldReadFeatureTogglesFromUsersTogglesFile() throws Exception { FeatureToggle featureToggle1 = new FeatureToggle("key1", "desc1", true); FeatureToggle featureToggle2 = new FeatureToggle("key2", "desc2", false); setupUserToggles(featureToggle1, featureToggle2); FeatureToggleRepository repository = new FeatureToggleRepository(environment); assertThat(repository.userToggles(), is(new FeatureToggles(featureToggle1, featureToggle2))); } @Test public void shouldNotFailWhenSpecifiedUserTogglesFileIsNotFound() throws Exception { setupUserToggleFileAs(new File("a-non-existent-file")); FeatureToggleRepository repository = new FeatureToggleRepository(environment); assertThat(repository.userToggles(), is(new FeatureToggles())); } @Test public void shouldNotFailWhenContentOfUserTogglesFileIsInvalid() throws Exception { File toggleFile = TestFileUtil.createTempFile("available.toggle.test"); FileUtils.writeStringToFile(toggleFile, "SOME-INVALID-CONTENT", UTF_8); setupUserToggleFileAs(toggleFile); FeatureToggleRepository repository = new FeatureToggleRepository(environment); assertThat(repository.userToggles(), is(new FeatureToggles())); } @Test public void shouldAllowChangingValueOfAToggleWhenTheUserTogglesFileDoesNotExist() throws Exception { File togglesDir = temporaryFolder.newFolder("toggles.dir"); File nonExistentUserToggleFile = new File(togglesDir, "a-non-existent-file"); setupUserToggleFileAs(nonExistentUserToggleFile); setupAvailableToggles(new FeatureToggle("key1", "desc1", true)); FeatureToggleRepository repository = new FeatureToggleRepository(environment); repository.changeValueOfToggle("key1", false); assertThat(repository.availableToggles(), is(new FeatureToggles(new FeatureToggle("key1", "desc1", true)))); assertThat(repository.userToggles(), is(new FeatureToggles(new FeatureToggle("key1", null, false)))); } @Test public void shouldAllowChangingValueOfAToggleWhenTheUserTogglesFileDoesExist() throws Exception { setupAvailableToggles(new FeatureToggle("key1", "desc1", true), new FeatureToggle("key2", "desc2", true)); setupUserToggles(new FeatureToggle("key1", "desc1", true)); FeatureToggleRepository repository = new FeatureToggleRepository(environment); repository.changeValueOfToggle("key1", false); assertThat(repository.availableToggles(), is(new FeatureToggles(new FeatureToggle("key1", "desc1", true), new FeatureToggle("key2", "desc2", true)))); assertThat(repository.userToggles(), is(new FeatureToggles(new FeatureToggle("key1", "desc1", false)))); } @Test public void shouldFailWhenUnableToWriteToUserTogglesFile_DuringChangingOfAToggleValue() throws Exception { setupAvailableToggles(new FeatureToggle("key1", "desc1", true)); File userTogglesFile = setupUserToggles(new FeatureToggle("key1", "desc1", true)); userTogglesFile.setReadOnly(); FeatureToggleRepository repository = new FeatureToggleRepository(environment); try { repository.changeValueOfToggle("key1", false); fail("Should have failed to write"); } catch (RuntimeException e) { assertThat(e.getMessage(), containsString(userTogglesFile.getPath())); } } @Test public void whileChangingAToggleValue_shouldNotPersist_ValueHasBeenChangedFlag() throws Exception { String fieldForHasBeenChangedFlag = "hasBeenChangedFromDefault"; assertNotNull( "This can never be null, but can throw an exception. If you've renamed the field mentioned above" + "(in FeatureToggle class), please change it in this test too. Otherwise, this test can pass, wrongly.", FeatureToggle.class.getDeclaredField(fieldForHasBeenChangedFlag)); setupAvailableToggles(new FeatureToggle("key1", "desc1", true)); File userTogglesFile = setupUserToggles( new FeatureToggle("key1", "desc1", false).withValueHasBeenChangedFlag(true)); FeatureToggleRepository repository = new FeatureToggleRepository(environment); repository.changeValueOfToggle("key1", false); assertThat(repository.userToggles(), is( new FeatureToggles(new FeatureToggle("key1", "desc1", false).withValueHasBeenChangedFlag(false)))); assertThat(FileUtils.readFileToString(userTogglesFile, UTF_8), containsString("key1")); assertThat(FileUtils.readFileToString(userTogglesFile, UTF_8), containsString("desc1")); assertThat(FileUtils.readFileToString(userTogglesFile, UTF_8), not(containsString(fieldForHasBeenChangedFlag))); /* The first time the file is written, it is written by hand in this test. Force it to write again, * so that the actual JSON write logic is used. */ repository.changeValueOfToggle("key1", true); assertThat(repository.userToggles(), is( new FeatureToggles(new FeatureToggle("key1", "desc1", true).withValueHasBeenChangedFlag(false)))); assertThat(FileUtils.readFileToString(userTogglesFile, UTF_8), containsString("key1")); assertThat(FileUtils.readFileToString(userTogglesFile, UTF_8), containsString("desc1")); assertThat(FileUtils.readFileToString(userTogglesFile, UTF_8), not(containsString(fieldForHasBeenChangedFlag))); } @Test public void ensureThatTheRealTogglesFileIsValid() throws Exception { String realAvailableTogglesFilePath = new SystemEnvironment() .get(SystemEnvironment.AVAILABLE_FEATURE_TOGGLES_FILE_PATH); File realAvailableTogglesFile = new File(getClass().getResource(realAvailableTogglesFilePath).toURI()); String currentContentOfRealAvailableTogglesFile = FileUtils.readFileToString(realAvailableTogglesFile, UTF_8); try { new Gson().fromJson(currentContentOfRealAvailableTogglesFile, FeatureToggleRepository.FeatureToggleFileContentRepresentation.class); } catch (Exception e) { fail("Check contents of " + realAvailableTogglesFilePath + ". Contents should be valid and be equivalent" + " to FeatureToggleRepository.FeatureToggleFileContentRepresentation.class. Contents were:\n" + currentContentOfRealAvailableTogglesFile + "\n. Exception was: " + e.getMessage()); } } private void setupAvailableToggleFileAs(String fileResourcePath) { when(environment.get(SystemEnvironment.AVAILABLE_FEATURE_TOGGLES_FILE_PATH)).thenReturn(fileResourcePath); } private void setupUserToggleFileAs(File file) { when(environment.configDir()).thenReturn(file.getParentFile()); when(environment.get(SystemEnvironment.USER_FEATURE_TOGGLES_FILE_PATH_RELATIVE_TO_CONFIG_DIR)) .thenReturn(file.getName()); } private void setupAvailableToggles(FeatureToggle... toggles) throws Exception { setupAvailableToggleFileAs(TEST_AVAILABLE_TOGGLES_PATH); FileUtils.writeStringToFile(availableTogglesFile(), convertTogglesToJson(toggles), UTF_8); } private File setupUserToggles(FeatureToggle... toggles) throws Exception { File toggleFile = temporaryFolder.newFile("user.toggle.test"); setupUserToggleFileAs(toggleFile); FileUtils.writeStringToFile(toggleFile, convertTogglesToJson(toggles), UTF_8); return toggleFile; } /* Write by hand to remove unnecessary coupling to actual write. */ private String convertTogglesToJson(FeatureToggle[] toggles) { List<String> jsonContentForEachToggle = new ArrayList<>(); for (FeatureToggle toggle : toggles) { jsonContentForEachToggle .add(MessageFormat.format("'{'\"key\": \"{0}\", \"description\": \"{1}\", \"value\": {2}'}'", toggle.key(), toggle.description(), String.valueOf(toggle.isOn()))); } return "{ \"version\": \"1\", \"toggles\": [" + StringUtils.join(jsonContentForEachToggle, ",").trim() + "]}"; } private File availableTogglesFile() throws URISyntaxException { return new File(getClass().getResource(TEST_AVAILABLE_TOGGLES_PATH).toURI()); } }