All checks were successful
/ Build and Release Library (push) Successful in 1m29s
Reviewed-on: #1
345 lines
10 KiB
Scala
345 lines
10 KiB
Scala
package gs.config.v0.audit
|
|
|
|
import cats.data.NonEmptyList
|
|
import cats.effect.IO
|
|
import gs.config.v0.AuditedConfiguration
|
|
import gs.config.v0.ConfigError
|
|
import gs.config.v0.ConfigKey
|
|
import gs.config.v0.ConfigName
|
|
import gs.config.v0.Configurable
|
|
import gs.config.v0.Configuration
|
|
import gs.config.v0.GsSuite
|
|
import gs.config.v0.source.ConfigSource
|
|
import gs.config.v0.source.MemoryConfigSource
|
|
import java.time.Instant
|
|
import java.time.LocalDate
|
|
|
|
class AuditedConfigurationTests extends GsSuite:
|
|
import AuditedConfigurationTests.*
|
|
|
|
given CanEqual[LocalDate, LocalDate] = CanEqual.derived
|
|
given CanEqual[Instant, Instant] = CanEqual.derived
|
|
|
|
iotest(
|
|
"should not return values, but should record attempts to find, when no config exists"
|
|
) {
|
|
for
|
|
config <- Configuration
|
|
.audited(ConfigSource.inMemory[IO](Map.empty))
|
|
.build()
|
|
string <- config.getValue(Keys.KString)
|
|
int <- config.getValue(Keys.KInt)
|
|
long <- config.getValue(Keys.KLong)
|
|
bool <- config.getValue(Keys.KBool)
|
|
localDate <- config.getValue(Keys.KLocalDate)
|
|
instant <- config.getValue(Keys.KInstant)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
assertEquals(string, Left(ConfigError.MissingValue(Names.KString)))
|
|
assertEquals(int, Left(ConfigError.MissingValue(Names.KInt)))
|
|
assertEquals(long, Left(ConfigError.MissingValue(Names.KLong)))
|
|
assertEquals(bool, Left(ConfigError.MissingValue(Names.KBool)))
|
|
assertEquals(localDate, Left(ConfigError.MissingValue(Names.KLocalDate)))
|
|
assertEquals(instant, Left(ConfigError.MissingValue(Names.KInstant)))
|
|
assertMissing(config, Names.KString, manifest)
|
|
assertMissing(config, Names.KInt, manifest)
|
|
assertMissing(config, Names.KLong, manifest)
|
|
assertMissing(config, Names.KBool, manifest)
|
|
assertMissing(config, Names.KLocalDate, manifest)
|
|
assertMissing(config, Names.KInstant, manifest)
|
|
}
|
|
|
|
iotest(
|
|
"should not return values, but should record attempts to find, when no config exists, across multiple sources"
|
|
) {
|
|
for
|
|
config <- Configuration
|
|
.audited(ConfigSource.inMemory[IO](Map.empty))
|
|
.withMemorySource(Map.empty)
|
|
.withSource(ConfigSource.empty[IO])
|
|
.build()
|
|
string <- config.getValue(Keys.KString)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
assertEquals(string, Left(ConfigError.MissingValue(Names.KString)))
|
|
assertEquals(
|
|
manifest.get(Names.KString),
|
|
Some(
|
|
List(
|
|
ConfigQueryResult.Failure(
|
|
sources = config.sources.toList.map(_.name),
|
|
error = ConfigError.MissingValue(Names.KString)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
iotest("should find and audit a string value") {
|
|
testFound(Keys.KString, "test")
|
|
}
|
|
|
|
iotest("should find and audit a boolan value") {
|
|
testFound(Keys.KBool, true)
|
|
}
|
|
|
|
iotest("should find and audit an integer value") {
|
|
testFound(Keys.KInt, 11)
|
|
}
|
|
|
|
iotest("should find and audit a long value") {
|
|
testFound(Keys.KLong, 33L)
|
|
}
|
|
|
|
iotest("should find and audit a local date value") {
|
|
testFound(Keys.KLocalDate, LocalDate.now())
|
|
}
|
|
|
|
iotest("should find and audit an instant value") {
|
|
testFound(Keys.KInstant, Instant.now())
|
|
}
|
|
|
|
iotest("should find a value in the first source, skipping the next sources") {
|
|
val key = Keys.KString
|
|
val expectedValue = "value"
|
|
for
|
|
config <- Configuration
|
|
.audited(
|
|
ConfigSource
|
|
.inMemory[IO](Map(key.name.unwrap() -> expectedValue.toString()))
|
|
)
|
|
.withSource(ConfigSource.empty[IO])
|
|
.build()
|
|
value <- config.getValue(key)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
assertEquals(value, Right(expectedValue))
|
|
assertSuccess(config, key.name, manifest, expectedValue.toString())
|
|
}
|
|
|
|
iotest("should instantiate for memory and environment sources") {
|
|
for
|
|
c1 <- AuditedConfiguration.forSource[IO](emptyMemorySource()).build()
|
|
c2 <- AuditedConfiguration.forEnvironmentSource[IO].build()
|
|
c3 <- AuditedConfiguration.forMemorySource[IO](Map.empty).build()
|
|
c4 <- AuditedConfiguration.forSources[IO](
|
|
NonEmptyList(emptyMemorySource(), List(emptyMemorySource()))
|
|
)
|
|
c5 <- AuditedConfiguration
|
|
.forEnvironmentSource[IO]
|
|
.withMemorySource(Map.empty)
|
|
.withEnvironmentSource()
|
|
.withSource(emptyMemorySource())
|
|
.build()
|
|
c6 <- Configuration.auditedEnvironmentOnly[IO]
|
|
yield
|
|
assertEquals(c1.sources.size, 1)
|
|
assertEquals(c2.sources.size, 1)
|
|
assertEquals(c3.sources.size, 1)
|
|
assertEquals(c4.sources.size, 2)
|
|
assertEquals(c5.sources.size, 4)
|
|
assertEquals(c6.sources.size, 1)
|
|
}
|
|
|
|
iotest("should audit the use of default values") {
|
|
val name = Names.KString
|
|
val defaultValue = "value"
|
|
val key = ConfigKey.WithDefaultValue(name, defaultValue)
|
|
for
|
|
config <- Configuration
|
|
.audited(ConfigSource.inMemory[IO](Map.empty))
|
|
.withMemorySource(Map.empty)
|
|
.build()
|
|
value <- config.getValue(key)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
assertEquals(value, Right(defaultValue))
|
|
assertEquals(
|
|
manifest.get(name),
|
|
Some(
|
|
List(ConfigQueryResult.UsedDefault(config.sources.toList.map(_.name)))
|
|
)
|
|
)
|
|
}
|
|
|
|
iotest("should detect and audit parsing failures") {
|
|
val name = Names.KInt
|
|
val rawValue = "not-an-integer"
|
|
val key = ConfigKey.Required[Int](name)
|
|
for
|
|
config <- Configuration
|
|
.audited(ConfigSource.inMemory[IO](Map(name.unwrap() -> rawValue)))
|
|
.withMemorySource(Map.empty)
|
|
.build()
|
|
value <- config.getValue(key)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
val expectedError = ConfigError.CannotParseValue(
|
|
configName = name,
|
|
candidateValue = rawValue,
|
|
source = config.sources.head.name
|
|
)
|
|
assertEquals(value, Left(expectedError))
|
|
assertEquals(
|
|
manifest.get(name),
|
|
Some(
|
|
List(
|
|
ConfigQueryResult.Failure(
|
|
sources = List(config.sources.head.name),
|
|
error = expectedError
|
|
)
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
iotest("should detect and audit parsing failures - EitherT") {
|
|
val name = Names.KInt
|
|
val rawValue = "not-an-integer"
|
|
val key = ConfigKey.Required[Int](name)
|
|
for
|
|
config <- Configuration
|
|
.audited(ConfigSource.inMemory[IO](Map(name.unwrap() -> rawValue)))
|
|
.withMemorySource(Map.empty)
|
|
.build()
|
|
value <- config.getValueT(key).value
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
val expectedError = ConfigError.CannotParseValue(
|
|
configName = name,
|
|
candidateValue = rawValue,
|
|
source = config.sources.head.name
|
|
)
|
|
assertEquals(value, Left(expectedError))
|
|
assertEquals(
|
|
manifest.get(name),
|
|
Some(
|
|
List(
|
|
ConfigQueryResult.Failure(
|
|
sources = List(config.sources.head.name),
|
|
error = expectedError
|
|
)
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
iotest("should audit multiple accesses of the same key") {
|
|
val key = Keys.KString
|
|
val expectedValue = "value"
|
|
for
|
|
config <- Configuration
|
|
.audited(
|
|
ConfigSource
|
|
.inMemory[IO](Map(key.name.unwrap() -> expectedValue.toString()))
|
|
)
|
|
.build()
|
|
v1 <- config.getValue(key)
|
|
v2 <- config.getValue(key)
|
|
v3 <- config.getValue(key)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
assertEquals(v1, Right(expectedValue))
|
|
assertEquals(v2, Right(expectedValue))
|
|
assertEquals(v3, Right(expectedValue))
|
|
assertEquals(
|
|
manifest.get(key.name),
|
|
Some(
|
|
List(
|
|
ConfigQueryResult
|
|
.Success(Some(config.sources.head.name), expectedValue),
|
|
ConfigQueryResult
|
|
.Success(Some(config.sources.head.name), expectedValue),
|
|
ConfigQueryResult.Success(
|
|
Some(config.sources.head.name),
|
|
expectedValue
|
|
)
|
|
)
|
|
)
|
|
)
|
|
}
|
|
|
|
private def emptyMemorySource(): MemoryConfigSource[IO] =
|
|
MemoryConfigSource(Map.empty)
|
|
|
|
private def testFound[A: Configurable](
|
|
key: ConfigKey[A],
|
|
expectedValue: A
|
|
): IO[Any] =
|
|
for
|
|
config <- Configuration
|
|
.audited(
|
|
ConfigSource
|
|
.inMemory[IO](Map(key.name.unwrap() -> expectedValue.toString()))
|
|
)
|
|
.build()
|
|
value <- config.getValue(key)
|
|
manifest <- config.manifest.snapshot()
|
|
yield
|
|
assertEquals(value, Right(expectedValue))
|
|
assertSuccess(config, key.name, manifest, expectedValue.toString())
|
|
|
|
private def assertMissing(
|
|
config: AuditedConfiguration[IO],
|
|
name: ConfigName,
|
|
manifest: Map[ConfigName, List[ConfigQueryResult]]
|
|
): Unit =
|
|
assertEquals(
|
|
manifest.get(name),
|
|
Some(
|
|
List(
|
|
ConfigQueryResult.Failure(
|
|
sources = List(config.sources.head.name),
|
|
error = ConfigError.MissingValue(name)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
private def assertSuccess(
|
|
config: AuditedConfiguration[IO],
|
|
name: ConfigName,
|
|
manifest: Map[ConfigName, List[ConfigQueryResult]],
|
|
expectedRawValue: String
|
|
): Unit =
|
|
assertEquals(
|
|
manifest.get(name),
|
|
Some(
|
|
List(
|
|
ConfigQueryResult.Success(
|
|
Some(config.sources.head.name),
|
|
expectedRawValue
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
object AuditedConfigurationTests:
|
|
|
|
object Names:
|
|
|
|
val KString: ConfigName = ConfigName("string")
|
|
val KInt: ConfigName = ConfigName("int")
|
|
val KLong: ConfigName = ConfigName("long")
|
|
val KBool: ConfigName = ConfigName("bool")
|
|
val KLocalDate: ConfigName = ConfigName("localdate")
|
|
val KInstant: ConfigName = ConfigName("instant")
|
|
|
|
end Names
|
|
|
|
object Keys:
|
|
|
|
val KString: ConfigKey[String] = ConfigKey.Required[String](Names.KString)
|
|
val KInt: ConfigKey[Int] = ConfigKey.Required[Int](Names.KInt)
|
|
val KLong: ConfigKey[Long] = ConfigKey.Required[Long](Names.KLong)
|
|
val KBool: ConfigKey[Boolean] = ConfigKey.Required[Boolean](Names.KBool)
|
|
|
|
val KLocalDate: ConfigKey[LocalDate] =
|
|
ConfigKey.Required[LocalDate](Names.KLocalDate)
|
|
|
|
val KInstant: ConfigKey[Instant] =
|
|
ConfigKey.Required[Instant](Names.KInstant)
|
|
|
|
end Keys
|
|
|
|
end AuditedConfigurationTests
|