mirror of
https://github.com/corda/corda.git
synced 2024-12-18 20:47:57 +00:00
INFRA-553 Move HashLookupCommandTest to unit test (#6537)
Move HashLookupCommandTest to unit test and add a test covering that a nonsense hash ID is correctly rejected.
This commit is contained in:
parent
250ed8a21a
commit
9aa745ef14
@ -1,96 +0,0 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import co.paralleluniverse.fibers.Suspendable
|
||||
import com.google.common.io.Files
|
||||
import com.jcraft.jsch.ChannelExec
|
||||
import com.jcraft.jsch.JSch
|
||||
import com.jcraft.jsch.Session
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.flows.FlowLogic
|
||||
import net.corda.core.flows.StartableByRPC
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.startFlow
|
||||
import net.corda.core.utilities.getOrThrow
|
||||
import net.corda.node.services.Permissions
|
||||
import net.corda.testing.contracts.DummyContract
|
||||
import net.corda.testing.core.ALICE_NAME
|
||||
import net.corda.testing.driver.DriverParameters
|
||||
import net.corda.testing.driver.NodeHandle
|
||||
import net.corda.testing.driver.driver
|
||||
import net.corda.testing.node.User
|
||||
import net.corda.testing.node.internal.DUMMY_CONTRACTS_CORDAPP
|
||||
import org.bouncycastle.util.io.Streams
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class HashLookupCommandTest {
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `hash lookup command returns correct response`() {
|
||||
val user = User("u", "p", setOf(Permissions.all()))
|
||||
|
||||
driver(DriverParameters(notarySpecs = emptyList(), cordappsForAllNodes = listOf(DUMMY_CONTRACTS_CORDAPP))) {
|
||||
val nodeFuture = startNode(providedName = ALICE_NAME, rpcUsers = listOf(user), startInSameProcess = true)
|
||||
val node = nodeFuture.getOrThrow()
|
||||
val txId = issueTransaction(node)
|
||||
val txIdHashed = txId.sha256()
|
||||
|
||||
testCommand(user, node, command = "hashLookup $txId", expected = "Found a matching transaction with Id: $txId")
|
||||
testCommand(user, node, command = "hashLookup $txIdHashed", expected = "Found a matching transaction with Id: $txId")
|
||||
testCommand(user, node, command = "hashLookup ${SecureHash.randomSHA256()}", expected = "No matching transaction found")
|
||||
}
|
||||
}
|
||||
|
||||
private fun testCommand(user: User, node: NodeHandle, command: String, expected: String) {
|
||||
val session = connectToShell(user, node)
|
||||
val channel = session.openChannel("exec") as ChannelExec
|
||||
channel.setCommand(command)
|
||||
channel.connect(5000)
|
||||
|
||||
assertTrue(channel.isConnected)
|
||||
|
||||
val response = String(Streams.readAll(channel.inputStream))
|
||||
val matchFound = response.lines().any { line ->
|
||||
line.contains(expected)
|
||||
}
|
||||
channel.disconnect()
|
||||
assertTrue(matchFound)
|
||||
session.disconnect()
|
||||
}
|
||||
|
||||
private fun connectToShell(user: User, node: NodeHandle): Session {
|
||||
val conf = ShellConfiguration(commandsDirectory = Files.createTempDir().toPath(),
|
||||
user = user.username, password = user.password,
|
||||
hostAndPort = node.rpcAddress,
|
||||
sshdPort = 2224)
|
||||
|
||||
InteractiveShell.startShell(conf)
|
||||
InteractiveShell.nodeInfo()
|
||||
|
||||
val session = JSch().getSession("u", "localhost", 2224)
|
||||
session.setConfig("StrictHostKeyChecking", "no")
|
||||
session.setPassword("p")
|
||||
session.connect()
|
||||
|
||||
assertTrue(session.isConnected)
|
||||
return session
|
||||
}
|
||||
|
||||
private fun issueTransaction(node: NodeHandle): SecureHash {
|
||||
return node.rpc.startFlow(::DummyIssue).returnValue.get()
|
||||
}
|
||||
}
|
||||
|
||||
@StartableByRPC
|
||||
internal class DummyIssue : FlowLogic<SecureHash>() {
|
||||
@Suspendable
|
||||
override fun call(): SecureHash {
|
||||
val me = serviceHub.myInfo.legalIdentities.first().ref(0)
|
||||
val fakeNotary = me.party
|
||||
val builder = DummyContract.generateInitial(1, fakeNotary as Party, me)
|
||||
val stx = serviceHub.signInitialTransaction(builder)
|
||||
serviceHub.recordTransactions(stx)
|
||||
return stx.id
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package net.corda.tools.shell;
|
||||
|
||||
import net.corda.core.crypto.SecureHash;
|
||||
import net.corda.core.internal.VisibleForTesting;
|
||||
import net.corda.core.messaging.CordaRPCOps;
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping;
|
||||
import org.crsh.cli.Argument;
|
||||
@ -13,13 +14,14 @@ import org.crsh.text.Decoration;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Named("hashLookup")
|
||||
public class HashLookupShellCommand extends InteractiveShellCommand {
|
||||
private static Logger logger = LoggerFactory.getLogger(HashLookupShellCommand.class);
|
||||
final private String manualText ="Checks if a transaction matching a specified Id hash value is recorded on this node.\n\n" +
|
||||
private static final String manualText ="Checks if a transaction matching a specified Id hash value is recorded on this node.\n\n" +
|
||||
"Both the transaction Id and the hashed value of a transaction Id (as returned by the Notary in case of a double-spend) is a valid input.\n" +
|
||||
"This is mainly intended to be used for troubleshooting notarisation issues when a\n" +
|
||||
"state is claimed to be already consumed by another transaction.\n\n" +
|
||||
@ -29,25 +31,32 @@ public class HashLookupShellCommand extends InteractiveShellCommand {
|
||||
@Man(manualText)
|
||||
|
||||
public void main(@Usage("A transaction Id or a hexadecimal SHA-256 hash value representing the hashed transaction Id") @Argument(unquote = false) String txIdHash) {
|
||||
CordaRPCOps proxy = ops();
|
||||
try {
|
||||
hashLookup(out, proxy, txIdHash);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
out.println(manualText);
|
||||
out.println(ex.getMessage(), Decoration.bold, Color.red);
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected static void hashLookup(PrintWriter out, CordaRPCOps proxy, String txIdHash) throws IllegalArgumentException {
|
||||
logger.info("Executing command \"hashLookup\".");
|
||||
|
||||
if (txIdHash == null) {
|
||||
out.println(manualText);
|
||||
out.println("Please provide a hexadecimal transaction Id hash value or a transaction Id", Decoration.bold, Color.red);
|
||||
return;
|
||||
throw new IllegalArgumentException("Please provide a hexadecimal transaction Id hash value or a transaction Id");
|
||||
}
|
||||
|
||||
CordaRPCOps proxy = ops();
|
||||
List<StateMachineTransactionMapping> mapping = proxy.stateMachineRecordedTransactionMappingSnapshot();
|
||||
|
||||
SecureHash txIdHashParsed;
|
||||
try {
|
||||
txIdHashParsed = SecureHash.parse(txIdHash);
|
||||
} catch (IllegalArgumentException e) {
|
||||
out.println("The provided string is not a valid hexadecimal SHA-256 hash value", Decoration.bold, Color.red);
|
||||
return;
|
||||
throw new IllegalArgumentException("The provided string is not a valid hexadecimal SHA-256 hash value");
|
||||
}
|
||||
|
||||
List<StateMachineTransactionMapping> mapping = proxy.stateMachineRecordedTransactionMappingSnapshot();
|
||||
Optional<SecureHash> match = mapping.stream()
|
||||
.map(StateMachineTransactionMapping::getTransactionId)
|
||||
.filter(
|
||||
@ -59,7 +68,7 @@ public class HashLookupShellCommand extends InteractiveShellCommand {
|
||||
SecureHash found = match.get();
|
||||
out.println("Found a matching transaction with Id: " + found.toString());
|
||||
} else {
|
||||
out.println("No matching transaction found", Decoration.bold, Color.red);
|
||||
throw new IllegalArgumentException("No matching transaction found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,67 @@
|
||||
package net.corda.tools.shell
|
||||
|
||||
import net.corda.core.crypto.SecureHash
|
||||
import net.corda.core.crypto.sha256
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.StateMachineTransactionMapping
|
||||
import org.hamcrest.MatcherAssert
|
||||
import org.hamcrest.core.StringContains
|
||||
import org.junit.Test
|
||||
import org.mockito.Mockito
|
||||
import java.io.CharArrayWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.UUID
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class HashLookupCommandTest {
|
||||
companion object {
|
||||
private val DEFAULT_TXID: SecureHash = SecureHash.randomSHA256()
|
||||
|
||||
private fun ops(vararg txIds: SecureHash): CordaRPCOps? {
|
||||
val snapshot: List<StateMachineTransactionMapping> = txIds.map { txId ->
|
||||
StateMachineTransactionMapping(StateMachineRunId(UUID.randomUUID()), txId)
|
||||
}
|
||||
return Mockito.mock(CordaRPCOps::class.java).apply {
|
||||
Mockito.`when`(stateMachineRecordedTransactionMappingSnapshot()).thenReturn(snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
private fun runCommand(ops: CordaRPCOps?, txIdHash: String): String {
|
||||
val arrayWriter = CharArrayWriter()
|
||||
return PrintWriter(arrayWriter).use {
|
||||
HashLookupShellCommand.hashLookup(it, ops, txIdHash)
|
||||
it.flush()
|
||||
arrayWriter.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `hash lookup command returns correct response`() {
|
||||
val ops = ops(DEFAULT_TXID)
|
||||
var response = runCommand(ops, DEFAULT_TXID.toString())
|
||||
|
||||
MatcherAssert.assertThat(response, StringContains.containsString("Found a matching transaction with Id: $DEFAULT_TXID"))
|
||||
|
||||
// Verify the hash of the TX ID also works
|
||||
response = runCommand(ops, DEFAULT_TXID.sha256().toString())
|
||||
MatcherAssert.assertThat(response, StringContains.containsString("Found a matching transaction with Id: $DEFAULT_TXID"))
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `should reject invalid txid`() {
|
||||
val ops = ops(DEFAULT_TXID)
|
||||
assertFailsWith<IllegalArgumentException>("The provided string is not a valid hexadecimal SHA-256 hash value") {
|
||||
runCommand(ops, "abcdefgh")
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout=300_000)
|
||||
fun `should reject unknown txid`() {
|
||||
val ops = ops(DEFAULT_TXID)
|
||||
assertFailsWith<IllegalArgumentException>("No matching transaction found") {
|
||||
runCommand(ops, SecureHash.randomSHA256().toString())
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user