CORDA-1837 - Reject nodes that have the same organisation name in driver tests (#3740)

* Reject nodes that have the same organisation name as a previously registered node rather than the same X500 name

DriverDSLImpl -> NetworkVisibilityController -> register check organisation name rather than X500 name and throw IllegalStateException if already exists
Added test to DriverTests to test multiple organisation names end exceptionally

* Remove redundant test and store the organisation names rather than X500 names for driver tests
This commit is contained in:
Dan Newton 2018-08-04 13:26:41 +01:00 committed by Katelyn Baker
parent 83a0988517
commit 599aa709dd
2 changed files with 77 additions and 12 deletions

View File

@ -125,22 +125,22 @@ class DriverTests {
} }
@Test @Test
fun `driver rejects multiple nodes with the same name`() { fun `driver rejects multiple nodes with the same name parallel`() {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
driver(DriverParameters(startNodesInProcess = true)) { val nodes = listOf(newNode(DUMMY_BANK_A_NAME), newNode(DUMMY_BANK_B_NAME), newNode(DUMMY_BANK_A_NAME))
assertThatIllegalArgumentException().isThrownBy {
assertThatThrownBy { listOf(newNode(DUMMY_BANK_A_NAME)(), newNode(DUMMY_BANK_B_NAME)(), newNode(DUMMY_BANK_A_NAME)()).transpose().getOrThrow() }.isInstanceOf(IllegalArgumentException::class.java) nodes.parallelStream().map { it.invoke() }.toList().transpose().getOrThrow()
}
} }
} }
@Test @Test
fun `driver rejects multiple nodes with the same name parallel`() { fun `driver rejects multiple nodes with the same organisation name`() {
driver(DriverParameters(startNodesInProcess = true, notarySpecs = emptyList())) {
driver(DriverParameters(startNodesInProcess = true)) { newNode(CordaX500Name(commonName = "Notary", organisation = "R3CEV", locality = "New York", country = "US"))().getOrThrow()
assertThatIllegalArgumentException().isThrownBy {
val nodes = listOf(newNode(DUMMY_BANK_A_NAME), newNode(DUMMY_BANK_B_NAME), newNode(DUMMY_BANK_A_NAME)) newNode(CordaX500Name(commonName = "Regulator", organisation = "R3CEV", locality = "New York", country = "US"))().getOrThrow()
}
assertThatThrownBy { nodes.parallelStream().map { it.invoke() }.toList().transpose().getOrThrow() }.isInstanceOf(IllegalArgumentException::class.java)
} }
} }

View File

@ -917,6 +917,71 @@ class DriverDSLImpl(
} }
} }
/**
* Keeps track of how many nodes each node sees and gates nodes from completing their startNode [CordaFuture] until all
* current nodes see everyone.
*/
private class NetworkVisibilityController {
private val nodeVisibilityHandles = ThreadBox(HashMap<String, VisibilityHandle>())
fun register(name: CordaX500Name): VisibilityHandle {
val handle = VisibilityHandle()
nodeVisibilityHandles.locked {
require(name.organisation !in keys) {
"Node with organisation name ${name.organisation} is already started or starting"
}
put(name.organisation, handle)
}
return handle
}
private fun checkIfAllVisible() {
nodeVisibilityHandles.locked {
val minView = values.stream().mapToInt { it.visibleNodeCount }.min().orElse(0)
if (minView >= size) {
values.forEach { it.future.set(Unit) }
}
}
}
inner class VisibilityHandle : AutoCloseable {
internal val future = openFuture<Unit>()
internal var visibleNodeCount = 0
private var subscription: Subscription? = null
fun listen(rpc: CordaRPCOps): CordaFuture<Unit> {
check(subscription == null)
val (snapshot, updates) = rpc.networkMapFeed()
visibleNodeCount = snapshot.size
checkIfAllVisible()
subscription = updates.subscribe {
when (it) {
is NetworkMapCache.MapChange.Added -> {
visibleNodeCount++
checkIfAllVisible()
}
is NetworkMapCache.MapChange.Removed -> {
visibleNodeCount--
checkIfAllVisible()
}
is NetworkMapCache.MapChange.Modified -> {
// Nothing to do here but better being exhaustive.
}
}
}
return future
}
override fun close() {
subscription?.unsubscribe()
nodeVisibilityHandles.locked {
values -= this@VisibilityHandle
checkIfAllVisible()
}
}
}
}
interface InternalDriverDSL : DriverDSL, CordformContext { interface InternalDriverDSL : DriverDSL, CordformContext {
private companion object { private companion object {
private val DEFAULT_POLL_INTERVAL = 500.millis private val DEFAULT_POLL_INTERVAL = 500.millis