diff --git a/internal/interpreter/interpreter_test.go b/internal/interpreter/interpreter_test.go index e8a39a4d..2bf2f453 100644 --- a/internal/interpreter/interpreter_test.go +++ b/internal/interpreter/interpreter_test.go @@ -1,21 +1,37 @@ package interpreter_test import ( + "bytes" "context" "encoding/json" "math/big" "github.com/formancehq/numscript/internal/flags" machine "github.com/formancehq/numscript/internal/interpreter" + "github.com/formancehq/numscript/internal/specs_format" "testing" "github.com/formancehq/numscript/internal/parser" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +const scriptsFolder = "../../testdata/script-tests" + +func TestScripts(t *testing.T) { + rawSpecs, err := specs_format.ReadSpecsFiles([]string{scriptsFolder}) + require.Nil(t, err) + + var buf bytes.Buffer + buf.WriteByte('\n') + ok := specs_format.RunSpecs(&buf, &buf, rawSpecs) + if !ok { + t.Log(buf.String()) + t.Fail() + } +} + type TestCase struct { source string program *parser.Program @@ -31,9 +47,7 @@ func NewTestCase() TestCase { meta: machine.AccountsMetadata{}, balances: make(map[string]map[string]*big.Int), expected: CaseResult{ - Postings: []machine.Posting{}, - TxMetadata: make(map[string]machine.Value), - Error: nil, + Error: nil, }, } } @@ -118,7 +132,7 @@ func testWithFeatureFlag(t *testing.T, testCase TestCase, flagName string) { }, removeRange(err)) } - execResult, err := machine.RunProgram( + _, err := machine.RunProgram( context.Background(), *prog, testCase.vars, @@ -135,23 +149,6 @@ func testWithFeatureFlag(t *testing.T, testCase TestCase, flagName string) { } else { require.NoError(t, err) } - if err != nil { - return - } - - if expected.Postings == nil { - expected.Postings = make([]Posting, 0) - } - if expected.TxMetadata == nil { - expected.TxMetadata = make(map[string]machine.Value) - } - if expected.AccountMetadata == nil { - expected.AccountMetadata = machine.AccountsMetadata{} - } - - assert.Equal(t, expected.Postings, execResult.Postings) - assert.Equal(t, expected.TxMetadata, execResult.Metadata) - assert.Equal(t, expected.AccountMetadata, execResult.AccountsMetadata) } func TestStaticStore(t *testing.T) { @@ -197,252 +194,91 @@ type CaseResult struct { type Posting = machine.Posting -func TestSend(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [EUR/2 100] ( - source=@alice - destination=@bob - )`) - tc.setBalance("alice", "EUR/2", 100) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "EUR/2", - Amount: big.NewInt(100), - Source: "alice", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSetTxMeta(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - set_tx_meta("num", 42) - set_tx_meta("str", "abc") - set_tx_meta("asset", COIN) - set_tx_meta("account", @acc) - set_tx_meta("portion", 12%) - `) - - tc.expected = CaseResult{ - TxMetadata: map[string]machine.Value{ - "num": machine.NewMonetaryInt(42), - "str": machine.String("abc"), - "asset": machine.Asset("COIN"), - "account": machine.AccountAddress("acc"), - "portion": machine.Portion(*big.NewRat(12, 100)), - }, - Error: nil, - } - test(t, tc) -} - -func TestSetAccountMeta(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - set_account_meta(@acc, "num", 42) - set_account_meta(@acc, "str", "abc") - set_account_meta(@acc, "asset", COIN) - set_account_meta(@acc, "account", @acc) - set_account_meta(@acc, "portion", 2/7) - set_account_meta(@acc, "portion-perc", 1%) - `) - - tc.expected = CaseResult{ - AccountMetadata: machine.AccountsMetadata{ - "acc": { - "num": "42", - "str": "abc", - "asset": "COIN", - "account": "acc", - "portion": "2/7", - "portion-perc": "1/100", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestOverrideAccountMeta(t *testing.T) { +func TestBadPortionSyntax(t *testing.T) { tc := NewTestCase() - tc.meta = machine.AccountsMetadata{ - "acc": { - "initial": "0", - "overridden": "1", - }, + tc.compile(t, `vars { + portion $por } - tc.compile(t, ` - set_account_meta(@acc, "overridden", 100) - set_account_meta(@acc, "new", 2) + send [COIN 3] ( + source = @world + destination = { + $por to @a + remaining kept + } + ) `) + tc.setVarsFromJSON(t, `{ + "por": "not a portion" + }`) tc.expected = CaseResult{ - AccountMetadata: machine.AccountsMetadata{ - "acc": { - "overridden": "100", - "new": "2", - }, + Postings: []Posting{}, + Error: machine.BadPortionParsingErr{ + Source: "not a portion", + Reason: "invalid format", }, - Error: nil, } test(t, tc) } -func TestVariables(t *testing.T) { +func TestInvalidAllotInSendAll(t *testing.T) { tc := NewTestCase() - tc.compile(t, `vars { - account $rider - account $driver - string $description - number $nb - asset $ass - } - send [$ass 999] ( - source=$rider - destination=$driver - ) - set_tx_meta("description", $description) - set_tx_meta("ride", $nb)`) - tc.vars = map[string]string{ - "rider": "users:001", - "driver": "users:002", - "description": "midnight ride", - "nb": "1", - "ass": "EUR/2", - } - tc.setBalance("users:001", "EUR/2", 1000) + tc.compile(t, `send [USD/2 *] ( + source = { + 1/2 from @a + 2/3 from @b + } + destination = @dest + )`) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "EUR/2", - Amount: big.NewInt(999), - Source: "users:001", - Destination: "users:002", - }, - }, - TxMetadata: map[string]machine.Value{ - "description": machine.String("midnight ride"), - "ride": machine.NewMonetaryInt(1), - }, - Error: nil, + Error: machine.InvalidAllotmentInSendAll{}, } test(t, tc) } -func TestVariablesJSON(t *testing.T) { +func TestDivByZero(t *testing.T) { tc := NewTestCase() - tc.compile(t, `vars { - account $rider - account $driver - string $description - number $nb - asset $ass - portion $por - } - send [$ass 999] ( - source=$rider - destination=$driver - ) - set_tx_meta("description", $description) - set_tx_meta("ride", $nb) - set_tx_meta("por", $por)`) - tc.setVarsFromJSON(t, `{ - "por": "42%", - "rider": "users:001", - "driver": "users:002", - "description": "midnight ride", - "nb": "1", - "ass": "EUR/2" - }`) - tc.setBalance("users:001", "EUR/2", 1000) + src := tc.compile(t, `set_tx_meta("k", 3/0)`) tc.expected = CaseResult{ - - Postings: []Posting{ - { - Asset: "EUR/2", - Amount: big.NewInt(999), - Source: "users:001", - Destination: "users:002", - }, - }, - TxMetadata: map[string]machine.Value{ - "description": machine.String("midnight ride"), - "ride": machine.NewMonetaryInt(1), - "por": machine.Portion(*big.NewRat(42, 100)), + Error: machine.DivideByZero{ + Numerator: big.NewInt(3), + Range: parser.RangeOfIndexed(src, "3/0", 0), }, - Error: nil, } test(t, tc) } -func TestPortionSyntax(t *testing.T) { +func TestInvalidUnboundedWorldInSendAll(t *testing.T) { tc := NewTestCase() - tc.compile(t, `vars { - portion $por - } - send [COIN 3] ( + tc.compile(t, `send [USD/2 *] ( source = @world - destination = { - $por to @a - remaining kept - } - ) - `) - tc.setVarsFromJSON(t, `{ - "por": "1/3" - }`) + destination = @dest + )`) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1), - Source: "world", - Destination: "a", - }, - }, - Error: nil, + Error: machine.InvalidUnboundedInSendAll{Name: "world"}, } test(t, tc) } -func TestBadPortionSyntax(t *testing.T) { +func TestInvalidUnboundedInSendAll(t *testing.T) { tc := NewTestCase() - tc.compile(t, `vars { - portion $por - } - send [COIN 3] ( - source = @world - destination = { - $por to @a - remaining kept - } - ) - `) - tc.setVarsFromJSON(t, `{ - "por": "not a portion" - }`) + tc.compile(t, `send [USD/2 *] ( + source = @a allowing unbounded overdraft + destination = @dest + )`) tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.BadPortionParsingErr{ - Source: "not a portion", - Reason: "invalid format", - }, + Error: machine.InvalidUnboundedInSendAll{Name: "a"}, } test(t, tc) } -func TestSource(t *testing.T) { +func TestInsufficientFunds(t *testing.T) { tc := NewTestCase() tc.compile(t, `vars { account $balance account $payment account $seller } - send [GEM 15] ( + send [GEM 16] ( source = { $balance $payment @@ -457,4362 +293,669 @@ func TestSource(t *testing.T) { tc.setBalance("users:001", "GEM", 3) tc.setBalance("payments:001", "GEM", 12) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(3), - Source: "users:001", - Destination: "users:002", - }, - { - Asset: "GEM", - Amount: big.NewInt(12), - Source: "payments:001", - Destination: "users:002", - }, + Postings: []Posting{}, + Error: machine.MissingFundsErr{ + Asset: "GEM", + Needed: *big.NewInt(16), + Available: *big.NewInt(15), }, - Error: nil, } test(t, tc) } -func TestAllocation(t *testing.T) { +func TestTrackBalances2(t *testing.T) { tc := NewTestCase() - tc.compile(t, `vars { - account $rider - account $driver - } - send [GEM 15] ( - source = $rider - destination = { - 80% to $driver - 8% to @a - 12% to @b - } + tc.compile(t, ` + send [COIN 50] ( + source = @a + destination = @z + ) + send [COIN 50] ( + source = @a + destination = @z )`) - tc.setVarsFromJSON(t, `{ - "rider": "users:001", - "driver": "users:002" - }`) - tc.setBalance("users:001", "GEM", 15) + tc.setBalance("a", "COIN", 60) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(13), - Source: "users:001", - Destination: "users:002", - }, - { - Asset: "GEM", - Amount: big.NewInt(1), - Source: "users:001", - Destination: "a", - }, - { - Asset: "GEM", - Amount: big.NewInt(1), - Source: "users:001", - Destination: "b", - }, + Postings: []Posting{}, + Error: machine.MissingFundsErr{ + Asset: "COIN", + Needed: *big.NewInt(50), + Available: *big.NewInt(10), }, - Error: nil, } test(t, tc) } -func TestDynamicAllocation(t *testing.T) { +func TestInvalidSourceAllotmentSum(t *testing.T) { tc := NewTestCase() - tc.compile(t, `vars { - portion $p - } - send [GEM 15] ( - source = @a - destination = { - 80% to @b - $p to @c - remaining to @d + tc.compile(t, `send [COIN 100] ( + source = { + 42% from @world } + destination = @dest )`) - tc.setVarsFromJSON(t, `{ - "p": "15%" - }`) - tc.setBalance("a", "GEM", 15) + tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(13), - Source: "a", - Destination: "b", - }, - { - Asset: "GEM", - Amount: big.NewInt(2), - Source: "a", - Destination: "c", - }, + Error: machine.InvalidAllotmentSum{ + ActualSum: *big.NewRat(42, 100), }, - Error: nil, } test(t, tc) } -func TestSendAll(t *testing.T) { +func TestInvalidDestinationAllotmentSum(t *testing.T) { tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @users:001 - destination = @platform + tc.compile(t, `send [COIN 100] ( + source = @world + destination = { + 1/4 to @x + } )`) - tc.setBalance("users:001", "USD/2", 17) + tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(17), - Source: "users:001", - Destination: "platform", - }, + Error: machine.InvalidAllotmentSum{ + ActualSum: *big.NewRat(1, 4), }, - Error: nil, } test(t, tc) } -func TestSendAllWhenNegative(t *testing.T) { +func TestSourceAllotmentInvalidAmt(t *testing.T) { tc := NewTestCase() - tc.compile(t, ` - send [USD/2 *] ( - source = @users:001 - destination = @platform -) -`) - tc.setBalance("users:001", "USD/2", -100) - tc.expected = CaseResult{ - Postings: []Posting{}, // zero posting is trimmed - Error: nil, - } - test(t, tc) -} + tc.compile(t, `send [COIN 100] ( + source = { + // a doesn't have enough amount + 10% from @a -func TestSendAllWhenNegativeWithOverdraft(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [USD/2 *] ( - source = @users:001 allowing overdraft up to [USD/2 150] - destination = @platform -) -`) - tc.setBalance("users:001", "USD/2", -100) + // world has, but the computation has already failed + remaining from @world + } + destination = @d + )`) + tc.setBalance("a", "COIN", 1) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(150 - 100), - Source: "users:001", - Destination: "platform", - }, - }, // zero posting is trimmed - Error: nil, + Error: machine.MissingFundsErr{ + Asset: "COIN", + Needed: *big.NewInt(10), + Available: *big.NewInt(1), + }, } test(t, tc) } -func TestSendAllVariable(t *testing.T) { +func TestNegativeBalance(t *testing.T) { tc := NewTestCase() tc.compile(t, ` vars { - account $src - account $dest + monetary $balance = balance(@a, EUR/2) } - send [USD/2 *] ( - source = $src - destination = $dest + send $balance ( + source = @world + destination = @dest )`) - tc.setVarsFromJSON(t, `{ - "src": "users:001", - "dest": "platform" - }`) - tc.setBalance("users:001", "USD/2", 17) + tc.setBalance("a", "EUR/2", -100) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(17), - Source: "users:001", - Destination: "platform", - }, + Error: machine.NegativeBalanceError{ + Account: "a", + Amount: *big.NewInt(-100), }, - Error: nil, } test(t, tc) } -func TestSendAlltMaxWhenNoAmount(t *testing.T) { +func TestNegativeBalanceLiteral(t *testing.T) { tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = max [USD/2 5] from @src + tc.compile(t, ` + send [EUR/2 -100] ( + source = @world destination = @dest - ) - `) - tc.setBalance("src1", "USD/2", 0) + )`) tc.expected = CaseResult{ - Postings: []Posting{}, - Error: nil, + Error: machine.NegativeAmountErr{ + Amount: machine.MonetaryInt(*big.NewInt(-100)), + }, } test(t, tc) } -func TestNegativeMaxSendAll(t *testing.T) { +// TODO TestVariablesParsing, TestSetVarsFromJSON, TestResolveResources, TestResolveBalances, TestMachine + +func TestOverdraftBadCurrency(t *testing.T) { tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = max [USD/2 -50] from @src - destination = @dest - ) - `) - tc.setBalance("src", "USD/2", 0) + tc.compile(t, ` +send [COIN 100] ( + source = @users:1234 allowing overdraft up to [WRONGCURR 100] + destination = @dest +) +`) tc.expected = CaseResult{ - Postings: []Posting{ - // Posting omitted - // { - // Asset: "USD/2", - // Amount: big.NewInt(0), - // Source: "src", - // Destination: "dest", - // }, + Error: machine.MismatchedCurrencyError{ + Expected: "COIN", + Got: "WRONGCURR", }, - Error: nil, } test(t, tc) } -func TestNegativeMax(t *testing.T) { +func TestOverdraftWhenNotEnoughFunds(t *testing.T) { tc := NewTestCase() - tc.compile(t, `send [USD/2 100] ( - source = { - max [USD/2 -50] from @src - @world - } - destination = @dest - ) - `) - tc.setBalance("src", "USD/2", 0) - tc.expected = CaseResult{ - Postings: []Posting{ + tc.compile(t, ` +send [COIN 100] ( + source = @users:1234 allowing overdraft up to [COIN 10] + destination = @dest +) +`) - { - Asset: "USD/2", - Amount: big.NewInt(100), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} + tc.setBalance("users:1234", "COIN", 1) -func TestSendAllDestinatioAllot(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @users:001 - destination = { - 1/3 to @d1 - 2/3 to @d2 - } - )`) - tc.setBalance("users:001", "USD/2", 30) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "users:001", - Destination: "d1", - }, - { - Asset: "USD/2", - Amount: big.NewInt(20), - Source: "users:001", - Destination: "d2", - }, + Error: machine.MissingFundsErr{ + Asset: "COIN", + Needed: *big.NewInt(100), + Available: *big.NewInt(11), }, - Error: nil, } test(t, tc) } -func TestSendAllDestinatioAllotComplex(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = { - @users:001 - @users:002 - } - destination = { - 1/3 to @d1 - 2/3 to @d2 - } +func TestErrors(t *testing.T) { + t.Run("wrong type for send literal", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + send @bad:type ( + source = @a + destination = @b )`) - tc.setBalance("users:001", "USD/2", 15) - tc.setBalance("users:002", "USD/2", 15) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "users:001", - Destination: "d1", - }, - { - Asset: "USD/2", - Amount: big.NewInt(5), - Source: "users:001", - Destination: "d2", - }, - { - Asset: "USD/2", - Amount: big.NewInt(15), - Source: "users:002", - Destination: "d2", + tc.expected = CaseResult{ + Error: machine.TypeError{ + Expected: "monetary", + Value: machine.AccountAddress("bad:type"), }, - }, - Error: nil, + } + test(t, tc) + }) + + t.Run("wrong type for account literal", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + vars { + number $var_src } - test(t, tc) -} -func TestInvalidAllotInSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( + send [COIN 10] ( source = { - 1/2 from @a - 2/3 from @b + 1/2 from @world + remaining from { + @empty + max [COIN 100] from $var_src + } } - destination = @dest + destination = @b )`) - tc.expected = CaseResult{ - Error: machine.InvalidAllotmentInSendAll{}, - } - test(t, tc) -} + tc.setVarsFromJSON(t, `{"var_src": "42"}`) -func TestDivByZero(t *testing.T) { - tc := NewTestCase() - src := tc.compile(t, `set_tx_meta("k", 3/0)`) - tc.expected = CaseResult{ - Error: machine.DivideByZero{ - Numerator: big.NewInt(3), - Range: parser.RangeOfIndexed(src, "3/0", 0), - }, - } - test(t, tc) -} + tc.expected = CaseResult{ + Error: machine.TypeError{ + Expected: "account", + Value: machine.NewMonetaryInt(42), + }, + } + test(t, tc) + }) -func TestInvalidUnboundedWorldInSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @world - destination = @dest - )`) - tc.expected = CaseResult{ - Error: machine.InvalidUnboundedInSendAll{Name: "world"}, + t.Run("wrong type for account cap", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + vars { + string $v } - test(t, tc) -} -func TestInvalidUnboundedInSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @a allowing unbounded overdraft - destination = @dest + send [COIN 10] ( + source = max $v from @src + destination = @b )`) - tc.expected = CaseResult{ - Error: machine.InvalidUnboundedInSendAll{Name: "a"}, - } - test(t, tc) -} + tc.setVarsFromJSON(t, `{"v": "abc"}`) -func TestOverdraftInSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @src allowing overdraft up to [USD/2 10] - destination = @dest - )`) - tc.setBalance("src", "USD/2", 1000) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(1010), - Source: "src", - Destination: "dest", + tc.expected = CaseResult{ + Error: machine.TypeError{ + Expected: "monetary", + Value: machine.String("abc"), }, - }, - } - test(t, tc) -} + } + test(t, tc) + }) -func TestOverdraftInSendAllWhenNoop(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @src allowing overdraft up to [USD/2 10] - destination = @dest + t.Run("unbound variable", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + send $unbound_var ( + source = @a + destination = @b )`) - tc.setBalance("src", "USD/2", 1) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(11), - Source: "src", - Destination: "dest", - }, - }, - } - test(t, tc) -} -func TestSendAlltMaxInSrc(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = { - max [USD/2 5] from @src1 - @src2 - } - destination = @dest - ) - `) - tc.setBalance("src1", "USD/2", 100) - tc.setBalance("src2", "USD/2", 200) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(5), - Source: "src1", - Destination: "dest", - }, - { - Asset: "USD/2", - Amount: big.NewInt(200), - Source: "src2", - Destination: "dest", + tc.expected = CaseResult{ + Error: machine.UnboundVariableErr{ + Name: "unbound_var", + Range: parser.RangeOfIndexed(tc.source, "$unbound_var", 0), }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSendAlltMaxInDest(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @src - destination = { - max [USD/2 10] to @d1 - remaining to @d2 } - ) - `) - tc.setBalance("src", "USD/2", 100) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "src", - Destination: "d1", - }, - { - Asset: "USD/2", - Amount: big.NewInt(90), - Source: "src", - Destination: "d2", - }, - }, - Error: nil, - } - test(t, tc) -} + test(t, tc) + }) -func TestManyMaxDest(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 100] ( - source = @world - destination = { - max [USD/2 10] to @d1 - max [USD/2 12] to @d2 - remaining to @rem - } - ) - `) - tc.setBalance("src", "USD/2", 100) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "world", - Destination: "d1", - }, - { - Asset: "USD/2", - Amount: big.NewInt(12), - Source: "world", - Destination: "d2", - }, - { - Asset: "USD/2", - Amount: big.NewInt(100 - 10 - 12), - Source: "world", - Destination: "rem", - }, - }, - Error: nil, + t.Run("missing variable from json", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + vars { + monetary $x } - test(t, tc) -} -func TestManyKeptDest(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 100] ( - source = @world - destination = { - max [USD/2 10] kept - max [USD/2 12] to @d2 - remaining to @rem - } - ) - `) - tc.setBalance("src", "USD/2", 100) - tc.expected = CaseResult{ - Postings: []Posting{ - // { - // Asset: "USD/2", - // Amount: big.NewInt(10), - // Source: "world", - // Destination: "", - // }, - { - Asset: "USD/2", - Amount: big.NewInt(12), - Source: "world", - Destination: "d2", - }, - { - Asset: "USD/2", - Amount: big.NewInt(100 - 10 - 12), - Source: "world", - Destination: "rem", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSendAllManyMaxInDest(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = @src - destination = { - max [USD/2 10] to @d1 - max [USD/2 20] to @d2 - remaining to @d3 - } - ) - `) - tc.setBalance("src", "USD/2", 15) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "src", - Destination: "d1", - }, - { - Asset: "USD/2", - Amount: big.NewInt(5), - Source: "src", - Destination: "d2", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSendAllMulti(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [USD/2 *] ( - source = { - @users:001:wallet - @users:001:credit - } - destination = @platform - ) - `) - tc.setBalance("users:001:wallet", "USD/2", 19) - tc.setBalance("users:001:credit", "USD/2", 22) - tc.expected = CaseResult{ - - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(19), - Source: "users:001:wallet", - Destination: "platform", - }, - { - Asset: "USD/2", - Amount: big.NewInt(22), - Source: "users:001:credit", - Destination: "platform", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestInsufficientFunds(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `vars { - account $balance - account $payment - account $seller - } - send [GEM 16] ( - source = { - $balance - $payment - } - destination = $seller - )`) - tc.setVarsFromJSON(t, `{ - "balance": "users:001", - "payment": "payments:001", - "seller": "users:002" - }`) - tc.setBalance("users:001", "GEM", 3) - tc.setBalance("payments:001", "GEM", 12) - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.MissingFundsErr{ - Asset: "GEM", - Needed: *big.NewInt(16), - Available: *big.NewInt(15), - }, - } - test(t, tc) -} - -func TestWorldSource(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [GEM 15] ( - source = { - @a - @world - } - destination = @b - )`) - tc.setBalance("a", "GEM", 1) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(1), - Source: "a", - Destination: "b", - }, - { - Asset: "GEM", - Amount: big.NewInt(14), - Source: "world", - Destination: "b", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestNoEmptyPostings(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [GEM 2] ( - source = @world - destination = { - 90% to @a - 10% to @b - } - )`) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(2), - Source: "world", - Destination: "a", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestEmptyPostings(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [GEM *] ( - source = @foo - destination = @bar - )`) - tc.setBalance("foo", "GEM", 0) - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: nil, - } - test(t, tc) -} - -func TestAllocateDontTakeTooMuch(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [CREDIT 200] ( - source = { - @users:001 - @users:002 - } - destination = { - 1/2 to @foo - 1/2 to @bar - } - )`) - tc.setBalance("users:001", "CREDIT", 100) - tc.setBalance("users:002", "CREDIT", 110) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "CREDIT", - Amount: big.NewInt(100), - Source: "users:001", - Destination: "foo", - }, - { - Asset: "CREDIT", - Amount: big.NewInt(100), - Source: "users:002", - Destination: "bar", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestMetadata(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `vars { - account $sale - account $seller = meta($sale, "seller") - portion $commission = meta($seller, "commission") - } - send [EUR/2 100] ( - source = $sale - destination = { - remaining to $seller - $commission to @platform - } - )`) - tc.setVarsFromJSON(t, `{ - "sale": "sales:042" - }`) - tc.meta = machine.AccountsMetadata{ - "sales:042": { - "seller": "users:053", - }, - "users:053": { - "commission": "12.5%", - }, - } - tc.setBalance("sales:042", "EUR/2", 2500) - tc.setBalance("users:053", "EUR/2", 500) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "EUR/2", - Amount: big.NewInt(88), - Source: "sales:042", - Destination: "users:053", - }, - { - Asset: "EUR/2", - Amount: big.NewInt(12), - Source: "sales:042", - Destination: "platform", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestTrackBalances(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 50] ( - source = @world - destination = @a - ) - send [COIN 100] ( - source = @a - destination = @b - )`) - tc.setBalance("a", "COIN", 50) - tc.expected = CaseResult{ - - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(50), - Source: "world", - Destination: "a", - }, - { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "a", - Destination: "b", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestTrackBalances2(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 50] ( - source = @a - destination = @z - ) - send [COIN 50] ( - source = @a - destination = @z - )`) - tc.setBalance("a", "COIN", 60) - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.MissingFundsErr{ - Asset: "COIN", - Needed: *big.NewInt(50), - Available: *big.NewInt(10), - }, - } - test(t, tc) -} - -func TestKeptInSendAllInorder(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN *] ( - source = @src - destination = { - max [COIN 1] kept - remaining to @dest - } - )`) - - tc.setBalance("src", "COIN", 10) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(9), - Source: "src", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestRemainingKeptInSendAllInorder(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN *] ( - source = @src - destination = { - max [COIN 1] to @dest - remaining kept - } - )`) - - tc.setBalance("src", "COIN", 1000) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1), - Source: "src", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestTrackBalancesSendAll(t *testing.T) { - // TODO double check - tc := NewTestCase() - tc.compile(t, ` - send [COIN *] ( - source = @src - destination = @dest1 - ) - send [COIN *] ( - source = @src - destination = @dest2 - )`) - tc.setBalance("src", "COIN", 42) - tc.expected = CaseResult{ - - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(42), - Source: "src", - Destination: "dest1", - }, - // { - // Asset: "COIN", - // Amount: big.NewInt(0), - // Source: "src", - // Destination: "dest2", - // }, - }, - Error: nil, - } - test(t, tc) -} - -func TestTrackBalances3(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN *] ( - source = @foo - destination = { - max [COIN 1000] to @bar - remaining kept - } - ) - send [COIN *] ( - source = @foo - destination = @bar - )`) - tc.setBalance("foo", "COIN", 2000) - tc.expected = CaseResult{ - - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1000), - Source: "foo", - Destination: "bar", - }, - { - Asset: "COIN", - Amount: big.NewInt(1000), - Source: "foo", - Destination: "bar", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSourceAllotment(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = { - 60% from @a - 35.5% from @b - 4.5% from @c - } - destination = @d - )`) - tc.setBalance("a", "COIN", 100) - tc.setBalance("b", "COIN", 100) - tc.setBalance("c", "COIN", 100) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(61), - Source: "a", - Destination: "d", - }, - { - Asset: "COIN", - Amount: big.NewInt(35), - Source: "b", - Destination: "d", - }, - { - Asset: "COIN", - Amount: big.NewInt(4), - Source: "c", - Destination: "d", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestVariablePortionPart(t *testing.T) { - tc := NewTestCase() - tc.setVarsFromJSON(t, `{ - "num": "1", - "den": "3" - }`) - - tc.compile(t, ` - vars { - number $num - number $den - } - - send [COIN 9] ( - source = @world - destination = { - $num/3 to @a // 1/3 - 2/$den to @b // 2/3 - } - )`) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(3), - Source: "world", - Destination: "a", - }, - { - Asset: "COIN", - Amount: big.NewInt(6), - Source: "world", - Destination: "b", - }, - }, - Error: nil, - } - test(t, tc) -} -func TestInvalidSourceAllotmentSum(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = { - 42% from @world - } - destination = @dest - )`) - - tc.expected = CaseResult{ - Error: machine.InvalidAllotmentSum{ - ActualSum: *big.NewRat(42, 100), - }, - } - test(t, tc) -} - -func TestInvalidDestinationAllotmentSum(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = @world - destination = { - 1/4 to @x - } - )`) - - tc.expected = CaseResult{ - Error: machine.InvalidAllotmentSum{ - ActualSum: *big.NewRat(1, 4), - }, - } - test(t, tc) -} - -func TestSourceAllotmentInvalidAmt(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = { - // a doesn't have enough amount - 10% from @a - - // world has, but the computation has already failed - remaining from @world - } - destination = @d - )`) - tc.setBalance("a", "COIN", 1) - tc.expected = CaseResult{ - Error: machine.MissingFundsErr{ - Asset: "COIN", - Needed: *big.NewInt(10), - Available: *big.NewInt(1), - }, - } - test(t, tc) -} - -func TestSourceOverlapping(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 99] ( - source = { - 15% from { - @b - @a - } - 30% from @a - remaining from @a - } - destination = @world - )`) - tc.setBalance("a", "COIN", 99) - tc.setBalance("b", "COIN", 3) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(3), - Source: "b", - Destination: "world", - }, - { - Asset: "COIN", - Amount: big.NewInt(96), - Source: "a", - Destination: "world", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestCappedWhenMoreThanBalance(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 100] ( - source = { - max [COIN 200] from @world - @src - } - destination = @platform - ) - `) - tc.setBalance("src", "COIN", 1000) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "world", - Destination: "platform", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestCappedWhenLessThanNeeded(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 100] ( - source = { - max [COIN 40] from @src1 - @src2 - } - destination = @platform - ) - `) - tc.setBalance("src1", "COIN", 1000) - tc.setBalance("src2", "COIN", 1000) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(40), - Source: "src1", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(60), - Source: "src2", - Destination: "platform", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSourceComplex(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `vars { - monetary $max - } - send [COIN 200] ( - source = { - 50% from { - max [COIN 4] from @a - @b - @c - } - remaining from max $max from @d - } - destination = @platform - )`) - tc.setVarsFromJSON(t, `{ - "max": "COIN 120" - }`) - tc.setBalance("a", "COIN", 1000) - tc.setBalance("b", "COIN", 40) - tc.setBalance("c", "COIN", 1000) - tc.setBalance("d", "COIN", 1000) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(4), - Source: "a", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(40), - Source: "b", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(56), - Source: "c", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "d", - Destination: "platform", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestKeptInorder(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = @world - destination = { - max [COIN 10] kept - remaining to @dest - } - )`) - - tc.expected = CaseResult{ - Postings: []Posting{ - // 10 COIN are kept - { - Asset: "COIN", - Amount: big.NewInt(90), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) - -} - -func TestRemainingKeptInorder(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = @world - destination = { - max [COIN 1] to @a - remaining kept - } - )`) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1), - Source: "world", - Destination: "a", - }, - }, - Error: nil, - } - test(t, tc) - -} - -func TestKeptWithBalance(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = @src - destination = { - max [COIN 10] kept - remaining to @dest - } - )`) - - tc.setBalance("src", "COIN", 1000) - - tc.expected = CaseResult{ - Postings: []Posting{ - // 10 COIN are kept - { - Asset: "COIN", - Amount: big.NewInt(90), - Source: "src", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) - -} - -func TestRemainingNone(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 10] ( - source = @world - destination = { - max [COIN 10] to @a - remaining to @b - } - )`) - - tc.expected = CaseResult{ - Postings: []Posting{ - // 10 COIN are kept - { - Asset: "COIN", - Amount: big.NewInt(10), - Source: "world", - Destination: "a", - }, - }, - Error: nil, - } - test(t, tc) - -} - -func TestRemainingNoneInSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN *] ( - source = @src - destination = { - max [COIN 10] to @a - remaining to @b - } - )`) - - tc.setBalance("src", "COIN", 10) - tc.expected = CaseResult{ - Postings: []Posting{ - // 10 COIN are kept - { - Asset: "COIN", - Amount: big.NewInt(10), - Source: "src", - Destination: "a", - }, - }, - Error: nil, - } - test(t, tc) - -} - -func TestDestinationComplex(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = @world - destination = { - 20% to @a - 20% kept - 60% to { - max [COIN 10] to @b - remaining to @c - } - } - )`) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(20), - Source: "world", - Destination: "a", - }, - { - Asset: "COIN", - Amount: big.NewInt(10), - Source: "world", - Destination: "b", - }, - { - Asset: "COIN", - Amount: big.NewInt(50), - Source: "world", - Destination: "c", - }, - }, - Error: nil, - } - test(t, tc) -} - -// TODO TestNeededBalances, TestSetTxMeta, TestSetAccountMeta - -func TestSendZero(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 0] ( - source = @src - destination = @dest - )`) - tc.expected = CaseResult{ - Postings: []Posting{ - // Zero posting is omitted - // { - // Asset: "COIN", - // Amount: big.NewInt(0), - // Source: "src", - // Destination: "dest", - // }, - }, - Error: nil, - } - test(t, tc) -} - -func TestBalance(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - monetary $balance = balance(@a, EUR/2) - } - - send $balance ( - source = @world - destination = @dest - )`) - tc.setBalance("a", "EUR/2", 123) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "EUR/2", - Amount: big.NewInt(123), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestNegativeBalance(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - monetary $balance = balance(@a, EUR/2) - } - - send $balance ( - source = @world - destination = @dest - )`) - tc.setBalance("a", "EUR/2", -100) - tc.expected = CaseResult{ - Error: machine.NegativeBalanceError{ - Account: "a", - Amount: *big.NewInt(-100), - }, - } - test(t, tc) -} - -func TestNegativeBalanceLiteral(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [EUR/2 -100] ( - source = @world - destination = @dest - )`) - tc.expected = CaseResult{ - Error: machine.NegativeAmountErr{ - Amount: machine.MonetaryInt(*big.NewInt(-100)), - }, - } - test(t, tc) -} - -func TestBalanceNotFound(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - monetary $balance = balance(@a, EUR/2) - } - - send $balance ( - source = @world - destination = @dest - )`) - tc.expected = CaseResult{ - Postings: []Posting{ - // Zero posting is omitted - // { - // Asset: "EUR/2", - // Amount: big.NewInt(0), - // Source: "world", - // Destination: "dest", - // }, - }, - Error: nil, - } - test(t, tc) -} - -func TestInoderDestination(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `send [COIN 100] ( - source = @world - destination = { - max [COIN 20] to @dest1 - remaining to @dest2 - } - )`) - tc.setBalance("a", "COIN", 123) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(20), - Source: "world", - Destination: "dest1", - }, - { - Asset: "COIN", - Amount: big.NewInt(80), - Source: "world", - Destination: "dest2", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestVariableBalance(t *testing.T) { - script := ` - vars { - monetary $initial = balance(@A, USD/2) - } - send [USD/2 100] ( - source = { - @A - @C - } - destination = { - max $initial to @B - remaining to @D - } - )` - - t.Run("1", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("A", "USD/2", 40) - tc.setBalance("C", "USD/2", 90) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(40), - Source: "A", - Destination: "B", - }, - { - Asset: "USD/2", - Amount: big.NewInt(60), - Source: "C", - Destination: "D", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("2", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("A", "USD/2", 400) - tc.setBalance("C", "USD/2", 90) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(100), - Source: "A", - Destination: "B", - }, - }, - Error: nil, - } - test(t, tc) - }) - - script = ` - vars { - account $acc - monetary $initial = balance($acc, USD/2) - } - send [USD/2 100] ( - source = { - $acc - @C - } - destination = { - max $initial to @B - remaining to @D - } - )` - - t.Run("3", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("A", "USD/2", 40) - tc.setBalance("C", "USD/2", 90) - tc.setVarsFromJSON(t, `{"acc": "A"}`) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(40), - Source: "A", - Destination: "B", - }, - { - Asset: "USD/2", - Amount: big.NewInt(60), - Source: "C", - Destination: "D", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("4", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("A", "USD/2", 400) - tc.setBalance("C", "USD/2", 90) - tc.setVarsFromJSON(t, `{"acc": "A"}`) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(100), - Source: "A", - Destination: "B", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("5", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - monetary $max = balance(@maxAcc, COIN) - } - send [COIN 200] ( - source = { - 50% from { - max [COIN 4] from @a - @b - @c - } - remaining from max $max from @d - } - destination = @platform - )`) - tc.setBalance("maxAcc", "COIN", 120) - tc.setBalance("a", "COIN", 1000) - tc.setBalance("b", "COIN", 40) - tc.setBalance("c", "COIN", 1000) - tc.setBalance("d", "COIN", 1000) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(4), - Source: "a", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(40), - Source: "b", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(56), - Source: "c", - Destination: "platform", - }, - { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "d", - Destination: "platform", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("send negative monetary", func(t *testing.T) { - tc := NewTestCase() - script = ` - vars { - monetary $amount = balance(@src, USD/2) - } - send $amount ( - source = @A - destination = @B - )` - tc.compile(t, script) - tc.setBalance("src", "USD/2", -40) - tc.expected = CaseResult{ - Error: machine.NegativeBalanceError{ - Account: "src", - Amount: *big.NewInt(-40), - }, - } - test(t, tc) - }) -} - -// TODO TestVariablesParsing, TestSetVarsFromJSON, TestResolveResources, TestResolveBalances, TestMachine - -// TODO -// func TestVariablesErrors(t *testing.T) { -// tc := NewTestCase() -// tc.compile(t, `vars { -// monetary $mon -// } -// send $mon ( -// source = @alice -// destination = @bob -// )`) -// tc.setBalance("alice", "COIN", 10) -// tc.vars = map[string]string{ -// "mon": "COIN -1", -// } -// tc.expected = CaseResult{ -// Postings: []Posting{}, -// Error: &machine.ErrInvalidVars{}, -// ErrorContains: "negative amount", -// } -// test(t, tc) -// } - -func TestBalanceSimple(t *testing.T) { - script := ` - vars { - monetary $bal = balance(@alice, USD/2) - } - - send $bal ( - source = @world - destination = @dest - ) - -` - - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD/2", 10) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestAskBalanceTwice(t *testing.T) { - script := ` - vars { - monetary $bal = balance(@alice, USD/2) - } - - send $bal ( - source = @alice - destination = @dest - ) -` - - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD/2", 10) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(10), - Source: "alice", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestVariableAsset(t *testing.T) { - script := ` - vars { - asset $ass - monetary $bal = balance(@alice, $ass) - } - - send [$ass 15] ( - source = { - @alice - @bob - } - destination = @swap - ) - - send [$ass *] ( - source = @swap - destination = { - max $bal to @alice_2 - remaining to @bob_2 - } - )` - - tc := NewTestCase() - tc.compile(t, script) - tc.vars = map[string]string{ - "ass": "USD", - } - tc.setBalance("alice", "USD", 10) - tc.setBalance("bob", "USD", 10) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "alice", - Destination: "swap", - }, - { - Asset: "USD", - Amount: big.NewInt(5), - Source: "bob", - Destination: "swap", - }, - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "swap", - Destination: "alice_2", - }, - { - Asset: "USD", - Amount: big.NewInt(5), - Source: "swap", - Destination: "bob_2", - }, - }, - Error: nil, - } - test(t, tc) -} - -// TODO TestSaveFromAccount - -func TestUseDifferentAssetsWithSameSourceAccount(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `vars { - account $a_account -} -send [A 100] ( - source = $a_account allowing unbounded overdraft - destination = @account1 -) -send [B 100] ( - source = @world - destination = @account2 -)`) - tc.setBalance("account1", "A", 100) - tc.setBalance("account2", "B", 100) - tc.setVarsFromJSON(t, `{"a_account": "world"}`) - tc.expected = CaseResult{ - - Postings: []Posting{{ - Source: "world", - Destination: "account1", - Amount: big.NewInt(100), - Asset: "A", - }, { - Source: "world", - Destination: "account2", - Amount: big.NewInt(100), - Asset: "B", - }}, - } - test(t, tc) -} - -func TestMaxWithUnboundedOverdraft(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` -send [COIN 100] ( - source = { - max [COIN 10] from @account1 allowing unbounded overdraft - @account2 - } - destination = @world -)`) - tc.setBalance("account1", "COIN", 10000) - tc.setBalance("account2", "COIN", 10000) - tc.expected = CaseResult{ - Postings: []Posting{{ - Source: "account1", - Destination: "world", - Amount: big.NewInt(10), - Asset: "COIN", - }, { - Source: "account2", - Destination: "world", - Amount: big.NewInt(90), - Asset: "COIN", - }}, - } - test(t, tc) -} - -func TestOverdraftWhenEnoughFunds(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` -send [COIN 100] ( - source = @users:1234 allowing overdraft up to [COIN 100] - destination = @dest -) -`) - tc.expected = CaseResult{ - Postings: []Posting{{ - Source: "users:1234", - Destination: "dest", - Amount: big.NewInt(100), - Asset: "COIN", - }}, - } - test(t, tc) -} - -func TestOverdraftNotEnoughFunds(t *testing.T) { - tc := NewTestCase() - tc.setBalance("users:2345:main", "USD/2", 8000) - tc.compile(t, ` - send [USD/2 2200] ( - source = { - // let the user pay with their credit account first, - @users:2345:credit allowing overdraft up to [USD/2 1000] - // then, use their main balance - @users:2345:main - } - destination = @payments:4567 - ) - `) - - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "users:2345:credit", - "payments:4567", - big.NewInt(1000), - "USD/2", - }, - { - "users:2345:main", - "payments:4567", - big.NewInt(1200), - "USD/2", - }, - }, - } - test(t, tc) -} - -func TestOverdraftBadCurrency(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` -send [COIN 100] ( - source = @users:1234 allowing overdraft up to [WRONGCURR 100] - destination = @dest -) -`) - tc.expected = CaseResult{ - Error: machine.MismatchedCurrencyError{ - Expected: "COIN", - Got: "WRONGCURR", - }, - } - test(t, tc) -} - -func TestOverdraftWhenNotEnoughFunds(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` -send [COIN 100] ( - source = @users:1234 allowing overdraft up to [COIN 10] - destination = @dest -) -`) - - tc.setBalance("users:1234", "COIN", 1) - - tc.expected = CaseResult{ - Error: machine.MissingFundsErr{ - Asset: "COIN", - Needed: *big.NewInt(100), - Available: *big.NewInt(11), - }, - } - test(t, tc) -} - -func TestErrors(t *testing.T) { - t.Run("wrong type for send literal", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send @bad:type ( - source = @a - destination = @b - )`) - tc.expected = CaseResult{ - Error: machine.TypeError{ - Expected: "monetary", - Value: machine.AccountAddress("bad:type"), - }, - } - test(t, tc) - }) - - t.Run("wrong type for account literal", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - number $var_src - } - - send [COIN 10] ( - source = { - 1/2 from @world - remaining from { - @empty - max [COIN 100] from $var_src - } - } - destination = @b - )`) - tc.setVarsFromJSON(t, `{"var_src": "42"}`) - - tc.expected = CaseResult{ - Error: machine.TypeError{ - Expected: "account", - Value: machine.NewMonetaryInt(42), - }, - } - test(t, tc) - }) - - t.Run("wrong type for account cap", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - string $v - } - - send [COIN 10] ( - source = max $v from @src - destination = @b - )`) - tc.setVarsFromJSON(t, `{"v": "abc"}`) - - tc.expected = CaseResult{ - Error: machine.TypeError{ - Expected: "monetary", - Value: machine.String("abc"), - }, - } - test(t, tc) - }) - - t.Run("unbound variable", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send $unbound_var ( - source = @a - destination = @b - )`) - - tc.expected = CaseResult{ - Error: machine.UnboundVariableErr{ - Name: "unbound_var", - Range: parser.RangeOfIndexed(tc.source, "$unbound_var", 0), - }, - } - test(t, tc) - }) - - t.Run("missing variable from json", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - monetary $x - } - - send $x ( - source = @a - destination = @b - )`) - - tc.expected = CaseResult{ - Error: machine.MissingVariableErr{ - Name: "x", - }, - } - test(t, tc) - }) - - t.Run("unbound fn", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `unbound_fn(1, 2)`) - - tc.expected = CaseResult{ - Error: machine.UnboundFunctionErr{ - Name: "unbound_fn", - }, - } - test(t, tc) - }) - - t.Run("unbound fn (origin)", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - number $x = unbound_fn(1, 2) - } - `) - - tc.expected = CaseResult{ - Error: machine.UnboundFunctionErr{ - Name: "unbound_fn", - }, - } - test(t, tc) - }) - - t.Run("wrong fn arity", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `set_tx_meta()`) - - tc.expected = CaseResult{ - Error: machine.BadArityErr{ - ExpectedArity: 2, - GivenArguments: 0, - }, - } - test(t, tc) - }) - - t.Run("wrong fn type", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, `set_tx_meta(@key_wrong_type, "value")`) - tc.expected = CaseResult{ - Error: machine.TypeError{ - Expected: "string", - Value: machine.AccountAddress("key_wrong_type"), - }, - } - test(t, tc) - }) - - t.Run("invalid variable type", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { - invalidt $x - } - `) - tc.setVarsFromJSON(t, `{"x": "42"}`) - tc.expected = CaseResult{ - Error: machine.InvalidTypeErr{ - Name: "invalidt", - }, - } - test(t, tc) - }) - - t.Run("bad currency type in max (source)", func(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [EUR/2 1] ( - source = max [USD/2 10] from @world - destination = @b - ) - `) - tc.expected = CaseResult{ - Error: machine.MismatchedCurrencyError{ - Expected: "EUR/2", - Got: "USD/2", - }, - } - test(t, tc) - }) -} - -func TestNestedRemaining(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 100] ( - source = @world - destination = { - 10% to { - remaining to { - 100% to { - max [GEM 1] to @dest1 - remaining kept - } - } - } - remaining to @dest2 - } - ) - `) - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "world", - "dest1", - big.NewInt(1), - "GEM", - }, - { - "world", - "dest2", - big.NewInt(90), // the 90% of 100GEM - "GEM", - }, - }, - } - test(t, tc) -} - -func TestNestedRemainingComplex(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [EUR/2 10000] ( - source = @orders:1234 - destination = { - 15% to { - 20% to @platform:commission:sales_tax - remaining to { - 5% to { - // users - max [EUR/2 1000] to @users:1234:cashback - remaining kept - } - remaining to @platform:commission:revenue - } - } - remaining to @merchants:6789 - } - ) - `) - tc.setBalance("orders:1234", "EUR/2", 10000) - - tc.expected = CaseResult{ - Postings: []machine.Posting{ - // 15% of 10000 == 1500 - - // inside the 20% branch: - { - "orders:1234", - "platform:commission:sales_tax", - big.NewInt(300), - "EUR/2", - }, - - // 5% of 1200 is 60 - { - "orders:1234", - "users:1234:cashback", - big.NewInt(60), // cap doesn't apply here - "EUR/2", - }, - - // 95% of 1200 is 1140 - { - "orders:1234", - "platform:commission:revenue", - big.NewInt(1140), // cap doesn't apply here - "EUR/2", - }, - - // we are left with 85% of 10000 == 8500 - { - "orders:1234", - "merchants:6789", - big.NewInt(8500), - "EUR/2", - }, - }, - } - test(t, tc) -} - -func TestTrackBalancesTricky(t *testing.T) { - t.Skip() - - tc := NewTestCase() - tc.setBalance("src", "COIN", 5) - tc.compile(t, ` - send [COIN 25] ( // send 10 + 15 - source= { - max [COIN 10] from @world - @src // src only has 5 before the program starts - } - destination = { - max [COIN 10] to @src - remaining to @dest // but @src needs to send 15 here - } - ) - `) - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "world", - "src", - big.NewInt(10), - "GEM", - }, - { - "src", - "dest", - big.NewInt(15), - "GEM", - }, - }, - } - test(t, tc) -} - -func TestZeroPostings(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 100] ( - source = { - @a - @world - } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "world", - "dest", - big.NewInt(100), - "COIN", - }, - }, - } - test(t, tc) -} - -func TestZeroPostingsDestination(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 100] ( - source = @world - destination = { - max [COIN 0] to @d1 - remaining to @d2 - } - ) - `) - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "world", - "d2", - big.NewInt(100), - "COIN", - }, - }, - } - test(t, tc) -} - -func TestZeroPostingsExplicitInorder(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 0] ( - source = { - @a - @b - @c - } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []machine.Posting{}, - } - test(t, tc) -} - -func TestZeroPostingsExplicitAllotment(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 0] ( - source = { - 1/2 from @a - 1/2 from @b - } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []machine.Posting{}, - } - test(t, tc) -} - -func TestUnboundedOverdraftWhenNotEnoughFunds(t *testing.T) { - tc := NewTestCase() - tc.setBalance("users:2345:main", "USD/2", 8000) - tc.compile(t, ` - send [USD/2 100] ( - source = @empty allowing unbounded overdraft - destination = @dest - ) - `) - - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "empty", - "dest", - big.NewInt(100), - "USD/2", - }, - }, - } - test(t, tc) -} - -// Numscript playground examples -func TestOvedraftsPlaygroundExample(t *testing.T) { - tc := NewTestCase() - tc.setBalance("users:2345:main", "USD/2", 8000) - tc.compile(t, ` - send [USD/2 100] ( - source = @users:1234 allowing unbounded overdraft - destination = @payments:4567 - ) - - send [USD/2 6000] ( - source = { - // let the user pay with their credit account first, - @users:2345:credit allowing overdraft up to [USD/2 1000] - // then, use their main balance - @users:2345:main - } - destination = @payments:4567 - ) - `) - - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "users:1234", - "payments:4567", - big.NewInt(100), - "USD/2", - }, - - { - "users:2345:credit", - "payments:4567", - big.NewInt(1000), - "USD/2", - }, - { - "users:2345:main", - "payments:4567", - big.NewInt(5000), - "USD/2", - }, - }, - } - test(t, tc) -} - -func TestCascadingSources(t *testing.T) { - tc := NewTestCase() - tc.setBalance("users:1234:main", "USD/2", 5000) - tc.setBalance("users:1234:vouchers:2024-01-31", "USD/2", 1000) - tc.setBalance("users:1234:vouchers:2024-02-17", "USD/2", 3000) - tc.setBalance("users:1234:vouchers:2024-03-22", "USD/2", 10000) - - tc.compile(t, ` - send [USD/2 10000] ( - source = { - // first, pull from the user balance - @users:1234:main - // then, pull from the user's vouchers, - // fairly using the ones that expire first - @users:1234:vouchers:2024-01-31 - @users:1234:vouchers:2024-02-17 - @users:1234:vouchers:2024-03-22 - } - destination = @orders:4567:payment - ) - `) - - tc.expected = CaseResult{ - Postings: []machine.Posting{ - { - "users:1234:main", - "orders:4567:payment", - big.NewInt(5000), - "USD/2", - }, - { - "users:1234:vouchers:2024-01-31", - "orders:4567:payment", - big.NewInt(1000), - "USD/2", - }, - { - "users:1234:vouchers:2024-02-17", - "orders:4567:payment", - big.NewInt(3000), - "USD/2", - }, - { - "users:1234:vouchers:2024-03-22", - "orders:4567:payment", - big.NewInt(1000), - "USD/2", - }, - }, - } - test(t, tc) -} - -func TestUseBalanceTwice(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - vars { monetary $v = balance(@src, COIN) } - - send $v ( - source = @src - destination = @dest - )`) - - tc.setBalance("src", "COIN", 50) - tc.expected = CaseResult{ - - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(50), - Source: "src", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestBigInt(t *testing.T) { - script := ` - vars { number $amt } - - send [USD/2 $amt] ( - source = @world - destination = { - 100% to @dest - remaining kept - } - ) - - ` - - tc := NewTestCase() - tc.compile(t, script) - - // max safe int is 9223372036854775807 - // this number is 99999223372036854775807 - - amt, ok := new(big.Int).SetString("99999223372036854775807", 10) - if !ok { - panic("Invalid number") - } - tc.vars = map[string]string{ - "amt": amt.String(), - } - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: amt, - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestInvalidNumberLiteral(t *testing.T) { - script := ` - vars { number $amt } - - send [$amt USD/2] ( - source = @world - destination = @dest - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.vars = map[string]string{ - "amt": "not a number", - } - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.InvalidNumberLiteral{Range: parser.Range{}, Source: "not a number"}, - } - test(t, tc) -} - -func TestBigIntMonetary(t *testing.T) { - script := ` - vars { monetary $amt } - - send $amt ( - source = @world - destination = { - 100% to @dest - remaining kept - } - ) - - ` - - tc := NewTestCase() - tc.compile(t, script) - - amt, ok := new(big.Int).SetString("99999223372036854775807", 10) - if !ok { - panic("Invalid number") - } - tc.vars = map[string]string{ - "amt": "USD/123 " + amt.String(), - } - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/123", - Amount: amt, - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} - -func TestSaveFromAccount(t *testing.T) { - - t.Run("simple", func(t *testing.T) { - script := ` - save [USD 10] from @alice - - send [USD 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "alice", - Destination: "bob", - }, - { - Asset: "USD", - Amount: big.NewInt(20), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("save causes failure", func(t *testing.T) { - script := ` - save [USD/2 1] from @alice - - send [USD/2 30] ( - source = @alice - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD/2", 30) - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.MissingFundsErr{ - Asset: "USD/2", - Needed: *big.NewInt(30), - Available: *big.NewInt(29), - }, - } - test(t, tc) - }) - - t.Run("save all", func(t *testing.T) { - script := ` - save [USD *] from @alice - - send [USD 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - // 0-posting omitted - { - Asset: "USD", - Amount: big.NewInt(30), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("save more than balance", func(t *testing.T) { - script := ` - save [USD 30] from @alice - - send [USD 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - // 0-posting omitted - { - Asset: "USD", - Amount: big.NewInt(30), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("with asset var", func(t *testing.T) { - script := ` - vars { - asset $ass - } - save [$ass 10] from @alice - - send [$ass 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.vars = map[string]string{ - "ass": "USD", - } - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "alice", - Destination: "bob", - }, - { - Asset: "USD", - Amount: big.NewInt(20), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("with monetary var", func(t *testing.T) { - script := ` - vars { - monetary $mon - } - - save $mon from @alice - - send [USD 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.vars = map[string]string{ - "mon": "USD 10", - } - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "alice", - Destination: "bob", - }, - { - Asset: "USD", - Amount: big.NewInt(20), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("multi postings", func(t *testing.T) { - script := ` - send [USD 10] ( - source = @alice - destination = @bob - ) - - save [USD 5] from @alice - - send [USD 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "alice", - Destination: "bob", - }, - { - Asset: "USD", - Amount: big.NewInt(5), - Source: "alice", - Destination: "bob", - }, - { - Asset: "USD", - Amount: big.NewInt(25), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("save a different asset", func(t *testing.T) { - script := ` - save [COIN 100] from @alice - - send [USD 30] ( - source = { - @alice - @world - } - destination = @bob - )` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("alice", "COIN", 100) - tc.setBalance("alice", "USD", 20) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD", - Amount: big.NewInt(20), - Source: "alice", - Destination: "bob", - }, - { - Asset: "USD", - Amount: big.NewInt(10), - Source: "world", - Destination: "bob", - }, - }, - Error: nil, - } - test(t, tc) - }) - - t.Run("negative amount", func(t *testing.T) { - script := ` - - save [USD -100] from @A` - tc := NewTestCase() - tc.compile(t, script) - tc.setBalance("A", "USD", -100) - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.NegativeAmountErr{ - Amount: machine.NewMonetaryInt(-100), - }, - } - test(t, tc) - }) -} - -func TestOverdraftFunctionWhenNegative(t *testing.T) { - script := ` - vars { monetary $amt = overdraft(@acc, EUR/2) } - - send $amt ( - source = @world - destination = @dest - ) - - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.setBalance("acc", "EUR/2", -100) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "EUR/2", - Amount: big.NewInt(100), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOverdraftFunctionFeatureFlag) -} - -func TestOverdraftFunctionWhenZero(t *testing.T) { - script := ` - vars { monetary $amt = overdraft(@acc, EUR/2) } - - send $amt ( - source = @world - destination = @dest - ) - - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{ - // zero posting is omitted - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOverdraftFunctionFeatureFlag) -} - -func TestOverdraftFunctionWhenPositive(t *testing.T) { - script := ` - vars { monetary $amt = overdraft(@acc, EUR/2) } - - send $amt ( - source = @world - destination = @dest - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.setBalance("acc", "EUR/2", 100) - - tc.expected = CaseResult{ - Postings: []Posting{ - // zero posting is omitted - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOverdraftFunctionFeatureFlag) -} - -func TestOverdraftFunctionUseCaseRemoveDebt(t *testing.T) { - script := ` - vars { monetary $amt = overdraft(@user:001, USD/2) } - - - // we have at most 1000 USD/2 to remove user:001's debt - send [USD/2 1000] ( - source = @world - destination = { - // but we send at most what we need to cancel the debt - max $amt to @user:001 - remaining kept - } - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.setBalance("user:001", "USD/2", -100) - - tc.expected = CaseResult{ - Postings: []Posting{ - machine.Posting{ - Asset: "USD/2", - Amount: big.NewInt(100), - Source: "world", - Destination: "user:001", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOverdraftFunctionFeatureFlag) -} - -func TestAddMonetariesSameCurrency(t *testing.T) { - script := ` - send [COIN 1] + [COIN 2] ( - source = @world - destination = @dest - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1 + 2), - Source: "world", - Destination: "dest", - }, - }, - } - test(t, tc) -} - -func TestAddNumbers(t *testing.T) { - script := ` - set_tx_meta("k", 1 + 2) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - TxMetadata: map[string]machine.Value{ - "k": machine.NewMonetaryInt(1 + 2), - }, - } - test(t, tc) -} - -func TestAddNumbersInvalidRightType(t *testing.T) { - script := ` - set_tx_meta("k", 1 + "not a number") - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Error: machine.TypeError{ - Expected: "number", - Value: machine.String("not a number"), - }, - } - test(t, tc) -} - -func TestAddMonetariesDifferentCurrencies(t *testing.T) { - script := ` - send [USD/2 1] + [EUR/2 2] ( - source = @world - destination = @dest - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.MismatchedCurrencyError{ - Expected: "USD/2", - Got: "EUR/2", - }, - } - test(t, tc) -} - -func TestAddInvalidLeftType(t *testing.T) { - script := ` - set_tx_meta("k", EUR/2 + EUR/3) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.TypeError{ - Expected: "monetary|number", - Value: machine.Asset("EUR/2"), - }, - } - test(t, tc) -} - -func TestSubNumbers(t *testing.T) { - script := ` - set_tx_meta("k", 10 - 1) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - TxMetadata: map[string]machine.Value{ - "k": machine.NewMonetaryInt(10 - 1), - }, - } - test(t, tc) -} - -func TestSubMonetaries(t *testing.T) { - script := ` - set_tx_meta("k", [USD/2 10] - [USD/2 3]) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - TxMetadata: map[string]machine.Value{ - "k": machine.Monetary{ - Amount: machine.NewMonetaryInt(10 - 3), - Asset: "USD/2", - }, - }, - } - test(t, tc) -} - -func TestOneofInSourceSendFirstBranch(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 15] ( - source = oneof { - @a allowing unbounded overdraft // this branch succeeded - @empty - } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(15), - Source: "a", - Destination: "dest", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofInSource(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 15] ( - source = oneof { - @a allowing overdraft up to [GEM 14] // this doesn't succeed - @world - } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(15), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofAllFailing(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 1] ( - source = oneof { - @empty1 - @empty2 - @empty3 - } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.MissingFundsErr{ - Asset: "GEM", - Needed: *big.NewInt(1), - Available: *big.NewInt(0), - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofInSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM *] ( - source = oneof { - @s1 // only this is executed - @s2 - @s3 - } - destination = @dest - ) - `) - tc.setBalance("s1", "GEM", 10) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(10), - Source: "s1", - Destination: "dest", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofSingleton(t *testing.T) { - tc := NewTestCase() - tc.setBalance("a", "GEM", 10) - tc.compile(t, ` - send [GEM 10] ( - source = oneof { @a } - destination = @dest - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(10), - Source: "a", - Destination: "dest", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofDestinationFirstClause(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 10] ( - source = @world - destination = oneof { - max [GEM 99999] to @a - remaining to @b - } - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(10), - Source: "world", - Destination: "a", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofDestinationSecondClause(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 10] ( - source = @world - destination = oneof { - max [GEM 9] to @a - max [GEM 10] to @b - remaining to @rem - } - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(10), - Source: "world", - Destination: "b", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestOneofDestinationRemainingClause(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [GEM 100] ( - source = @world - destination = oneof { - max [GEM 9] to @a - max [GEM 10] to @b - remaining to @rem - } - ) - `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "GEM", - Amount: big.NewInt(100), - Source: "world", - Destination: "rem", - }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) -} - -func TestInvalidAccount(t *testing.T) { - script := ` - vars { - account $acc - } - set_tx_meta("k", $acc) - ` - - tc := NewTestCase() - tc.setVarsFromJSON(t, ` - { - "acc": "!invalid acc.." - } - `) - - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.InvalidAccountName{ - Name: "!invalid acc..", - }, - } - test(t, tc) -} - -func TestInvalidInterpAccount(t *testing.T) { - script := ` - vars { - string $status - } - set_tx_meta("k", @user:$status) - ` - - tc := NewTestCase() - tc.setVarsFromJSON(t, ` - { - "status": "!invalid acc.." - } - `) - - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.InvalidAccountName{ - Name: "user:!invalid acc..", - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAccountInterpolationFlag) -} - -func TestAccountInterp(t *testing.T) { - script := ` - vars { - number $id - string $status - account $acc - } - set_tx_meta("k", @acc:$id:$status:$acc) - ` - - tc := NewTestCase() - tc.setVarsFromJSON(t, ` - { - "id": "42", - "status": "pending", - "acc": "user:001" - } - `) - - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - TxMetadata: map[string]machine.Value{ - "k": machine.AccountAddress("acc:42:pending:user:001"), - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAccountInterpolationFlag) -} - -func TestAccountInvalidString(t *testing.T) { - script := ` - vars { - monetary $m - } - set_tx_meta("k", @acc:$m) - ` - - tc := NewTestCase() - tc.setVarsFromJSON(t, ` - { - "m": "USD/2 10" - } - `) - - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.CannotCastToString{ - Range: parser.RangeOfIndexed(script, "@acc:$m", 0), - Value: machine.Monetary{ - Amount: machine.NewMonetaryInt(10), - Asset: machine.Asset("USD/2"), - }, - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAccountInterpolationFlag) -} - -func TestMidscriptBalance(t *testing.T) { - script := ` - send balance(@acc, USD/2) ( - source = @world - destination = @dest - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.setBalance("acc", "USD/2", 42) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(42), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - - testWithFeatureFlag(t, tc, flags.ExperimentalMidScriptFunctionCall) -} - -func TestMidscriptBalanceAfterDecrease(t *testing.T) { - script := ` - // @acc has [10 USD/2] initially - - send [USD/2 3] ( - source = @acc - destination = @world - ) - - // @acc has [7 USD/2] left - send balance(@acc, USD/2) ( - source = @world - destination = @dest - ) - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.setBalance("acc", "USD/2", 10) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD/2", - Amount: big.NewInt(3), - Source: "acc", - Destination: "world", - }, - - { - Asset: "USD/2", - Amount: big.NewInt(7), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - - testWithFeatureFlag(t, tc, flags.ExperimentalMidScriptFunctionCall) -} - -func TestExprInVarOrigin(t *testing.T) { - script := ` - vars { - number $x = 1 + 2 - } - ` - - tc := NewTestCase() - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: nil, - } - - testWithFeatureFlag(t, tc, flags.ExperimentalMidScriptFunctionCall) -} - -func TestInvalidNestedMetaCall(t *testing.T) { - script := ` - vars { - number $x = 1 + meta(@acc, "k") - } - ` - - tc := NewTestCase() - tc.meta = machine.AccountsMetadata{ - "acc": { - "k": "42", - }, - } - tc.compile(t, script) - - tc.expected = CaseResult{ - Error: machine.InvalidNestedMeta{}, - } - - testWithFeatureFlag(t, tc, flags.ExperimentalMidScriptFunctionCall) -} - -func TestGetAssetFunction(t *testing.T) { - script := ` - vars { asset $a = get_asset([ABC 100]) } - - set_tx_meta("asset", $a) - - ` - - tc := NewTestCase() - tc.compile(t, script) - tc.expected = CaseResult{ - Postings: []Posting{}, - TxMetadata: map[string]machine.Value{ - "asset": machine.Asset("ABC"), - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalGetAssetFunctionFeatureFlag) -} - -func TestGetAmountFunction(t *testing.T) { - script := ` - vars { number $a = get_amount([ABC 100]) } - - set_tx_meta("amt", $a) - - ` - - tc := NewTestCase() - tc.compile(t, script) - tc.expected = CaseResult{ - Postings: []Posting{}, - TxMetadata: map[string]machine.Value{ - "amt": machine.NewMonetaryInt(100), - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalGetAmountFunctionFeatureFlag) -} - -func TestColorSend(t *testing.T) { - script := ` - send [COIN 100] ( - source = @world \ "RED" - destination = @dest - ) - ` + send $x ( + source = @a + destination = @b + )`) - tc := NewTestCase() - tc.compile(t, script) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_RED", - Amount: big.NewInt(100), - Source: "world", - Destination: "dest", + tc.expected = CaseResult{ + Error: machine.MissingVariableErr{ + Name: "x", }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) -} + } + test(t, tc) + }) -func TestColorSendOverdrat(t *testing.T) { - script := ` - send [COIN 100] ( - source = @acc \ "RED" allowing unbounded overdraft - destination = @dest - ) - ` + t.Run("unbound fn", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, `unbound_fn(1, 2)`) - tc := NewTestCase() - tc.compile(t, script) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_RED", - Amount: big.NewInt(100), - Source: "acc", - Destination: "dest", + tc.expected = CaseResult{ + Error: machine.UnboundFunctionErr{ + Name: "unbound_fn", }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) -} - -func TestColorRestrictBalance(t *testing.T) { - script := ` - send [COIN 20] ( - source = @acc \ "RED" - destination = @dest - ) - ` + } + test(t, tc) + }) - tc := NewTestCase() - tc.setBalance("acc", "COIN", 1) - tc.setBalance("acc", "COIN_RED", 100) - tc.compile(t, script) + t.Run("unbound fn (origin)", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + vars { + number $x = unbound_fn(1, 2) + } + `) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_RED", - Amount: big.NewInt(20), - Source: "acc", - Destination: "dest", + tc.expected = CaseResult{ + Error: machine.UnboundFunctionErr{ + Name: "unbound_fn", }, - }, - Error: nil, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) -} - -func TestColorRestrictBalanceWhenMissingFunds(t *testing.T) { - script := ` - send [COIN 20] ( - source = @acc \ "RED" - destination = @dest - ) - ` - - tc := NewTestCase() - tc.setBalance("acc", "COIN", 100) - tc.setBalance("acc", "COIN_RED", 1) - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{}, - Error: machine.MissingFundsErr{ - Needed: *big.NewInt(20), - Available: *big.NewInt(1), - Asset: "COIN", - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) -} - -func TestColorRestrictionInSendAll(t *testing.T) { - script := ` - send [COIN *] ( - source = @src \ "RED" - destination = @dest - ) - ` - - tc := NewTestCase() + } + test(t, tc) + }) - tc.setBalance("src", "COIN_RED", 42) - tc.compile(t, script) + t.Run("wrong fn arity", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, `set_tx_meta()`) - tc.expected = CaseResult{ - Postings: []Posting{{ - Asset: "COIN_RED", - Amount: big.NewInt(42), - Source: "src", - Destination: "dest", - }}, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) -} + tc.expected = CaseResult{ + Error: machine.BadArityErr{ + ExpectedArity: 2, + GivenArguments: 0, + }, + } + test(t, tc) + }) -func TestColorInorder(t *testing.T) { + t.Run("wrong fn type", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, `set_tx_meta(@key_wrong_type, "value")`) + tc.expected = CaseResult{ + Error: machine.TypeError{ + Expected: "string", + Value: machine.AccountAddress("key_wrong_type"), + }, + } + test(t, tc) + }) - script := ` - send [COIN 100] ( - source = { - @src \ "RED" - @src \ "BLUE" - @src + t.Run("invalid variable type", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + vars { + invalidt $x } - destination = @dest - ) - ` - - tc := NewTestCase() - tc.setBalance("src", "COIN", 100) - tc.setBalance("src", "COIN_RED", 20) - tc.setBalance("src", "COIN_BLUE", 30) - tc.compile(t, script) - - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_RED", - Amount: big.NewInt(20), - Source: "src", - Destination: "dest", - }, - { - Asset: "COIN_BLUE", - Amount: big.NewInt(30), - Source: "src", - Destination: "dest", + `) + tc.setVarsFromJSON(t, `{"x": "42"}`) + tc.expected = CaseResult{ + Error: machine.InvalidTypeErr{ + Name: "invalidt", }, - { - Asset: "COIN", - Amount: big.NewInt(50), - Source: "src", - Destination: "dest", + } + test(t, tc) + }) + + t.Run("bad currency type in max (source)", func(t *testing.T) { + tc := NewTestCase() + tc.compile(t, ` + send [EUR/2 1] ( + source = max [USD/2 10] from @world + destination = @b + ) + `) + tc.expected = CaseResult{ + Error: machine.MismatchedCurrencyError{ + Expected: "EUR/2", + Got: "USD/2", }, - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) + } + test(t, tc) + }) } -func TestColorInorderSendAll(t *testing.T) { - - script := ` - send [COIN *] ( - source = { - @src \ "RED" - @src \ "BLUE" - @src - } - destination = @dest - ) - ` +func TestTrackBalancesTricky(t *testing.T) { + t.Skip() tc := NewTestCase() - tc.setBalance("src", "COIN", 100) - tc.setBalance("src", "COIN_RED", 20) - tc.setBalance("src", "COIN_BLUE", 30) - tc.compile(t, script) - + tc.setBalance("src", "COIN", 5) + tc.compile(t, ` + send [COIN 25] ( // send 10 + 15 + source= { + max [COIN 10] from @world + @src // src only has 5 before the program starts + } + destination = { + max [COIN 10] to @src + remaining to @dest // but @src needs to send 15 here + } + ) + `) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_RED", - Amount: big.NewInt(20), - Source: "src", - Destination: "dest", - }, + Postings: []machine.Posting{ { - Asset: "COIN_BLUE", - Amount: big.NewInt(30), - Source: "src", - Destination: "dest", + "world", + "src", + big.NewInt(10), + "GEM", }, { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "src", - Destination: "dest", + "src", + "dest", + big.NewInt(15), + "GEM", }, }, } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) + test(t, tc) } -func TestNoDoubleSpendingInColoredSendAll(t *testing.T) { - +func TestInvalidNumberLiteral(t *testing.T) { script := ` - send [COIN *] ( - source = { - @src \ "X" - @src \ "X" - @src - } - destination = @dest - ) + vars { number $amt } + + send [$amt USD/2] ( + source = @world + destination = @dest + ) ` tc := NewTestCase() - tc.setBalance("src", "COIN", 100) - tc.setBalance("src", "COIN_X", 20) tc.compile(t, script) + tc.vars = map[string]string{ + "amt": "not a number", + } + tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_X", - Amount: big.NewInt(20), - Source: "src", - Destination: "dest", - }, - { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "src", - Destination: "dest", - }, - }, + Postings: []Posting{}, + Error: machine.InvalidNumberLiteral{Range: parser.Range{}, Source: "not a number"}, } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) + test(t, tc) } -func TestNoDoubleSpendingInColoredSend(t *testing.T) { - script := ` - send [COIN 100] ( - source = { - @src \ "X" - @src \ "X" - @src - } - destination = @dest - ) - ` +func TestSaveFromAccount(t *testing.T) { - tc := NewTestCase() - tc.setBalance("src", "COIN", 99999) - tc.setBalance("src", "COIN_X", 20) - tc.compile(t, script) + t.Run("save causes failure", func(t *testing.T) { + script := ` + save [USD/2 1] from @alice - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN_X", - Amount: big.NewInt(20), - Source: "src", - Destination: "dest", + send [USD/2 30] ( + source = @alice + destination = @bob + )` + tc := NewTestCase() + tc.compile(t, script) + tc.setBalance("alice", "USD/2", 30) + tc.expected = CaseResult{ + Postings: []Posting{}, + Error: machine.MissingFundsErr{ + Asset: "USD/2", + Needed: *big.NewInt(30), + Available: *big.NewInt(29), }, - { - Asset: "COIN", - Amount: big.NewInt(80), - Source: "src", - Destination: "dest", + } + test(t, tc) + }) + + t.Run("negative amount", func(t *testing.T) { + script := ` + + save [USD -100] from @A` + tc := NewTestCase() + tc.compile(t, script) + tc.setBalance("A", "USD", -100) + tc.expected = CaseResult{ + Postings: []Posting{}, + Error: machine.NegativeAmountErr{ + Amount: machine.NewMonetaryInt(-100), }, - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) + } + test(t, tc) + }) } -func TestEmptyColor(t *testing.T) { - // empty string color behaves as no color - +func TestAddNumbersInvalidRightType(t *testing.T) { script := ` - send [COIN *] ( - source = @src \ "" // <- same as just '@src' - destination = @dest - ) + set_tx_meta("k", 1 + "not a number") ` tc := NewTestCase() - tc.setBalance("src", "COIN", 100) tc.compile(t, script) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(100), - Source: "src", - Destination: "dest", - }, + Error: machine.TypeError{ + Expected: "number", + Value: machine.String("not a number"), }, } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) + test(t, tc) } -func TestColorWithAssetPrecision(t *testing.T) { +func TestAddMonetariesDifferentCurrencies(t *testing.T) { script := ` - send [USD/4 10] ( - source = @src \ "COL" allowing unbounded overdraft - destination = @dest - ) + send [USD/2 1] + [EUR/2 2] ( + source = @world + destination = @dest + ) ` tc := NewTestCase() tc.compile(t, script) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "USD_COL/4", - Amount: big.NewInt(10), - Source: "src", - Destination: "dest", - }, + Postings: []Posting{}, + Error: machine.MismatchedCurrencyError{ + Expected: "USD/2", + Got: "EUR/2", }, } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) + test(t, tc) } -func TestInvalidColor(t *testing.T) { +func TestAddInvalidLeftType(t *testing.T) { script := ` - send [USD 10] ( - source = @src \ "!!" allowing unbounded overdraft - destination = @dest - ) + set_tx_meta("k", EUR/2 + EUR/3) ` tc := NewTestCase() tc.compile(t, script) tc.expected = CaseResult{ - Error: machine.InvalidColor{ - Color: "!!", - Range: parser.RangeOfIndexed(script, `"!!"`, 0), - }, - } - testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) -} - -func TestUpdateBalances(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` -// @alice balance is 100 initially -send [USD 200] ( - source = { - @alice - @alice - @world - } - destination = @dest -) - `) - tc.setBalance("alice", "USD", 100) - tc.expected = CaseResult{ - Postings: []Posting{ - - { - Asset: "USD", - Amount: big.NewInt(100), - Source: "alice", - Destination: "dest", - }, - { - Asset: "USD", - Amount: big.NewInt(100), - Source: "world", - Destination: "dest", - }, + Postings: []Posting{}, + Error: machine.TypeError{ + Expected: "monetary|number", + Value: machine.Asset("EUR/2"), }, - Error: nil, } test(t, tc) } -func TestUpdateBalancesWithOneof(t *testing.T) { +func TestOneofAllFailing(t *testing.T) { tc := NewTestCase() tc.compile(t, ` -// @alice balance is 100 initially -send [USD 200] ( - source = oneof { - @alice - {@alice @world} - } - destination = @dest -) + send [GEM 1] ( + source = oneof { + @empty1 + @empty2 + @empty3 + } + destination = @dest + ) `) - tc.setBalance("alice", "USD", 100) tc.expected = CaseResult{ - Postings: []Posting{ - - { - Asset: "USD", - Amount: big.NewInt(100), - Source: "alice", - Destination: "dest", - }, - { - Asset: "USD", - Amount: big.NewInt(100), - Source: "world", - Destination: "dest", - }, + Postings: []Posting{}, + Error: machine.MissingFundsErr{ + Asset: "GEM", + Needed: *big.NewInt(1), + Available: *big.NewInt(0), }, - Error: nil, } testWithFeatureFlag(t, tc, flags.ExperimentalOneofFeatureFlag) } -func TestSendWhenNegativeBalance(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 10] ( - source = { - @s - @world +func TestInvalidAccount(t *testing.T) { + script := ` + vars { + account $acc } - destination = @dest - ) - `) - tc.setBalance("s", "COIN", -5) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(10), - Source: "world", - Destination: "dest", - }, - }, - Error: nil, - } - test(t, tc) -} + set_tx_meta("k", $acc) + ` -func TestOverdraftWhenNegativeOvedraftInSendAll(t *testing.T) { tc := NewTestCase() - tc.compile(t, ` - send [COIN *] ( - source = { - @s allowing overdraft up to [COIN -10] + tc.setVarsFromJSON(t, ` + { + "acc": "!invalid acc.." } - destination = @dest - ) `) - tc.setBalance("s", "COIN", 1) + + tc.compile(t, script) + tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1), - Source: "s", - Destination: "dest", - }, + Postings: []Posting{}, + Error: machine.InvalidAccountName{ + Name: "!invalid acc..", }, - Error: nil, } test(t, tc) } -func TestOverdraftWhenNegativeBalanceInSendAll(t *testing.T) { +func TestInvalidInterpAccount(t *testing.T) { + script := ` + vars { + string $status + } + set_tx_meta("k", @user:$status) + ` + tc := NewTestCase() - tc.compile(t, ` - send [COIN *] ( - source = { - @s allowing overdraft up to [COIN 2] + tc.setVarsFromJSON(t, ` + { + "status": "!invalid acc.." } - destination = @dest - ) `) - tc.setBalance("s", "COIN", -1) + + tc.compile(t, script) + tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1), - Source: "s", - Destination: "dest", - }, + Postings: []Posting{}, + Error: machine.InvalidAccountName{ + Name: "user:!invalid acc..", }, - Error: nil, } - test(t, tc) + testWithFeatureFlag(t, tc, flags.ExperimentalAccountInterpolationFlag) } -func TestOverdraftWhenNegativeBalance(t *testing.T) { +func TestAccountInvalidString(t *testing.T) { + script := ` + vars { + monetary $m + } + set_tx_meta("k", @acc:$m) + ` + tc := NewTestCase() - tc.compile(t, ` - send [COIN 10] ( - source = { - @s allowing overdraft up to [COIN -10] + tc.setVarsFromJSON(t, ` + { + "m": "USD/2 10" } - destination = @dest - ) `) - tc.setBalance("s", "COIN", 11) + + tc.compile(t, script) + tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(10), - Source: "s", - Destination: "dest", + Postings: []Posting{}, + Error: machine.CannotCastToString{ + Range: parser.RangeOfIndexed(script, "@acc:$m", 0), + Value: machine.Monetary{ + Amount: machine.NewMonetaryInt(10), + Asset: machine.Asset("USD/2"), }, }, - Error: nil, } - test(t, tc) + testWithFeatureFlag(t, tc, flags.ExperimentalAccountInterpolationFlag) } -func TestDoNotExceedOverdraft(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 10] ( - source = { - // should pull 3 (otherwise the overdraft exceeds 5) - @s allowing overdraft up to [COIN 5] - - @world +func TestInvalidNestedMetaCall(t *testing.T) { + script := ` + vars { + number $x = 1 + meta(@acc, "k") } - destination = @dest - ) - `) - tc.setBalance("s", "COIN", -2) - tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(3), - Source: "s", - Destination: "dest", - }, + ` - { - Asset: "COIN", - Amount: big.NewInt(10 - 3), - Source: "world", - Destination: "dest", - }, + tc := NewTestCase() + tc.meta = machine.AccountsMetadata{ + "acc": { + "k": "42", }, - Error: nil, } - test(t, tc) -} + tc.compile(t, script) -func TestDoNotExceedOverdraftOnSendAll(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN *] ( - source = @s allowing overdraft up to [COIN 5] - destination = @dest - ) - `) - tc.setBalance("s", "COIN", -4) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(1), - Source: "s", - Destination: "dest", - }, - }, - Error: nil, + Error: machine.InvalidNestedMeta{}, } - test(t, tc) + + testWithFeatureFlag(t, tc, flags.ExperimentalMidScriptFunctionCall) } -func TestDoNotExceedOverdraftWhenDoubleSpending(t *testing.T) { - tc := NewTestCase() - tc.compile(t, ` - send [COIN 10] ( - source = { - // should pull 2 - @s allowing overdraft up to [COIN 2] +func TestColorRestrictBalanceWhenMissingFunds(t *testing.T) { + script := ` + send [COIN 20] ( + source = @acc \ "RED" + destination = @dest + ) + ` - // should pull other 3 (otherwise the overdraft exceeds 5) - @s allowing overdraft up to [COIN 5] + tc := NewTestCase() + tc.setBalance("acc", "COIN", 100) + tc.setBalance("acc", "COIN_RED", 1) + tc.compile(t, script) - @world - } - destination = @dest - ) - `) tc.expected = CaseResult{ - Postings: []Posting{ - { - Asset: "COIN", - Amount: big.NewInt(2 + 3), - Source: "s", - Destination: "dest", - }, - - { - Asset: "COIN", - Amount: big.NewInt(10 - 5), - Source: "world", - Destination: "dest", - }, + Postings: []Posting{}, + Error: machine.MissingFundsErr{ + Needed: *big.NewInt(20), + Available: *big.NewInt(1), + Asset: "COIN", }, - Error: nil, } - test(t, tc) + testWithFeatureFlag(t, tc, flags.ExperimentalAssetColors) } func TestSafeMaxWithdraft(t *testing.T) { diff --git a/testdata/script-tests/add-monetaries-same-currency.num b/testdata/script-tests/add-monetaries-same-currency.num new file mode 100644 index 00000000..9379d1f5 --- /dev/null +++ b/testdata/script-tests/add-monetaries-same-currency.num @@ -0,0 +1,6 @@ + + send [COIN 1] + [COIN 2] ( + source = @world + destination = @dest + ) + diff --git a/testdata/script-tests/add-monetaries-same-currency.num.specs.json b/testdata/script-tests/add-monetaries-same-currency.num.specs.json new file mode 100644 index 00000000..9842f611 --- /dev/null +++ b/testdata/script-tests/add-monetaries-same-currency.num.specs.json @@ -0,0 +1,15 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 3, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/add-numbers.num b/testdata/script-tests/add-numbers.num new file mode 100644 index 00000000..f9448b47 --- /dev/null +++ b/testdata/script-tests/add-numbers.num @@ -0,0 +1,3 @@ + + set_tx_meta("k", 1 + 2) + diff --git a/testdata/script-tests/add-numbers.num.specs.json b/testdata/script-tests/add-numbers.num.specs.json new file mode 100644 index 00000000..c651e29f --- /dev/null +++ b/testdata/script-tests/add-numbers.num.specs.json @@ -0,0 +1,10 @@ +{ + "testCases": [ + { + "it": "-", + "expect.txMetadata": { + "k": "3" + } + } + ] +} diff --git a/testdata/script-tests/allocate-dont-take-too-much.num b/testdata/script-tests/allocate-dont-take-too-much.num new file mode 100644 index 00000000..ba706fb7 --- /dev/null +++ b/testdata/script-tests/allocate-dont-take-too-much.num @@ -0,0 +1,10 @@ +send [CREDIT 200] ( + source = { + @users:001 + @users:002 + } + destination = { + 1/2 to @foo + 1/2 to @bar + } +) diff --git a/testdata/script-tests/allocate-dont-take-too-much.num.specs.json b/testdata/script-tests/allocate-dont-take-too-much.num.specs.json new file mode 100644 index 00000000..1fc6d9f3 --- /dev/null +++ b/testdata/script-tests/allocate-dont-take-too-much.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "CREDIT": 100 + }, + "users:002": { + "CREDIT": 110 + } + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "foo", + "amount": 100, + "asset": "CREDIT" + }, + { + "source": "users:002", + "destination": "bar", + "amount": 100, + "asset": "CREDIT" + } + ] + } + ] +} diff --git a/testdata/script-tests/allocation.num b/testdata/script-tests/allocation.num new file mode 100644 index 00000000..54c5b085 --- /dev/null +++ b/testdata/script-tests/allocation.num @@ -0,0 +1,12 @@ +vars { + account $rider + account $driver +} +send [GEM 15] ( + source = $rider + destination = { + 80% to $driver + 8% to @a + 12% to @b + } +) diff --git a/testdata/script-tests/allocation.num.specs.json b/testdata/script-tests/allocation.num.specs.json new file mode 100644 index 00000000..02b08a47 --- /dev/null +++ b/testdata/script-tests/allocation.num.specs.json @@ -0,0 +1,36 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "GEM": 15 + } + }, + "variables": { + "driver": "users:002", + "rider": "users:001" + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "users:002", + "amount": 13, + "asset": "GEM" + }, + { + "source": "users:001", + "destination": "a", + "amount": 1, + "asset": "GEM" + }, + { + "source": "users:001", + "destination": "b", + "amount": 1, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/ask-balance-twice.num b/testdata/script-tests/ask-balance-twice.num new file mode 100644 index 00000000..39869830 --- /dev/null +++ b/testdata/script-tests/ask-balance-twice.num @@ -0,0 +1,10 @@ + +vars { + monetary $bal = balance(@alice, USD/2) +} + +send $bal ( + source = @alice + destination = @dest +) + diff --git a/testdata/script-tests/ask-balance-twice.num.specs.json b/testdata/script-tests/ask-balance-twice.num.specs.json new file mode 100644 index 00000000..435146cf --- /dev/null +++ b/testdata/script-tests/ask-balance-twice.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD/2": 10 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "dest", + "amount": 10, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/balance-not-found.num b/testdata/script-tests/balance-not-found.num new file mode 100644 index 00000000..c18626bf --- /dev/null +++ b/testdata/script-tests/balance-not-found.num @@ -0,0 +1,9 @@ + +vars { + monetary $balance = balance(@a, EUR/2) +} + +send $balance ( + source = @world + destination = @dest +) diff --git a/testdata/script-tests/balance-not-found.num.specs.json b/testdata/script-tests/balance-not-found.num.specs.json new file mode 100644 index 00000000..3c8a442a --- /dev/null +++ b/testdata/script-tests/balance-not-found.num.specs.json @@ -0,0 +1,10 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": {} + } + } + ] +} diff --git a/testdata/script-tests/balance-simple.num b/testdata/script-tests/balance-simple.num new file mode 100644 index 00000000..ba8158fb --- /dev/null +++ b/testdata/script-tests/balance-simple.num @@ -0,0 +1,11 @@ + +vars { + monetary $bal = balance(@alice, USD/2) +} + +send $bal ( + source = @world + destination = @dest +) + + diff --git a/testdata/script-tests/balance-simple.num.specs.json b/testdata/script-tests/balance-simple.num.specs.json new file mode 100644 index 00000000..53ffac19 --- /dev/null +++ b/testdata/script-tests/balance-simple.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD/2": 10 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 10, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/balance.num b/testdata/script-tests/balance.num new file mode 100644 index 00000000..c18626bf --- /dev/null +++ b/testdata/script-tests/balance.num @@ -0,0 +1,9 @@ + +vars { + monetary $balance = balance(@a, EUR/2) +} + +send $balance ( + source = @world + destination = @dest +) diff --git a/testdata/script-tests/balance.num.specs.json b/testdata/script-tests/balance.num.specs.json new file mode 100644 index 00000000..fc6986c0 --- /dev/null +++ b/testdata/script-tests/balance.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "EUR/2": 123 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 123, + "asset": "EUR/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/big-int-monetary.num b/testdata/script-tests/big-int-monetary.num new file mode 100644 index 00000000..e0ac8e80 --- /dev/null +++ b/testdata/script-tests/big-int-monetary.num @@ -0,0 +1,12 @@ + + vars { monetary $amt } + + send $amt ( + source = @world + destination = { + 100% to @dest + remaining kept + } + ) + + diff --git a/testdata/script-tests/big-int-monetary.num.specs.json b/testdata/script-tests/big-int-monetary.num.specs.json new file mode 100644 index 00000000..808caa3d --- /dev/null +++ b/testdata/script-tests/big-int-monetary.num.specs.json @@ -0,0 +1,18 @@ +{ + "testCases": [ + { + "it": "-", + "variables": { + "amt": "USD/123 99999223372036854775807" + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 99999223372036854775807, + "asset": "USD/123" + } + ] + } + ] +} diff --git a/testdata/script-tests/big-int.num b/testdata/script-tests/big-int.num new file mode 100644 index 00000000..991b1ae0 --- /dev/null +++ b/testdata/script-tests/big-int.num @@ -0,0 +1,12 @@ + + vars { number $amt } + + send [USD/2 $amt] ( + source = @world + destination = { + 100% to @dest + remaining kept + } + ) + + diff --git a/testdata/script-tests/big-int.num.specs.json b/testdata/script-tests/big-int.num.specs.json new file mode 100644 index 00000000..469f990b --- /dev/null +++ b/testdata/script-tests/big-int.num.specs.json @@ -0,0 +1,18 @@ +{ + "testCases": [ + { + "it": "-", + "variables": { + "amt": "99999223372036854775807" + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 99999223372036854775807, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/capped-when-less-than-needed.num b/testdata/script-tests/capped-when-less-than-needed.num new file mode 100644 index 00000000..e370a55e --- /dev/null +++ b/testdata/script-tests/capped-when-less-than-needed.num @@ -0,0 +1,9 @@ + +send [COIN 100] ( + source = { + max [COIN 40] from @src1 + @src2 + } + destination = @platform +) + diff --git a/testdata/script-tests/capped-when-less-than-needed.num.specs.json b/testdata/script-tests/capped-when-less-than-needed.num.specs.json new file mode 100644 index 00000000..c519e2f0 --- /dev/null +++ b/testdata/script-tests/capped-when-less-than-needed.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src1": { + "COIN": 1000 + }, + "src2": { + "COIN": 1000 + } + }, + "expect.postings": [ + { + "source": "src1", + "destination": "platform", + "amount": 40, + "asset": "COIN" + }, + { + "source": "src2", + "destination": "platform", + "amount": 60, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/capped-when-more-than-balance.num b/testdata/script-tests/capped-when-more-than-balance.num new file mode 100644 index 00000000..93362522 --- /dev/null +++ b/testdata/script-tests/capped-when-more-than-balance.num @@ -0,0 +1,9 @@ + +send [COIN 100] ( + source = { + max [COIN 200] from @world + @src + } + destination = @platform +) + diff --git a/testdata/script-tests/capped-when-more-than-balance.num.specs.json b/testdata/script-tests/capped-when-more-than-balance.num.specs.json new file mode 100644 index 00000000..2f7cdcf9 --- /dev/null +++ b/testdata/script-tests/capped-when-more-than-balance.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 1000 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "platform", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/cascading-sources.num b/testdata/script-tests/cascading-sources.num new file mode 100644 index 00000000..6ec44ec0 --- /dev/null +++ b/testdata/script-tests/cascading-sources.num @@ -0,0 +1,14 @@ + +send [USD/2 10000] ( + source = { + // first, pull from the user balance + @users:1234:main + // then, pull from the user's vouchers, + // fairly using the ones that expire first + @users:1234:vouchers:2024-01-31 + @users:1234:vouchers:2024-02-17 + @users:1234:vouchers:2024-03-22 + } + destination = @orders:4567:payment + ) + diff --git a/testdata/script-tests/cascading-sources.num.specs.json b/testdata/script-tests/cascading-sources.num.specs.json new file mode 100644 index 00000000..7fe60a30 --- /dev/null +++ b/testdata/script-tests/cascading-sources.num.specs.json @@ -0,0 +1,47 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:1234:main": { + "USD/2": 5000 + }, + "users:1234:vouchers:2024-01-31": { + "USD/2": 1000 + }, + "users:1234:vouchers:2024-02-17": { + "USD/2": 3000 + }, + "users:1234:vouchers:2024-03-22": { + "USD/2": 10000 + } + }, + "expect.postings": [ + { + "source": "users:1234:main", + "destination": "orders:4567:payment", + "amount": 5000, + "asset": "USD/2" + }, + { + "source": "users:1234:vouchers:2024-01-31", + "destination": "orders:4567:payment", + "amount": 1000, + "asset": "USD/2" + }, + { + "source": "users:1234:vouchers:2024-02-17", + "destination": "orders:4567:payment", + "amount": 3000, + "asset": "USD/2" + }, + { + "source": "users:1234:vouchers:2024-03-22", + "destination": "orders:4567:payment", + "amount": 1000, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/destination-complex.num b/testdata/script-tests/destination-complex.num new file mode 100644 index 00000000..e2ec975e --- /dev/null +++ b/testdata/script-tests/destination-complex.num @@ -0,0 +1,11 @@ +send [COIN 100] ( + source = @world + destination = { + 20% to @a + 20% kept + 60% to { + max [COIN 10] to @b + remaining to @c + } + } +) diff --git a/testdata/script-tests/destination-complex.num.specs.json b/testdata/script-tests/destination-complex.num.specs.json new file mode 100644 index 00000000..3f4a6a01 --- /dev/null +++ b/testdata/script-tests/destination-complex.num.specs.json @@ -0,0 +1,27 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 20, + "asset": "COIN" + }, + { + "source": "world", + "destination": "b", + "amount": 10, + "asset": "COIN" + }, + { + "source": "world", + "destination": "c", + "amount": 50, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/do-not-exceed-overdraft-on-send-all.num b/testdata/script-tests/do-not-exceed-overdraft-on-send-all.num new file mode 100644 index 00000000..626bd519 --- /dev/null +++ b/testdata/script-tests/do-not-exceed-overdraft-on-send-all.num @@ -0,0 +1,6 @@ + +send [COIN *] ( + source = @s allowing overdraft up to [COIN 5] + destination = @dest +) + diff --git a/testdata/script-tests/do-not-exceed-overdraft-on-send-all.num.specs.json b/testdata/script-tests/do-not-exceed-overdraft-on-send-all.num.specs.json new file mode 100644 index 00000000..e9c46c85 --- /dev/null +++ b/testdata/script-tests/do-not-exceed-overdraft-on-send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": { + "COIN": -4 + } + }, + "expect.postings": [ + { + "source": "s", + "destination": "dest", + "amount": 1, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/do-not-exceed-overdraft-when-double-spending.num b/testdata/script-tests/do-not-exceed-overdraft-when-double-spending.num new file mode 100644 index 00000000..d2e26aaa --- /dev/null +++ b/testdata/script-tests/do-not-exceed-overdraft-when-double-spending.num @@ -0,0 +1,14 @@ + +send [COIN 10] ( + source = { + // should pull 2 + @s allowing overdraft up to [COIN 2] + + // should pull other 3 (otherwise the overdraft exceeds 5) + @s allowing overdraft up to [COIN 5] + + @world + } + destination = @dest +) + diff --git a/testdata/script-tests/do-not-exceed-overdraft-when-double-spending.num.specs.json b/testdata/script-tests/do-not-exceed-overdraft-when-double-spending.num.specs.json new file mode 100644 index 00000000..6169ae9a --- /dev/null +++ b/testdata/script-tests/do-not-exceed-overdraft-when-double-spending.num.specs.json @@ -0,0 +1,24 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": {} + }, + "expect.postings": [ + { + "source": "s", + "destination": "dest", + "amount": 5, + "asset": "COIN" + }, + { + "source": "world", + "destination": "dest", + "amount": 5, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/do-not-exceed-overdraft.num b/testdata/script-tests/do-not-exceed-overdraft.num new file mode 100644 index 00000000..4bb09328 --- /dev/null +++ b/testdata/script-tests/do-not-exceed-overdraft.num @@ -0,0 +1,11 @@ + +send [COIN 10] ( + source = { + // should pull 3 (otherwise the overdraft exceeds 5) + @s allowing overdraft up to [COIN 5] + + @world + } + destination = @dest +) + diff --git a/testdata/script-tests/do-not-exceed-overdraft.num.specs.json b/testdata/script-tests/do-not-exceed-overdraft.num.specs.json new file mode 100644 index 00000000..d83e602c --- /dev/null +++ b/testdata/script-tests/do-not-exceed-overdraft.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": { + "COIN": -2 + } + }, + "expect.postings": [ + { + "source": "s", + "destination": "dest", + "amount": 3, + "asset": "COIN" + }, + { + "source": "world", + "destination": "dest", + "amount": 7, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/dynamic-allocation.num b/testdata/script-tests/dynamic-allocation.num new file mode 100644 index 00000000..65555254 --- /dev/null +++ b/testdata/script-tests/dynamic-allocation.num @@ -0,0 +1,11 @@ +vars { + portion $p +} +send [GEM 15] ( + source = @a + destination = { + 80% to @b + $p to @c + remaining to @d + } +) diff --git a/testdata/script-tests/dynamic-allocation.num.specs.json b/testdata/script-tests/dynamic-allocation.num.specs.json new file mode 100644 index 00000000..ef002331 --- /dev/null +++ b/testdata/script-tests/dynamic-allocation.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "GEM": 15 + } + }, + "variables": { + "p": "15%" + }, + "expect.postings": [ + { + "source": "a", + "destination": "b", + "amount": 13, + "asset": "GEM" + }, + { + "source": "a", + "destination": "c", + "amount": 2, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/empty-postings.num b/testdata/script-tests/empty-postings.num new file mode 100644 index 00000000..ea9c2286 --- /dev/null +++ b/testdata/script-tests/empty-postings.num @@ -0,0 +1,4 @@ +send [GEM *] ( + source = @foo + destination = @bar +) diff --git a/testdata/script-tests/empty-postings.num.specs.json b/testdata/script-tests/empty-postings.num.specs.json new file mode 100644 index 00000000..2b4b338c --- /dev/null +++ b/testdata/script-tests/empty-postings.num.specs.json @@ -0,0 +1,12 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "foo": { + "GEM": 0 + } + } + } + ] +} diff --git a/testdata/script-tests/experimental/account-interpolation/account-interp.num b/testdata/script-tests/experimental/account-interpolation/account-interp.num new file mode 100644 index 00000000..9702c159 --- /dev/null +++ b/testdata/script-tests/experimental/account-interpolation/account-interp.num @@ -0,0 +1,8 @@ + + vars { + number $id + string $status + account $acc + } + set_tx_meta("k", @acc:$id:$status:$acc) + diff --git a/testdata/script-tests/experimental/account-interpolation/account-interp.num.specs.json b/testdata/script-tests/experimental/account-interpolation/account-interp.num.specs.json new file mode 100644 index 00000000..97effe85 --- /dev/null +++ b/testdata/script-tests/experimental/account-interpolation/account-interp.num.specs.json @@ -0,0 +1,16 @@ +{ + "featureFlags": ["experimental-account-interpolation"], + "testCases": [ + { + "it": "-", + "variables": { + "acc": "user:001", + "id": "42", + "status": "pending" + }, + "expect.txMetadata": { + "k": "acc:42:pending:user:001" + } + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-inorder-send-all.num b/testdata/script-tests/experimental/asset-colors/color-inorder-send-all.num new file mode 100644 index 00000000..c97061c0 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-inorder-send-all.num @@ -0,0 +1,10 @@ + + send [COIN *] ( + source = { + @src \ "RED" + @src \ "BLUE" + @src + } + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-inorder-send-all.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-inorder-send-all.num.specs.json new file mode 100644 index 00000000..4efe1917 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-inorder-send-all.num.specs.json @@ -0,0 +1,37 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 100, + "COIN_BLUE": 30, + "COIN_RED": 20 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 20, + "asset": "COIN_RED" + }, + { + "source": "src", + "destination": "dest", + "amount": 30, + "asset": "COIN_BLUE" + }, + { + "source": "src", + "destination": "dest", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-inorder.num b/testdata/script-tests/experimental/asset-colors/color-inorder.num new file mode 100644 index 00000000..aaf4ab32 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-inorder.num @@ -0,0 +1,10 @@ + + send [COIN 100] ( + source = { + @src \ "RED" + @src \ "BLUE" + @src + } + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-inorder.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-inorder.num.specs.json new file mode 100644 index 00000000..2b306b8f --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-inorder.num.specs.json @@ -0,0 +1,37 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 100, + "COIN_BLUE": 30, + "COIN_RED": 20 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 20, + "asset": "COIN_RED" + }, + { + "source": "src", + "destination": "dest", + "amount": 30, + "asset": "COIN_BLUE" + }, + { + "source": "src", + "destination": "dest", + "amount": 50, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-restrict-balance-when-missing-funds.num b/testdata/script-tests/experimental/asset-colors/color-restrict-balance-when-missing-funds.num new file mode 100644 index 00000000..09b6a2ce --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-restrict-balance-when-missing-funds.num @@ -0,0 +1,6 @@ + + send [COIN 20] ( + source = @acc \ "RED" + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-restrict-balance-when-missing-funds.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-restrict-balance-when-missing-funds.num.specs.json new file mode 100644 index 00000000..ad2f94d1 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-restrict-balance-when-missing-funds.num.specs.json @@ -0,0 +1,17 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": { + "COIN": 100, + "COIN_RED": 1 + } + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-restrict-balance.num b/testdata/script-tests/experimental/asset-colors/color-restrict-balance.num new file mode 100644 index 00000000..09b6a2ce --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-restrict-balance.num @@ -0,0 +1,6 @@ + + send [COIN 20] ( + source = @acc \ "RED" + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-restrict-balance.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-restrict-balance.num.specs.json new file mode 100644 index 00000000..a4d32470 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-restrict-balance.num.specs.json @@ -0,0 +1,24 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": { + "COIN": 1, + "COIN_RED": 100 + } + }, + "expect.postings": [ + { + "source": "acc", + "destination": "dest", + "amount": 20, + "asset": "COIN_RED" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-restriction-in-send-all.num b/testdata/script-tests/experimental/asset-colors/color-restriction-in-send-all.num new file mode 100644 index 00000000..562ee213 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-restriction-in-send-all.num @@ -0,0 +1,6 @@ + + send [COIN *] ( + source = @src \ "RED" + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-restriction-in-send-all.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-restriction-in-send-all.num.specs.json new file mode 100644 index 00000000..8bde6bea --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-restriction-in-send-all.num.specs.json @@ -0,0 +1,23 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN_RED": 42 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 42, + "asset": "COIN_RED" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-send-overdrat.num b/testdata/script-tests/experimental/asset-colors/color-send-overdrat.num new file mode 100644 index 00000000..b6193431 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-send-overdrat.num @@ -0,0 +1,6 @@ + + send [COIN 100] ( + source = @acc \ "RED" allowing unbounded overdraft + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-send-overdrat.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-send-overdrat.num.specs.json new file mode 100644 index 00000000..5cfde80f --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-send-overdrat.num.specs.json @@ -0,0 +1,18 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "acc", + "destination": "dest", + "amount": 100, + "asset": "COIN_RED" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-send.num b/testdata/script-tests/experimental/asset-colors/color-send.num new file mode 100644 index 00000000..811ae4c2 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-send.num @@ -0,0 +1,6 @@ + + send [COIN 100] ( + source = @world \ "RED" + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-send.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-send.num.specs.json new file mode 100644 index 00000000..4288aed7 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-send.num.specs.json @@ -0,0 +1,18 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 100, + "asset": "COIN_RED" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/color-with-asset-precision.num b/testdata/script-tests/experimental/asset-colors/color-with-asset-precision.num new file mode 100644 index 00000000..72e83bb2 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-with-asset-precision.num @@ -0,0 +1,6 @@ + + send [USD/4 10] ( + source = @src \ "COL" allowing unbounded overdraft + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/color-with-asset-precision.num.specs.json b/testdata/script-tests/experimental/asset-colors/color-with-asset-precision.num.specs.json new file mode 100644 index 00000000..f9b3821e --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/color-with-asset-precision.num.specs.json @@ -0,0 +1,18 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 10, + "asset": "USD_COL/4" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/empty-color.num b/testdata/script-tests/experimental/asset-colors/empty-color.num new file mode 100644 index 00000000..4b1c698e --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/empty-color.num @@ -0,0 +1,6 @@ + + send [COIN *] ( + source = @src \ "" // <- same as just '@src' + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/empty-color.num.specs.json b/testdata/script-tests/experimental/asset-colors/empty-color.num.specs.json new file mode 100644 index 00000000..571f280c --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/empty-color.num.specs.json @@ -0,0 +1,23 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 100 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send-all.num b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send-all.num new file mode 100644 index 00000000..235f0b93 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send-all.num @@ -0,0 +1,10 @@ + + send [COIN *] ( + source = { + @src \ "X" + @src \ "X" + @src + } + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send-all.num.specs.json b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send-all.num.specs.json new file mode 100644 index 00000000..a1b7d2f9 --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send-all.num.specs.json @@ -0,0 +1,30 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 100, + "COIN_X": 20 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 20, + "asset": "COIN_X" + }, + { + "source": "src", + "destination": "dest", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send.num b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send.num new file mode 100644 index 00000000..91ebdabc --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send.num @@ -0,0 +1,10 @@ + + send [COIN 100] ( + source = { + @src \ "X" + @src \ "X" + @src + } + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send.num.specs.json b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send.num.specs.json new file mode 100644 index 00000000..da4b831a --- /dev/null +++ b/testdata/script-tests/experimental/asset-colors/no-double-spending-in-colored-send.num.specs.json @@ -0,0 +1,30 @@ +{ + "featureFlags": [ + "experimental-asset-colors" + ], + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 99999, + "COIN_X": 20 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 20, + "asset": "COIN_X" + }, + { + "source": "src", + "destination": "dest", + "amount": 80, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/get-amount-function/get-amount-function.num b/testdata/script-tests/experimental/get-amount-function/get-amount-function.num new file mode 100644 index 00000000..b56c7106 --- /dev/null +++ b/testdata/script-tests/experimental/get-amount-function/get-amount-function.num @@ -0,0 +1,6 @@ + + vars { number $a = get_amount([ABC 100]) } + + set_tx_meta("amt", $a) + + diff --git a/testdata/script-tests/experimental/get-amount-function/get-amount-function.num.specs.json b/testdata/script-tests/experimental/get-amount-function/get-amount-function.num.specs.json new file mode 100644 index 00000000..d52e8c6e --- /dev/null +++ b/testdata/script-tests/experimental/get-amount-function/get-amount-function.num.specs.json @@ -0,0 +1,11 @@ +{ + "featureFlags": ["experimental-get-amount-function"], + "testCases": [ + { + "it": "-", + "expect.txMetadata": { + "amt": "100" + } + } + ] +} diff --git a/testdata/script-tests/experimental/get-asset-function/get-asset-function.num b/testdata/script-tests/experimental/get-asset-function/get-asset-function.num new file mode 100644 index 00000000..b67b45af --- /dev/null +++ b/testdata/script-tests/experimental/get-asset-function/get-asset-function.num @@ -0,0 +1,6 @@ + + vars { asset $a = get_asset([ABC 100]) } + + set_tx_meta("asset", $a) + + diff --git a/testdata/script-tests/experimental/get-asset-function/get-asset-function.num.specs.json b/testdata/script-tests/experimental/get-asset-function/get-asset-function.num.specs.json new file mode 100644 index 00000000..3b3997b7 --- /dev/null +++ b/testdata/script-tests/experimental/get-asset-function/get-asset-function.num.specs.json @@ -0,0 +1,11 @@ +{ + "featureFlags": ["experimental-get-asset-function"], + "testCases": [ + { + "it": "-", + "expect.txMetadata": { + "asset": "ABC" + } + } + ] +} diff --git a/testdata/script-tests/experimental/mid-script-function-call/expr-in-var-origin.num b/testdata/script-tests/experimental/mid-script-function-call/expr-in-var-origin.num new file mode 100644 index 00000000..f9e35488 --- /dev/null +++ b/testdata/script-tests/experimental/mid-script-function-call/expr-in-var-origin.num @@ -0,0 +1,5 @@ + + vars { + number $x = 1 + 2 + } + diff --git a/testdata/script-tests/experimental/mid-script-function-call/expr-in-var-origin.num.specs.json b/testdata/script-tests/experimental/mid-script-function-call/expr-in-var-origin.num.specs.json new file mode 100644 index 00000000..6f1091ad --- /dev/null +++ b/testdata/script-tests/experimental/mid-script-function-call/expr-in-var-origin.num.specs.json @@ -0,0 +1,10 @@ +{ + "featureFlags": [ + "experimental-mid-script-function-call" + ], + "testCases": [ + { + "it": "-" + } + ] +} diff --git a/testdata/script-tests/experimental/mid-script-function-call/midscript-balance-after-decrease.num b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance-after-decrease.num new file mode 100644 index 00000000..a5eaaf2d --- /dev/null +++ b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance-after-decrease.num @@ -0,0 +1,14 @@ + + // @acc has [10 USD/2] initially + + send [USD/2 3] ( + source = @acc + destination = @world + ) + + // @acc has [7 USD/2] left + send balance(@acc, USD/2) ( + source = @world + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/mid-script-function-call/midscript-balance-after-decrease.num.specs.json b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance-after-decrease.num.specs.json new file mode 100644 index 00000000..03d6d9c3 --- /dev/null +++ b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance-after-decrease.num.specs.json @@ -0,0 +1,29 @@ +{ + "featureFlags": [ + "experimental-mid-script-function-call" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": { + "USD/2": 10 + } + }, + "expect.postings": [ + { + "source": "acc", + "destination": "world", + "amount": 3, + "asset": "USD/2" + }, + { + "source": "world", + "destination": "dest", + "amount": 7, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/mid-script-function-call/midscript-balance.num b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance.num new file mode 100644 index 00000000..8d6603e7 --- /dev/null +++ b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance.num @@ -0,0 +1,6 @@ + + send balance(@acc, USD/2) ( + source = @world + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/mid-script-function-call/midscript-balance.num.specs.json b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance.num.specs.json new file mode 100644 index 00000000..3436e8ce --- /dev/null +++ b/testdata/script-tests/experimental/mid-script-function-call/midscript-balance.num.specs.json @@ -0,0 +1,23 @@ +{ + "featureFlags": [ + "experimental-mid-script-function-call" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": { + "USD/2": 42 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 42, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-all-failing.num b/testdata/script-tests/experimental/oneof/oneof-all-failing.num new file mode 100644 index 00000000..54ffc904 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-all-failing.num @@ -0,0 +1,10 @@ + +send [GEM 1] ( + source = oneof { + @empty1 + @empty2 + @empty3 + } + destination = @dest +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-all-failing.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-all-failing.num.specs.json new file mode 100644 index 00000000..6e799080 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-all-failing.num.specs.json @@ -0,0 +1,16 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "balances": { + "empty1": {}, + "empty2": {}, + "empty3": {} + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-destination-first-clause.num b/testdata/script-tests/experimental/oneof/oneof-destination-first-clause.num new file mode 100644 index 00000000..6720e8e5 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-destination-first-clause.num @@ -0,0 +1,9 @@ + +send [GEM 10] ( + source = @world + destination = oneof { + max [GEM 99999] to @a + remaining to @b + } +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-destination-first-clause.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-destination-first-clause.num.specs.json new file mode 100644 index 00000000..5cad0202 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-destination-first-clause.num.specs.json @@ -0,0 +1,18 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 10, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-destination-remaining-clause.num b/testdata/script-tests/experimental/oneof/oneof-destination-remaining-clause.num new file mode 100644 index 00000000..c9cee5aa --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-destination-remaining-clause.num @@ -0,0 +1,10 @@ + +send [GEM 100] ( + source = @world + destination = oneof { + max [GEM 9] to @a + max [GEM 10] to @b + remaining to @rem + } +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-destination-remaining-clause.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-destination-remaining-clause.num.specs.json new file mode 100644 index 00000000..e182b93e --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-destination-remaining-clause.num.specs.json @@ -0,0 +1,18 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "rem", + "amount": 100, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-destination-second-clause.num b/testdata/script-tests/experimental/oneof/oneof-destination-second-clause.num new file mode 100644 index 00000000..5b87eb1b --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-destination-second-clause.num @@ -0,0 +1,10 @@ + +send [GEM 10] ( + source = @world + destination = oneof { + max [GEM 9] to @a + max [GEM 10] to @b + remaining to @rem + } +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-destination-second-clause.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-destination-second-clause.num.specs.json new file mode 100644 index 00000000..25e711fc --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-destination-second-clause.num.specs.json @@ -0,0 +1,18 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "b", + "amount": 10, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-in-send-all.num b/testdata/script-tests/experimental/oneof/oneof-in-send-all.num new file mode 100644 index 00000000..2df711af --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-in-send-all.num @@ -0,0 +1,10 @@ + +send [GEM *] ( + source = oneof { + @s1 // only this is executed + @s2 + @s3 + } + destination = @dest +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-in-send-all.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-in-send-all.num.specs.json new file mode 100644 index 00000000..11f21592 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-in-send-all.num.specs.json @@ -0,0 +1,25 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "balances": { + "s1": { + "GEM": 10 + }, + "s2": {}, + "s3": {} + }, + "expect.postings": [ + { + "source": "s1", + "destination": "dest", + "amount": 10, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-in-source-send-first-branch.num b/testdata/script-tests/experimental/oneof/oneof-in-source-send-first-branch.num new file mode 100644 index 00000000..d9d6883a --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-in-source-send-first-branch.num @@ -0,0 +1,9 @@ + +send [GEM 15] ( + source = oneof { + @a allowing unbounded overdraft // this branch succeeded + @empty + } + destination = @dest +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-in-source-send-first-branch.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-in-source-send-first-branch.num.specs.json new file mode 100644 index 00000000..4dc294b5 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-in-source-send-first-branch.num.specs.json @@ -0,0 +1,21 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "balances": { + "empty": {} + }, + "expect.postings": [ + { + "source": "a", + "destination": "dest", + "amount": 15, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-in-source.num b/testdata/script-tests/experimental/oneof/oneof-in-source.num new file mode 100644 index 00000000..893235cc --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-in-source.num @@ -0,0 +1,9 @@ + +send [GEM 15] ( + source = oneof { + @a allowing overdraft up to [GEM 14] // this doesn't succeed + @world + } + destination = @dest +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-in-source.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-in-source.num.specs.json new file mode 100644 index 00000000..fa4d2413 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-in-source.num.specs.json @@ -0,0 +1,21 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "balances": { + "a": {} + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 15, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/oneof-singleton.num b/testdata/script-tests/experimental/oneof/oneof-singleton.num new file mode 100644 index 00000000..4cd38c23 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-singleton.num @@ -0,0 +1,6 @@ + +send [GEM 10] ( + source = oneof { @a } + destination = @dest +) + diff --git a/testdata/script-tests/experimental/oneof/oneof-singleton.num.specs.json b/testdata/script-tests/experimental/oneof/oneof-singleton.num.specs.json new file mode 100644 index 00000000..617d51e0 --- /dev/null +++ b/testdata/script-tests/experimental/oneof/oneof-singleton.num.specs.json @@ -0,0 +1,23 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "GEM": 10 + } + }, + "expect.postings": [ + { + "source": "a", + "destination": "dest", + "amount": 10, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/oneof/update-balances-with-oneof.num b/testdata/script-tests/experimental/oneof/update-balances-with-oneof.num new file mode 100644 index 00000000..d388c12b --- /dev/null +++ b/testdata/script-tests/experimental/oneof/update-balances-with-oneof.num @@ -0,0 +1,10 @@ + +// @alice balance is 100 initially +send [USD 200] ( +source = oneof { + @alice + {@alice @world} +} +destination = @dest +) + diff --git a/testdata/script-tests/experimental/oneof/update-balances-with-oneof.num.specs.json b/testdata/script-tests/experimental/oneof/update-balances-with-oneof.num.specs.json new file mode 100644 index 00000000..a768236f --- /dev/null +++ b/testdata/script-tests/experimental/oneof/update-balances-with-oneof.num.specs.json @@ -0,0 +1,29 @@ +{ + "featureFlags": [ + "experimental-oneof" + ], + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 100 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "dest", + "amount": 100, + "asset": "USD" + }, + { + "source": "world", + "destination": "dest", + "amount": 100, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-use-case-remove-debt.num b/testdata/script-tests/experimental/overdraft-function/overdraft-function-use-case-remove-debt.num new file mode 100644 index 00000000..640e6766 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-use-case-remove-debt.num @@ -0,0 +1,14 @@ + + vars { monetary $amt = overdraft(@user:001, USD/2) } + + + // we have at most 1000 USD/2 to remove user:001's debt + send [USD/2 1000] ( + source = @world + destination = { + // but we send at most what we need to cancel the debt + max $amt to @user:001 + remaining kept + } + ) + diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-use-case-remove-debt.num.specs.json b/testdata/script-tests/experimental/overdraft-function/overdraft-function-use-case-remove-debt.num.specs.json new file mode 100644 index 00000000..b31e3688 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-use-case-remove-debt.num.specs.json @@ -0,0 +1,23 @@ +{ + "featureFlags": [ + "experimental-overdraft-function" + ], + "testCases": [ + { + "it": "-", + "balances": { + "user:001": { + "USD/2": -100 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "user:001", + "amount": 100, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-negative.num b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-negative.num new file mode 100644 index 00000000..594adb67 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-negative.num @@ -0,0 +1,9 @@ + + vars { monetary $amt = overdraft(@acc, EUR/2) } + + send $amt ( + source = @world + destination = @dest + ) + + diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-negative.num.specs.json b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-negative.num.specs.json new file mode 100644 index 00000000..071e38dc --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-negative.num.specs.json @@ -0,0 +1,23 @@ +{ + "featureFlags": [ + "experimental-overdraft-function" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": { + "EUR/2": -100 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 100, + "asset": "EUR/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-positive.num b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-positive.num new file mode 100644 index 00000000..03a6b905 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-positive.num @@ -0,0 +1,8 @@ + + vars { monetary $amt = overdraft(@acc, EUR/2) } + + send $amt ( + source = @world + destination = @dest + ) + diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-positive.num.specs.json b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-positive.num.specs.json new file mode 100644 index 00000000..0bc7d910 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-positive.num.specs.json @@ -0,0 +1,15 @@ +{ + "featureFlags": [ + "experimental-overdraft-function" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": { + "EUR/2": 100 + } + } + } + ] +} diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-zero.num b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-zero.num new file mode 100644 index 00000000..594adb67 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-zero.num @@ -0,0 +1,9 @@ + + vars { monetary $amt = overdraft(@acc, EUR/2) } + + send $amt ( + source = @world + destination = @dest + ) + + diff --git a/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-zero.num.specs.json b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-zero.num.specs.json new file mode 100644 index 00000000..2615f9d0 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/overdraft-function-when-zero.num.specs.json @@ -0,0 +1,13 @@ +{ + "featureFlags": [ + "experimental-overdraft-function" + ], + "testCases": [ + { + "it": "-", + "balances": { + "acc": {} + } + } + ] +} diff --git a/testdata/script-tests/experimental/overdraft-function/reach-zero.num b/testdata/script-tests/experimental/overdraft-function/reach-zero.num new file mode 100644 index 00000000..aa524d33 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/reach-zero.num @@ -0,0 +1,15 @@ +vars { + number $amt + monetary $invoice_001_overdraft = overdraft(@invoice:001, USD/2) + monetary $invoice_002_overdraft = overdraft(@invoice:002, USD/2) +} + + +send [USD/2 $amt] ( + source = @world + destination = { + max $invoice_001_overdraft to @invoice:001 + max $invoice_002_overdraft to @invoice:002 + remaining kept + } +) diff --git a/testdata/script-tests/experimental/overdraft-function/reach-zero.num.specs.json b/testdata/script-tests/experimental/overdraft-function/reach-zero.num.specs.json new file mode 100644 index 00000000..7cc09268 --- /dev/null +++ b/testdata/script-tests/experimental/overdraft-function/reach-zero.num.specs.json @@ -0,0 +1,65 @@ +{ + "$schema": "https://raw.githubusercontent.com/formancehq/numscript/29a351f9aa5ae03d72b85f55981e52dd57e81c07/specs.schema.json", + "variables": { + "amt": "100" + }, + "featureFlags": ["experimental-overdraft-function"], + "testCases": [ + { + "balances": { + "invoice:001": { "USD/2": -999 } + }, + "it": "only sends to the first one if there's not enough funds to top-up its balance", + "expect.postings": [ + { + "source": "world", + "destination": "invoice:001", + "amount": 100, + "asset": "USD/2" + } + ] + }, + { + "balances": { + "invoice:001": { "USD/2": -99 }, + "invoice:002": { "USD/2": -999 } + }, + "it": "it sends to the second source after topping-up the first one", + "expect.postings": [ + { + "source": "world", + "destination": "invoice:001", + "amount": 99, + "asset": "USD/2" + }, + { + "source": "world", + "destination": "invoice:002", + "amount": 1, + "asset": "USD/2" + } + ] + }, + { + "balances": { + "invoice:001": { "USD/2": -2 }, + "invoice:002": { "USD/2": -3 } + }, + "it": "it keeps the spare amount", + "expect.postings": [ + { + "source": "world", + "destination": "invoice:001", + "amount": 2, + "asset": "USD/2" + }, + { + "source": "world", + "destination": "invoice:002", + "amount": 3, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/inoder-destination.num b/testdata/script-tests/inoder-destination.num new file mode 100644 index 00000000..c439675c --- /dev/null +++ b/testdata/script-tests/inoder-destination.num @@ -0,0 +1,7 @@ +send [COIN 100] ( + source = @world + destination = { + max [COIN 20] to @dest1 + remaining to @dest2 + } +) diff --git a/testdata/script-tests/inoder-destination.num.specs.json b/testdata/script-tests/inoder-destination.num.specs.json new file mode 100644 index 00000000..06db229a --- /dev/null +++ b/testdata/script-tests/inoder-destination.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 123 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest1", + "amount": 20, + "asset": "COIN" + }, + { + "source": "world", + "destination": "dest2", + "amount": 80, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/insufficient-funds.num b/testdata/script-tests/insufficient-funds.num new file mode 100644 index 00000000..28c4ded9 --- /dev/null +++ b/testdata/script-tests/insufficient-funds.num @@ -0,0 +1,12 @@ +vars { + account $balance + account $payment + account $seller +} +send [GEM 16] ( + source = { + $balance + $payment + } + destination = $seller +) diff --git a/testdata/script-tests/insufficient-funds.num.specs.json b/testdata/script-tests/insufficient-funds.num.specs.json new file mode 100644 index 00000000..53642e8b --- /dev/null +++ b/testdata/script-tests/insufficient-funds.num.specs.json @@ -0,0 +1,21 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "payments:001": { + "GEM": 12 + }, + "users:001": { + "GEM": 3 + } + }, + "variables": { + "balance": "users:001", + "payment": "payments:001", + "seller": "users:002" + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/kept-in-send-all-inorder.num b/testdata/script-tests/kept-in-send-all-inorder.num new file mode 100644 index 00000000..4bcc9f8d --- /dev/null +++ b/testdata/script-tests/kept-in-send-all-inorder.num @@ -0,0 +1,7 @@ +send [COIN *] ( + source = @src + destination = { + max [COIN 1] kept + remaining to @dest + } +) diff --git a/testdata/script-tests/kept-in-send-all-inorder.num.specs.json b/testdata/script-tests/kept-in-send-all-inorder.num.specs.json new file mode 100644 index 00000000..49e83472 --- /dev/null +++ b/testdata/script-tests/kept-in-send-all-inorder.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 10 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 9, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/kept-inorder.num b/testdata/script-tests/kept-inorder.num new file mode 100644 index 00000000..3b6f200e --- /dev/null +++ b/testdata/script-tests/kept-inorder.num @@ -0,0 +1,7 @@ +send [COIN 100] ( + source = @world + destination = { + max [COIN 10] kept + remaining to @dest + } +) diff --git a/testdata/script-tests/kept-inorder.num.specs.json b/testdata/script-tests/kept-inorder.num.specs.json new file mode 100644 index 00000000..3eea10c9 --- /dev/null +++ b/testdata/script-tests/kept-inorder.num.specs.json @@ -0,0 +1,15 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 90, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/kept-with-balance.num b/testdata/script-tests/kept-with-balance.num new file mode 100644 index 00000000..d58dc3a6 --- /dev/null +++ b/testdata/script-tests/kept-with-balance.num @@ -0,0 +1,7 @@ +send [COIN 100] ( + source = @src + destination = { + max [COIN 10] kept + remaining to @dest + } +) diff --git a/testdata/script-tests/kept-with-balance.num.specs.json b/testdata/script-tests/kept-with-balance.num.specs.json new file mode 100644 index 00000000..5caac63f --- /dev/null +++ b/testdata/script-tests/kept-with-balance.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 1000 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 90, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/many-kept-dest.num b/testdata/script-tests/many-kept-dest.num new file mode 100644 index 00000000..4a57211e --- /dev/null +++ b/testdata/script-tests/many-kept-dest.num @@ -0,0 +1,9 @@ +send [USD/2 100] ( + source = @world + destination = { + max [USD/2 10] kept + max [USD/2 12] to @d2 + remaining to @rem + } +) + diff --git a/testdata/script-tests/many-kept-dest.num.specs.json b/testdata/script-tests/many-kept-dest.num.specs.json new file mode 100644 index 00000000..74715cb9 --- /dev/null +++ b/testdata/script-tests/many-kept-dest.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 100 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "d2", + "amount": 12, + "asset": "USD/2" + }, + { + "source": "world", + "destination": "rem", + "amount": 78, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/many-max-dest.num b/testdata/script-tests/many-max-dest.num new file mode 100644 index 00000000..ad274b27 --- /dev/null +++ b/testdata/script-tests/many-max-dest.num @@ -0,0 +1,9 @@ +send [USD/2 100] ( + source = @world + destination = { + max [USD/2 10] to @d1 + max [USD/2 12] to @d2 + remaining to @rem + } +) + diff --git a/testdata/script-tests/many-max-dest.num.specs.json b/testdata/script-tests/many-max-dest.num.specs.json new file mode 100644 index 00000000..d5638cb9 --- /dev/null +++ b/testdata/script-tests/many-max-dest.num.specs.json @@ -0,0 +1,32 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 100 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "d1", + "amount": 10, + "asset": "USD/2" + }, + { + "source": "world", + "destination": "d2", + "amount": 12, + "asset": "USD/2" + }, + { + "source": "world", + "destination": "rem", + "amount": 78, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/max-with-unbounded-overdraft.num b/testdata/script-tests/max-with-unbounded-overdraft.num new file mode 100644 index 00000000..2918e1f1 --- /dev/null +++ b/testdata/script-tests/max-with-unbounded-overdraft.num @@ -0,0 +1,8 @@ + +send [COIN 100] ( +source = { + max [COIN 10] from @account1 allowing unbounded overdraft + @account2 +} +destination = @world +) diff --git a/testdata/script-tests/max-with-unbounded-overdraft.num.specs.json b/testdata/script-tests/max-with-unbounded-overdraft.num.specs.json new file mode 100644 index 00000000..ab69b837 --- /dev/null +++ b/testdata/script-tests/max-with-unbounded-overdraft.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "account1": { + "COIN": 10000 + }, + "account2": { + "COIN": 10000 + } + }, + "expect.postings": [ + { + "source": "account1", + "destination": "world", + "amount": 10, + "asset": "COIN" + }, + { + "source": "account2", + "destination": "world", + "amount": 90, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/metadata.num b/testdata/script-tests/metadata.num new file mode 100644 index 00000000..60e75554 --- /dev/null +++ b/testdata/script-tests/metadata.num @@ -0,0 +1,12 @@ +vars { + account $sale + account $seller = meta($sale, "seller") + portion $commission = meta($seller, "commission") +} +send [EUR/2 100] ( + source = $sale + destination = { + remaining to $seller + $commission to @platform + } +) diff --git a/testdata/script-tests/metadata.num.specs.json b/testdata/script-tests/metadata.num.specs.json new file mode 100644 index 00000000..3f0788ad --- /dev/null +++ b/testdata/script-tests/metadata.num.specs.json @@ -0,0 +1,40 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "sales:042": { + "EUR/2": 2500 + }, + "users:053": { + "EUR/2": 500 + } + }, + "variables": { + "sale": "sales:042" + }, + "metadata": { + "sales:042": { + "seller": "users:053" + }, + "users:053": { + "commission": "12.5%" + } + }, + "expect.postings": [ + { + "source": "sales:042", + "destination": "users:053", + "amount": 88, + "asset": "EUR/2" + }, + { + "source": "sales:042", + "destination": "platform", + "amount": 12, + "asset": "EUR/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/negative-max-send-all.num b/testdata/script-tests/negative-max-send-all.num new file mode 100644 index 00000000..1711220b --- /dev/null +++ b/testdata/script-tests/negative-max-send-all.num @@ -0,0 +1,5 @@ +send [USD/2 *] ( + source = max [USD/2 -50] from @src + destination = @dest +) + diff --git a/testdata/script-tests/negative-max-send-all.num.specs.json b/testdata/script-tests/negative-max-send-all.num.specs.json new file mode 100644 index 00000000..e84b62df --- /dev/null +++ b/testdata/script-tests/negative-max-send-all.num.specs.json @@ -0,0 +1,12 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 0 + } + } + } + ] +} diff --git a/testdata/script-tests/negative-max.num b/testdata/script-tests/negative-max.num new file mode 100644 index 00000000..64baf52e --- /dev/null +++ b/testdata/script-tests/negative-max.num @@ -0,0 +1,8 @@ +send [USD/2 100] ( + source = { + max [USD/2 -50] from @src + @world + } + destination = @dest +) + diff --git a/testdata/script-tests/negative-max.num.specs.json b/testdata/script-tests/negative-max.num.specs.json new file mode 100644 index 00000000..39455ca4 --- /dev/null +++ b/testdata/script-tests/negative-max.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 0 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 100, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/nested-remaining-complex.num b/testdata/script-tests/nested-remaining-complex.num new file mode 100644 index 00000000..61c1e8ba --- /dev/null +++ b/testdata/script-tests/nested-remaining-complex.num @@ -0,0 +1,19 @@ + +send [EUR/2 10000] ( + source = @orders:1234 + destination = { + 15% to { + 20% to @platform:commission:sales_tax + remaining to { + 5% to { + // users + max [EUR/2 1000] to @users:1234:cashback + remaining kept + } + remaining to @platform:commission:revenue + } + } + remaining to @merchants:6789 + } +) + diff --git a/testdata/script-tests/nested-remaining-complex.num.specs.json b/testdata/script-tests/nested-remaining-complex.num.specs.json new file mode 100644 index 00000000..22af2c9f --- /dev/null +++ b/testdata/script-tests/nested-remaining-complex.num.specs.json @@ -0,0 +1,38 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "orders:1234": { + "EUR/2": 10000 + } + }, + "expect.postings": [ + { + "source": "orders:1234", + "destination": "platform:commission:sales_tax", + "amount": 300, + "asset": "EUR/2" + }, + { + "source": "orders:1234", + "destination": "users:1234:cashback", + "amount": 60, + "asset": "EUR/2" + }, + { + "source": "orders:1234", + "destination": "platform:commission:revenue", + "amount": 1140, + "asset": "EUR/2" + }, + { + "source": "orders:1234", + "destination": "merchants:6789", + "amount": 8500, + "asset": "EUR/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/nested-remaining.num b/testdata/script-tests/nested-remaining.num new file mode 100644 index 00000000..3314ef3f --- /dev/null +++ b/testdata/script-tests/nested-remaining.num @@ -0,0 +1,16 @@ + +send [GEM 100] ( + source = @world + destination = { + 10% to { + remaining to { + 100% to { + max [GEM 1] to @dest1 + remaining kept + } + } + } + remaining to @dest2 + } +) + diff --git a/testdata/script-tests/nested-remaining.num.specs.json b/testdata/script-tests/nested-remaining.num.specs.json new file mode 100644 index 00000000..9342d9ce --- /dev/null +++ b/testdata/script-tests/nested-remaining.num.specs.json @@ -0,0 +1,21 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "dest1", + "amount": 1, + "asset": "GEM" + }, + { + "source": "world", + "destination": "dest2", + "amount": 90, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/no-empty-postings.num b/testdata/script-tests/no-empty-postings.num new file mode 100644 index 00000000..5873f194 --- /dev/null +++ b/testdata/script-tests/no-empty-postings.num @@ -0,0 +1,7 @@ +send [GEM 2] ( + source = @world + destination = { + 90% to @a + 10% to @b + } +) diff --git a/testdata/script-tests/no-empty-postings.num.specs.json b/testdata/script-tests/no-empty-postings.num.specs.json new file mode 100644 index 00000000..55e39703 --- /dev/null +++ b/testdata/script-tests/no-empty-postings.num.specs.json @@ -0,0 +1,15 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 2, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/ovedrafts-playground-example.num b/testdata/script-tests/ovedrafts-playground-example.num new file mode 100644 index 00000000..4de132f5 --- /dev/null +++ b/testdata/script-tests/ovedrafts-playground-example.num @@ -0,0 +1,16 @@ + +send [USD/2 100] ( + source = @users:1234 allowing unbounded overdraft + destination = @payments:4567 +) + +send [USD/2 6000] ( + source = { + // let the user pay with their credit account first, + @users:2345:credit allowing overdraft up to [USD/2 1000] + // then, use their main balance + @users:2345:main + } + destination = @payments:4567 +) + diff --git a/testdata/script-tests/ovedrafts-playground-example.num.specs.json b/testdata/script-tests/ovedrafts-playground-example.num.specs.json new file mode 100644 index 00000000..2d281438 --- /dev/null +++ b/testdata/script-tests/ovedrafts-playground-example.num.specs.json @@ -0,0 +1,33 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:2345:credit": {}, + "users:2345:main": { + "USD/2": 8000 + } + }, + "expect.postings": [ + { + "source": "users:1234", + "destination": "payments:4567", + "amount": 100, + "asset": "USD/2" + }, + { + "source": "users:2345:credit", + "destination": "payments:4567", + "amount": 1000, + "asset": "USD/2" + }, + { + "source": "users:2345:main", + "destination": "payments:4567", + "amount": 5000, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-in-send-all-when-noop.num b/testdata/script-tests/overdraft-in-send-all-when-noop.num new file mode 100644 index 00000000..9a0c6aa1 --- /dev/null +++ b/testdata/script-tests/overdraft-in-send-all-when-noop.num @@ -0,0 +1,4 @@ +send [USD/2 *] ( + source = @src allowing overdraft up to [USD/2 10] + destination = @dest +) diff --git a/testdata/script-tests/overdraft-in-send-all-when-noop.num.specs.json b/testdata/script-tests/overdraft-in-send-all-when-noop.num.specs.json new file mode 100644 index 00000000..54c7c930 --- /dev/null +++ b/testdata/script-tests/overdraft-in-send-all-when-noop.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 1 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 11, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-in-send-all.num b/testdata/script-tests/overdraft-in-send-all.num new file mode 100644 index 00000000..9a0c6aa1 --- /dev/null +++ b/testdata/script-tests/overdraft-in-send-all.num @@ -0,0 +1,4 @@ +send [USD/2 *] ( + source = @src allowing overdraft up to [USD/2 10] + destination = @dest +) diff --git a/testdata/script-tests/overdraft-in-send-all.num.specs.json b/testdata/script-tests/overdraft-in-send-all.num.specs.json new file mode 100644 index 00000000..66ff0af4 --- /dev/null +++ b/testdata/script-tests/overdraft-in-send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 1000 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 1010, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-not-enough-funds.num b/testdata/script-tests/overdraft-not-enough-funds.num new file mode 100644 index 00000000..82fac422 --- /dev/null +++ b/testdata/script-tests/overdraft-not-enough-funds.num @@ -0,0 +1,11 @@ + +send [USD/2 2200] ( + source = { + // let the user pay with their credit account first, + @users:2345:credit allowing overdraft up to [USD/2 1000] + // then, use their main balance + @users:2345:main + } + destination = @payments:4567 + ) + diff --git a/testdata/script-tests/overdraft-not-enough-funds.num.specs.json b/testdata/script-tests/overdraft-not-enough-funds.num.specs.json new file mode 100644 index 00000000..2889d33d --- /dev/null +++ b/testdata/script-tests/overdraft-not-enough-funds.num.specs.json @@ -0,0 +1,27 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:2345:credit": {}, + "users:2345:main": { + "USD/2": 8000 + } + }, + "expect.postings": [ + { + "source": "users:2345:credit", + "destination": "payments:4567", + "amount": 1000, + "asset": "USD/2" + }, + { + "source": "users:2345:main", + "destination": "payments:4567", + "amount": 1200, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-when-enough-funds.num b/testdata/script-tests/overdraft-when-enough-funds.num new file mode 100644 index 00000000..ff0f3bdb --- /dev/null +++ b/testdata/script-tests/overdraft-when-enough-funds.num @@ -0,0 +1,6 @@ + +send [COIN 100] ( + source = @users:1234 allowing overdraft up to [COIN 100] + destination = @dest +) + diff --git a/testdata/script-tests/overdraft-when-enough-funds.num.specs.json b/testdata/script-tests/overdraft-when-enough-funds.num.specs.json new file mode 100644 index 00000000..2f30985a --- /dev/null +++ b/testdata/script-tests/overdraft-when-enough-funds.num.specs.json @@ -0,0 +1,18 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:1234": {} + }, + "expect.postings": [ + { + "source": "users:1234", + "destination": "dest", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-when-negative-balance-in-send-all.num b/testdata/script-tests/overdraft-when-negative-balance-in-send-all.num new file mode 100644 index 00000000..5629c216 --- /dev/null +++ b/testdata/script-tests/overdraft-when-negative-balance-in-send-all.num @@ -0,0 +1,8 @@ + +send [COIN *] ( + source = { + @s allowing overdraft up to [COIN 2] + } + destination = @dest +) + diff --git a/testdata/script-tests/overdraft-when-negative-balance-in-send-all.num.specs.json b/testdata/script-tests/overdraft-when-negative-balance-in-send-all.num.specs.json new file mode 100644 index 00000000..d8f1b287 --- /dev/null +++ b/testdata/script-tests/overdraft-when-negative-balance-in-send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": { + "COIN": -1 + } + }, + "expect.postings": [ + { + "source": "s", + "destination": "dest", + "amount": 1, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-when-negative-balance.num b/testdata/script-tests/overdraft-when-negative-balance.num new file mode 100644 index 00000000..46c0c0cd --- /dev/null +++ b/testdata/script-tests/overdraft-when-negative-balance.num @@ -0,0 +1,8 @@ + +send [COIN 10] ( + source = { + @s allowing overdraft up to [COIN -10] + } + destination = @dest +) + diff --git a/testdata/script-tests/overdraft-when-negative-balance.num.specs.json b/testdata/script-tests/overdraft-when-negative-balance.num.specs.json new file mode 100644 index 00000000..8b2e8d48 --- /dev/null +++ b/testdata/script-tests/overdraft-when-negative-balance.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": { + "COIN": 11 + } + }, + "expect.postings": [ + { + "source": "s", + "destination": "dest", + "amount": 10, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-when-negative-ovedraft-in-send-all.num b/testdata/script-tests/overdraft-when-negative-ovedraft-in-send-all.num new file mode 100644 index 00000000..3dd5492a --- /dev/null +++ b/testdata/script-tests/overdraft-when-negative-ovedraft-in-send-all.num @@ -0,0 +1,8 @@ + +send [COIN *] ( + source = { + @s allowing overdraft up to [COIN -10] + } + destination = @dest +) + diff --git a/testdata/script-tests/overdraft-when-negative-ovedraft-in-send-all.num.specs.json b/testdata/script-tests/overdraft-when-negative-ovedraft-in-send-all.num.specs.json new file mode 100644 index 00000000..8c9c6c6f --- /dev/null +++ b/testdata/script-tests/overdraft-when-negative-ovedraft-in-send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": { + "COIN": 1 + } + }, + "expect.postings": [ + { + "source": "s", + "destination": "dest", + "amount": 1, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/overdraft-when-not-enough-funds.num b/testdata/script-tests/overdraft-when-not-enough-funds.num new file mode 100644 index 00000000..49c95372 --- /dev/null +++ b/testdata/script-tests/overdraft-when-not-enough-funds.num @@ -0,0 +1,6 @@ + +send [COIN 100] ( + source = @users:1234 allowing overdraft up to [COIN 10] + destination = @dest +) + diff --git a/testdata/script-tests/overdraft-when-not-enough-funds.num.specs.json b/testdata/script-tests/overdraft-when-not-enough-funds.num.specs.json new file mode 100644 index 00000000..d6ff9698 --- /dev/null +++ b/testdata/script-tests/overdraft-when-not-enough-funds.num.specs.json @@ -0,0 +1,13 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:1234": { + "COIN": 1 + } + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/override-account-meta.num b/testdata/script-tests/override-account-meta.num new file mode 100644 index 00000000..b647332d --- /dev/null +++ b/testdata/script-tests/override-account-meta.num @@ -0,0 +1,4 @@ + +set_account_meta(@acc, "overridden", 100) +set_account_meta(@acc, "new", 2) + diff --git a/testdata/script-tests/override-account-meta.num.specs.json b/testdata/script-tests/override-account-meta.num.specs.json new file mode 100644 index 00000000..d474d112 --- /dev/null +++ b/testdata/script-tests/override-account-meta.num.specs.json @@ -0,0 +1,19 @@ +{ + "testCases": [ + { + "it": "-", + "metadata": { + "acc": { + "initial": "0", + "overridden": "1" + } + }, + "expect.metadata": { + "acc": { + "new": "2", + "overridden": "100" + } + } + } + ] +} diff --git a/testdata/script-tests/portion-syntax.num b/testdata/script-tests/portion-syntax.num new file mode 100644 index 00000000..b5ea6d51 --- /dev/null +++ b/testdata/script-tests/portion-syntax.num @@ -0,0 +1,11 @@ +vars { + portion $por +} +send [COIN 3] ( + source = @world + destination = { + $por to @a + remaining kept + } +) + diff --git a/testdata/script-tests/portion-syntax.num.specs.json b/testdata/script-tests/portion-syntax.num.specs.json new file mode 100644 index 00000000..f3ce4f62 --- /dev/null +++ b/testdata/script-tests/portion-syntax.num.specs.json @@ -0,0 +1,18 @@ +{ + "testCases": [ + { + "it": "-", + "variables": { + "por": "1/3" + }, + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 1, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/remaining-kept-in-send-all-inorder.num b/testdata/script-tests/remaining-kept-in-send-all-inorder.num new file mode 100644 index 00000000..fc0af8d4 --- /dev/null +++ b/testdata/script-tests/remaining-kept-in-send-all-inorder.num @@ -0,0 +1,7 @@ +send [COIN *] ( + source = @src + destination = { + max [COIN 1] to @dest + remaining kept + } +) diff --git a/testdata/script-tests/remaining-kept-in-send-all-inorder.num.specs.json b/testdata/script-tests/remaining-kept-in-send-all-inorder.num.specs.json new file mode 100644 index 00000000..b77df05a --- /dev/null +++ b/testdata/script-tests/remaining-kept-in-send-all-inorder.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 1000 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 1, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/remaining-kept-inorder.num b/testdata/script-tests/remaining-kept-inorder.num new file mode 100644 index 00000000..652fadd8 --- /dev/null +++ b/testdata/script-tests/remaining-kept-inorder.num @@ -0,0 +1,7 @@ +send [COIN 100] ( + source = @world + destination = { + max [COIN 1] to @a + remaining kept + } +) diff --git a/testdata/script-tests/remaining-kept-inorder.num.specs.json b/testdata/script-tests/remaining-kept-inorder.num.specs.json new file mode 100644 index 00000000..53d4216a --- /dev/null +++ b/testdata/script-tests/remaining-kept-inorder.num.specs.json @@ -0,0 +1,15 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 1, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/remaining-none-in-send-all.num b/testdata/script-tests/remaining-none-in-send-all.num new file mode 100644 index 00000000..e096037d --- /dev/null +++ b/testdata/script-tests/remaining-none-in-send-all.num @@ -0,0 +1,7 @@ +send [COIN *] ( + source = @src + destination = { + max [COIN 10] to @a + remaining to @b + } +) diff --git a/testdata/script-tests/remaining-none-in-send-all.num.specs.json b/testdata/script-tests/remaining-none-in-send-all.num.specs.json new file mode 100644 index 00000000..4b60aec5 --- /dev/null +++ b/testdata/script-tests/remaining-none-in-send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 10 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "a", + "amount": 10, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/remaining-none.num b/testdata/script-tests/remaining-none.num new file mode 100644 index 00000000..db3ca5bb --- /dev/null +++ b/testdata/script-tests/remaining-none.num @@ -0,0 +1,7 @@ +send [COIN 10] ( + source = @world + destination = { + max [COIN 10] to @a + remaining to @b + } +) diff --git a/testdata/script-tests/remaining-none.num.specs.json b/testdata/script-tests/remaining-none.num.specs.json new file mode 100644 index 00000000..65e8c02e --- /dev/null +++ b/testdata/script-tests/remaining-none.num.specs.json @@ -0,0 +1,15 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 10, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__multi-postings.num b/testdata/script-tests/save/save-from-account__multi-postings.num new file mode 100644 index 00000000..b25f8d84 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__multi-postings.num @@ -0,0 +1,15 @@ + + send [USD 10] ( + source = @alice + destination = @bob + ) + + save [USD 5] from @alice + + send [USD 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__multi-postings.num.specs.json b/testdata/script-tests/save/save-from-account__multi-postings.num.specs.json new file mode 100644 index 00000000..1882701e --- /dev/null +++ b/testdata/script-tests/save/save-from-account__multi-postings.num.specs.json @@ -0,0 +1,32 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 20 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "bob", + "amount": 10, + "asset": "USD" + }, + { + "source": "alice", + "destination": "bob", + "amount": 5, + "asset": "USD" + }, + { + "source": "world", + "destination": "bob", + "amount": 25, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__save-a-different-asset.num b/testdata/script-tests/save/save-from-account__save-a-different-asset.num new file mode 100644 index 00000000..b0611844 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-a-different-asset.num @@ -0,0 +1,10 @@ + + save [COIN 100] from @alice + + send [USD 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__save-a-different-asset.num.specs.json b/testdata/script-tests/save/save-from-account__save-a-different-asset.num.specs.json new file mode 100644 index 00000000..8adbde0f --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-a-different-asset.num.specs.json @@ -0,0 +1,27 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "COIN": 100, + "USD": 20 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "bob", + "amount": 20, + "asset": "USD" + }, + { + "source": "world", + "destination": "bob", + "amount": 10, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__save-all.num b/testdata/script-tests/save/save-from-account__save-all.num new file mode 100644 index 00000000..8e24fc1f --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-all.num @@ -0,0 +1,10 @@ + + save [USD *] from @alice + + send [USD 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__save-all.num.specs.json b/testdata/script-tests/save/save-from-account__save-all.num.specs.json new file mode 100644 index 00000000..15a5cc24 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 20 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "bob", + "amount": 30, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__save-causes-failure.num b/testdata/script-tests/save/save-from-account__save-causes-failure.num new file mode 100644 index 00000000..3b30737c --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-causes-failure.num @@ -0,0 +1,7 @@ + + save [USD/2 1] from @alice + + send [USD/2 30] ( + source = @alice + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__save-causes-failure.num.specs.json b/testdata/script-tests/save/save-from-account__save-causes-failure.num.specs.json new file mode 100644 index 00000000..00f4e4ab --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-causes-failure.num.specs.json @@ -0,0 +1,13 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD/2": 30 + } + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__save-more-than-balance.num b/testdata/script-tests/save/save-from-account__save-more-than-balance.num new file mode 100644 index 00000000..8bafcc7e --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-more-than-balance.num @@ -0,0 +1,10 @@ + + save [USD 30] from @alice + + send [USD 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__save-more-than-balance.num.specs.json b/testdata/script-tests/save/save-from-account__save-more-than-balance.num.specs.json new file mode 100644 index 00000000..15a5cc24 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__save-more-than-balance.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 20 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "bob", + "amount": 30, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__simple.num b/testdata/script-tests/save/save-from-account__simple.num new file mode 100644 index 00000000..659c87bb --- /dev/null +++ b/testdata/script-tests/save/save-from-account__simple.num @@ -0,0 +1,10 @@ + + save [USD 10] from @alice + + send [USD 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__simple.num.specs.json b/testdata/script-tests/save/save-from-account__simple.num.specs.json new file mode 100644 index 00000000..89dcf05e --- /dev/null +++ b/testdata/script-tests/save/save-from-account__simple.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 20 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "bob", + "amount": 10, + "asset": "USD" + }, + { + "source": "world", + "destination": "bob", + "amount": 20, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__with-asset-var.num b/testdata/script-tests/save/save-from-account__with-asset-var.num new file mode 100644 index 00000000..5c3c4b17 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__with-asset-var.num @@ -0,0 +1,13 @@ + + vars { + asset $ass + } + save [$ass 10] from @alice + + send [$ass 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__with-asset-var.num.specs.json b/testdata/script-tests/save/save-from-account__with-asset-var.num.specs.json new file mode 100644 index 00000000..035c7709 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__with-asset-var.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 20 + } + }, + "variables": { + "ass": "USD" + }, + "expect.postings": [ + { + "source": "alice", + "destination": "bob", + "amount": 10, + "asset": "USD" + }, + { + "source": "world", + "destination": "bob", + "amount": 20, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/save/save-from-account__with-monetary-var.num b/testdata/script-tests/save/save-from-account__with-monetary-var.num new file mode 100644 index 00000000..d257841a --- /dev/null +++ b/testdata/script-tests/save/save-from-account__with-monetary-var.num @@ -0,0 +1,14 @@ + + vars { + monetary $mon + } + + save $mon from @alice + + send [USD 30] ( + source = { + @alice + @world + } + destination = @bob + ) diff --git a/testdata/script-tests/save/save-from-account__with-monetary-var.num.specs.json b/testdata/script-tests/save/save-from-account__with-monetary-var.num.specs.json new file mode 100644 index 00000000..697b1729 --- /dev/null +++ b/testdata/script-tests/save/save-from-account__with-monetary-var.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 20 + } + }, + "variables": { + "mon": "USD 10" + }, + "expect.postings": [ + { + "source": "alice", + "destination": "bob", + "amount": 10, + "asset": "USD" + }, + { + "source": "world", + "destination": "bob", + "amount": 20, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-destinatio-allot-complex.num b/testdata/script-tests/send-all-destinatio-allot-complex.num new file mode 100644 index 00000000..17ade029 --- /dev/null +++ b/testdata/script-tests/send-all-destinatio-allot-complex.num @@ -0,0 +1,10 @@ +send [USD/2 *] ( + source = { + @users:001 + @users:002 + } + destination = { + 1/3 to @d1 + 2/3 to @d2 + } +) diff --git a/testdata/script-tests/send-all-destinatio-allot-complex.num.specs.json b/testdata/script-tests/send-all-destinatio-allot-complex.num.specs.json new file mode 100644 index 00000000..a44dc64e --- /dev/null +++ b/testdata/script-tests/send-all-destinatio-allot-complex.num.specs.json @@ -0,0 +1,35 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "USD/2": 15 + }, + "users:002": { + "USD/2": 15 + } + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "d1", + "amount": 10, + "asset": "USD/2" + }, + { + "source": "users:001", + "destination": "d2", + "amount": 5, + "asset": "USD/2" + }, + { + "source": "users:002", + "destination": "d2", + "amount": 15, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-destinatio-allot.num b/testdata/script-tests/send-all-destinatio-allot.num new file mode 100644 index 00000000..5a463c6d --- /dev/null +++ b/testdata/script-tests/send-all-destinatio-allot.num @@ -0,0 +1,7 @@ +send [USD/2 *] ( + source = @users:001 + destination = { + 1/3 to @d1 + 2/3 to @d2 + } +) diff --git a/testdata/script-tests/send-all-destinatio-allot.num.specs.json b/testdata/script-tests/send-all-destinatio-allot.num.specs.json new file mode 100644 index 00000000..3db9ef2f --- /dev/null +++ b/testdata/script-tests/send-all-destinatio-allot.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "USD/2": 30 + } + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "d1", + "amount": 10, + "asset": "USD/2" + }, + { + "source": "users:001", + "destination": "d2", + "amount": 20, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-many-max-in-dest.num b/testdata/script-tests/send-all-many-max-in-dest.num new file mode 100644 index 00000000..e80be743 --- /dev/null +++ b/testdata/script-tests/send-all-many-max-in-dest.num @@ -0,0 +1,9 @@ +send [USD/2 *] ( + source = @src + destination = { + max [USD/2 10] to @d1 + max [USD/2 20] to @d2 + remaining to @d3 + } +) + diff --git a/testdata/script-tests/send-all-many-max-in-dest.num.specs.json b/testdata/script-tests/send-all-many-max-in-dest.num.specs.json new file mode 100644 index 00000000..b7be78cb --- /dev/null +++ b/testdata/script-tests/send-all-many-max-in-dest.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 15 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "d1", + "amount": 10, + "asset": "USD/2" + }, + { + "source": "src", + "destination": "d2", + "amount": 5, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-multi.num b/testdata/script-tests/send-all-multi.num new file mode 100644 index 00000000..0046d50f --- /dev/null +++ b/testdata/script-tests/send-all-multi.num @@ -0,0 +1,8 @@ +send [USD/2 *] ( + source = { + @users:001:wallet + @users:001:credit + } + destination = @platform +) + diff --git a/testdata/script-tests/send-all-multi.num.specs.json b/testdata/script-tests/send-all-multi.num.specs.json new file mode 100644 index 00000000..b30bee30 --- /dev/null +++ b/testdata/script-tests/send-all-multi.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001:credit": { + "USD/2": 22 + }, + "users:001:wallet": { + "USD/2": 19 + } + }, + "expect.postings": [ + { + "source": "users:001:wallet", + "destination": "platform", + "amount": 19, + "asset": "USD/2" + }, + { + "source": "users:001:credit", + "destination": "platform", + "amount": 22, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-variable.num b/testdata/script-tests/send-all-variable.num new file mode 100644 index 00000000..ef8f28d4 --- /dev/null +++ b/testdata/script-tests/send-all-variable.num @@ -0,0 +1,10 @@ + +vars { + account $src + account $dest +} + +send [USD/2 *] ( + source = $src + destination = $dest +) diff --git a/testdata/script-tests/send-all-variable.num.specs.json b/testdata/script-tests/send-all-variable.num.specs.json new file mode 100644 index 00000000..4ab2bdf6 --- /dev/null +++ b/testdata/script-tests/send-all-variable.num.specs.json @@ -0,0 +1,24 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "USD/2": 17 + } + }, + "variables": { + "dest": "platform", + "src": "users:001" + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "platform", + "amount": 17, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-when-negative-with-overdraft.num b/testdata/script-tests/send-all-when-negative-with-overdraft.num new file mode 100644 index 00000000..410a82d6 --- /dev/null +++ b/testdata/script-tests/send-all-when-negative-with-overdraft.num @@ -0,0 +1,6 @@ + +send [USD/2 *] ( + source = @users:001 allowing overdraft up to [USD/2 150] + destination = @platform +) + diff --git a/testdata/script-tests/send-all-when-negative-with-overdraft.num.specs.json b/testdata/script-tests/send-all-when-negative-with-overdraft.num.specs.json new file mode 100644 index 00000000..86428b45 --- /dev/null +++ b/testdata/script-tests/send-all-when-negative-with-overdraft.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "USD/2": -100 + } + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "platform", + "amount": 50, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-all-when-negative.num b/testdata/script-tests/send-all-when-negative.num new file mode 100644 index 00000000..05f4f49f --- /dev/null +++ b/testdata/script-tests/send-all-when-negative.num @@ -0,0 +1,6 @@ + +send [USD/2 *] ( + source = @users:001 + destination = @platform +) + diff --git a/testdata/script-tests/send-all-when-negative.num.specs.json b/testdata/script-tests/send-all-when-negative.num.specs.json new file mode 100644 index 00000000..53012c16 --- /dev/null +++ b/testdata/script-tests/send-all-when-negative.num.specs.json @@ -0,0 +1,12 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "USD/2": -100 + } + } + } + ] +} diff --git a/testdata/script-tests/send-all.num b/testdata/script-tests/send-all.num new file mode 100644 index 00000000..5697e1fd --- /dev/null +++ b/testdata/script-tests/send-all.num @@ -0,0 +1,4 @@ +send [USD/2 *] ( + source = @users:001 + destination = @platform +) diff --git a/testdata/script-tests/send-all.num.specs.json b/testdata/script-tests/send-all.num.specs.json new file mode 100644 index 00000000..72b5d1d7 --- /dev/null +++ b/testdata/script-tests/send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "USD/2": 17 + } + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "platform", + "amount": 17, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-allt-max-in-dest.num b/testdata/script-tests/send-allt-max-in-dest.num new file mode 100644 index 00000000..6802ca35 --- /dev/null +++ b/testdata/script-tests/send-allt-max-in-dest.num @@ -0,0 +1,8 @@ +send [USD/2 *] ( + source = @src + destination = { + max [USD/2 10] to @d1 + remaining to @d2 + } +) + diff --git a/testdata/script-tests/send-allt-max-in-dest.num.specs.json b/testdata/script-tests/send-allt-max-in-dest.num.specs.json new file mode 100644 index 00000000..c7ac32a1 --- /dev/null +++ b/testdata/script-tests/send-allt-max-in-dest.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "USD/2": 100 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "d1", + "amount": 10, + "asset": "USD/2" + }, + { + "source": "src", + "destination": "d2", + "amount": 90, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-allt-max-in-src.num b/testdata/script-tests/send-allt-max-in-src.num new file mode 100644 index 00000000..5750e0bb --- /dev/null +++ b/testdata/script-tests/send-allt-max-in-src.num @@ -0,0 +1,8 @@ +send [USD/2 *] ( + source = { + max [USD/2 5] from @src1 + @src2 + } + destination = @dest +) + diff --git a/testdata/script-tests/send-allt-max-in-src.num.specs.json b/testdata/script-tests/send-allt-max-in-src.num.specs.json new file mode 100644 index 00000000..50090e85 --- /dev/null +++ b/testdata/script-tests/send-allt-max-in-src.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src1": { + "USD/2": 100 + }, + "src2": { + "USD/2": 200 + } + }, + "expect.postings": [ + { + "source": "src1", + "destination": "dest", + "amount": 5, + "asset": "USD/2" + }, + { + "source": "src2", + "destination": "dest", + "amount": 200, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-allt-max-when-no-amount.num b/testdata/script-tests/send-allt-max-when-no-amount.num new file mode 100644 index 00000000..1b798dc2 --- /dev/null +++ b/testdata/script-tests/send-allt-max-when-no-amount.num @@ -0,0 +1,5 @@ +send [USD/2 *] ( + source = max [USD/2 5] from @src + destination = @dest +) + diff --git a/testdata/script-tests/send-allt-max-when-no-amount.num.specs.json b/testdata/script-tests/send-allt-max-when-no-amount.num.specs.json new file mode 100644 index 00000000..69f16d57 --- /dev/null +++ b/testdata/script-tests/send-allt-max-when-no-amount.num.specs.json @@ -0,0 +1,13 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": {}, + "src1": { + "USD/2": 0 + } + } + } + ] +} diff --git a/testdata/script-tests/send-when-negative-balance.num b/testdata/script-tests/send-when-negative-balance.num new file mode 100644 index 00000000..9473fd8a --- /dev/null +++ b/testdata/script-tests/send-when-negative-balance.num @@ -0,0 +1,9 @@ + +send [COIN 10] ( + source = { + @s + @world + } + destination = @dest +) + diff --git a/testdata/script-tests/send-when-negative-balance.num.specs.json b/testdata/script-tests/send-when-negative-balance.num.specs.json new file mode 100644 index 00000000..847f6882 --- /dev/null +++ b/testdata/script-tests/send-when-negative-balance.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "s": { + "COIN": -5 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 10, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/send-zero.num b/testdata/script-tests/send-zero.num new file mode 100644 index 00000000..3e366808 --- /dev/null +++ b/testdata/script-tests/send-zero.num @@ -0,0 +1,5 @@ + +send [COIN 0] ( + source = @src + destination = @dest +) diff --git a/testdata/script-tests/send-zero.num.specs.json b/testdata/script-tests/send-zero.num.specs.json new file mode 100644 index 00000000..e16eacd8 --- /dev/null +++ b/testdata/script-tests/send-zero.num.specs.json @@ -0,0 +1,10 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": {} + } + } + ] +} diff --git a/testdata/script-tests/send.num b/testdata/script-tests/send.num new file mode 100644 index 00000000..7c568565 --- /dev/null +++ b/testdata/script-tests/send.num @@ -0,0 +1,4 @@ +send [EUR/2 100] ( + source=@alice + destination=@bob +) diff --git a/testdata/script-tests/send.num.specs.json b/testdata/script-tests/send.num.specs.json new file mode 100644 index 00000000..c58a4fc7 --- /dev/null +++ b/testdata/script-tests/send.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "EUR/2": 100 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "bob", + "amount": 100, + "asset": "EUR/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/set-account-meta.num b/testdata/script-tests/set-account-meta.num new file mode 100644 index 00000000..f636aedc --- /dev/null +++ b/testdata/script-tests/set-account-meta.num @@ -0,0 +1,8 @@ + + set_account_meta(@acc, "num", 42) + set_account_meta(@acc, "str", "abc") + set_account_meta(@acc, "asset", COIN) + set_account_meta(@acc, "account", @acc) + set_account_meta(@acc, "portion", 2/7) + set_account_meta(@acc, "portion-perc", 1%) + diff --git a/testdata/script-tests/set-account-meta.num.specs.json b/testdata/script-tests/set-account-meta.num.specs.json new file mode 100644 index 00000000..5fa713ab --- /dev/null +++ b/testdata/script-tests/set-account-meta.num.specs.json @@ -0,0 +1,17 @@ +{ + "testCases": [ + { + "it": "-", + "expect.metadata": { + "acc": { + "account": "acc", + "asset": "COIN", + "num": "42", + "portion": "2/7", + "portion-perc": "1/100", + "str": "abc" + } + } + } + ] +} diff --git a/testdata/script-tests/set-tx-meta.num b/testdata/script-tests/set-tx-meta.num new file mode 100644 index 00000000..1d58404a --- /dev/null +++ b/testdata/script-tests/set-tx-meta.num @@ -0,0 +1,7 @@ + +set_tx_meta("num", 42) +set_tx_meta("str", "abc") +set_tx_meta("asset", COIN) +set_tx_meta("account", @acc) +set_tx_meta("portion", 12%) + diff --git a/testdata/script-tests/set-tx-meta.num.specs.json b/testdata/script-tests/set-tx-meta.num.specs.json new file mode 100644 index 00000000..4730b6d8 --- /dev/null +++ b/testdata/script-tests/set-tx-meta.num.specs.json @@ -0,0 +1,14 @@ +{ + "testCases": [ + { + "it": "-", + "expect.txMetadata": { + "account": "acc", + "asset": "COIN", + "num": "42", + "portion": "3/25", + "str": "abc" + } + } + ] +} diff --git a/testdata/script-tests/source-allotment-invalid-amt.num b/testdata/script-tests/source-allotment-invalid-amt.num new file mode 100644 index 00000000..1216ee54 --- /dev/null +++ b/testdata/script-tests/source-allotment-invalid-amt.num @@ -0,0 +1,10 @@ +send [COIN 100] ( + source = { + // a doesn't have enough amount + 10% from @a + + // world has, but the computation has already failed + remaining from @world + } + destination = @d +) diff --git a/testdata/script-tests/source-allotment-invalid-amt.num.specs.json b/testdata/script-tests/source-allotment-invalid-amt.num.specs.json new file mode 100644 index 00000000..42603ca8 --- /dev/null +++ b/testdata/script-tests/source-allotment-invalid-amt.num.specs.json @@ -0,0 +1,13 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 1 + } + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/source-allotment.num b/testdata/script-tests/source-allotment.num new file mode 100644 index 00000000..eb918fcb --- /dev/null +++ b/testdata/script-tests/source-allotment.num @@ -0,0 +1,8 @@ +send [COIN 100] ( + source = { + 60% from @a + 35.5% from @b + 4.5% from @c + } + destination = @d +) diff --git a/testdata/script-tests/source-allotment.num.specs.json b/testdata/script-tests/source-allotment.num.specs.json new file mode 100644 index 00000000..d8c2cd01 --- /dev/null +++ b/testdata/script-tests/source-allotment.num.specs.json @@ -0,0 +1,38 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 100 + }, + "b": { + "COIN": 100 + }, + "c": { + "COIN": 100 + } + }, + "expect.postings": [ + { + "source": "a", + "destination": "d", + "amount": 61, + "asset": "COIN" + }, + { + "source": "b", + "destination": "d", + "amount": 35, + "asset": "COIN" + }, + { + "source": "c", + "destination": "d", + "amount": 4, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/source-complex.num b/testdata/script-tests/source-complex.num new file mode 100644 index 00000000..1a3005a2 --- /dev/null +++ b/testdata/script-tests/source-complex.num @@ -0,0 +1,14 @@ +vars { + monetary $max +} +send [COIN 200] ( + source = { + 50% from { + max [COIN 4] from @a + @b + @c + } + remaining from max $max from @d + } + destination = @platform +) diff --git a/testdata/script-tests/source-complex.num.specs.json b/testdata/script-tests/source-complex.num.specs.json new file mode 100644 index 00000000..02642dca --- /dev/null +++ b/testdata/script-tests/source-complex.num.specs.json @@ -0,0 +1,50 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 1000 + }, + "b": { + "COIN": 40 + }, + "c": { + "COIN": 1000 + }, + "d": { + "COIN": 1000 + } + }, + "variables": { + "max": "COIN 120" + }, + "expect.postings": [ + { + "source": "a", + "destination": "platform", + "amount": 4, + "asset": "COIN" + }, + { + "source": "b", + "destination": "platform", + "amount": 40, + "asset": "COIN" + }, + { + "source": "c", + "destination": "platform", + "amount": 56, + "asset": "COIN" + }, + { + "source": "d", + "destination": "platform", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/source-overlapping.num b/testdata/script-tests/source-overlapping.num new file mode 100644 index 00000000..77053563 --- /dev/null +++ b/testdata/script-tests/source-overlapping.num @@ -0,0 +1,11 @@ +send [COIN 99] ( + source = { + 15% from { + @b + @a + } + 30% from @a + remaining from @a + } + destination = @world +) diff --git a/testdata/script-tests/source-overlapping.num.specs.json b/testdata/script-tests/source-overlapping.num.specs.json new file mode 100644 index 00000000..6e4830d1 --- /dev/null +++ b/testdata/script-tests/source-overlapping.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 99 + }, + "b": { + "COIN": 3 + } + }, + "expect.postings": [ + { + "source": "b", + "destination": "world", + "amount": 3, + "asset": "COIN" + }, + { + "source": "a", + "destination": "world", + "amount": 96, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/source.num b/testdata/script-tests/source.num new file mode 100644 index 00000000..4c14091f --- /dev/null +++ b/testdata/script-tests/source.num @@ -0,0 +1,12 @@ +vars { + account $balance + account $payment + account $seller +} +send [GEM 15] ( + source = { + $balance + $payment + } + destination = $seller +) diff --git a/testdata/script-tests/source.num.specs.json b/testdata/script-tests/source.num.specs.json new file mode 100644 index 00000000..011726b8 --- /dev/null +++ b/testdata/script-tests/source.num.specs.json @@ -0,0 +1,34 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "payments:001": { + "GEM": 12 + }, + "users:001": { + "GEM": 3 + } + }, + "variables": { + "balance": "users:001", + "payment": "payments:001", + "seller": "users:002" + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "users:002", + "amount": 3, + "asset": "GEM" + }, + { + "source": "payments:001", + "destination": "users:002", + "amount": 12, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/sub-monetaries.num b/testdata/script-tests/sub-monetaries.num new file mode 100644 index 00000000..0b2190ca --- /dev/null +++ b/testdata/script-tests/sub-monetaries.num @@ -0,0 +1,3 @@ + + set_tx_meta("k", [USD/2 10] - [USD/2 3]) + diff --git a/testdata/script-tests/sub-monetaries.num.specs.json b/testdata/script-tests/sub-monetaries.num.specs.json new file mode 100644 index 00000000..8dcb6396 --- /dev/null +++ b/testdata/script-tests/sub-monetaries.num.specs.json @@ -0,0 +1,10 @@ +{ + "testCases": [ + { + "it": "-", + "expect.txMetadata": { + "k": "USD/2 7" + } + } + ] +} diff --git a/testdata/script-tests/sub-numbers.num b/testdata/script-tests/sub-numbers.num new file mode 100644 index 00000000..709b9780 --- /dev/null +++ b/testdata/script-tests/sub-numbers.num @@ -0,0 +1,3 @@ + + set_tx_meta("k", 10 - 1) + diff --git a/testdata/script-tests/sub-numbers.num.specs.json b/testdata/script-tests/sub-numbers.num.specs.json new file mode 100644 index 00000000..1bdb2e49 --- /dev/null +++ b/testdata/script-tests/sub-numbers.num.specs.json @@ -0,0 +1,10 @@ +{ + "testCases": [ + { + "it": "-", + "expect.txMetadata": { + "k": "9" + } + } + ] +} diff --git a/testdata/script-tests/track-balances-send-all.num b/testdata/script-tests/track-balances-send-all.num new file mode 100644 index 00000000..98483259 --- /dev/null +++ b/testdata/script-tests/track-balances-send-all.num @@ -0,0 +1,9 @@ + +send [COIN *] ( + source = @src + destination = @dest1 +) +send [COIN *] ( + source = @src + destination = @dest2 +) diff --git a/testdata/script-tests/track-balances-send-all.num.specs.json b/testdata/script-tests/track-balances-send-all.num.specs.json new file mode 100644 index 00000000..388bed24 --- /dev/null +++ b/testdata/script-tests/track-balances-send-all.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 42 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest1", + "amount": 42, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/track-balances.num b/testdata/script-tests/track-balances.num new file mode 100644 index 00000000..2530551d --- /dev/null +++ b/testdata/script-tests/track-balances.num @@ -0,0 +1,9 @@ + +send [COIN 50] ( + source = @world + destination = @a +) +send [COIN 100] ( + source = @a + destination = @b +) diff --git a/testdata/script-tests/track-balances.num.specs.json b/testdata/script-tests/track-balances.num.specs.json new file mode 100644 index 00000000..46bd9309 --- /dev/null +++ b/testdata/script-tests/track-balances.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 50 + } + }, + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 50, + "asset": "COIN" + }, + { + "source": "a", + "destination": "b", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/track-balances2.num b/testdata/script-tests/track-balances2.num new file mode 100644 index 00000000..0f31cb82 --- /dev/null +++ b/testdata/script-tests/track-balances2.num @@ -0,0 +1,9 @@ + +send [COIN 50] ( + source = @a + destination = @z +) +send [COIN 50] ( + source = @a + destination = @z +) diff --git a/testdata/script-tests/track-balances2.num.specs.json b/testdata/script-tests/track-balances2.num.specs.json new file mode 100644 index 00000000..9a740b96 --- /dev/null +++ b/testdata/script-tests/track-balances2.num.specs.json @@ -0,0 +1,13 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 60 + } + }, + "expect.missingFunds": true + } + ] +} diff --git a/testdata/script-tests/track-balances3.num b/testdata/script-tests/track-balances3.num new file mode 100644 index 00000000..21827dc9 --- /dev/null +++ b/testdata/script-tests/track-balances3.num @@ -0,0 +1,11 @@ +send [COIN *] ( + source = @foo + destination = { + max [COIN 1000] to @bar + remaining kept + } +) +send [COIN *] ( + source = @foo + destination = @bar +) diff --git a/testdata/script-tests/track-balances3.num.specs.json b/testdata/script-tests/track-balances3.num.specs.json new file mode 100644 index 00000000..ec9ec158 --- /dev/null +++ b/testdata/script-tests/track-balances3.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "foo": { + "COIN": 2000 + } + }, + "expect.postings": [ + { + "source": "foo", + "destination": "bar", + "amount": 1000, + "asset": "COIN" + }, + { + "source": "foo", + "destination": "bar", + "amount": 1000, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/unbounded-overdraft-when-not-enough-funds.num b/testdata/script-tests/unbounded-overdraft-when-not-enough-funds.num new file mode 100644 index 00000000..d0c527ab --- /dev/null +++ b/testdata/script-tests/unbounded-overdraft-when-not-enough-funds.num @@ -0,0 +1,6 @@ + +send [USD/2 100] ( + source = @empty allowing unbounded overdraft + destination = @dest +) + diff --git a/testdata/script-tests/unbounded-overdraft-when-not-enough-funds.num.specs.json b/testdata/script-tests/unbounded-overdraft-when-not-enough-funds.num.specs.json new file mode 100644 index 00000000..2429a124 --- /dev/null +++ b/testdata/script-tests/unbounded-overdraft-when-not-enough-funds.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:2345:main": { + "USD/2": 8000 + } + }, + "expect.postings": [ + { + "source": "empty", + "destination": "dest", + "amount": 100, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/update-balances.num b/testdata/script-tests/update-balances.num new file mode 100644 index 00000000..ae7b102b --- /dev/null +++ b/testdata/script-tests/update-balances.num @@ -0,0 +1,11 @@ + +// @alice balance is 100 initially +send [USD 200] ( +source = { + @alice + @alice + @world +} +destination = @dest +) + diff --git a/testdata/script-tests/update-balances.num.specs.json b/testdata/script-tests/update-balances.num.specs.json new file mode 100644 index 00000000..d02b3426 --- /dev/null +++ b/testdata/script-tests/update-balances.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 100 + } + }, + "expect.postings": [ + { + "source": "alice", + "destination": "dest", + "amount": 100, + "asset": "USD" + }, + { + "source": "world", + "destination": "dest", + "amount": 100, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/use-balance-twice.num b/testdata/script-tests/use-balance-twice.num new file mode 100644 index 00000000..b30491f3 --- /dev/null +++ b/testdata/script-tests/use-balance-twice.num @@ -0,0 +1,7 @@ + +vars { monetary $v = balance(@src, COIN) } + +send $v ( + source = @src + destination = @dest +) diff --git a/testdata/script-tests/use-balance-twice.num.specs.json b/testdata/script-tests/use-balance-twice.num.specs.json new file mode 100644 index 00000000..8481ec76 --- /dev/null +++ b/testdata/script-tests/use-balance-twice.num.specs.json @@ -0,0 +1,20 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "src": { + "COIN": 50 + } + }, + "expect.postings": [ + { + "source": "src", + "destination": "dest", + "amount": 50, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/use-different-assets-with-same-source-account.num b/testdata/script-tests/use-different-assets-with-same-source-account.num new file mode 100644 index 00000000..35baf9ed --- /dev/null +++ b/testdata/script-tests/use-different-assets-with-same-source-account.num @@ -0,0 +1,11 @@ +vars { +account $a_account +} +send [A 100] ( +source = $a_account allowing unbounded overdraft +destination = @account1 +) +send [B 100] ( +source = @world +destination = @account2 +) diff --git a/testdata/script-tests/use-different-assets-with-same-source-account.num.specs.json b/testdata/script-tests/use-different-assets-with-same-source-account.num.specs.json new file mode 100644 index 00000000..c14650e6 --- /dev/null +++ b/testdata/script-tests/use-different-assets-with-same-source-account.num.specs.json @@ -0,0 +1,32 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "account1": { + "A": 100 + }, + "account2": { + "B": 100 + } + }, + "variables": { + "a_account": "world" + }, + "expect.postings": [ + { + "source": "world", + "destination": "account1", + "amount": 100, + "asset": "A" + }, + { + "source": "world", + "destination": "account2", + "amount": 100, + "asset": "B" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-asset.num b/testdata/script-tests/variable-asset.num new file mode 100644 index 00000000..b964205a --- /dev/null +++ b/testdata/script-tests/variable-asset.num @@ -0,0 +1,21 @@ + + vars { + asset $ass + monetary $bal = balance(@alice, $ass) + } + + send [$ass 15] ( + source = { + @alice + @bob + } + destination = @swap + ) + + send [$ass *] ( + source = @swap + destination = { + max $bal to @alice_2 + remaining to @bob_2 + } + ) diff --git a/testdata/script-tests/variable-asset.num.specs.json b/testdata/script-tests/variable-asset.num.specs.json new file mode 100644 index 00000000..36028aa3 --- /dev/null +++ b/testdata/script-tests/variable-asset.num.specs.json @@ -0,0 +1,45 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "alice": { + "USD": 10 + }, + "bob": { + "USD": 10 + }, + "swap": {} + }, + "variables": { + "ass": "USD" + }, + "expect.postings": [ + { + "source": "alice", + "destination": "swap", + "amount": 10, + "asset": "USD" + }, + { + "source": "bob", + "destination": "swap", + "amount": 5, + "asset": "USD" + }, + { + "source": "swap", + "destination": "alice_2", + "amount": 10, + "asset": "USD" + }, + { + "source": "swap", + "destination": "bob_2", + "amount": 5, + "asset": "USD" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-balance__1.num b/testdata/script-tests/variable-balance__1.num new file mode 100644 index 00000000..6b5e0c59 --- /dev/null +++ b/testdata/script-tests/variable-balance__1.num @@ -0,0 +1,14 @@ + + vars { + monetary $initial = balance(@A, USD/2) + } + send [USD/2 100] ( + source = { + @A + @C + } + destination = { + max $initial to @B + remaining to @D + } + ) diff --git a/testdata/script-tests/variable-balance__1.num.specs.json b/testdata/script-tests/variable-balance__1.num.specs.json new file mode 100644 index 00000000..835adb53 --- /dev/null +++ b/testdata/script-tests/variable-balance__1.num.specs.json @@ -0,0 +1,29 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "A": { + "USD/2": 40 + }, + "C": { + "USD/2": 90 + } + }, + "expect.postings": [ + { + "source": "A", + "destination": "B", + "amount": 40, + "asset": "USD/2" + }, + { + "source": "C", + "destination": "D", + "amount": 60, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-balance__2.num b/testdata/script-tests/variable-balance__2.num new file mode 100644 index 00000000..6b5e0c59 --- /dev/null +++ b/testdata/script-tests/variable-balance__2.num @@ -0,0 +1,14 @@ + + vars { + monetary $initial = balance(@A, USD/2) + } + send [USD/2 100] ( + source = { + @A + @C + } + destination = { + max $initial to @B + remaining to @D + } + ) diff --git a/testdata/script-tests/variable-balance__2.num.specs.json b/testdata/script-tests/variable-balance__2.num.specs.json new file mode 100644 index 00000000..99695ecb --- /dev/null +++ b/testdata/script-tests/variable-balance__2.num.specs.json @@ -0,0 +1,23 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "A": { + "USD/2": 400 + }, + "C": { + "USD/2": 90 + } + }, + "expect.postings": [ + { + "source": "A", + "destination": "B", + "amount": 100, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-balance__3.num b/testdata/script-tests/variable-balance__3.num new file mode 100644 index 00000000..45a415fe --- /dev/null +++ b/testdata/script-tests/variable-balance__3.num @@ -0,0 +1,15 @@ + + vars { + account $acc + monetary $initial = balance($acc, USD/2) + } + send [USD/2 100] ( + source = { + $acc + @C + } + destination = { + max $initial to @B + remaining to @D + } + ) diff --git a/testdata/script-tests/variable-balance__3.num.specs.json b/testdata/script-tests/variable-balance__3.num.specs.json new file mode 100644 index 00000000..ec5a6f04 --- /dev/null +++ b/testdata/script-tests/variable-balance__3.num.specs.json @@ -0,0 +1,32 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "A": { + "USD/2": 40 + }, + "C": { + "USD/2": 90 + } + }, + "variables": { + "acc": "A" + }, + "expect.postings": [ + { + "source": "A", + "destination": "B", + "amount": 40, + "asset": "USD/2" + }, + { + "source": "C", + "destination": "D", + "amount": 60, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-balance__4.num b/testdata/script-tests/variable-balance__4.num new file mode 100644 index 00000000..45a415fe --- /dev/null +++ b/testdata/script-tests/variable-balance__4.num @@ -0,0 +1,15 @@ + + vars { + account $acc + monetary $initial = balance($acc, USD/2) + } + send [USD/2 100] ( + source = { + $acc + @C + } + destination = { + max $initial to @B + remaining to @D + } + ) diff --git a/testdata/script-tests/variable-balance__4.num.specs.json b/testdata/script-tests/variable-balance__4.num.specs.json new file mode 100644 index 00000000..308be728 --- /dev/null +++ b/testdata/script-tests/variable-balance__4.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "A": { + "USD/2": 400 + }, + "C": { + "USD/2": 90 + } + }, + "variables": { + "acc": "A" + }, + "expect.postings": [ + { + "source": "A", + "destination": "B", + "amount": 100, + "asset": "USD/2" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-balance__5.num b/testdata/script-tests/variable-balance__5.num new file mode 100644 index 00000000..0cdbd000 --- /dev/null +++ b/testdata/script-tests/variable-balance__5.num @@ -0,0 +1,15 @@ + + vars { + monetary $max = balance(@maxAcc, COIN) + } + send [COIN 200] ( + source = { + 50% from { + max [COIN 4] from @a + @b + @c + } + remaining from max $max from @d + } + destination = @platform + ) diff --git a/testdata/script-tests/variable-balance__5.num.specs.json b/testdata/script-tests/variable-balance__5.num.specs.json new file mode 100644 index 00000000..9170724f --- /dev/null +++ b/testdata/script-tests/variable-balance__5.num.specs.json @@ -0,0 +1,50 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "COIN": 1000 + }, + "b": { + "COIN": 40 + }, + "c": { + "COIN": 1000 + }, + "d": { + "COIN": 1000 + }, + "maxAcc": { + "COIN": 120 + } + }, + "expect.postings": [ + { + "source": "a", + "destination": "platform", + "amount": 4, + "asset": "COIN" + }, + { + "source": "b", + "destination": "platform", + "amount": 40, + "asset": "COIN" + }, + { + "source": "c", + "destination": "platform", + "amount": 56, + "asset": "COIN" + }, + { + "source": "d", + "destination": "platform", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/variable-portion-part.num b/testdata/script-tests/variable-portion-part.num new file mode 100644 index 00000000..56ea4cca --- /dev/null +++ b/testdata/script-tests/variable-portion-part.num @@ -0,0 +1,13 @@ + +vars { + number $num + number $den +} + +send [COIN 9] ( + source = @world + destination = { + $num/3 to @a // 1/3 + 2/$den to @b // 2/3 + } +) diff --git a/testdata/script-tests/variable-portion-part.num.specs.json b/testdata/script-tests/variable-portion-part.num.specs.json new file mode 100644 index 00000000..f39cee3e --- /dev/null +++ b/testdata/script-tests/variable-portion-part.num.specs.json @@ -0,0 +1,25 @@ +{ + "testCases": [ + { + "it": "-", + "variables": { + "den": "3", + "num": "1" + }, + "expect.postings": [ + { + "source": "world", + "destination": "a", + "amount": 3, + "asset": "COIN" + }, + { + "source": "world", + "destination": "b", + "amount": 6, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/variables-json.num b/testdata/script-tests/variables-json.num new file mode 100644 index 00000000..79a5c1a0 --- /dev/null +++ b/testdata/script-tests/variables-json.num @@ -0,0 +1,15 @@ +vars { + account $rider + account $driver + string $description + number $nb + asset $ass + portion $por +} +send [$ass 999] ( + source=$rider + destination=$driver +) +set_tx_meta("description", $description) +set_tx_meta("ride", $nb) +set_tx_meta("por", $por) diff --git a/testdata/script-tests/variables-json.num.specs.json b/testdata/script-tests/variables-json.num.specs.json new file mode 100644 index 00000000..54900447 --- /dev/null +++ b/testdata/script-tests/variables-json.num.specs.json @@ -0,0 +1,33 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "EUR/2": 1000 + } + }, + "variables": { + "ass": "EUR/2", + "description": "midnight ride", + "driver": "users:002", + "nb": "1", + "por": "42%", + "rider": "users:001" + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "users:002", + "amount": 999, + "asset": "EUR/2" + } + ], + "expect.txMetadata": { + "description": "midnight ride", + "por": "21/50", + "ride": "1" + } + } + ] +} diff --git a/testdata/script-tests/variables.num b/testdata/script-tests/variables.num new file mode 100644 index 00000000..ac501702 --- /dev/null +++ b/testdata/script-tests/variables.num @@ -0,0 +1,13 @@ +vars { + account $rider + account $driver + string $description + number $nb + asset $ass +} +send [$ass 999] ( + source=$rider + destination=$driver +) + set_tx_meta("description", $description) + set_tx_meta("ride", $nb) diff --git a/testdata/script-tests/variables.num.specs.json b/testdata/script-tests/variables.num.specs.json new file mode 100644 index 00000000..6705d59b --- /dev/null +++ b/testdata/script-tests/variables.num.specs.json @@ -0,0 +1,31 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "users:001": { + "EUR/2": 1000 + } + }, + "variables": { + "ass": "EUR/2", + "description": "midnight ride", + "driver": "users:002", + "nb": "1", + "rider": "users:001" + }, + "expect.postings": [ + { + "source": "users:001", + "destination": "users:002", + "amount": 999, + "asset": "EUR/2" + } + ], + "expect.txMetadata": { + "description": "midnight ride", + "ride": "1" + } + } + ] +} diff --git a/testdata/script-tests/world-source.num b/testdata/script-tests/world-source.num new file mode 100644 index 00000000..f0d37c09 --- /dev/null +++ b/testdata/script-tests/world-source.num @@ -0,0 +1,7 @@ +send [GEM 15] ( + source = { + @a + @world + } + destination = @b +) diff --git a/testdata/script-tests/world-source.num.specs.json b/testdata/script-tests/world-source.num.specs.json new file mode 100644 index 00000000..551e59f9 --- /dev/null +++ b/testdata/script-tests/world-source.num.specs.json @@ -0,0 +1,26 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": { + "GEM": 1 + } + }, + "expect.postings": [ + { + "source": "a", + "destination": "b", + "amount": 1, + "asset": "GEM" + }, + { + "source": "world", + "destination": "b", + "amount": 14, + "asset": "GEM" + } + ] + } + ] +} diff --git a/testdata/script-tests/zero-postings-destination.num b/testdata/script-tests/zero-postings-destination.num new file mode 100644 index 00000000..782067db --- /dev/null +++ b/testdata/script-tests/zero-postings-destination.num @@ -0,0 +1,9 @@ + +send [COIN 100] ( + source = @world + destination = { + max [COIN 0] to @d1 + remaining to @d2 + } +) + diff --git a/testdata/script-tests/zero-postings-destination.num.specs.json b/testdata/script-tests/zero-postings-destination.num.specs.json new file mode 100644 index 00000000..41299854 --- /dev/null +++ b/testdata/script-tests/zero-postings-destination.num.specs.json @@ -0,0 +1,15 @@ +{ + "testCases": [ + { + "it": "-", + "expect.postings": [ + { + "source": "world", + "destination": "d2", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +} diff --git a/testdata/script-tests/zero-postings-explicit-allotment.num b/testdata/script-tests/zero-postings-explicit-allotment.num new file mode 100644 index 00000000..b2c78a42 --- /dev/null +++ b/testdata/script-tests/zero-postings-explicit-allotment.num @@ -0,0 +1,9 @@ + +send [COIN 0] ( + source = { + 1/2 from @a + 1/2 from @b + } + destination = @dest +) + diff --git a/testdata/script-tests/zero-postings-explicit-allotment.num.specs.json b/testdata/script-tests/zero-postings-explicit-allotment.num.specs.json new file mode 100644 index 00000000..b50fd090 --- /dev/null +++ b/testdata/script-tests/zero-postings-explicit-allotment.num.specs.json @@ -0,0 +1,11 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": {}, + "b": {} + } + } + ] +} diff --git a/testdata/script-tests/zero-postings-explicit-inorder.num b/testdata/script-tests/zero-postings-explicit-inorder.num new file mode 100644 index 00000000..522b0cd5 --- /dev/null +++ b/testdata/script-tests/zero-postings-explicit-inorder.num @@ -0,0 +1,10 @@ + +send [COIN 0] ( + source = { + @a + @b + @c + } + destination = @dest +) + diff --git a/testdata/script-tests/zero-postings-explicit-inorder.num.specs.json b/testdata/script-tests/zero-postings-explicit-inorder.num.specs.json new file mode 100644 index 00000000..6b8953ba --- /dev/null +++ b/testdata/script-tests/zero-postings-explicit-inorder.num.specs.json @@ -0,0 +1,12 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": {}, + "b": {}, + "c": {} + } + } + ] +} diff --git a/testdata/script-tests/zero-postings.num b/testdata/script-tests/zero-postings.num new file mode 100644 index 00000000..3184249c --- /dev/null +++ b/testdata/script-tests/zero-postings.num @@ -0,0 +1,9 @@ + +send [COIN 100] ( + source = { + @a + @world + } + destination = @dest +) + diff --git a/testdata/script-tests/zero-postings.num.specs.json b/testdata/script-tests/zero-postings.num.specs.json new file mode 100644 index 00000000..75bb5a15 --- /dev/null +++ b/testdata/script-tests/zero-postings.num.specs.json @@ -0,0 +1,18 @@ +{ + "testCases": [ + { + "it": "-", + "balances": { + "a": {} + }, + "expect.postings": [ + { + "source": "world", + "destination": "dest", + "amount": 100, + "asset": "COIN" + } + ] + } + ] +}