1
0

Compare commits

..

No commits in common. "8bcc50ea1b91bd28ee3116236c8207a719dc4d5e" and "d84770970d07c6f28b59498fe6667c439f8f06d0" have entirely different histories.

27 changed files with 94 additions and 488 deletions

View File

@ -16,11 +16,6 @@ display = "Java Version"
default = "1.4.10" default = "1.4.10"
display = "Kotlin Version" display = "Kotlin Version"
[features]
[features.ktlint]
default = true
[versions] [versions]
http4k = "3.265.0" http4k = "3.265.0"
pebble = "3.1.4" pebble = "3.1.4"

50
pom.xml
View File

@ -64,12 +64,6 @@
<version>5.7.0</version> <version>5.7.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.7.0</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.assertj</groupId> <groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId> <artifactId>assertj-core</artifactId>
@ -156,50 +150,6 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
</plugin> </plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>ktlint</id>
<phase>verify</phase>
<configuration>
<target name="ktlint">
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
<arg value="src/**/*.kt"/>
</java>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>ktlint-format</id>
<configuration>
<target name="ktlint">
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
<arg value="-F"/>
<arg value="src/**/*.kt"/>
</java>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.pinterest</groupId>
<artifactId>ktlint</artifactId>
<version>0.39.0</version>
</dependency>
</dependencies>
</plugin>
</plugins> </plugins>
</build> </build>

View File

@ -24,15 +24,12 @@ data class Repository(val name: String, val url: String)
data class Input(val name: String, val display: String, val value: String? = null) data class Input(val name: String, val display: String, val value: String? = null)
data class Feature(val name: String, val value: Boolean = false)
data class Project( data class Project(
val name: String, val name: String,
val basePackage: String, val basePackage: String,
val inputs: List<Input>, val inputs: List<Input>,
val features: List<Feature>,
val dependencies: List<Dependency>, val dependencies: List<Dependency>,
val repositories: Set<Repository>, val repositories: Collection<Repository>,
) )
data class Version(val name: String, val value: String) data class Version(val name: String, val value: String)

View File

@ -7,7 +7,6 @@ import com.electronwill.nightconfig.core.Config as NightConfig
data class StarterConfig( data class StarterConfig(
val dependencies: List<Dependency>, val dependencies: List<Dependency>,
val inputs: List<Input>, val inputs: List<Input>,
val features: List<Feature>,
) )
class Config(private val cfg: NightConfig) { class Config(private val cfg: NightConfig) {
@ -55,11 +54,6 @@ class Config(private val cfg: NightConfig) {
Input(name, values["display"], values["default"]) Input(name, values["display"], values["default"])
} }
val features = cfg.configMap("features") return StarterConfig(dependencies, inputs)
.map { (name, values) ->
Feature(name, values["default"] ?: false)
}
return StarterConfig(dependencies, inputs, features)
} }
} }

View File

