Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,42 @@ import com.usvision.model.systemcomposite.System
class SystemBuilderException(message: String)
: RuntimeException(message)

class SystemBuilder(private val parent: SystemBuilder? = null) {
private lateinit var rootName: String
private lateinit var subsystems: MutableSet<System>
class CompanySystemBuilder(private val parent: CompanySystemBuilder? = null) {
private var rootName: String? = null
private var subsystems: MutableSet<System> = mutableSetOf()

private fun fluentInterface(instance: SystemBuilder = this, implementation: SystemBuilder.() -> Unit): SystemBuilder {
private fun fluentInterface(instance: CompanySystemBuilder = this, implementation: CompanySystemBuilder.() -> Unit): CompanySystemBuilder {
implementation()
return instance
}

fun setName(name: String) = fluentInterface { rootName = name }
fun setName(name: String) = fluentInterface {
this.rootName?.also {
throw SystemBuilderException("System already has a name: $rootName")
}
rootName = name
}

fun addSubsystems() = fluentInterface(SystemBuilder(this)) {
subsystems = mutableSetOf()
fun addSubsystems(): CompanySystemBuilder {
return CompanySystemBuilder(this)
}

fun endSubsystems(): SystemBuilder {
fun and(): CompanySystemBuilder {
endSubsystems()
return CompanySystemBuilder(this.parent)
}

fun endSubsystems(): CompanySystemBuilder {
if (parent == null)
throw SystemBuilderException("Attempted to close an environment that had not being opened")

if (this::rootName.isInitialized) {
val system = build()
parent.addSubsystem(system)
}
val system = build()
parent.addSubsystem(system)

return parent
}

fun thatHasMicroservices(): MicroserviceBuilder {
subsystems = mutableSetOf()
return MicroserviceBuilder(this)
}

Expand All @@ -53,15 +60,16 @@ class SystemBuilder(private val parent: SystemBuilder? = null) {
}

fun build(): System {
return CompanySystem(rootName).also { root ->
if (this::subsystems.isInitialized)
return this.rootName?.let { rootName ->
CompanySystem(rootName).also { root ->
subsystems.forEach(root::addSubsystem)
}
}
} ?: throw SystemBuilderException("Attempted to build a System with no name")
}
}

class MicroserviceBuilder(private val parent: SystemBuilder? = null) {
private lateinit var name: String
class MicroserviceBuilder(private val parent: CompanySystemBuilder? = null) {
private var name: String? = null
private val exposedOperations = mutableSetOf<Operation>()
private val consumedOperations = mutableSetOf<Operation>()
private val databases = mutableSetOf<Database>()
Expand All @@ -73,7 +81,7 @@ class MicroserviceBuilder(private val parent: SystemBuilder? = null) {
return this
}

fun endMicroservices(): SystemBuilder {
fun endMicroservices(): CompanySystemBuilder {
if (parent == null)
throw SystemBuilderException("Attempted to close an environment that had not being opened")

Expand All @@ -85,10 +93,14 @@ class MicroserviceBuilder(private val parent: SystemBuilder? = null) {

fun and(): MicroserviceBuilder {
endMicroservices()
return parent!!.thatHasMicroservices()
return MicroserviceBuilder(this.parent)
}

fun named(name: String) = fluentInterface {
this.name?.also {
throw SystemBuilderException("Microservice already has a name: $name")
}

this.name = name
}

Expand Down Expand Up @@ -117,12 +129,14 @@ class MicroserviceBuilder(private val parent: SystemBuilder? = null) {
}

fun build(): Microservice {
return Microservice(name).also { msvc ->
exposedOperations.forEach(msvc::exposeOperation)
consumedOperations.forEach(msvc::consumeOperation)
databases.forEach(msvc::addDatabaseConnection)
channelsPublished.forEach(msvc::addPublishChannel)
channelsSubscribed.forEach(msvc::addSubscribedChannel)
}
return this.name?.let { name ->
Microservice(name).also { msvc ->
exposedOperations.forEach(msvc::exposeOperation)
consumedOperations.forEach(msvc::consumeOperation)
databases.forEach(msvc::addDatabaseConnection)
channelsPublished.forEach(msvc::addPublishChannel)
channelsSubscribed.forEach(msvc::addSubscribedChannel)
}
} ?: throw SystemBuilderException("Attempted to build a Microservice with no name")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import io.mockk.verify
import org.junit.jupiter.api.assertThrows
import kotlin.test.*

internal class SystemBuilderTest {
private lateinit var underTest: SystemBuilder
internal class CompanySystemBuilderTest {
private lateinit var underTest: CompanySystemBuilder

@BeforeTest
fun `create clean, new instance of SystemBuilder`() {
underTest = SystemBuilder()
underTest = CompanySystemBuilder()
}

@Test
Expand Down Expand Up @@ -46,10 +46,38 @@ internal class SystemBuilderTest {
assertEquals(1, system.getSubsystemSet().size)
val firstLevelSubsys = system.getSubsystemSet().first()
assertIs<CompanySystem>(firstLevelSubsys)
assertEquals(2, firstLevelSubsys.getSubsystemSet().size)
assertIs<Microservice>(firstLevelSubsys.getSubsystemSet().first())
}


@Test
fun `it builds a system with two subsystems`() {

// given ... when
val system = underTest
.setName("Bank")
.addSubsystems()
.setName("Bank subsystem one")
.thatHasMicroservices()
.oneNamed("Balance API")
.exposingRestEndpoint("GET", "/account/balance", "account balance")
.endMicroservices()
.and()
.setName("Bank subsystem two")
.endSubsystems()
.build()

// then
assertIs<CompanySystem>(system)
assertEquals(2, system.getSubsystemSet().size)
val firstLevelSubsys = system.getSubsystemSet().first()
assertIs<CompanySystem>(firstLevelSubsys)
assertEquals(1, firstLevelSubsys.getSubsystemSet().size)
assertIs<Microservice>(firstLevelSubsys.getSubsystemSet().first())
}


@Test
fun `it defaults to a company system`() {
// given
Expand All @@ -66,20 +94,19 @@ internal class SystemBuilderTest {
}

@Test
fun `opening and closing a subsystem environment gives an empty subsys set`() {
fun `it throws SystemBuilderException when endSubsystems without setting a name for the subsystem`() {
// given
val name = "test"

// when
val result = underTest
.setName(name)
.addSubsystems()
.endSubsystems()
.build()

// then
assertIs<CompanySystem>(result)
assertContentEquals(listOf(), result.getSubsystemSet())
assertThrows<SystemBuilderException> {
result.endSubsystems()
}
}

@Test
Expand Down Expand Up @@ -113,7 +140,11 @@ internal class MicroserviceBuilderTest {

@BeforeTest
fun `create clean, new instance of MicroserviceBuilder`() {
underTest = MicroserviceBuilder()
val parent = spyk(CompanySystemBuilder())

// when
underTest = parent
.thatHasMicroservices()
}

@Test
Expand All @@ -128,16 +159,17 @@ internal class MicroserviceBuilderTest {
@Test
fun `it returns its parent when closing a properly opened microservice env`() {
// given
val parent = spyk(SystemBuilder())
underTest = MicroserviceBuilder(parent)
val parent = spyk(CompanySystemBuilder())
underTest = parent
.thatHasMicroservices()

// when
val environment = underTest
.named("something")
.endMicroservices()

// then
assertIs<SystemBuilder>(environment)
assertIs<CompanySystemBuilder>(environment)
assertEquals(parent, environment)
verify { parent.addMicroservice(any()) }
}
Expand All @@ -158,6 +190,15 @@ internal class MicroserviceBuilderTest {
assertNotNull(result.module)
}

@Test
fun `it throws SystemBuilderException when building a Microservice with no name`() {
// given nothing
// when ... then
assertThrows<SystemBuilderException> {
underTest.build()
}
}

@Test
fun `it adds exposed rest endpoints`() {
// given
Expand Down Expand Up @@ -289,12 +330,46 @@ internal class MicroserviceBuilderTest {
verify { underTest.named(name) }
}

@Test
fun `it throws SystemBuilderException when calling 'named' method twice`() {
// given
val name = "micro"

// when
val result = underTest
.named(name)


//then
assertThrows<SystemBuilderException> {
result.named(name)
}
}


@Test
fun `it throws SystemBuilderException when calling 'and' method twice without setting a name for the second microservice`() {
// given
val name = "micro"

// when
val result = underTest
.named(name)
.and()


//then
assertThrows<SystemBuilderException> {
result.and()
}
}

@Test
fun `it offers 'and' as a convenience for finishing one and beginning another`() {
// given
val nameOne = "name one"
val nameTwo = "name two"
val parent = spyk(SystemBuilder())
val parent = spyk(CompanySystemBuilder())

// when
parent
Expand Down