(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