public class AzureStore extends BreakerboxStore {
    public static final String PARTITION_KEY = "PartitionKey";
    public static final String ROW_KEY = "RowKey";
    protected final TableClient tableClient;

    public AzureStore(AzureTableConfiguration azureTableConfiguration,
                      Environment environment) {
        super(azureTableConfiguration, environment);
        this.tableClient = new TableClientFactory(azureTableConfiguration).create();
        environment.healthChecks().register("azure", new TableClientHealthcheck(tableClient));
    }

    private <T extends TableType> boolean delete(Optional<T> tableType) {
        return !tableType.isPresent() || tableClient.remove(tableType.get());
    }

    @Override
    public boolean initialize() {
        for (TableId tableId : TableId.values()) {
            tableClient.create(tableId);
        }
        return true;
    }

    @Override
    public boolean store(DependencyModel dependencyModel) {
        return tableClient.insertOrReplace(Entities.from(dependencyModel));
    }

    @Override
    public boolean store(ServiceModel serviceModel) {
        return tableClient.insertOrReplace(Entities.from(serviceModel));
    }

    @Override
    public boolean delete(ServiceModel serviceModel) {
        return delete(tableClient.<ServiceEntity>retrieve(Entities.from(serviceModel)));
    }

    @Override
    public boolean delete(DependencyModel dependencyModel) {
        return delete(tableClient.<DependencyEntity>retrieve(Entities.from(dependencyModel)));
    }

    @Override
    public boolean delete(ServiceId serviceId, DependencyId dependencyId) {
        return delete(tableClient.<ServiceEntity>retrieve(ServiceEntity.build(serviceId, dependencyId)));
    }

    @Override
    public boolean delete(DependencyId dependencyId, DateTime dateTime) {
        return delete(fetchByTimestamp(dependencyId, dateTime.getMillis()));
    }

    @Override
    public Optional<ServiceModel> retrieve(ServiceId serviceId, DependencyId dependencyId) {
        return Entities.toServiceModel(tableClient.retrieve(ServiceEntity.build(serviceId, dependencyId)));
    }

    @Override
    public Optional<DependencyModel> retrieve(DependencyId dependencyId, DateTime dateTime) {
        return Entities.toDependencyModel(fetchByTimestamp(dependencyId, dateTime.getMillis()));
    }

    @Override
    public Optional<DependencyModel> retrieveLatest(DependencyId dependencyId, ServiceId serviceId) {
        return fetchLatest(allDependenciesFor(dependencyId, serviceId));
    }

    @Override
    public Collection<ServiceModel> allServiceModels() {
        return Entities.toServiceModelList(allServiceEntities());
    }

    @Override
    public Collection<ServiceModel> listDependenciesFor(final ServiceId serviceId) {
        return Entities.toServiceModelList(allServiceEntities(serviceId));
    }

    @Override
    public Collection<DependencyModel> allDependenciesFor(DependencyId dependencyId, ServiceId serviceId) {
        try (Timer.Context timerContext = dependencyConfigs.time()) {
            return Entities.toDependencyModelList(tableClient.search(
                    TableId.DEPENDENCY, TableQuery
                    .from(DependencyEntity.class)
                    .where(TableQuery.combineFilters(
                            partitionEquals(dependencyId),
                            TableQuery.Operators.AND,
                            serviceIdEquals(serviceId)))));
        }
    }

    private static Optional<DependencyModel> fetchLatest(Iterable<DependencyModel> dependencyModels) {
        if (Iterables.isEmpty(dependencyModels)) {
            return Optional.empty();
        } else {
            return Optional.of(Ordering
                    .from(new DependencyModelByTimestamp())
                    .reverse()
                    .immutableSortedCopy(dependencyModels)
                    .get(0));
        }
    }

    private Optional<DependencyEntity> fetchByTimestamp(DependencyId dependencyId, long timestamp) {
        final ImmutableList<DependencyEntity> dependencyEntities = getConfiguration(dependencyId, timestamp);
        if (dependencyEntities.isEmpty()) {
            return Optional.empty();
        } else {
            return Optional.of(dependencyEntities.get(0));
        }
    }

    private ImmutableList<ServiceEntity> allServiceEntities(ServiceId serviceId) {
        try (Timer.Context timerContext = listService.time()) {
            return tableClient.search(TableId.SERVICE, TableQuery
                    .from(ServiceEntity.class)
                    .where(partitionKeyEquals(serviceId)));
        }
    }

