Warm tip: This article is reproduced from stackoverflow.com, please click
corda

Corda debugging flow tests in MockNetwork with Accounts

发布于 2020-04-05 23:39:38

Still new to Corda.

I am trying to run the Corda Accounts SupplyChain sample via a MockNetwork so that I can debug my flows. In order to do this I need to create the accounts and the run a SendInvoice flow. Here is my Flow Test

class FlowTests {
    private lateinit var network: MockNetwork
    private lateinit var buyer: StartedMockNode
    private lateinit var seller: StartedMockNode
    private lateinit var shippingCo: StartedMockNode

    @Before
    fun setup() {

        network = MockNetwork(MockNetworkParameters( networkParameters = testNetworkParameters(minimumPlatformVersion = 4),
                cordappsForAllNodes = listOf(
                        TestCordapp.findCordapp("com.accounts_SupplyChain.contracts"),
                        TestCordapp.findCordapp("com.accounts_SupplyChain.flows"),
                        TestCordapp.findCordapp("com.accounts_SupplyChain.schemas"),
                        TestCordapp.findCordapp("com.r3.corda.lib.accounts.contracts"),
                        // This CorDapp has the missing service from your error message.
                        TestCordapp.findCordapp("com.r3.corda.lib.accounts.workflows"),
                        TestCordapp.findCordapp("com.r3.corda.lib.ci")
                )))

        buyer = network.createPartyNode()
        seller = network.createPartyNode()
        shippingCo = network.createPartyNode()
        listOf(buyer,seller,shippingCo).forEach {it.registerInitiatedFlow(SendInvoiceResponder::class.java)}
        network.runNetwork()

    }
    @After
    fun tearDown() {
        network.stopNodes()
    }



    @Test
    fun Test1() {
        // Create Buyer Account
        val flow1 = CreateNewAccount("BuyerProcurement")
        val  future1  = buyer.startFlow(flow1);

        // Share the account
        val flow3 = ShareAccountTo("BuyerProcurement", shareTo = seller.info.singleIdentity())
        val future3 = buyer.startFlow(flow3)


        // On Seller Node
        val flow7 = CreateNewAccount("SellerSales")
        val future7 = seller.startFlow(flow7)

        val flow10 = ShareAccountTo("SellerSales", shareTo =  buyer.info.singleIdentity())
        val future10 = seller.startFlow(flow10)

        // Check the accounts are created on the buyer node
        val flowView = ViewMyAccounts()
        val fut = buyer.startFlow(flowView)

        // Check the accounts are creted on the seller Node
        val flowView2 = ViewMyAccounts()
        val fut2 = seller.startFlow(flowView)

        // Now send an Invoice
        val sendInvoice = SendInvoice(whoAmI = "SellerSales",whereTo = "BuyerProcurement", amount = 500)
        val futureSendInvoice = seller.startFlow(sendInvoice)

        network.runNetwork()

        val resp1= future1.getOrThrow()
        println(resp1.toString())
        val resp2 = future7.getOrThrow()
        println(resp2.toString())


        val buyerAccounts = fut.getOrThrow()
        println(buyerAccounts.toString())

        val sellerAccounts= fut2.getOrThrow()
        println(sellerAccounts.toString())

        // Get the invoice Created
        val invoiceCreation = futureSendInvoice.getOrThrow()
        println(invoiceCreation.toString())

    }
}

Not very elegant but it is just a test.

The response I get back is:

BuyerProcurement team's account was created. UUID is : 60d91f03-4112-4c2a-801b-95fe4297bf3b

SellerSales team's account was created. UUID is : 09a4a089-2a98-4148-a00d-252438d816f1

[BuyerProcurement]

[SellerSales]

so I think the accounts are created (maybe not !!). However when running the flow :

 val flow10 = ShareAccountTo("SellerSales", shareTo =  buyer.info.singleIdentity())
 val future10 = seller.startFlow(flow10)

I get an error :

java.util.NoSuchElementException: List is empty.

    at kotlin.collections.CollectionsKt___CollectionsKt.single(_Collections.kt:478)
    at com.accounts_SupplyChain.flows.SendInvoice.call(SendInvoice.kt:67)
    at com.accounts_SupplyChain.flows.SendInvoice.call(SendInvoice.kt:26)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:270)
    at net.corda.node.services.statemachine.FlowStateMachineImpl.run(FlowStateMachineImpl.kt:46)
    at co.paralleluniverse.fibers.Fiber.run1(Fiber.java:1092)
    at co.paralleluniverse.fibers.Fiber.exec(Fiber.java:788)
    at co.paralleluniverse.fibers.RunnableFiberTask.doExec(RunnableFiberTask.java:100)
    at co.paralleluniverse.fibers.RunnableFiberTask.run(RunnableFiberTask.java:91)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at net.corda.node.utilities.AffinityExecutor$ServiceAffinityExecutor$1$thread$1.run(AffinityExecutor.kt:63)

Setting breakpoints in SendInvoice.kt, I have discovered that it is caused by trying to get the targetAccount (BuyerProcurement) :

   @Suspendable
    override fun call(): String {

        //Generate key for transaction
        progressTracker.currentStep = GENERATING_KEYS
        val myAccount = accountService.accountInfo(whoAmI).single().state.data
        val myKey = subFlow(NewKeyForAccount(myAccount.identifier.id)).owningKey

/// ERROR HERE //////
        val targetAccount = accountService.accountInfo(whereTo).single().state.data
        val targetAcctAnonymousParty = subFlow(RequestKeyForAccount(targetAccount))



        //generating State for transfer
        progressTracker.currentStep = GENERATING_TRANSACTION
        val output = InvoiceState(amount, AnonymousParty(myKey),targetAcctAnonymousParty,UUID.randomUUID())
        val transactionBuilder = TransactionBuilder(serviceHub.networkMapCache.notaryIdentities.first())
        transactionBuilder.addOutputState(output)
                .addCommand(InvoiceStateContract.Commands.Create(), listOf(targetAcctAnonymousParty.owningKey,myKey))

Does anyone have any ideas. I need to be able to single step through my flows to determine why they don't complete.

Questioner
GarethW
Viewed
55
Haisheng Zhang 2020-02-03 19:51

If you need to query the account info after some step in test network, you’d call network.runNetwork() every step so that the flow is finalised formally, otherwise you'd not get the data (list is empty)