-
Notifications
You must be signed in to change notification settings - Fork 9
sdk/agent: add a first happy path test #255
Changes from all commits
4f75e29
0d63856
764dca2
442b88b
392b170
ad74b3b
fe5db7e
1969226
ff00ff0
89a7d30
3326e5e
32a8da7
4474a63
e50696b
a7973dc
5214249
85d1a53
3430c58
487092f
b1d0248
2a3f943
4232962
77dc195
6ddb567
d653992
c514fab
7a03c0e
dd55482
9388e31
f8743b4
6c2750b
e9f9c38
22a4aa4
1d73c0e
ae1584b
19d17c7
f23ad1e
a8fbd45
077926d
4f48c24
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
package agent | ||
|
||
import ( | ||
"bytes" | ||
"io" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stellar/experimental-payment-channels/sdk/state" | ||
"github.com/stellar/go/keypair" | ||
"github.com/stellar/go/network" | ||
"github.com/stellar/go/txnbuild" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
type sequenceNumberCollector func(accountID *keypair.FromAddress) (int64, error) | ||
|
||
func (f sequenceNumberCollector) GetSequenceNumber(accountID *keypair.FromAddress) (int64, error) { | ||
return f(accountID) | ||
} | ||
|
||
type balanceCollectorFunc func(accountID *keypair.FromAddress, asset state.Asset) (int64, error) | ||
|
||
func (f balanceCollectorFunc) GetBalance(accountID *keypair.FromAddress, asset state.Asset) (int64, error) { | ||
return f(accountID, asset) | ||
} | ||
|
||
type submitterFunc func(tx *txnbuild.Transaction) error | ||
|
||
func (f submitterFunc) SubmitTx(tx *txnbuild.Transaction) error { | ||
return f(tx) | ||
} | ||
|
||
func TestAgent_openPaymentClose(t *testing.T) { | ||
localEscrow := keypair.MustRandom() | ||
localSigner := keypair.MustRandom() | ||
remoteEscrow := keypair.MustRandom() | ||
remoteSigner := keypair.MustRandom() | ||
|
||
// Setup the local agent. | ||
localVars := struct { | ||
submittedTx *txnbuild.Transaction | ||
}{} | ||
localAgent := &Agent{ | ||
ObservationPeriodTime: 20 * time.Second, | ||
ObservationPeriodLedgerGap: 1, | ||
MaxOpenExpiry: 5 * time.Minute, | ||
NetworkPassphrase: network.TestNetworkPassphrase, | ||
SequenceNumberCollector: sequenceNumberCollector(func(accountID *keypair.FromAddress) (int64, error) { | ||
return 1, nil | ||
}), | ||
BalanceCollector: balanceCollectorFunc(func(accountID *keypair.FromAddress, asset state.Asset) (int64, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💭 thoughts on returning the passed in also could be a change in the PR that requires that, so maybe not this one more I think about it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can implement that in whatever test needs it. The way I've implement that is to set a new I'd do that either by setting a new function that returns a new hardcoded value, or by changing the |
||
return 100_0000000, nil | ||
}), | ||
Submitter: submitterFunc(func(tx *txnbuild.Transaction) error { | ||
localVars.submittedTx = tx | ||
return nil | ||
}), | ||
EscrowAccountKey: localEscrow.FromAddress(), | ||
EscrowAccountSigner: localSigner, | ||
LogWriter: io.Discard, | ||
} | ||
|
||
// Setup the remote agent. | ||
remoteVars := struct { | ||
submittedTx *txnbuild.Transaction | ||
}{} | ||
remoteAgent := &Agent{ | ||
ObservationPeriodTime: 20 * time.Second, | ||
ObservationPeriodLedgerGap: 1, | ||
MaxOpenExpiry: 5 * time.Minute, | ||
NetworkPassphrase: network.TestNetworkPassphrase, | ||
SequenceNumberCollector: sequenceNumberCollector(func(accountID *keypair.FromAddress) (int64, error) { | ||
return 1, nil | ||
}), | ||
BalanceCollector: balanceCollectorFunc(func(accountID *keypair.FromAddress, asset state.Asset) (int64, error) { | ||
return 100_0000000, nil | ||
}), | ||
Submitter: submitterFunc(func(tx *txnbuild.Transaction) error { | ||
remoteVars.submittedTx = tx | ||
return nil | ||
}), | ||
EscrowAccountKey: remoteEscrow.FromAddress(), | ||
EscrowAccountSigner: remoteSigner, | ||
LogWriter: io.Discard, | ||
} | ||
|
||
// Connect the two agents. | ||
type ReadWriter struct { | ||
io.Reader | ||
io.Writer | ||
} | ||
localMsgs := bytes.Buffer{} | ||
remoteMsgs := bytes.Buffer{} | ||
localAgent.conn = ReadWriter{ | ||
Reader: &remoteMsgs, | ||
Writer: &localMsgs, | ||
} | ||
remoteAgent.conn = ReadWriter{ | ||
Reader: &localMsgs, | ||
Writer: &remoteMsgs, | ||
} | ||
err := localAgent.hello() | ||
require.NoError(t, err) | ||
err = remoteAgent.receive() | ||
require.NoError(t, err) | ||
err = remoteAgent.hello() | ||
require.NoError(t, err) | ||
err = localAgent.receive() | ||
require.NoError(t, err) | ||
|
||
// Open the channel. | ||
err = localAgent.Open() | ||
require.NoError(t, err) | ||
err = remoteAgent.receive() | ||
require.NoError(t, err) | ||
err = localAgent.receive() | ||
require.NoError(t, err) | ||
|
||
// Expect the open tx to have been submitted. | ||
openTx, err := localAgent.channel.OpenTx() | ||
require.NoError(t, err) | ||
assert.Equal(t, openTx, localVars.submittedTx) | ||
localVars.submittedTx = nil | ||
|
||
// Make a payment. | ||
err = localAgent.Payment("50.0") | ||
require.NoError(t, err) | ||
err = remoteAgent.receive() | ||
require.NoError(t, err) | ||
err = localAgent.receive() | ||
require.NoError(t, err) | ||
|
||
// Make another payment. | ||
err = remoteAgent.Payment("20.0") | ||
require.NoError(t, err) | ||
err = localAgent.receive() | ||
require.NoError(t, err) | ||
err = remoteAgent.receive() | ||
require.NoError(t, err) | ||
|
||
// Expect no txs to have been submitted for payments. | ||
assert.Nil(t, localVars.submittedTx) | ||
assert.Nil(t, remoteVars.submittedTx) | ||
|
||
// Declare the close, and start negotiating for an early close. | ||
err = localAgent.DeclareClose() | ||
require.NoError(t, err) | ||
|
||
// Expect the declaration tx to have been submitted. | ||
localDeclTx, _, err := localAgent.channel.CloseTxs() | ||
require.NoError(t, err) | ||
assert.Equal(t, localDeclTx, localVars.submittedTx) | ||
|
||
// Receive the declaration at the remote and complete negotiation. | ||
err = remoteAgent.receive() | ||
require.NoError(t, err) | ||
err = localAgent.receive() | ||
require.NoError(t, err) | ||
|
||
// Expect the close tx to have been submitted. | ||
_, localCloseTx, err := localAgent.channel.CloseTxs() | ||
require.NoError(t, err) | ||
_, remoteCloseTx, err := remoteAgent.channel.CloseTxs() | ||
require.NoError(t, err) | ||
assert.Equal(t, localCloseTx, remoteCloseTx) | ||
assert.Equal(t, localCloseTx, localVars.submittedTx) | ||
assert.Equal(t, remoteCloseTx, remoteVars.submittedTx) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
❓ what's the reason for re-creating these functions instead of using the exported ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm defining a type, which can be a function, that satisfies each of the interfaces so that in tests I can write quick little functions inline for them. This is a common pattern most known for its use in the stdlib's http package, namely the
http.HandleFunc
type that is a function that implements thehttp.Handle
interface.There are a couple proposals being debated at the moment to make it possible in Go to convert a function to a single-function interface: golang/go#21670, golang/go#$47487. If either proposal ever gets accepted it'll make it unnecessary to define these types in tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
oops I asked that and then figured it out and thought I deleted the question. Thanks for the additional info though makes sense!