(patch) series of updates and beginning to write tests
This commit is contained in:
parent
eddc5d5ae8
commit
962cae1268
13 changed files with 117 additions and 9 deletions
|
@ -11,6 +11,6 @@ repos:
|
||||||
description: Enforces using only 'LF' line endings.
|
description: Enforces using only 'LF' line endings.
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- repo: https://git.garrity.co/garrity-software/gs-pre-commit-scala
|
- repo: https://git.garrity.co/garrity-software/gs-pre-commit-scala
|
||||||
rev: v1.0.0
|
rev: v1.0.1
|
||||||
hooks:
|
hooks:
|
||||||
- id: scalafmt
|
- id: scalafmt
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// See: https://github.com/scalameta/scalafmt/tags for the latest tags.
|
// See: https://github.com/scalameta/scalafmt/tags for the latest tags.
|
||||||
version = 3.7.17
|
version = 3.8.1
|
||||||
runner.dialect = scala3
|
runner.dialect = scala3
|
||||||
maxColumn = 80
|
maxColumn = 80
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ lazy val sharedSettings = Seq(
|
||||||
|
|
||||||
lazy val testSettings = Seq(
|
lazy val testSettings = Seq(
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"org.scalameta" %% "munit" % "1.0.0-M10" % Test
|
"org.scalameta" %% "munit" % "1.0.0-RC1" % Test
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ object AuditedConfiguration:
|
||||||
*/
|
*/
|
||||||
def forSources[F[_]: Sync](
|
def forSources[F[_]: Sync](
|
||||||
sources: NonEmptyList[ConfigSource[F]]
|
sources: NonEmptyList[ConfigSource[F]]
|
||||||
): F[Configuration[F]] =
|
): F[AuditedConfiguration[F]] =
|
||||||
ConfigManifest
|
ConfigManifest
|
||||||
.standard[F]
|
.standard[F]
|
||||||
.map(manifest =>
|
.map(manifest =>
|
||||||
|
@ -119,7 +119,7 @@ object AuditedConfiguration:
|
||||||
def withSource(source: ConfigSource[F]): Builder[F] =
|
def withSource(source: ConfigSource[F]): Builder[F] =
|
||||||
copy(sources = this.sources.append(source))
|
copy(sources = this.sources.append(source))
|
||||||
|
|
||||||
def build(): F[Configuration[F]] =
|
def build(): F[AuditedConfiguration[F]] =
|
||||||
ConfigManifest
|
ConfigManifest
|
||||||
.standard[F]
|
.standard[F]
|
||||||
.map(manifest =>
|
.map(manifest =>
|
||||||
|
|
|
@ -6,6 +6,8 @@ package gs.config.v0
|
||||||
sealed trait ConfigError
|
sealed trait ConfigError
|
||||||
|
|
||||||
object ConfigError:
|
object ConfigError:
|
||||||
|
given CanEqual[ConfigError, ConfigError] = CanEqual.derived
|
||||||
|
|
||||||
/** Attempted to retreive the value for some [[ConfigKey]], but no value could
|
/** Attempted to retreive the value for some [[ConfigKey]], but no value could
|
||||||
* be found.
|
* be found.
|
||||||
*
|
*
|
||||||
|
|
|
@ -24,23 +24,33 @@ object Configurable:
|
||||||
): Configurable[A] = C
|
): Configurable[A] = C
|
||||||
|
|
||||||
given Configurable[String] with
|
given Configurable[String] with
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
def parse(raw: String): Option[String] = Some(raw)
|
def parse(raw: String): Option[String] = Some(raw)
|
||||||
|
|
||||||
given Configurable[Int] with
|
given Configurable[Int] with
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
def parse(raw: String): Option[Int] = Try(raw.toInt).toOption
|
def parse(raw: String): Option[Int] = Try(raw.toInt).toOption
|
||||||
|
|
||||||
given Configurable[Long] with
|
given Configurable[Long] with
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
def parse(raw: String): Option[Long] = Try(raw.toLong).toOption
|
def parse(raw: String): Option[Long] = Try(raw.toLong).toOption
|
||||||
|
|
||||||
given Configurable[Boolean] with
|
given Configurable[Boolean] with
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
def parse(raw: String): Option[Boolean] = Try(raw.toBoolean).toOption
|
def parse(raw: String): Option[Boolean] = Try(raw.toBoolean).toOption
|
||||||
|
|
||||||
given Configurable[LocalDate] with
|
given Configurable[LocalDate] with
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
def parse(raw: String): Option[LocalDate] =
|
def parse(raw: String): Option[LocalDate] =
|
||||||
Try(LocalDate.parse(raw)).toOption
|
Try(LocalDate.parse(raw)).toOption
|
||||||
|
|
||||||
given Configurable[Instant] with
|
given Configurable[Instant] with
|
||||||
|
/** @inheritDocs
|
||||||
def parse(raw: String): Option[Instant] =
|
*/
|
||||||
Try(Instant.parse(raw)).toOption
|
def parse(raw: String): Option[Instant] = Try(Instant.parse(raw)).toOption
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package gs.config.v0
|
package gs.config.v0
|
||||||
|
|
||||||
|
import cats.data.EitherT
|
||||||
import cats.effect.Sync
|
import cats.effect.Sync
|
||||||
import gs.config.v0.source.ConfigSource
|
import gs.config.v0.source.ConfigSource
|
||||||
|
|
||||||
|
@ -16,6 +17,19 @@ trait Configuration[F[_]]:
|
||||||
*/
|
*/
|
||||||
def getValue[A: Configurable](key: ConfigKey[A]): F[Either[ConfigError, A]]
|
def getValue[A: Configurable](key: ConfigKey[A]): F[Either[ConfigError, A]]
|
||||||
|
|
||||||
|
/** Retrieve a value based on some key. Return an `EitherT` as the response,
|
||||||
|
* rather than `F[Either[ConfigError, A]]`
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The key that identifies the piece of configuration to retrieve.
|
||||||
|
* @return
|
||||||
|
* The value, or an error if no value is present. Expressed as an
|
||||||
|
* `EitherT`.
|
||||||
|
*/
|
||||||
|
def getValueT[A: Configurable](
|
||||||
|
key: ConfigKey[A]
|
||||||
|
): EitherT[F, ConfigError, A] = EitherT(getValue(key))
|
||||||
|
|
||||||
object Configuration:
|
object Configuration:
|
||||||
|
|
||||||
/** Start building a new [[AuditedConfiguration]].
|
/** Start building a new [[AuditedConfiguration]].
|
||||||
|
@ -34,7 +48,7 @@ object Configuration:
|
||||||
* @return
|
* @return
|
||||||
* The new [[Configuration]].
|
* The new [[Configuration]].
|
||||||
*/
|
*/
|
||||||
def auditedEnvironmentOnly[F[_]: Sync]: F[Configuration[F]] =
|
def auditedEnvironmentOnly[F[_]: Sync]: F[AuditedConfiguration[F]] =
|
||||||
AuditedConfiguration.forEnvironmentSource[F].build()
|
AuditedConfiguration.forEnvironmentSource[F].build()
|
||||||
|
|
||||||
end Configuration
|
end Configuration
|
||||||
|
|
|
@ -9,6 +9,8 @@ sealed trait ConfigQueryResult
|
||||||
|
|
||||||
object ConfigQueryResult:
|
object ConfigQueryResult:
|
||||||
|
|
||||||
|
given CanEqual[ConfigQueryResult, ConfigQueryResult] = CanEqual.derived
|
||||||
|
|
||||||
/** Represents a query for some configuration that completed successfully.
|
/** Represents a query for some configuration that completed successfully.
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source
|
||||||
|
|
|
@ -40,9 +40,13 @@ object ConfigSource:
|
||||||
|
|
||||||
final class Empty[F[_]: Applicative] extends ConfigSource[F]:
|
final class Empty[F[_]: Applicative] extends ConfigSource[F]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override def getValue(key: ConfigKey[?]): F[Option[String]] =
|
override def getValue(key: ConfigKey[?]): F[Option[String]] =
|
||||||
Applicative[F].pure(None)
|
Applicative[F].pure(None)
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override val name: String = "empty"
|
override val name: String = "empty"
|
||||||
|
|
||||||
end ConfigSource
|
end ConfigSource
|
||||||
|
|
|
@ -8,9 +8,13 @@ import gs.config.v0.ConfigKey
|
||||||
*/
|
*/
|
||||||
final class EnvironmentConfigSource[F[_]: Sync] extends ConfigSource[F]:
|
final class EnvironmentConfigSource[F[_]: Sync] extends ConfigSource[F]:
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override def getValue(
|
override def getValue(
|
||||||
key: ConfigKey[?]
|
key: ConfigKey[?]
|
||||||
): F[Option[String]] =
|
): F[Option[String]] =
|
||||||
Sync[F].delay(sys.env.get(key.name.toEnvironmentVariable()))
|
Sync[F].delay(sys.env.get(key.name.toEnvironmentVariable()))
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override val name: String = "environment"
|
override val name: String = "environment"
|
||||||
|
|
|
@ -16,6 +16,8 @@ final class MemoryConfigSource[F[_]: Applicative](
|
||||||
) extends ConfigSource[F]:
|
) extends ConfigSource[F]:
|
||||||
val id: UUID = UUID.randomUUID()
|
val id: UUID = UUID.randomUUID()
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override def getValue(
|
override def getValue(
|
||||||
key: ConfigKey[?]
|
key: ConfigKey[?]
|
||||||
): F[Option[String]] =
|
): F[Option[String]] =
|
||||||
|
@ -24,4 +26,6 @@ final class MemoryConfigSource[F[_]: Applicative](
|
||||||
.get(key.name.toRawString())
|
.get(key.name.toRawString())
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** @inheritDocs
|
||||||
|
*/
|
||||||
override lazy val name: String = s"in-memory-$id"
|
override lazy val name: String = s"in-memory-$id"
|
||||||
|
|
17
src/test/scala/gs/config/v0/GsSuite.scala
Normal file
17
src/test/scala/gs/config/v0/GsSuite.scala
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package gs.config.v0
|
||||||
|
|
||||||
|
import cats.effect.IO
|
||||||
|
import cats.effect.unsafe.IORuntime
|
||||||
|
|
||||||
|
abstract class GsSuite extends munit.FunSuite:
|
||||||
|
given IORuntime = IORuntime.global
|
||||||
|
|
||||||
|
def iotest(
|
||||||
|
name: String
|
||||||
|
)(
|
||||||
|
body: => IO[Any]
|
||||||
|
)(
|
||||||
|
implicit
|
||||||
|
loc: munit.Location
|
||||||
|
): Unit =
|
||||||
|
test(new munit.TestOptions(name))(body.unsafeRunSync())
|
|
@ -0,0 +1,51 @@
|
||||||
|
package gs.config.v0.audit
|
||||||
|
|
||||||
|
import cats.effect.IO
|
||||||
|
import gs.config.v0.ConfigError
|
||||||
|
import gs.config.v0.ConfigKey
|
||||||
|
import gs.config.v0.ConfigName
|
||||||
|
import gs.config.v0.Configuration
|
||||||
|
import gs.config.v0.GsSuite
|
||||||
|
import gs.config.v0.source.ConfigSource
|
||||||
|
|
||||||
|
class AuditedConfigurationTests extends GsSuite:
|
||||||
|
import AuditedConfigurationTests.*
|
||||||
|
|
||||||
|
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)
|
||||||
|
manifest <- config.manifest.snapshot()
|
||||||
|
yield
|
||||||
|
assert(string == Left(ConfigError.MissingValue(Names.KString)))
|
||||||
|
assert(
|
||||||
|
manifest.get(Names.KString) == Some(
|
||||||
|
List(
|
||||||
|
ConfigQueryResult.Failure(
|
||||||
|
sources = List(config.sources.head.name),
|
||||||
|
error = ConfigError.MissingValue(Names.KString)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
object AuditedConfigurationTests:
|
||||||
|
|
||||||
|
object Names:
|
||||||
|
|
||||||
|
val KString: ConfigName = ConfigName("string")
|
||||||
|
|
||||||
|
end Names
|
||||||
|
|
||||||
|
object Keys:
|
||||||
|
|
||||||
|
val KString: ConfigKey[String] = ConfigKey.Required[String](Names.KString)
|
||||||
|
|
||||||
|
end Keys
|
||||||
|
|
||||||
|
end AuditedConfigurationTests
|
Loading…
Add table
Reference in a new issue