@ -8,6 +8,7 @@ import org.koin.dsl.onClose
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import starter.ProjectZip import starter.ProjectZip
import starter.config.Config
import starter.views.Views import starter.views.Views
val mainModule = module { val mainModule = module {

View File

@ -1,23 +1,57 @@
package starter.modules package starter.modules
import com.mitchellbosecke.pebble.extension.escaper.SafeString
import com.mitchellbosecke.pebble.template.EvaluationContext
import com.mitchellbosecke.pebble.template.PebbleTemplate
import org.intellij.lang.annotations.Language
import org.koin.dsl.bind import org.koin.dsl.bind
import org.koin.dsl.module import org.koin.dsl.module
import starter.pebble.DepAsXmlPebbleFunction import starter.Dependency
import starter.pebble.HasFeatureFilter
import starter.utils.PebbleEngineBuilder import starter.utils.PebbleEngineBuilder
import starter.utils.PebbleEngineBuilder.CacheType.ConcurrentMap import starter.utils.PebbleEngineBuilder.CacheType.ConcurrentMap
import starter.utils.PebbleFilter
import starter.utils.PebbleFunction import starter.utils.PebbleFunction
class DepAsXmlPebbleFunction : PebbleFunction {
override val name = "depAsXml"
override fun getArgumentNames() = listOf("dependency")
override fun execute(args: Map<String, Any>, self: PebbleTemplate, context: EvaluationContext, lineNumber: Int): Any {
val dep = args["dependency"] as Dependency
fun startTag(name: String): String {
@Language("html") @Suppress("UnnecessaryVariable")
val result = """<span class="text-gray-700">&lt;</span><span class="text-red-700">$name</span><span class="text-gray-700">&gt;</span>"""
return result
}
fun endTag(name: String): String {
@Language("html") @Suppress("UnnecessaryVariable")
val result = """<span class="text-gray-700">&lt;/</span><span class="text-red-700">$name</span><span class="text-gray-700">&gt;</span>"""
return result
}
fun tag(name: String, content: String): String {
return """${startTag(name)}$content${endTag(name)}"""
}
val result = """
|${startTag("dependency")}
| ${tag("groupId", dep.groupId)}
| ${tag("artifactId", dep.artifactId)}
| ${tag("version", dep.version.value)}
|${endTag("dependency")}
""".trimMargin()
return SafeString(result)
}
}
val pebbleModule = module { val pebbleModule = module {
single { DepAsXmlPebbleFunction() } bind PebbleFunction::class single { DepAsXmlPebbleFunction() } bind PebbleFunction::class
single { HasFeatureFilter() } bind PebbleFilter::class
single { single {
PebbleEngineBuilder { PebbleEngineBuilder {
cache = ConcurrentMap cache = ConcurrentMap
functions(getAll()) functions(getAll())
filters(getAll())
classPath { suffix = ".twig" } classPath { suffix = ".twig" }
} }
} }

View File

@ -11,19 +11,15 @@ import starter.routes.IndexRouteSupplier
import starter.routes.RouteSupplier import starter.routes.RouteSupplier
import starter.routes.ZipRouteSupplier import starter.routes.ZipRouteSupplier
import starter.routes.toRouter import starter.routes.toRouter
import starter.utils.ProjectExtractor
import starter.utils.ProjectExtractorImpl
val routesModule = module { val routesModule = module {
single { IndexRouteSupplier(get()) } bind RouteSupplier::class single { IndexRouteSupplier(get()) } bind RouteSupplier::class
single { ZipRouteSupplier(get(), get()) } bind RouteSupplier::class single { ZipRouteSupplier(get(), get()) } bind RouteSupplier::class
single<ProjectExtractor> { ProjectExtractorImpl(get()) }
single { single {
ServerFilters.CatchAll().then( ServerFilters.CatchAll().then(
routes( routes(
static(ResourceLoader.Classpath("/assets")), static(ResourceLoader.Classpath("/assets")),
getAll<RouteSupplier>().toRouter() getAll<RouteSupplier>().toRouter()
) ))
)
} }
} }

View File

@ -10,5 +10,4 @@ val templateModule = module {
single { LogbackTemplate(get()) } bind Template::class single { LogbackTemplate(get()) } bind Template::class
single { GitignoreTemplate(get()) } bind Template::class single { GitignoreTemplate(get()) } bind Template::class
single { JunitTemplate(get()) } bind Template::class single { JunitTemplate(get()) } bind Template::class
single { EditorConfigTemplate(get()) } bind Template::class
} }

View File

@ -1,51 +0,0 @@
package starter.pebble
import com.mitchellbosecke.pebble.extension.escaper.SafeString
import com.mitchellbosecke.pebble.template.EvaluationContext
import com.mitchellbosecke.pebble.template.PebbleTemplate
import org.intellij.lang.annotations.Language
import starter.Dependency
import starter.utils.PebbleFunction
// We need a custom function since pebble inserts whitespace everywhere
class DepAsXmlPebbleFunction : PebbleFunction {
override val name = "depAsXml"
override fun getArgumentNames() = listOf("dependency")
override fun execute(
args: Map<String, Any>,
self: PebbleTemplate,
context: EvaluationContext,
lineNumber: Int,
): SafeString {
val dep = args["dependency"] as Dependency
fun tagName(name: String) = """<span class="text-red-700">$name</span>"""
fun startTag(name: String): String {
@Language("html") @Suppress("UnnecessaryVariable")
val result = """<span class="text-gray-700">&lt;</span>""" +
"""${tagName(name)}<span class="text-gray-700">&gt;</span>"""
return result
}
fun endTag(name: String): String {
@Language("html") @Suppress("UnnecessaryVariable")
val result = """<span class="text-gray-700">&lt;/</span>""" +
"""${tagName(name)}<span class="text-gray-700">&gt;</span>"""
return result
}
fun tag(name: String, content: String) = """${startTag(name)}$content${endTag(name)}"""
val result = """
|${startTag("dependency")}
| ${tag("groupId", dep.groupId)}
| ${tag("artifactId", dep.artifactId)}
| ${tag("version", dep.version.value)}
|${endTag("dependency")}
""".trimMargin()
return SafeString(result)
}
}

View File

@ -1,27 +0,0 @@
package starter.pebble
import com.mitchellbosecke.pebble.template.EvaluationContext
import com.mitchellbosecke.pebble.template.PebbleTemplate
import starter.Feature
import starter.utils.PebbleFilter
class HasFeatureFilter : PebbleFilter {
override val name = "hasFeature"
override fun getArgumentNames() = listOf("name")
override fun apply(
input: Any,
args: MutableMap<String, Any>,
self: PebbleTemplate,
context: EvaluationContext,
lineNumber: Int,
): Boolean {
@Suppress("UNCHECKED_CAST")
val features = input as List<Feature>
val name = args["name"] as String
return features.any { it.name == name }
}
}

View File

@ -1,35 +1,54 @@
package starter.routes package starter.routes
import org.http4k.core.Method import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.Response import org.http4k.core.Response
import org.http4k.core.body.form
import org.http4k.core.body.formAsMap
import org.http4k.core.with import org.http4k.core.with
import org.http4k.routing.bind import org.http4k.routing.bind
import starter.Project
import starter.ProjectZip import starter.ProjectZip
import starter.config.StarterConfig
import starter.extensions.attachment import starter.extensions.attachment
import starter.extensions.badRequest import starter.extensions.badRequest
import starter.extensions.ok import starter.extensions.ok
import starter.utils.ProjectExtractor
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
class ZipRouteSupplier( class ZipRouteSupplier(
private val projectExtractor: ProjectExtractor, private val conf: StarterConfig,
private val projectZip: ProjectZip, private val projectZip: ProjectZip,
) : RouteSupplier { ) : RouteSupplier {
private fun handle(req: Request): Response { override fun get() = "/" bind Method.POST to { req ->
val project = projectExtractor(req) ?: return Response.badRequest()
val outputStream = projectZip.createZip(project) val deps = conf.dependencies.filter {
req.form(it.name) != null
}
return Response.ok().with( val inputKeys = conf.inputs.map { it.name }
attachment( val inputs = req.formAsMap()
value = ByteArrayInputStream(outputStream.toByteArray()), .filter { it.key in inputKeys }
name = "${project.name}.zip", .map { (name, value) ->
contentType = "application/zip" conf.inputs.find { it.name == name }!!.copy(value = value.first())
}
val projectName = inputs.find { it.name == "name" }!!.value!!
val basePackage = inputs.find { it.name == "basePackage" }!!.value!!
if (basePackage.contains("/") || basePackage.contains("..")) {
Response.badRequest()
} else {
val repositories = deps.mapNotNull { it.repository }.toSet()
val project = Project(projectName, basePackage, inputs, deps, repositories)
val outputStream = projectZip.createZip(project)
Response.ok().with(
attachment(
value = ByteArrayInputStream(outputStream.toByteArray()),
name = "$projectName.zip",
contentType = "application/zip"
)
) )
) }
} }
override fun get() = "/" bind Method.POST to ::handle
} }

View File

@ -1,15 +0,0 @@
package starter.templates
import com.mitchellbosecke.pebble.PebbleEngine
import starter.Project
import starter.utils.render
class EditorConfigTemplate(private val engine: PebbleEngine) : Template {
override fun path(project: Project) =
".editorconfig"
override fun enabled(project: Project) = project.features.any { it.name == "ktlint" }
override fun render(project: Project) =
engine.render("starter/editorconfig/index")
}

View File

@ -2,6 +2,7 @@ package starter.templates
import com.mitchellbosecke.pebble.PebbleEngine import com.mitchellbosecke.pebble.PebbleEngine
import starter.Project import starter.Project
import starter.utils.prettyPrintXml
import starter.utils.render import starter.utils.render
class JunitTemplate(private val engine: PebbleEngine) : Template { class JunitTemplate(private val engine: PebbleEngine) : Template {

View File

@ -6,7 +6,7 @@ import starter.utils.render
class MainTemplate(private val engine: PebbleEngine) : Template { class MainTemplate(private val engine: PebbleEngine) : Template {
override fun path(project: Project) = override fun path(project: Project) =
"src/main/kotlin/" + project.name.toLowerCase().capitalize() + ".kt" "src/main/kotlin/" + project.basePackage.replace('.', '/') + "/" + project.name.toLowerCase().capitalize() + ".kt"
override fun render(project: Project) = override fun render(project: Project) =
engine.render("starter/main/index", mapOf("basePackage" to project.basePackage)) engine.render("starter/main/index", mapOf("basePackage" to project.basePackage))

View File

@ -2,6 +2,7 @@ package starter.templates
import com.mitchellbosecke.pebble.PebbleEngine import com.mitchellbosecke.pebble.PebbleEngine
import starter.Project import starter.Project
import starter.Version
import starter.utils.prettyPrintXml import starter.utils.prettyPrintXml
import starter.utils.render import starter.utils.render
@ -13,7 +14,6 @@ class PomTemplate(private val engine: PebbleEngine) : Template {
"dependencies" to project.dependencies.sortedBy { it.scope }, "dependencies" to project.dependencies.sortedBy { it.scope },
"repositories" to project.repositories, "repositories" to project.repositories,
"kotlinxSerialization" to project.dependencies.any { it.name == "kotlinx-serialization" }, "kotlinxSerialization" to project.dependencies.any { it.name == "kotlinx-serialization" },
"features" to project.features,
"versions" to project.dependencies.map { it.version }.toSet() "versions" to project.dependencies.map { it.version }.toSet()
) )

View File

@ -64,20 +64,16 @@ class PebbleEngineBuilder {
PebbleEngine.Builder() PebbleEngine.Builder()
.loader(builder.loader) .loader(builder.loader)
.cacheActive(builder.cache != CacheType.None) .cacheActive(builder.cache != CacheType.None)
.templateCache( .templateCache(when (builder.cache) {
when (builder.cache) { CacheType.None -> NoOpTemplateCache()
CacheType.None -> NoOpTemplateCache() CacheType.Caffeine -> CaffeineTemplateCache()
CacheType.Caffeine -> CaffeineTemplateCache() CacheType.ConcurrentMap -> ConcurrentMapTemplateCache()
CacheType.ConcurrentMap -> ConcurrentMapTemplateCache() })
} .tagCache(when (builder.cache) {
) CacheType.None -> NoOpTagCache()
.tagCache( CacheType.Caffeine -> CaffeineTagCache()
when (builder.cache) { CacheType.ConcurrentMap -> ConcurrentMapTagCache()
CacheType.None -> NoOpTagCache() })
CacheType.Caffeine -> CaffeineTagCache()
CacheType.ConcurrentMap -> ConcurrentMapTagCache()
}
)
.extension(object : AbstractExtension() { .extension(object : AbstractExtension() {
override fun getFunctions(): Map<String, Function>? = override fun getFunctions(): Map<String, Function>? =
builder.functions.associateBy { it.name }.ifEmpty { null } builder.functions.associateBy { it.name }.ifEmpty { null }

View File

@ -1,44 +0,0 @@
package starter.utils
import org.http4k.core.Request
import org.http4k.core.body.form
import org.http4k.core.body.formAsMap
import starter.Project
import starter.config.StarterConfig
interface ProjectExtractor {
operator fun invoke(request: Request): Project?
}
class ProjectExtractorImpl(private val conf: StarterConfig) : ProjectExtractor {
override fun invoke(request: Request): Project? {
val deps = conf.dependencies.filter {
request.form(it.name) != null
}
val formMap = request.formAsMap()
val inputKeys = conf.inputs.map { it.name }
val inputs = formMap
.filter { it.key in inputKeys }
.map { (name, value) ->
conf.inputs.find { it.name == name }!!.copy(value = value.first())
}
val features = formMap
.filter { it.key in conf.features.map { it.name } }
.map { (name, _) ->
conf.features.find { it.name == name }!!.copy(value = true)
}
val projectName = inputs.find { it.name == "name" }?.value ?: return null
val basePackage = inputs.find { it.name == "basePackage" }?.value ?: return null
return if (basePackage.contains("/") || basePackage.contains("..")) {
null
} else {
val repositories = deps.mapNotNull { it.repository }.toSet()
Project(projectName, basePackage, inputs, features, deps, repositories)
}
}
}

View File

@ -7,8 +7,7 @@ import starter.utils.render
class Views(private val engine: PebbleEngine, config: StarterConfig) { class Views(private val engine: PebbleEngine, config: StarterConfig) {
private val args = mapOf( private val args = mapOf(
"dependencies" to config.dependencies.groupBy { it.category }.toSortedMap(), "dependencies" to config.dependencies.groupBy { it.category }.toSortedMap(),
"inputs" to config.inputs, "inputs" to config.inputs
"features" to config.features,
) )
fun index() = engine.render("views/index", args) fun index() = engine.render("views/index", args)

View File

@ -1,18 +0,0 @@
# editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{kt, kts}]
indent_size = 4
insert_final_newline = true
continuation_indent_size=4
max_line_length = 120
disabled_rules = no-wildcard-imports
kotlin_imports_layout = idea

View File

@ -10,7 +10,5 @@
{% include "starter/pom/plugins/@shade" %} {% include "starter/pom/plugins/@shade" %}
{% include "starter/pom/plugins/@ktlint" %}
</plugins> </plugins>
</build> </build>

View File

@ -8,7 +8,6 @@
<properties> <properties>
<java.version>{{ javaVersion }}</java.version> <java.version>{{ javaVersion }}</java.version>
<kotlin.version>{{ kotlinVersion }}</kotlin.version> <kotlin.version>{{ kotlinVersion }}</kotlin.version>
<kotlin.code.style>official</kotlin.code.style>
<maven.compiler.target>${java.version}</maven.compiler.target> <maven.compiler.target>${java.version}</maven.compiler.target>
<maven.compiler.source>${java.version}</maven.compiler.source> <maven.compiler.source>${java.version}</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

View File

@ -1,46 +0,0 @@
{% if features | hasFeature("ktlint") %}
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>1.7</version>
<executions>
<execution>
<id>ktlint</id>
<phase>verify</phase>
<configuration>
<target name="ktlint">
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
<arg value="src/**/*.kt"/>
</java>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>ktlint-format</id>
<configuration>
<target name="ktlint">
<java taskname="ktlint" dir="${basedir}" fork="true" failonerror="true"
classname="com.pinterest.ktlint.Main" classpathref="maven.plugin.classpath">
<arg value="-F"/>
<arg value="src/**/*.kt"/>
</java>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.pinterest</groupId>
<artifactId>ktlint</artifactId>
<version>0.39.0</version>
</dependency>
</dependencies>
</plugin>
{% endif %}

View File

@ -10,19 +10,6 @@
{% endfor %} {% endfor %}
<div class="mt-4"> <div class="mt-4">
<section>
<h2 class="category">Features</h2>
<ul>
{% for feature in features %}
<label class="m-2">
<input name="{{ feature.name }}"
type="checkbox"{% if feature.value %} checked{% endif %}>
<span>{{ feature.name }}</span>
</label>
{% endfor %}
</ul>
</section>
{% for category in dependencies %} {% for category in dependencies %}
<section> <section>
<h2 class="category">{{ category.key }}</h2> <h2 class="category">{{ category.key }}</h2>

View File

@ -1,46 +0,0 @@
package starter
import starter.config.StarterConfig
val testConfig = StarterConfig(
dependencies = listOf(
Dependency(
name = "h2",
groupId = "com.h2database",
artifactId = "h2",
version = Version("h2", "1.4.200"),
default = false,
category = Category.Database,
scope = Scope.Compile,
logger = null,
repository = null,
),
Dependency(
name = "assertj",
groupId = "org.assertj",
artifactId = "assertj-core",
version = Version("assertj", "3.17.2"),
default = true,
category = Category.Test,
scope = Scope.Test,
logger = null,
repository = null,
),
Dependency(
name = "koin",
groupId = "org.koin",
artifactId = "koin-core",
version = Version("koin", "2.1.6"),
default = true,
category = Category.Injection,
scope = Scope.Compile,
logger = null,
repository = Repository("jcenter", "https://jcenter.bintray.com"),
),
),
inputs = listOf(
Input(name = "name", display = "name", value = "example"),
Input(name = "basePackage", display = "Base package", value = "org.example"),
),
features = listOf(Feature("ktlint", true))
)

View File

@ -30,7 +30,6 @@ internal class PomTemplateTest {
name = "Test", name = "Test",
basePackage = "org.test", basePackage = "org.test",
inputs = conf.inputs, inputs = conf.inputs,
features = emptyList(),
dependencies = conf.dependencies, dependencies = conf.dependencies,
repositories = conf.dependencies.mapNotNull { it.repository }.toSet() repositories = conf.dependencies.mapNotNull { it.repository }.toSet()
) )
@ -73,6 +72,7 @@ internal class PomTemplateTest {
.associate { it.nodeName to it.firstChild.nodeValue } .associate { it.nodeName to it.firstChild.nodeValue }
Triple(map["groupId"]!!, map["artifactId"]!!, map["version"] ?: "") Triple(map["groupId"]!!, map["artifactId"]!!, map["version"] ?: "")
}.filterNot { it.second == "kotlin-stdlib-jdk8" } }.filterNot { it.second == "kotlin-stdlib-jdk8" }
println(deps.joinToString("\n")) println(deps.joinToString("\n"))
@ -116,5 +116,6 @@ internal class PomTemplateTest {
val kotlinxPluginDep = "$kotlinMavenPlugin/dependencies/dependency/artifactId" val kotlinxPluginDep = "$kotlinMavenPlugin/dependencies/dependency/artifactId"
assertThat(xml.extract(kotlinxPluginDep)) assertThat(xml.extract(kotlinxPluginDep))
.isEqualTo("kotlin-maven-serialization") .isEqualTo("kotlin-maven-serialization")
} }
} }

View File

@ -1,109 +0,0 @@
package starter.utils
import org.assertj.core.api.Assertions.assertThat
import org.http4k.core.Method
import org.http4k.core.Request
import org.http4k.core.body.Form
import org.http4k.core.body.toBody
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.MethodSource
import org.koin.dsl.koinApplication
import org.koin.dsl.module
import starter.Feature
import starter.Input
import starter.Project
import starter.modules.routesModule
import starter.testConfig
import java.util.stream.Stream
internal class ProjectExtractorImplTest {
private val fakeModule = module {
single { testConfig }
}
private val koin = koinApplication {
modules(routesModule, fakeModule)
}.koin
private val projectExtractor = koin.get<ProjectExtractor>()
@Suppress("unused")
fun invalidProjectsSource(): Stream<Form> = Stream.of(
emptyList(),
listOf(
"basePackage" to "org.example",
),
listOf(
"basePackage" to "org.example/a",
"name" to "test"
),
)
@ParameterizedTest
@MethodSource("invalidProjectsSource")
fun invalidProjects(form: Form) {
val request = Request(Method.POST, "")
.body(form.toBody())
val project = projectExtractor(request)
assertThat(project).isNull()
}
@Suppress("unused")
fun validProjectsSource(): Stream<Pair<Form, Project>> = Stream.of(
listOf(
"name" to "Test",
"basePackage" to "org.example",
"ktlint" to "",
) to Project(
name = "Test",
basePackage = "org.example",
inputs = listOf(
Input(name = "name", display = "name", value = "Test"),
Input(name = "basePackage", display = "Base package", value = "org.example"),
),
features = listOf(
Feature(name = "ktlint", value = true)
),
dependencies = listOf(),
repositories = setOf(),
),
listOf(
"name" to "Test",
"basePackage" to "org.example",
"ktlint" to "",
"koin" to "",
) to Project(
name = "Test",
basePackage = "org.example",
inputs = listOf(
Input(name = "name", display = "name", value = "Test"),
Input(name = "basePackage", display = "Base package", value = "org.example"),
),
features = listOf(
Feature(name = "ktlint", value = true)
),
dependencies = listOf(
testConfig.dependencies.find { it.name == "koin" }!!
),
repositories = setOf(
testConfig.dependencies.find { it.name == "koin" }!!.repository!!
),
),
)
@ParameterizedTest
@MethodSource("validProjectsSource")
fun validProjects(pair: Pair<Form, Project>) {
val (form, expected) = pair
val request = Request(Method.POST, "")
.body(form.toBody())
val project = projectExtractor(request)
assertThat(project).isEqualTo(expected)
}
}

View File

@ -1,4 +0,0 @@
junit.jupiter.testinstance.lifecycle.default=per_class
junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=same_thread
junit.jupiter.execution.parallel.mode.classes.default=concurrent