<commit_before>package org.ciroque.ccr.datastores

import java.util.UUID

import org.ciroque.ccr.core.Commons
import org.ciroque.ccr.datastores.DataStoreResults.{Found, NotFound}
import org.ciroque.ccr.models.ConfigurationFactory
import org.ciroque.ccr.models.ConfigurationFactory.{Temporality, Configuration}
import org.joda.time.DateTime
import org.scalatest._
import org.scalatest.concurrent.PatienceConfiguration.Timeout
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.time.Span
import spray.json._

abstract class SettingsDataStoreTests
  extends FunSpec
  with Matchers
  with ScalaFutures
  with BeforeAndAfterAll
  with BeforeAndAfterEach {

  import org.slf4j.Logger
  import spray.json.{JsObject, JsString}

  implicit val settingsDataStore: SettingsDataStore
  implicit val logger: Logger

  val testEnvironment = "test-environment"
  val testApplication = "test-application"
  val testScope = "test-scope"
  val testSetting = "test-settings"
  val testValue = JsObject(
    "test-key" → JsString("test-value"),
    "nested" → JsObject("nested-key" → JsString("nested-value"))
    , "a_number" → JsNumber(1440)
    , "a_boolean" → JsBoolean(true)
    , "a_null" → JsNull
    , "an_array" → JsArray(Vector(JsString("One"), JsNumber(3.3), JsNumber(2), JsObject("four" → JsNumber(4))))
  )
  val testEffectiveAt = DateTime.now().minusMonths(1)
  val testExpiresAt = DateTime.now().plusMonths(1)
  val ttl = 360000

  val prodEnvironment: String = "prod"
  val qaEnvironment: String = "qa"
  val devEnvironment: String = "dev"
  val application: String = "app"
  val application2: String = "app2"
  val application3: String = "app3"
  val application4: String = "app4"
  val loggingScope: String = "logging"
  val logLevelSetting: String = "loglevel"
  val logRotationSetting: String = "logrotation"
  val logFilenameSetting: String = "logfilename"

  val wildcardEnvironment: String = "environment:wildcard"
  val wildcardApplication: String = "application:wildcard"
  val wildcardScope: String = "scope:wildcard"
  val wildcardSetting: String = "setting:wildcard"

  val nonExistentSegment: String = "non-existent"

  val sourceIdEnvironment = "env:sourceid"
  val uniqueEnvironment = "UNIQ:Environment"
  val sourceId = UUID.randomUUID()
  val secondSourceId = UUID.randomUUID().toString

  val testConfiguration = ConfigurationFactory(
    testEnvironment,
    testApplication,
    testScope,
    testSetting,
    testValue,
    testEffectiveAt,
    testExpiresAt,
    ttl
  )

  val jsonValue = JsObject(Map("KEY" → JsString("value")))
  val activeLogLevelConfiguration: Configuration = ConfigurationFactory(prodEnvironment, application3, loggingScope, logLevelSetting, JsString("BING"), DateTime.now().minusYears(1), DateTime.now().plusDays(7), 360000L)
  val defaultLogRotationConfiguration: Configuration = ConfigurationFactory("default", "app4", loggingScope, logRotationSetting, JsString("12hours"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L)
  val wildcardConfiguration: Configuration = ConfigurationFactory(wildcardEnvironment, wildcardApplication, wildcardScope, wildcardSetting, JsString("WILDCARD"), DateTime.now().minusDays(2), DateTime.now().plusDays(2), 360000L)
  val configurationWithSourceId: Configuration = ConfigurationFactory(
    UUID.randomUUID(),
    sourceIdEnvironment,
    "app:sourceid",
    "scp:sourceid",
    "stg:sourceid",
    Some(sourceId.toString),
    jsonValue,
    DateTime.now().minusDays(7),
    DateTime.now().plusDays(7),
    5 * 60 * 1000L)

  val alternateConfigurationWithSourceId = configurationWithSourceId.copy(_id = UUID.randomUUID(), key = configurationWithSourceId.key.copy(sourceId = Some(secondSourceId)), value = JsString("SOMETHING_DIFFERENT"))

  override def beforeEach(): Unit = {
    import org.ciroque.ccr.logging.CachingLogger
    logger.asInstanceOf[CachingLogger].reset()
  }

  override def beforeAll(): Unit = {
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application2, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application2, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application2, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application2, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application2, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application2, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application2, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application2, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application2, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    /* --- temporality --- */
    settingsDataStore.insertConfiguration(activeLogLevelConfiguration)
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().plusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().plusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().plusDays(8), DateTime.now().plusYears(1), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L))

    settingsDataStore.insertConfiguration(defaultLogRotationConfiguration)
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, "app4", loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L))

    settingsDataStore.insertConfiguration(wildcardConfiguration)

    settingsDataStore.insertConfiguration(configurationWithSourceId)
    settingsDataStore.insertConfiguration(alternateConfigurationWithSourceId)
  }

  private def assertLogEvents(name: String, count: Int, shouldInclude: String*) = {
    import org.ciroque.ccr.logging.CachingLogger
    val clog = logger.asInstanceOf[CachingLogger]
    clog.getEvents.size shouldBe count
    val logEvent = clog.getEvents.head
    (name +: shouldInclude).foreach(expected => logEvent should include(expected))
  }

  describe("configurations") {

    it("Inserts a valid configuration") {
      whenReady(settingsDataStore.insertConfiguration(testConfiguration), Timeout(Span.Max)) {
        retrievedConfiguration =>
          retrievedConfiguration should be(DataStoreResults.Added(testConfiguration))
      }

      assertLogEvents("insertConfiguration", 1, "added-configuration")
    }

    it("Fails to insert an existing configuration") {
      whenReady(settingsDataStore.insertConfiguration(testConfiguration), Timeout(Span.Max)) {
        case failure: DataStoreResults.Failure => failure.message should be(Commons.DatastoreErrorMessages.DuplicateKeyError)
        case _ => fail("should have encountered a Failure in the datastore (Duplicate Key)")
      }

      assertLogEvents("insertConfiguration", 1, "added-configuration")
    }

    it("Updates an existing configuration") {
      val modifiedConfiguration = testConfiguration.copy(
        temporality = Temporality(
          testConfiguration.temporality.effectiveAt,
          testConfiguration.temporality.expiresAt,
          5000))
      whenReady(settingsDataStore.updateConfiguration(modifiedConfiguration), Timeout(Span.Max)) {
        dsr =>
          dsr should be(DataStoreResults.Updated(testConfiguration, modifiedConfiguration))
      }

      assertLogEvents("updateConfiguration", 1, "original-configuration", "validated-configuration")
    }

    it("Returns a single, active configuration when given a valid path") {
      whenReady(settingsDataStore.retrieveConfiguration(prodEnvironment, application3, loggingScope, logLevelSetting), Timeout(Span.Max)) {
        case Found(config) =>
          config match {
            case conf: List[Configuration] =>
              conf.size should be(1)
              conf.head should be(activeLogLevelConfiguration)
              conf.head.isActive should be(true)
            case something => fail(s"Expected to get a Configuration. Got a $something instead")
          }
        case NotFound(msg) => fail(s"NotFound -> $msg")
      }

      assertLogEvents("retrieveConfiguration", 1, prodEnvironment, application3, loggingScope, logLevelSetting)
    }

    it("Returns a NotFound when the environment / application / scope / setting combination does not exist") {
      whenReady(settingsDataStore.retrieveConfiguration(nonExistentSegment, nonExistentSegment, nonExistentSegment, nonExistentSegment), Timeout(Span.Max)) {
        result =>
          result should be(DataStoreResults.NotFound(s"environment '$nonExistentSegment' / application '$nonExistentSegment' / scope '$nonExistentSegment' / setting '$nonExistentSegment' combination was not found"))
      }<commit_msg>Setting up for next test<commit_after>package org.ciroque.ccr.datastores

import java.util.UUID

import org.ciroque.ccr.core.Commons
import org.ciroque.ccr.datastores.DataStoreResults.{Found, NotFound}
import org.ciroque.ccr.models.ConfigurationFactory
import org.ciroque.ccr.models.ConfigurationFactory.{Temporality, Configuration}
import org.joda.time.DateTime
import org.scalatest._
import org.scalatest.concurrent.PatienceConfiguration.Timeout
import org.scalatest.concurrent.ScalaFutures
import org.scalatest.time.Span
import spray.json._

abstract class SettingsDataStoreTests
  extends FunSpec
  with Matchers
  with ScalaFutures
  with BeforeAndAfterAll
  with BeforeAndAfterEach {

  import org.slf4j.Logger
  import spray.json.{JsObject, JsString}

  implicit val settingsDataStore: SettingsDataStore
  implicit val logger: Logger

  val testEnvironment = "test-environment"
  val testApplication = "test-application"
  val testScope = "test-scope"
  val testSetting = "test-settings"
  val testValue = JsObject(
    "test-key" → JsString("test-value"),
    "nested" → JsObject("nested-key" → JsString("nested-value"))
    , "a_number" → JsNumber(1440)
    , "a_boolean" → JsBoolean(true)
    , "a_null" → JsNull
    , "an_array" → JsArray(Vector(JsString("One"), JsNumber(3.3), JsNumber(2), JsObject("four" → JsNumber(4))))
  )
  val testEffectiveAt = DateTime.now().minusMonths(1)
  val testExpiresAt = DateTime.now().plusMonths(1)
  val ttl = 360000

  val prodEnvironment: String = "prod"
  val qaEnvironment: String = "qa"
  val devEnvironment: String = "dev"
  val application: String = "app"
  val application2: String = "app2"
  val application3: String = "app3"
  val application4: String = "app4"
  val loggingScope: String = "logging"
  val logLevelSetting: String = "loglevel"
  val logRotationSetting: String = "logrotation"
  val logFilenameSetting: String = "logfilename"

  val wildcardEnvironment: String = "environment:wildcard"
  val wildcardApplication: String = "application:wildcard"
  val wildcardScope: String = "scope:wildcard"
  val wildcardSetting: String = "setting:wildcard"

  val nonExistentSegment: String = "non-existent"

  val sourceIdEnvironment = "env:sourceid"
  val uniqueEnvironment = "UNIQ:Environment"
  val sourceId = UUID.randomUUID()
  val secondSourceId = UUID.randomUUID().toString

  val testConfiguration = ConfigurationFactory(
    testEnvironment,
    testApplication,
    testScope,
    testSetting,
    testValue,
    testEffectiveAt,
    testExpiresAt,
    ttl
  )

  val jsonValue = JsObject(Map("KEY" → JsString("value")))
  val activeLogLevelConfiguration: Configuration = ConfigurationFactory(prodEnvironment, application3, loggingScope, logLevelSetting, JsString("BING"), DateTime.now().minusYears(1), DateTime.now().plusDays(7), 360000L)
  val defaultLogRotationConfiguration: Configuration = ConfigurationFactory("default", "app4", loggingScope, logRotationSetting, JsString("12hours"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L)
  val wildcardConfiguration: Configuration = ConfigurationFactory(wildcardEnvironment, wildcardApplication, wildcardScope, wildcardSetting, JsString("WILDCARD"), DateTime.now().minusDays(2), DateTime.now().plusDays(2), 360000L)
  val configurationWithSourceId: Configuration = ConfigurationFactory(
    UUID.randomUUID(),
    sourceIdEnvironment,
    "app:sourceid",
    "scp:sourceid",
    "stg:sourceid",
    Some(sourceId.toString),
    jsonValue,
    DateTime.now().minusDays(7),
    DateTime.now().plusDays(7),
    5 * 60 * 1000L)

  val alternateConfigurationWithSourceId = configurationWithSourceId.copy(_id = UUID.randomUUID(), key = configurationWithSourceId.key.copy(sourceId = Some(secondSourceId)), value = JsString("SOMETHING_DIFFERENT"))

  override def beforeEach(): Unit = {
    import org.ciroque.ccr.logging.CachingLogger
    logger.asInstanceOf[CachingLogger].reset()
  }

  override def beforeAll(): Unit = {
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application2, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application2, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(devEnvironment, application2, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application2, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application2, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(qaEnvironment, application2, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application2, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application2, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application2, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().minusDays(7), 360000L))

    /* --- temporality --- */
    settingsDataStore.insertConfiguration(activeLogLevelConfiguration)
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().minusYears(1), DateTime.now().plusDays(7), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().minusYears(1), DateTime.now().plusDays(7), 360000L))

    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logLevelSetting, JsString("DEBUG"), DateTime.now().plusDays(8), DateTime.now().plusYears(1), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logFilenameSetting, JsString("output.log"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L))
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, application3, loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L))

    settingsDataStore.insertConfiguration(defaultLogRotationConfiguration)
    settingsDataStore.insertConfiguration(ConfigurationFactory(prodEnvironment, "app4", loggingScope, logRotationSetting, JsString("24hours"), DateTime.now().plusDays(7), DateTime.now().plusYears(1), 360000L))

    settingsDataStore.insertConfiguration(wildcardConfiguration)

    settingsDataStore.insertConfiguration(configurationWithSourceId)
    settingsDataStore.insertConfiguration(alternateConfigurationWithSourceId)
  }

  private def assertLogEvents(name: String, count: Int, shouldInclude: String*) = {
    import org.ciroque.ccr.logging.CachingLogger
    val clog = logger.asInstanceOf[CachingLogger]
    clog.getEvents.size shouldBe count
    val logEvent = clog.getEvents.head
    (name +: shouldInclude).foreach(expected => logEvent should include(expected))
  }

  describe("configurations") {

    it("Inserts a valid configuration") {
      whenReady(settingsDataStore.insertConfiguration(testConfiguration), Timeout(Span.Max)) {
        retrievedConfiguration =>
          retrievedConfiguration should be(DataStoreResults.Added(testConfiguration))
      }

      assertLogEvents("insertConfiguration", 1, "added-configuration")
    }

    it("Fails to insert an existing configuration") {
      whenReady(settingsDataStore.insertConfiguration(testConfiguration), Timeout(Span.Max)) {
        case failure: DataStoreResults.Failure => failure.message should be(Commons.DatastoreErrorMessages.DuplicateKeyError)
        case _ => fail("should have encountered a Failure in the datastore (Duplicate Key)")
      }

      assertLogEvents("insertConfiguration", 1, "added-configuration")
    }

    it("Updates an existing configuration") {
      val modifiedConfiguration = testConfiguration.copy(
        temporality = Temporality(
          testConfiguration.temporality.effectiveAt,
          testConfiguration.temporality.expiresAt,
          5000))
      whenReady(settingsDataStore.updateConfiguration(modifiedConfiguration), Timeout(Span.Max)) {
        dsr =>
          dsr should be(DataStoreResults.Updated(testConfiguration, modifiedConfiguration))
      }

      assertLogEvents("updateConfiguration", 1, "original-configuration", "validated-configuration")
    }

    it("fails to update a configuration that does not exist") {
      pending
    }

    it("Returns a single, active configuration when given a valid path") {
      whenReady(settingsDataStore.retrieveConfiguration(prodEnvironment, application3, loggingScope, logLevelSetting), Timeout(Span.Max)) {
        case Found(config) =>
          config match {
            case conf: List[Configuration] =>
              conf.size should be(1)
              conf.head should be(activeLogLevelConfiguration)
              conf.head.isActive should be(true)
            case something => fail(s"Expected to get a Configuration. Got a $something instead")
          }
        case NotFound(msg) => fail(s"NotFound -> $msg")
      }

      assertLogEvents("retrieveConfiguration", 1, prodEnvironment, application3, loggingScope, logLevelSetting)
    }

    it("Returns a NotFound when the environment / application / scope / setting combination does not exist") {
      whenReady(settingsDataStore.retrieveConfiguration(nonExistentSegment, nonExistentSegment, nonExistentSegment, nonExistentSegment), Timeout(Span.Max)) {
        result =>
          result should be(DataStoreResults.NotFound(s"environment '$nonExistentSegment' / application '$nonExistentSegment' / scope '$nonExistentSegment' / setting '$nonExistentSegment' combination was not found"))
      }
{'fraction_non_alphanumeric': 0.11371210918901761, 'fraction_numerical': 0.023488335184891285, 'mean_word_length': 6.180911680911681, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5452999', 'n_tokens_mistral': 7202, 'n_tokens_neox': 6719, 'n_words': 1282}