/*
* Copyright 2014 Avanza Bank AB
*
* 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.avanza.astrix.config;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import org.junit.Test;
public class DynamicConfigTest {
MapConfigSource firstSource = new MapConfigSource();
MapConfigSource secondSource = new MapConfigSource();
DynamicConfig dynamicConfig = new DynamicConfig(Arrays.asList(firstSource, secondSource));
@Test
public void propertyIsResolvedToFirstOccurenceInConfigSources() throws Exception {
DynamicStringProperty stringProperty = dynamicConfig.getStringProperty("foo", "defaultFoo");
secondSource.set("foo", "secondValue");
assertEquals("secondValue", stringProperty.get());
firstSource.set("foo", "firstValue");
assertEquals("firstValue", stringProperty.get());
secondSource.set("foo", "secondNewValue");
assertEquals("firstValue", stringProperty.get());
}
@Test
public void propertyValueReturnsToDefaultValueWhenConfigSourceValueIsRemoved() throws Exception {
DynamicStringProperty stringProperty = dynamicConfig.getStringProperty("foo", "defaultFoo");
firstSource.set("foo", "firstValue");
assertEquals("firstValue", stringProperty.get());
firstSource.set("foo", null);
assertEquals("defaultFoo", stringProperty.get());
}
@Test
public void booleanProperty() throws Exception {
DynamicBooleanProperty booleanProperty = dynamicConfig.getBooleanProperty("foo", false);
secondSource.set("foo", "true");
assertTrue(booleanProperty.get());
firstSource.set("foo", "false");
assertFalse(booleanProperty.get());
}
@Test
public void intProperty() throws Exception {
DynamicIntProperty intProperty = dynamicConfig.getIntProperty("foo", 0);
assertEquals(0, intProperty.get());
secondSource.set("foo", "2");
assertEquals(2, intProperty.get());
firstSource.set("foo", "1");
assertEquals(1, intProperty.get());
}
@Test
public void unparsableBooleanPropertiesAreIgnored() throws Exception {
DynamicBooleanProperty booleanProperty = dynamicConfig.getBooleanProperty("foo", false);
secondSource.set("foo", "true");
assertTrue(booleanProperty.get());
firstSource.set("foo", "true[L]");
assertTrue(booleanProperty.get());
secondSource.set("foo", "MALFORMED");
assertTrue(booleanProperty.get());
secondSource.set("foo", "false");
assertFalse(booleanProperty.get());
firstSource.set("foo", "true");
assertTrue(booleanProperty.get());
firstSource.set("foo", "MALFORMED");
assertTrue(booleanProperty.get());
}
@Test
public void unparsableIntPropertiesAreIgnored() throws Exception {
DynamicIntProperty intProperty = dynamicConfig.getIntProperty("foo", 0);
secondSource.set("foo", "2d");
assertEquals(0, intProperty.get());
secondSource.set("foo", "1");
assertEquals(1, intProperty.get());
firstSource.set("foo", "1s");
assertEquals(1, intProperty.get());
}
@Test
public void merge() throws Exception {
MapConfigSource thirdSource = new MapConfigSource();
DynamicConfig dynamicConfigB = new DynamicConfig(Arrays.asList(thirdSource));
DynamicConfig merged = DynamicConfig.merged(dynamicConfig, dynamicConfigB);
assertEquals("defaultValue", merged.getStringProperty("foo", "defaultValue").get());
thirdSource.set("foo", "thirdValue");
assertEquals("thirdValue", merged.getStringProperty("foo", "defaultValue").get());
firstSource.set("foo", "firstValue");
assertEquals("firstValue", merged.getStringProperty("foo", "defaultValue").get());
secondSource.set("foo", "secondValue");
assertEquals("firstValue", merged.getStringProperty("foo", "defaultValue").get());
}
@Test
public void propertyListenerSupport_StringType() throws Exception {
firstSource.set("foo", "1");
secondSource.set("foo", "2");
Queue<String> events = new LinkedBlockingQueue<>();
DynamicStringProperty prop = dynamicConfig.getStringProperty("foo", "-");
prop.addListener(events::add);
assertEquals("1", prop.getCurrentValue());
assertNull("Listener should not be notified before a property actually changes", events.poll());
secondSource.set("foo", "22");
assertNull("Listener should not be notfied when a property changes but resolved property has the same value", events.poll());
firstSource.set("foo", "11");
assertEquals("Listener should be notified when property resolved changes", "11", events.poll());
secondSource.set("foo", "11");
assertNull(events.poll());
}
@Test
public void propertyListenerSupport_IntType() throws Exception {
firstSource.set("intProp", "1");
DynamicIntProperty prop = dynamicConfig.getIntProperty("intProp", 0);
Queue<Integer> events = new LinkedBlockingQueue<>();
prop.addListener(events::add);
assertEquals(1, prop.getCurrentValue().intValue());
assertNull("Listener should not be notified before a property actually changes", events.poll());
firstSource.set("intProp", "11");
assertEquals("Listener should be notified when property resolved changes", Integer.valueOf(11), events.poll());
}
@Test
public void propertyListenerSupport_LongType() throws Exception {
firstSource.set("longProp", "1");
DynamicLongProperty prop = dynamicConfig.getLongProperty("longProp", 0);
Queue<Long> events = new LinkedBlockingQueue<>();
prop.addListener(events::add);
assertEquals(1, prop.getCurrentValue().longValue());
assertNull("Listener should not be notified before a property actually changes", events.poll());
firstSource.set("longProp", "11");
assertEquals("Listener should be notified when property resolved changes", Long.valueOf(11), events.poll());
}
@Test
public void propertyListenerSupport_BooleanType() throws Exception {
firstSource.set("booleanProp", "true");
DynamicBooleanProperty prop = dynamicConfig.getBooleanProperty("booleanProp", false);
Queue<Boolean> events = new LinkedBlockingQueue<>();
prop.addListener(events::add);
assertEquals(Boolean.TRUE, prop.getCurrentValue());
assertNull("Listener should not be notified before a property actually changes", events.poll());
firstSource.set("booleanProp", "false");
assertEquals("Listener should be notified when property resolved changes", Boolean.FALSE, events.poll());
}
@Test
public void globalPropertyListenerIsNotifiedWhenNewPropertyIsCreated() throws Exception {
firstSource.set("booleanProp", "true");
DynamicBooleanProperty prop = dynamicConfig.getBooleanProperty("booleanProp", false);
Queue<Boolean> events = new LinkedBlockingQueue<>();
prop.addListener(events::add);
assertEquals(Boolean.TRUE, prop.getCurrentValue());
assertNull("Listener should not be notified before a property actually changes", events.poll());
firstSource.set("booleanProp", "false");
assertEquals("Listener should be notified when property resolved changes", Boolean.FALSE, events.poll());
}
@Test
public void globalConfigListenerSupport_PropertyCreatedEvents() throws Exception {
class Prop {
String name;
Object val;
public Prop(String name, Object val) {
this.name = name;
this.val = val;
}
@Override
public String toString() {
return name + "=" + val;
}
}
MapConfigSource firstSource = new MapConfigSource();
firstSource.set("foo", "1");
MapConfigSource secondSource = new MapConfigSource();
secondSource.set("foo", "2");
DynamicConfig config = DynamicConfig.create(firstSource, secondSource);
Queue<Prop> propertyCreatedEvents = new LinkedBlockingQueue<>();
config.addListener(new DynamicConfigListener() {
@Override
public void propertyCreated(String propertyName, Object initialValue) {
propertyCreatedEvents.add(new Prop(propertyName, initialValue));
}
});
assertNull(propertyCreatedEvents.poll());
config.getStringProperty("foo", "default");
assertEquals("foo", propertyCreatedEvents.poll().name);
// Should return cached value and not fire created event
config.getStringProperty("foo", "default");
assertNull(propertyCreatedEvents.poll());
}
@Test
public void globalConfigListenerSupport_PropertyUpdateEvents() throws Exception {
class Prop {
String name;
Object val;
public Prop(String name, Object val) {
this.name = name;
this.val = val;
}
@Override
public String toString() {
return name + "=" + val;
}
}
MapConfigSource firstSource = new MapConfigSource();
firstSource.set("foo", "1");
MapConfigSource secondSource = new MapConfigSource();
secondSource.set("foo", "2");
DynamicConfig config = DynamicConfig.create(firstSource, secondSource);
Queue<Prop> propertyChangeEvents = new LinkedBlockingQueue<>();
config.addListener(new DynamicConfigListener() {
@Override
public void propertyChanged(String propertyName, Object newValue) {
propertyChangeEvents.add(new Prop(propertyName, newValue));
}
});
assertNull(propertyChangeEvents.poll());
config.getStringProperty("foo", "default");
assertNull(propertyChangeEvents.poll());
firstSource.set("foo", "11");
Prop prop = propertyChangeEvents.poll();
assertNotNull(prop);
assertEquals("foo", prop.name);
assertEquals("11", prop.val);
secondSource.set("foo", "11");
assertNull(propertyChangeEvents.poll());
}
}