Skip to content

Commit

Permalink
Fix dotenv properties as testable
Browse files Browse the repository at this point in the history
  • Loading branch information
uzzu committed Apr 15, 2020
1 parent 4670365 commit 7609778
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 39 deletions.
15 changes: 9 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@ allprojects {
}
}

ktlint {
verbose.set(true)
outputToConsole.set(true)
reporters {
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
subprojects {
apply(plugin = "org.jlleitschuh.gradle.ktlint")
ktlint {
verbose.set(true)
outputToConsole.set(true)
reporters {
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
}
ignoreFailures.set(true)
}
ignoreFailures.set(true)
}
10 changes: 0 additions & 10 deletions plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ plugins {
`java-gradle-plugin`
`maven-publish`
kotlin("jvm")
id("org.jlleitschuh.gradle.ktlint")
}

dependencies {
Expand Down Expand Up @@ -37,15 +36,6 @@ tasks {
}
}

ktlint {
verbose.set(true)
outputToConsole.set(true)
reporters {
reporter(org.jlleitschuh.gradle.ktlint.reporter.ReporterType.CHECKSTYLE)
}
ignoreFailures.set(true)
}

// region publishing

object Artifact {
Expand Down
24 changes: 24 additions & 0 deletions plugin/src/main/kotlin/co/uzzu/dotenv/EnvProviders.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package co.uzzu.dotenv

/**
* Provides environment variable
*/
interface EnvProvider {
/**
* @return A environment variable for name.
*/
fun getenv(name: String): String?

/**
* @return All environment variables.
*/
fun getenv(): Map<String, String>
}

/**
* EnvProvider implementation using System#getenv
*/
class SystemEnvProvider : EnvProvider {
override fun getenv(name: String): String? = System.getenv(name)
override fun getenv(): Map<String, String> = System.getenv()
}
29 changes: 16 additions & 13 deletions plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvPlugin.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package co.uzzu.dotenv.gradle

import co.uzzu.dotenv.DotEnvParser
import co.uzzu.dotenv.EnvProvider
import co.uzzu.dotenv.SystemEnvProvider
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.ExtensionAware
Expand All @@ -9,29 +11,30 @@ import java.nio.charset.Charset
@Suppress("unused")
class DotEnvPlugin : Plugin<Project> {
override fun apply(target: Project) {
val envTemplate = target.rootProject.envTemplate()
val envSource = target.rootProject.envSource()
val envMerged = envTemplate.keys
.union(envSource.keys)
.map { it to envSource[it] }
val envProvider = SystemEnvProvider()
val dotenvTemplate = target.rootProject.dotenvTemplate()
val dotenvSource = target.rootProject.dotenvSource()
val dotenvMerged = dotenvTemplate.keys
.union(dotenvSource.keys)
.map { it to dotenvSource[it] }
.toMap()

target.applyEnv(envMerged)
target.subprojects { it.applyEnv(envMerged) }
target.applyEnv(envProvider, dotenvMerged)
target.subprojects { it.applyEnv(envProvider, dotenvMerged) }
}

private fun Project.applyEnv(envProperties: Map<String, String?>) {
private fun Project.applyEnv(envProvider: EnvProvider, dotenvProperties: Map<String, String?>) {
val env =
extensions.create("env", DotEnvRoot::class.java, envProperties) as ExtensionAware
envProperties.forEach { (name, value) ->
env.extensions.create(name, DotEnvProperty::class.java, name, value)
extensions.create("env", DotEnvRoot::class.java, envProvider, dotenvProperties) as ExtensionAware
dotenvProperties.forEach { (name, value) ->
env.extensions.create(name, DotEnvProperty::class.java, envProvider, name, value)
}
}

private fun Project.envTemplate(filename: String = ".env.template") =
private fun Project.dotenvTemplate(filename: String = ".env.template"): Map<String, String> =
readText(filename).let(DotEnvParser::parse)

private fun Project.envSource(filename: String = ".env") =
private fun Project.dotenvSource(filename: String = ".env"): Map<String, String> =
readText(filename).let(DotEnvParser::parse)

private fun Project.readText(filename: String): String {
Expand Down
29 changes: 19 additions & 10 deletions plugin/src/main/kotlin/co/uzzu/dotenv/gradle/DotEnvProperties.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
package co.uzzu.dotenv.gradle

open class DotEnvRoot(private val map: Map<String, String>) {
import co.uzzu.dotenv.EnvProvider

open class DotEnvRoot(
private val envProvider: EnvProvider,
private val map: Map<String, String>
) {
/**
* @return Indicates an environment variable with specified name is present
*/
fun isPresent(name: String): Boolean =
System.getenv()[name]?.let { true }
envProvider.getenv()[name]?.let { true }
?: map[name]?.let { true }
?: false

Expand All @@ -14,7 +19,7 @@ open class DotEnvRoot(private val map: Map<String, String>) {
* @throws IllegalStateException if it was not set
*/
fun fetch(name: String) =
System.getenv()[name]
envProvider.getenv()[name]
?: map[name]
?: throw IllegalStateException("""Environment variable $name was not set.""")

Expand All @@ -23,25 +28,29 @@ open class DotEnvRoot(private val map: Map<String, String>) {
* @throws IllegalStateException if it was not set
*/
fun fetch(name: String, defaultValue: String) =
System.getenv()[name]
envProvider.getenv()[name]
?: map[name]
?: defaultValue

/**
* @return An environment variable. If it was not set, returns specified default value
*/
fun fetchOrNull(name: String): String? =
System.getenv()[name]
envProvider.getenv()[name]
?: map[name]
}

open class DotEnvProperty(private val name: String, private val dotenvValue: String?) {
open class DotEnvProperty(
private val envProvider: EnvProvider,
private val name: String,
private val dotenvValue: String?
) {

/**
* @return Indicates an environment variable is present
*/
val isPresent: Boolean
get() = System.getenv()[name]?.let { true }
get() = envProvider.getenv()[name]?.let { true }
?: dotenvValue?.let { true }
?: false

Expand All @@ -51,22 +60,22 @@ open class DotEnvProperty(private val name: String, private val dotenvValue: Str
*/
val value: String
get() =
System.getenv()[name]
envProvider.getenv()[name]
?: dotenvValue
?: throw IllegalStateException("""Environment variable $name was not set.""")

/**
* @return An environment variable. If it was not set, returns specified default value
*/
fun orElse(defaultValue: String): String =
System.getenv()[name]
envProvider.getenv()[name]
?: dotenvValue
?: defaultValue

/**
* @return An environment variable. If it was not set, returns null.
*/
fun orNull(): String? =
System.getenv()[name]
envProvider.getenv()[name]
?: dotenvValue
}
8 changes: 8 additions & 0 deletions plugin/src/test/kotlin/co/uzzu/dotenv/InMemoryEnvProvider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package co.uzzu.dotenv

class InMemoryEnvProvider(
private val map: Map<String, String>
) : EnvProvider {
override fun getenv(name: String): String? = map[name]
override fun getenv(): Map<String, String> = map
}
96 changes: 96 additions & 0 deletions plugin/src/test/kotlin/co/uzzu/dotenv/gradle/DotEnvPropertyTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package co.uzzu.dotenv.gradle

import co.uzzu.dotenv.InMemoryEnvProvider
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.Test

class DotEnvPropertyTest {

@Test
fun isPresentByEnv() {
val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env"))
val property = DotEnvProperty(envProvider, "FOO", null)
assertTrue(property.isPresent)
}

@Test
fun isPresentByDotEnv() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertTrue(property.isPresent)
}

@Test
fun isNotPresent() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", null)
assertFalse(property.isPresent)
}

@Test
fun valueFromEnv() {
val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env"))
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertEquals("env", property.value)
}

@Test
fun valueFromDotEnv() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertEquals("dotenv", property.value)
}

@Test
fun throwIfNoVariables() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", null)
assertThrows(IllegalStateException::class.java) { property.value }
}

@Test
fun valueOrElseFromEnv() {
val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env"))
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertEquals("env", property.value)
}

@Test
fun valueOrElseFromDotEnv() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertEquals("dotenv", property.value)
}

@Test
fun valueOrElse() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", null)
assertEquals("default", property.orElse("default"))
}

@Test
fun fetchOrNullFromEnv() {
val envProvider = InMemoryEnvProvider(mapOf("FOO" to "env"))
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertEquals("env", property.orNull())
}

@Test
fun fetchOrNullFromDotEnv() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", "dotenv")
assertEquals("dotenv", property.orNull())
}

@Test
fun fetchOrNull() {
val envProvider = InMemoryEnvProvider(mapOf())
val property = DotEnvProperty(envProvider, "FOO", null)
assertNull(property.orNull())
}
}
Loading

0 comments on commit 7609778

Please sign in to comment.