    private static String partitionKeyEquals(ServiceId serviceId) {
        return TableQuery
                .generateFilterCondition(
                        PARTITION_KEY,
                        TableQuery.QueryComparisons.EQUAL,
                        serviceId.getId());
    }

    private ImmutableList<ServiceEntity> allServiceEntities() {
        try (Timer.Context timerContext = listService.time()) {
            return tableClient.search(TableId.SERVICE, TableQuery
                    .from(ServiceEntity.class));
        }
    }

    private ImmutableList<DependencyEntity> getConfiguration(DependencyId dependencyId, long targetTimeStamp) {
        try (Timer.Context timerContext = dependencyConfigs.time()) {
            return tableClient.search(TableId.DEPENDENCY, TableQuery
                    .from(DependencyEntity.class)
                    .where(TableQuery.combineFilters(
                                partitionEquals(dependencyId),
                                TableQuery.Operators.AND,
                                timestampEquals(targetTimeStamp))));
        }
    }

    private static String serviceIdEquals(ServiceId serviceId) {
        return TableQuery.generateFilterCondition(
                "ServiceName",
                TableQuery.QueryComparisons.EQUAL,
                serviceId.getId());
    }

    private static String timestampEquals(long timestamp) {
        return TableQuery.generateFilterCondition(
                ROW_KEY,
                TableQuery.QueryComparisons.EQUAL,
                String.valueOf(timestamp)
        );
    }

