mirror of
https://github.com/corda/corda.git
synced 2025-06-18 23:28:21 +00:00
updated following review
This commit is contained in:
91
docs/build/html/protocol-state-machines.html
vendored
91
docs/build/html/protocol-state-machines.html
vendored
@ -116,6 +116,7 @@
|
||||
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-seller">Implementing the seller</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#implementing-the-buyer">Implementing the buyer</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#progress-tracking">Progress tracking</a></li>
|
||||
<li class="toctree-l2"><a class="reference internal" href="#unit-testing">Unit testing</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="toctree-l1"><a class="reference internal" href="oracles.html">Writing oracle services</a></li>
|
||||
@ -696,6 +697,96 @@ and linked ahead of time.</p>
|
||||
<p>In future, the progress tracking framework will become a vital part of how exceptions, errors, and other faults are
|
||||
surfaced to human operators for investigation and resolution.</p>
|
||||
</div>
|
||||
<div class="section" id="unit-testing">
|
||||
<h2>Unit testing<a class="headerlink" href="#unit-testing" title="Permalink to this headline">¶</a></h2>
|
||||
<p>A protocol can be a fairly complex thing that interacts with many services and other parties over the network. That
|
||||
means unit testing one requires some infrastructure to provide lightweight mock implementations. The MockNetwork
|
||||
provides this testing infrastructure layer; you can find this class in the node module</p>
|
||||
<p>A good example to examine for learning how to unit test protocols is the <code class="docutils literal"><span class="pre">ResolveTransactionsProtocol</span></code> tests. This
|
||||
protocol takes care of downloading and verifying transaction graphs, with all the needed dependencies. We start
|
||||
with this basic skeleton:</p>
|
||||
<div class="codeset container">
|
||||
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">ResolveTransactionsProtocolTest</span> <span class="p">{</span>
|
||||
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">net</span><span class="p">:</span> <span class="n">MockNetwork</span>
|
||||
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">a</span><span class="p">:</span> <span class="n">MockNetwork</span><span class="p">.</span><span class="n">MockNode</span>
|
||||
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">b</span><span class="p">:</span> <span class="n">MockNetwork</span><span class="p">.</span><span class="n">MockNode</span>
|
||||
<span class="k">lateinit</span> <span class="k">var</span> <span class="py">notary</span><span class="p">:</span> <span class="n">Party</span>
|
||||
|
||||
<span class="n">@Before</span>
|
||||
<span class="k">fun</span> <span class="nf">setup</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="n">net</span> <span class="p">=</span> <span class="n">MockNetwork</span><span class="p">()</span>
|
||||
<span class="k">val</span> <span class="py">nodes</span> <span class="p">=</span> <span class="n">net</span><span class="p">.</span><span class="n">createSomeNodes</span><span class="p">()</span>
|
||||
<span class="n">a</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">partyNodes</span><span class="p">[</span><span class="m">0</span><span class="p">]</span>
|
||||
<span class="n">b</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">partyNodes</span><span class="p">[</span><span class="m">1</span><span class="p">]</span>
|
||||
<span class="n">notary</span> <span class="p">=</span> <span class="n">nodes</span><span class="p">.</span><span class="n">notaryNode</span><span class="p">.</span><span class="n">info</span><span class="p">.</span><span class="n">identity</span>
|
||||
<span class="n">net</span><span class="p">.</span><span class="n">runNetwork</span><span class="p">()</span>
|
||||
<span class="p">}</span>
|
||||
|
||||
<span class="n">@After</span>
|
||||
<span class="k">fun</span> <span class="nf">tearDown</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="n">net</span><span class="p">.</span><span class="n">stopNodes</span><span class="p">()</span>
|
||||
<span class="p">}</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>We create a mock network in our <code class="docutils literal"><span class="pre">@Before</span></code> setup method and create a couple of nodes. We also record the identity
|
||||
of the notary in our test network, which will come in handy later. We also tidy up when we’re done.</p>
|
||||
<p>Next, we write a test case:</p>
|
||||
<div class="codeset container">
|
||||
<div class="highlight-kotlin"><div class="highlight"><pre><span></span>@Test
|
||||
fun resolveFromTwoHashes() {
|
||||
val (stx1, stx2) = makeTransactions()
|
||||
val p = ResolveTransactionsProtocol(setOf(stx2.id), a.info.identity)
|
||||
val future = b.services.startProtocol("resolve", p)
|
||||
net.runNetwork()
|
||||
val results = future.get()
|
||||
assertEquals(listOf(stx1.id, stx2.id), results.map { it.id })
|
||||
assertEquals(stx1, b.storage.validatedTransactions.getTransaction(stx1.id))
|
||||
assertEquals(stx2, b.storage.validatedTransactions.getTransaction(stx2.id))
|
||||
}
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>We’ll take a look at the <code class="docutils literal"><span class="pre">makeTransactions</span></code> function in a moment. For now, it’s enough to know that it returns two
|
||||
<code class="docutils literal"><span class="pre">SignedTransaction</span></code> objects, the second of which spends the first. Both transactions are known by node A
|
||||
but not node B.</p>
|
||||
<p>The test logic is simple enough: we create the protocol, giving it node A’s identity as the target to talk to.
|
||||
Then we start it on node B and use the <code class="docutils literal"><span class="pre">net.runNetwork()</span></code> method to bounce messages around until things have
|
||||
settled (i.e. there are no more messages waiting to be delivered). All this is done using an in memory message
|
||||
routing implementation that is fast to initialise and use. Finally, we obtain the result of the protocol and do
|
||||
some tests on it. We also check the contents of node B’s database to see that the protocol had the intended effect
|
||||
on the node’s persistent state.</p>
|
||||
<p>Here’s what <code class="docutils literal"><span class="pre">makeTransactions</span></code> looks like:</p>
|
||||
<div class="codeset container">
|
||||
<div class="highlight-kotlin"><div class="highlight"><pre><span></span><span class="k">private</span> <span class="k">fun</span> <span class="nf">makeTransactions</span><span class="p">():</span> <span class="n">Pair</span><span class="p"><</span><span class="n">SignedTransaction</span><span class="p">,</span> <span class="n">SignedTransaction</span><span class="p">></span> <span class="p">{</span>
|
||||
<span class="c1">// Make a chain of custody of dummy states and insert into node A.</span>
|
||||
<span class="k">val</span> <span class="py">dummy1</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">DummyContract</span><span class="p">.</span><span class="n">generateInitial</span><span class="p">(</span><span class="n">MEGA_CORP</span><span class="p">.</span><span class="n">ref</span><span class="p">(</span><span class="m">1</span><span class="p">),</span> <span class="m">0</span><span class="p">,</span> <span class="n">notary</span><span class="p">).</span><span class="n">let</span> <span class="p">{</span>
|
||||
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">MEGA_CORP_KEY</span><span class="p">)</span>
|
||||
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">DUMMY_NOTARY_KEY</span><span class="p">)</span>
|
||||
<span class="n">it</span><span class="p">.</span><span class="n">toSignedTransaction</span><span class="p">(</span><span class="k">false</span><span class="p">)</span>
|
||||
<span class="p">}</span>
|
||||
<span class="k">val</span> <span class="py">dummy2</span><span class="p">:</span> <span class="n">SignedTransaction</span> <span class="p">=</span> <span class="n">DummyContract</span><span class="p">.</span><span class="n">move</span><span class="p">(</span><span class="n">dummy1</span><span class="p">.</span><span class="n">tx</span><span class="p">.</span><span class="n">outRef</span><span class="p">(</span><span class="m">0</span><span class="p">),</span> <span class="n">MINI_CORP_PUBKEY</span><span class="p">).</span><span class="n">let</span> <span class="p">{</span>
|
||||
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">MEGA_CORP_KEY</span><span class="p">)</span>
|
||||
<span class="n">it</span><span class="p">.</span><span class="n">signWith</span><span class="p">(</span><span class="n">DUMMY_NOTARY_KEY</span><span class="p">)</span>
|
||||
<span class="n">it</span><span class="p">.</span><span class="n">toSignedTransaction</span><span class="p">()</span>
|
||||
<span class="p">}</span>
|
||||
<span class="n">a</span><span class="p">.</span><span class="n">services</span><span class="p">.</span><span class="n">recordTransactions</span><span class="p">(</span><span class="n">dummy1</span><span class="p">,</span> <span class="n">dummy2</span><span class="p">)</span>
|
||||
<span class="k">return</span> <span class="n">Pair</span><span class="p">(</span><span class="n">dummy1</span><span class="p">,</span> <span class="n">dummy2</span><span class="p">)</span>
|
||||
<span class="p">}</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>We’re using the <code class="docutils literal"><span class="pre">DummyContract</span></code>, a simple test smart contract which stores a single number in its states, along
|
||||
with ownership and issuer information. You can issue such states, exit them and re-assign ownership (move them).
|
||||
It doesn’t do anything else. This code simply creates a transaction that issues a dummy state (the issuer is
|
||||
<code class="docutils literal"><span class="pre">MEGA_CORP</span></code>, a pre-defined unit test identity), signs it with the test notary and MegaCorp keys and then
|
||||
converts the builder to the final <code class="docutils literal"><span class="pre">SignedTransaction</span></code>. It then does so again, but this time instead of issuing
|
||||
it re-assigns ownership instead. The chain of two transactions is finally committed to node A by sending them
|
||||
directly to the <code class="docutils literal"><span class="pre">a.services.recordTransaction</span></code> method (note that this method doesn’t check the transactions are
|
||||
valid).</p>
|
||||
<p>And that’s it: you can explore the documentation for the <a class="reference external" href="api/com.r3corda.node.internal.testing/-mock-network/index.html">MockNode API</a> here.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user