    private static String partitionEquals(DependencyId dependencyId) {
        return TableQuery.generateFilterCondition(
                PARTITION_KEY,
                TableQuery.QueryComparisons.EQUAL,
                dependencyId.getId());
    }
} public class TableClient {

    private final CloudTableClient cloudTableClient;
    private static final Logger LOG = LoggerFactory.getLogger(TableClient.class);

    public TableClient(CloudTableClient cloudTableClient) {
        this.cloudTableClient = checkNotNull(cloudTableClient, "cloudTableClient cannot be null");
    }

    private CloudTable tableRefrence(AzureTableName table) {
        try {
            return cloudTableClient.getTableReference(table.toString());
        } catch (URISyntaxException e) {
            LOG.warn("URI exception creating table: {},", table, e);
        } catch (StorageException e) {
            LOG.warn("Error generating TableClient: {}", table, e);
        }
        throw new IllegalStateException("Could not create table: " + table);
    }

    public void create(AzureTableName table) {
        try {
            tableRefrence(table).createIfNotExists();
            return;
        } catch (StorageException e) {
            LOG.warn("Error creating table: {}", table, e);
        }
        throw new IllegalStateException("Could not create table: " + table);
    }

    public boolean insert(TableType entity) {
        try {
            return tableRefrence(entity.getAzureTableName())
                    .execute(TableOperation.insert(entity))
                    .getHttpStatusCode() == Response.Status.NO_CONTENT.getStatusCode();
        } catch (StorageException e) {
            LOG.warn("Error performing operation on Storage service", e);
        }
        throw new IllegalStateException("Error adding data in table " + entity.getAzureTableName());
    }

    public boolean insertOrReplace(TableType entity) {
        try {
            final TableResult tableResult = tableRefrence(entity.getAzureTableName())
                    .execute(TableOperation.insertOrReplace(entity));
            switch (Response.Status.fromStatusCode(tableResult.getHttpStatusCode())) {
                case CREATED:
                case NO_CONTENT:
                    return true;
                default:
                    return false;
            }
        } catch (StorageException e) {
            LOG.warn("Error performing operation on Storage service", e);
        }
        throw new IllegalStateException("Error insertOrReplace in table " + entity.getAzureTableName());
    }

    public <EntityType extends TableServiceEntity> Optional<EntityType> retrieve(TableKey tableKey) {
        try {
            return Optional.ofNullable(tableRefrence(tableKey.getTable())
                    .execute(TableOperation.retrieve(tableKey.getPartitionKey(), tableKey.getRowKey(), tableKey.getEntityClass()))
                    .getResultAsType());
        } catch (StorageException e) {
            LOG.warn("Error retrieving entity from table: {}", tableKey.getTable(), e);
        }
        return Optional.empty();
    }

    public boolean update(TableType entity) {
        try {
            return tableRefrence(entity.getAzureTableName())
                    .execute(TableOperation.replace(entity))
                    .getHttpStatusCode() == Response.Status.NO_CONTENT.getStatusCode();
        } catch (StorageException e) {
            LOG.warn("Error updating row in table: {}", entity.getAzureTableName(), e);
        }
        throw new IllegalStateException("Error updating data in table: " + entity.getAzureTableName());
    }

    public boolean destroy(AzureTableName azureTableName) {
        try {
            return tableRefrence(azureTableName).deleteIfExists();
        } catch (StorageException e) {
            LOG.warn("Error deleting table from Azure", e);
        }
        throw new IllegalStateException("Unable to destroy table " + azureTableName);
    }

    public boolean exists(AzureTableName azureTableName) {
        try {
            return tableRefrence(azureTableName).exists();
        } catch (StorageException e) {
            LOG.warn("Error accessing azure table", e);
        }
        throw new IllegalStateException("Error verifying if table " + azureTableName + " exists");
    }

    public <EntityType extends TableEntity> ImmutableList<EntityType> search(AzureTableName azureTableName,
                                                                             TableQuery<EntityType> query) {
        return ImmutableList.copyOf(tableRefrence(azureTableName).execute(query));
    }

    public Iterable<String> listTables() {
        return cloudTableClient.listTables();
    }

    public boolean remove(TableType entity) {
        try {
            return tableRefrence(entity.getAzureTableName())
                    .execute(TableOperation.delete(entity))
                    .getHttpStatusCode() == Response.Status.NO_CONTENT.getStatusCode();
        } catch (StorageException e) {
            LOG.warn("Error updating row in table: {}", entity.getAzureTableName(), e);
        }
        throw new IllegalStateException("Error updating data in table: " + entity.getAzureTableName());
    }
} public class TableClientFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(TableClientFactory.class);
    private final AzureTableConfiguration azureTableConfiguration;

    public TableClientFactory(AzureTableConfiguration azureTableConfiguration) {
        this.azureTableConfiguration = checkNotNull(azureTableConfiguration, "azureTableConfiguration cannot be null");
    }

    public TableClient create() {
        try {
            final CloudStorageAccount storageAccount =
                    new CloudStorageAccount(azureTableConfiguration.getStorageCredentialsAccountAndKey(), true);
            final CloudTableClient cloudTableClient = storageAccount.createCloudTableClient();
            final TableRequestOptions defaultOptions = new TableRequestOptions();
            defaultOptions.setRetryPolicyFactory(new RetryLinearRetry(
                    Ints.checkedCast(azureTableConfiguration.getRetryInterval().toMilliseconds()),
                    azureTableConfiguration.getRetryAttempts()));
            defaultOptions.setTimeoutIntervalInMs(Ints.checkedCast(azureTableConfiguration.getTimeout().toMilliseconds()));
            cloudTableClient.setDefaultRequestOptions(defaultOptions);
            return new TableClient(cloudTableClient);
        } catch (URISyntaxException err) {
            LOGGER.error("Failed to create a TableClient", err);
            throw new IllegalArgumentException(err);
        }
    }
} public class DependencyEntity extends TableType implements TableKey {
    private static final ObjectMapper OBJECTMAPPER = Jackson.newObjectMapper();
    private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceEntity.class);

    private String tenacityConfigurationAsString;
    private String user;
    private String serviceName;

    private DependencyEntity(DependencyId dependencyId,
                             long timeStamp,
                             String userName,
                             String configuration,
                             ServiceId serviceId) {
        super(TableId.DEPENDENCY);
        this.partitionKey = dependencyId.getId();
        this.rowKey = String.valueOf(timeStamp);
        this.tenacityConfigurationAsString = configuration;
        this.user = userName;
        this.serviceName = serviceId.getId();
    }

    public static DependencyEntity build(DependencyId dependencyId,
                                         String username,
                                         TenacityConfiguration tenacityConfiguration,
                                         ServiceId serviceId) {
        return build(dependencyId, System.currentTimeMillis(), username, tenacityConfiguration, serviceId);
    }

    public static DependencyEntity build(DependencyId dependencyId,
                                         long timeStamp,
                                         String userName,
                                         TenacityConfiguration configuration,
                                         ServiceId serviceId) {
        try {
            final String configurationAsString = OBJECTMAPPER.writeValueAsString(configuration);
            return new DependencyEntity(dependencyId, timeStamp, userName, configurationAsString, serviceId);
        } catch (JsonProcessingException err) {
            LOGGER.warn("Could not convert TenacityConfiguration to json", err);
            throw new RuntimeException(err);
        }
    }

    public Optional<TenacityConfiguration> getConfiguration() {
        try {
            final TenacityConfiguration dependencyConfiguration = OBJECTMAPPER.readValue(tenacityConfigurationAsString, TenacityConfiguration.class);
            final Set<?> validationErrors = VALIDATOR.validate(dependencyConfiguration);
            if (!validationErrors.isEmpty()) {
                LOGGER.warn("Failed to validate TenacityConfiguration", validationErrors.toString());
            }
            return Optional.of(dependencyConfiguration);
        } catch (Exception err) {
            LOGGER.warn("Failed to parse TenacityConfiguration", err);
        }
        return Optional.empty();
    }

    public long getConfigurationTimestamp() {
        return Long.parseLong(rowKey);
    }

    public static TenacityConfiguration defaultConfiguration() {
        return new TenacityConfiguration();
    }

    public ServiceId getServiceId() {
        return ServiceId.from(serviceName);
    }

    public DependencyId getDependencyId() {
        return DependencyId.from(partitionKey);
    }

    @Override
    public Class<? extends TableServiceEntity> getEntityClass() {
        return DependencyEntity.class;
    }

    @Override
    public AzureTableName getTable() {
        return TableId.DEPENDENCY;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        if (!super.equals(o)) return false;

        DependencyEntity that = (DependencyEntity) o;

        if (serviceName != null ? !serviceName.equals(that.serviceName) : that.serviceName != null) return false;
        if (tenacityConfigurationAsString != null ? !tenacityConfigurationAsString.equals(that.tenacityConfigurationAsString) : that.tenacityConfigurationAsString != null)
            return false;
        if (user != null ? !user.equals(that.user) : that.user != null) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + (tenacityConfigurationAsString != null ? tenacityConfigurationAsString.hashCode() : 0);
        result = 31 * result + (user != null ? user.hashCode() : 0);
        result = 31 * result + (serviceName != null ? serviceName.hashCode() : 0);
        return result;
    }

    /**
     * @deprecated kept for backward compatibility
     * For Azure bean serialization via reflection
     */

    @Deprecated
    public DependencyEntity() {
        super(TableId.DEPENDENCY);
    }

    /**
     * @deprecated kept for backward compatibility
     * @return
     */
    @Deprecated
    public String getTenacityConfigurationAsString() {
        return tenacityConfigurationAsString;
    }

    /**
     * @deprecated kept for backward compatibility
     * @param tenacityConfigurationAsString
     */
    @Deprecated
    public void setTenacityConfigurationAsString(String tenacityConfigurationAsString) {
        this.tenacityConfigurationAsString = tenacityConfigurationAsString;
    }

    /**
     * @deprecated kept for backward compatibility
     * @return
     */
    @Deprecated
    public String getServiceName() {
        return serviceName;
    }

    /**
     * @deprecated kept for backward compatibility
     * @param serviceName
     */
    @Deprecated
    public void setServiceName(String serviceName) {
        this.serviceName = serviceName;
    }

    /**
     * Getter for user field.
     * NOTE: This method is exposed, but is also used for bean serialization. Don't remove this.
     */
    public String getUser() {
        return user;
    }

    /**
     * @deprecated kept for backward compatibility
     * @param user
     */
    @Deprecated
    public void setUser(String user) {
        this.user = user;
    }
} public class Entities {
    private Entities() {}

    public static Optional<ServiceModel> toServiceModel(Optional<ServiceEntity> serviceEntity) {
        return serviceEntity.map(Entities::toModel);
    }
    
    public static ServiceModel toModel(ServiceEntity serviceEntity) {
        return new ServiceModel(serviceEntity.getServiceId(), serviceEntity.getDependencyId());
    }

    public static ServiceEntity from(ServiceModel serviceModel) {
        return ServiceEntity.build(serviceModel.getServiceId(), serviceModel.getDependencyId());
    }

    public static Optional<DependencyModel> toDependencyModel(Optional<DependencyEntity> dependencyEntity) {
        return dependencyEntity.map(Entities::toModel);
    }
    
    public static DependencyModel toModel(DependencyEntity dependencyEntity) {
        return new DependencyModel(
                dependencyEntity.getDependencyId(),
                new DateTime(dependencyEntity.getConfigurationTimestamp()),
                dependencyEntity.getConfiguration().get(),
                dependencyEntity.getUser(),
                dependencyEntity.getServiceId());
    }

    public static DependencyEntity from(DependencyModel dependencyModel) {
        return DependencyEntity.build(
                dependencyModel.getDependencyId(),
                dependencyModel.getDateTime().getMillis(),
                dependencyModel.getUser(),
                dependencyModel.getTenacityConfiguration(),
                dependencyModel.getServiceId());
    }

    public static Collection<ServiceModel> toServiceModelList(Collection<ServiceEntity> serviceEntities) {
        return serviceEntities
                .stream()
                .map(Entities::toModel)
                .collect(ImmutableList.toImmutableList());
    }

    public static Collection<DependencyModel> toDependencyModelList(Collection<DependencyEntity> dependencyEntities) {
        return dependencyEntities
                .stream()
                .map(Entities::toModel)
                .collect(ImmutableList.toImmutableList());
    }
} public abstract class BreakerboxStore {
    protected final Timer listServices;
    protected final Timer listService;
    protected final Timer dependencyConfigs;

    @SuppressWarnings("unused")
    protected <StoreConfiguration> BreakerboxStore(StoreConfiguration storeConfiguration,
                                                   Environment environment) {
        this.listServices = environment.metrics().timer(name(BreakerboxStore.class, "list-services"));
        this.listService = environment.metrics().timer(name(BreakerboxStore.class, "list-service"));
        this.dependencyConfigs = environment.metrics().timer(name(BreakerboxStore.class, "latest-dependency-config"));
    }

    public abstract boolean initialize() throws Exception;
    public abstract boolean store(DependencyModel dependencyModel);
    public abstract boolean store(ServiceModel serviceModel);
    public abstract boolean delete(ServiceModel serviceModel);
    public abstract boolean delete(DependencyModel dependencyModel);
    public abstract boolean delete(ServiceId serviceId, DependencyId dependencyId);
    public abstract boolean delete(DependencyId dependencyId, DateTime dateTime);
    public abstract Optional<ServiceModel> retrieve(ServiceId serviceId, DependencyId dependencyId);
    public abstract Optional<DependencyModel> retrieve(DependencyId dependencyId, DateTime dateTime);
    public abstract Optional<DependencyModel> retrieveLatest(DependencyId dependencyId, ServiceId serviceId);
    public abstract Collection<ServiceModel> allServiceModels();
    public abstract Collection<ServiceModel> listDependenciesFor(ServiceId serviceId);
    public abstract Collection<DependencyModel> allDependenciesFor(DependencyId dependencyId, ServiceId serviceId);
} public class DependencyId implements TenacityPropertyKey {
    private final String id;

    private DependencyId(String id) {
        this.id = id.toUpperCase();
    }

    public String getId() {
        return id;
    }

    public static DependencyId from(String id) {
        return new DependencyId(id);
    }

    @Override
    public String name() {
        return id;
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        final DependencyId other = (DependencyId) obj;
        return Objects.equals(this.id, other.id);
    }

    @Override
    public String toString() {
        return id;
    }
} public class ServiceId {
    private final String id;

    private ServiceId(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public static ServiceId from(String id) {
        return new ServiceId(id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        final ServiceId other = (ServiceId) obj;
        return Objects.equals(this.id, other.id);
    }

    @Override
    public String toString() {
        return id;
    }
} public class DependencyModel {
    private final DependencyId dependencyId;
    private final DateTime dateTime;
    private final TenacityConfiguration tenacityConfiguration;
    private final String user;
    private final ServiceId serviceId;

    public DependencyModel(DependencyId dependencyId,
                           DateTime dateTime,
                           TenacityConfiguration tenacityConfiguration,
                           String user,
                           ServiceId serviceId) {
        this.dependencyId = dependencyId;
        this.dateTime = dateTime;
        this.tenacityConfiguration = tenacityConfiguration;
        this.user = user;
        this.serviceId = serviceId;
    }

    public DependencyId getDependencyId() {
        return dependencyId;
    }

    public DateTime getDateTime() {
        return dateTime;
    }

    public TenacityConfiguration getTenacityConfiguration() {
        return tenacityConfiguration;
    }

    public String getUser() {
        return user;
    }

    public ServiceId getServiceId() {
        return serviceId;
    }

    @Override
    public int hashCode() {
        return Objects.hash(dependencyId, dateTime, tenacityConfiguration, user, serviceId);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        final DependencyModel other = (DependencyModel) obj;
        return Objects.equals(this.dependencyId, other.dependencyId)
                && Objects.equals(this.dateTime, other.dateTime)
                && Objects.equals(this.tenacityConfiguration, other.tenacityConfiguration)
                && Objects.equals(this.user, other.user)
                && Objects.equals(this.serviceId, other.serviceId);
    }
} public class ServiceModel {
    private final ServiceId serviceId;
    private final DependencyId dependencyId;

    public ServiceModel(ServiceId serviceId, DependencyId dependencyId) {
        this.serviceId = serviceId;
        this.dependencyId = dependencyId;
    }

    public ServiceId getServiceId() {
        return serviceId;
    }

    public DependencyId getDependencyId() {
        return dependencyId;
    }

    @Override
    public int hashCode() {
        return Objects.hash(serviceId, dependencyId);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        final ServiceModel other = (ServiceModel) obj;
        return Objects.equals(this.serviceId, other.serviceId)
                && Objects.equals(this.dependencyId, other.dependencyId);
    }
}
import com.yammer.breakerbox.azure.AzureStore;
import com.yammer.breakerbox.azure.TableClient;
import com.yammer.breakerbox.azure.TableClientFactory;
import com.yammer.breakerbox.azure.model.DependencyEntity;
import com.yammer.breakerbox.azure.model.Entities;
import com.yammer.breakerbox.store.BreakerboxStore;
import com.yammer.breakerbox.store.DependencyId;
import com.yammer.breakerbox.store.ServiceId;
import com.yammer.breakerbox.store.model.DependencyModel;
import com.yammer.breakerbox.store.model.ServiceModel;
import com.yammer.tenacity.core.config.CircuitBreakerConfiguration;
import com.yammer.tenacity.core.config.SemaphoreConfiguration;
import com.yammer.tenacity.core.config.TenacityConfiguration;
import com.yammer.tenacity.core.config.ThreadPoolConfiguration;
import org.joda.time.DateTime;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.util.Optional;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
package com.yammer.breakerbox.azure.tests;




public class AzureStoreTest extends WithConfiguration {
    private BreakerboxStore breakerboxStore;
    private TableClient tableClient;
    private ServiceId testServiceId;
    private DependencyId testDependencyId;
    private TenacityConfiguration dependencyConfiguration;
    private long timestamp;
    private String user;

    @Before
    public void setup() throws Exception {
        tableClient = new TableClientFactory(azureTableConfiguration).create();
        breakerboxStore = new AzureStore(azureTableConfiguration, environment());
        testServiceId = ServiceId.from(UUID.randomUUID().toString());
        testDependencyId = DependencyId.from(UUID.randomUUID().toString());
        timestamp = 1345938944000L;
        dependencyConfiguration = new TenacityConfiguration(
                new ThreadPoolConfiguration(12, 23, 34, 45, 56, 67),
                new CircuitBreakerConfiguration(1, 2, 3, 4, 5),
                new SemaphoreConfiguration(1, 2),
                6789);
        user = "USER";
        assumeTrue(validAzureAccount());
        assertTrue(breakerboxStore.initialize());
    }

    @After
    public void teardown() {
        try {
            assertTrue(breakerboxStore.delete(new ServiceModel(testServiceId, testDependencyId)));
            assertTrue(breakerboxStore.delete(testServiceId, testDependencyId));
            assertTrue(breakerboxStore.delete(testDependencyId, new DateTime(timestamp)));
            removeDependencyModel(breakerboxStore.retrieveLatest(testDependencyId, testServiceId));
        } catch (Exception err) {
        }
    }

    private void removeDependencyModel(Optional<DependencyModel> dependencyModel) {
        dependencyModel.ifPresent((value) -> assertTrue(breakerboxStore.delete(value)));
    }

    @Test
    public void testListServices() {
        assertTrue(breakerboxStore.store(new ServiceModel(testServiceId, testDependencyId)));
        assertThat(breakerboxStore.allServiceModels())
                .contains(new ServiceModel(testServiceId, testDependencyId));
    }

    @Test
    public void testListDependencies() {
        assertTrue(breakerboxStore.store(new ServiceModel(testServiceId, testDependencyId)));
        assertThat(breakerboxStore.listDependenciesFor(testServiceId))
                .contains(new ServiceModel(testServiceId, testDependencyId));
        assertThat(breakerboxStore.listDependenciesFor(ServiceId.from(UUID.randomUUID().toString())))
                .isEmpty();
    }

    @Test
    public void testGetDependencyConfigurations(){
        final DependencyEntity dependencyEntity = DependencyEntity.build(testDependencyId, timestamp, user, dependencyConfiguration, testServiceId);
        final Optional<DependencyModel> expectedModel = Optional.of(Entities.toModel(dependencyEntity));