mirror of
https://github.com/corda/corda.git
synced 2025-01-18 02:39:51 +00:00
Initial checkin for Trade Finance work into experimental module
This commit is contained in:
parent
ac0d0ec0ec
commit
92e05f07aa
@ -0,0 +1,331 @@
|
|||||||
|
package com.r3corda.contracts
|
||||||
|
|
||||||
|
import com.r3corda.contracts.asset.Cash
|
||||||
|
import com.r3corda.contracts.testing.CASH
|
||||||
|
import com.r3corda.contracts.testing.`issued by`
|
||||||
|
import com.r3corda.contracts.testing.`owned by`
|
||||||
|
import com.r3corda.contracts.testing.`with notary`
|
||||||
|
import com.r3corda.core.contracts.DOLLARS
|
||||||
|
import com.r3corda.core.contracts.LedgerTransaction
|
||||||
|
import com.r3corda.core.contracts.`issued by`
|
||||||
|
import com.r3corda.core.contracts.verifyToLedgerTransaction
|
||||||
|
import com.r3corda.core.node.services.testing.MockStorageService
|
||||||
|
import com.r3corda.core.seconds
|
||||||
|
import com.r3corda.core.serialization.OpaqueBytes
|
||||||
|
import com.r3corda.core.testing.*
|
||||||
|
import org.junit.Test
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
//import java.util.*
|
||||||
|
//import kotlin.test.fail
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unit test cases that confirms the correct behavior of the AccountReceivable smart contract
|
||||||
|
*/
|
||||||
|
class AccountReceivableTests {
|
||||||
|
val INVOICE_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
|
||||||
|
val PAST_INVOICE_TIME = Instant.parse("2014-04-17T12:00:00.00Z")
|
||||||
|
|
||||||
|
val defaultRef = OpaqueBytes(ByteArray(1, { 1 }))
|
||||||
|
val defaultIssuer = MEGA_CORP.ref(defaultRef)
|
||||||
|
|
||||||
|
val notary = DUMMY_NOTARY
|
||||||
|
|
||||||
|
val invoiceProperties = Invoice.InvoiceProperties(
|
||||||
|
invoiceID = "123",
|
||||||
|
seller = LocDataStructures.Company(
|
||||||
|
name = "Mega Corp LTD.",
|
||||||
|
address = "123 Main St. Awesome Town, ZZ 11111",
|
||||||
|
phone = null
|
||||||
|
),
|
||||||
|
buyer = LocDataStructures.Company(
|
||||||
|
name = "Sandworm Imports",
|
||||||
|
address = "555 Elm St. Little Town, VV, 22222",
|
||||||
|
phone = null
|
||||||
|
),
|
||||||
|
invoiceDate = INVOICE_TIME.atZone(ZoneOffset.UTC).toLocalDate(),
|
||||||
|
term = 60,
|
||||||
|
goods = arrayListOf<LocDataStructures.PricedGood>(
|
||||||
|
LocDataStructures.PricedGood(
|
||||||
|
description = "Salt",
|
||||||
|
purchaseOrderRef = null,
|
||||||
|
quantity = 10,
|
||||||
|
unitPrice = 3.DOLLARS `issued by` defaultIssuer,
|
||||||
|
grossWeight = null
|
||||||
|
),
|
||||||
|
LocDataStructures.PricedGood(
|
||||||
|
description = "Pepper",
|
||||||
|
purchaseOrderRef = null,
|
||||||
|
quantity = 20,
|
||||||
|
unitPrice = 4.DOLLARS `issued by` defaultIssuer,
|
||||||
|
grossWeight = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val initialInvoiceState = Invoice.State(MINI_CORP, ALICE, false,invoiceProperties)
|
||||||
|
val initialAR = AccountReceivable.createARFromInvoice(initialInvoiceState, 0.9, notary)
|
||||||
|
|
||||||
|
enum class WhatKind {
|
||||||
|
PAST, FUTURE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateInvoiceIssueTxn(kind: WhatKind = WhatKind.FUTURE): LedgerTransaction {
|
||||||
|
val genTX: LedgerTransaction = run {
|
||||||
|
val pastProp = initialInvoiceState.props.copy(invoiceDate =
|
||||||
|
PAST_INVOICE_TIME.atZone(ZoneOffset.UTC).toLocalDate())
|
||||||
|
|
||||||
|
val invoice: Invoice.State = if (kind == WhatKind.PAST) {
|
||||||
|
initialInvoiceState.copy(props = pastProp)
|
||||||
|
} else {
|
||||||
|
initialInvoiceState
|
||||||
|
}
|
||||||
|
|
||||||
|
val gtx = invoice.generateInvoice(DUMMY_NOTARY).apply {
|
||||||
|
setTime(TEST_TX_TIME, DUMMY_NOTARY, 30.seconds)
|
||||||
|
signWith(MINI_CORP_KEY)
|
||||||
|
signWith(DUMMY_NOTARY_KEY)
|
||||||
|
}
|
||||||
|
gtx.toSignedTransaction().verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE, MockStorageService().attachments)
|
||||||
|
}
|
||||||
|
return genTX
|
||||||
|
}
|
||||||
|
|
||||||
|
fun issuedInvoice(): Invoice.State {
|
||||||
|
return generateInvoiceIssueTxn().outputs.filterIsInstance<Invoice.State>().single()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun issuedInvoiceWithPastDate(): Invoice.State {
|
||||||
|
return generateInvoiceIssueTxn(WhatKind.PAST).outputs.filterIsInstance<Invoice.State>().single()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Apply - requireThat Tests`() {
|
||||||
|
//Happy Path Apply
|
||||||
|
transaction {
|
||||||
|
input() { issuedInvoice() }
|
||||||
|
output { issuedInvoice().copy(assigned = true) }
|
||||||
|
output { initialAR.data }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
accepts()
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { initialAR.data }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "Required com.r3corda.contracts.Invoice.Commands command"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { initialAR.data }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "There must be an input Invoice state"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { issuedInvoice() }
|
||||||
|
output { initialInvoiceState.copy(assigned = true) }
|
||||||
|
output { initialAR.data }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
this `fails requirement` "must be timestamped"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { issuedInvoice() }
|
||||||
|
output { initialInvoiceState.copy(assigned = true) }
|
||||||
|
output { initialAR.data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "AR state must be applied"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { issuedInvoice() }
|
||||||
|
output { initialInvoiceState.copy(assigned = true) }
|
||||||
|
output { initialAR.data.copy(props = initialAR.data.props.copy(invoiceID = "BOB")) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "AR properties must match input invoice"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { issuedInvoiceWithPastDate() }
|
||||||
|
output { issuedInvoiceWithPastDate().copy(assigned = true) }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoiceWithPastDate(), 0.9, notary).data }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the payment date must be in the future"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { issuedInvoice() }
|
||||||
|
output { issuedInvoice().copy(assigned = true) }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 1.9, notary).data }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "The discount factor is invalid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Issue - requireThat Tests`() {
|
||||||
|
//Happy Path Apply
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
accepts()
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "input status must be applied"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Applied) }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Applied) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "output status must be issued"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Applied) }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.95, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "properties must match"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Extinguish - requireThat Tests`() {
|
||||||
|
//Happy Path Extinguish
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoiceWithPastDate(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Extinguish() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
accepts()
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoice(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Extinguish() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the payment date must be today or in the the past"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input() { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoiceWithPastDate(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Applied) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Extinguish() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "input status must be issued"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoiceWithPastDate(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
output { AccountReceivable.createARFromInvoice(
|
||||||
|
issuedInvoiceWithPastDate(), 0.9, notary).data.copy(status = AccountReceivable.StatusEnum.Issued) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Extinguish() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "output state must not exist"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun ok() {
|
||||||
|
createARAndSendToBank().verify()
|
||||||
|
}
|
||||||
|
|
||||||
|
val START_TIME = Instant.parse("2015-04-17T12:00:00.00Z")
|
||||||
|
val APPLY_TIME = Instant.parse("2015-04-17T12:05:00.00Z")
|
||||||
|
val ISSUE_TIME = Instant.parse("2015-04-17T12:15:00.00Z")
|
||||||
|
val END_TIME = Instant.parse("2015-04-27T12:00:00.00Z")
|
||||||
|
|
||||||
|
private fun createARAndSendToBank(): TransactionGroupDSL<AccountReceivable.State> {
|
||||||
|
|
||||||
|
return transactionGroupFor {
|
||||||
|
roots {
|
||||||
|
transaction(99.DOLLARS.CASH `issued by` defaultIssuer`owned by` MEGA_CORP_PUBKEY `with notary` DUMMY_NOTARY label "bank's money")
|
||||||
|
transaction(110.DOLLARS.CASH `issued by` defaultIssuer `owned by` ALICE_PUBKEY `with notary` DUMMY_NOTARY label "buyer's money")
|
||||||
|
}
|
||||||
|
|
||||||
|
val newProps = invoiceProperties.copy(invoiceDate = START_TIME.atZone(ZoneOffset.UTC).toLocalDate(),
|
||||||
|
term = 5)
|
||||||
|
val newInvoice = initialInvoiceState.copy(props = newProps)
|
||||||
|
val ar = AccountReceivable.createARFromInvoice(newInvoice, 0.90, notary)
|
||||||
|
|
||||||
|
// 1. Create new invoice
|
||||||
|
transaction {
|
||||||
|
output("new invoice") { newInvoice }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(START_TIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. create new AR
|
||||||
|
transaction {
|
||||||
|
input("new invoice")
|
||||||
|
output("applied invoice") { initialInvoiceState.copy(assigned=true, props = newProps) }
|
||||||
|
output("new AR") { ar.data }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Apply() }
|
||||||
|
timestamp(APPLY_TIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. issue AR
|
||||||
|
transaction {
|
||||||
|
input ("new AR")
|
||||||
|
input("bank's money")
|
||||||
|
output ("issued AR") {
|
||||||
|
ar.data.copy(status=AccountReceivable.StatusEnum.Issued)
|
||||||
|
}
|
||||||
|
output { 99.DOLLARS.CASH `owned by` MINI_CORP_PUBKEY }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Cash.Commands.Move() }
|
||||||
|
arg(MINI_CORP_PUBKEY) { AccountReceivable.Commands.Issue() }
|
||||||
|
timestamp(ISSUE_TIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. extinguish AR
|
||||||
|
transaction {
|
||||||
|
input ("applied invoice")
|
||||||
|
input ("issued AR")
|
||||||
|
input ("buyer's money")
|
||||||
|
output { 110.DOLLARS.CASH `owned by` MEGA_CORP_PUBKEY }
|
||||||
|
arg(ALICE_PUBKEY) { Cash.Commands.Move() }
|
||||||
|
arg(MINI_CORP_PUBKEY) { Invoice.Commands.Extinguish() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { AccountReceivable.Commands.Extinguish() }
|
||||||
|
timestamp(END_TIME)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,352 @@
|
|||||||
|
package com.r3corda.contracts
|
||||||
|
|
||||||
|
import com.r3corda.core.contracts.*
|
||||||
|
import com.r3corda.core.crypto.SecureHash
|
||||||
|
import com.r3corda.core.node.services.testing.MockStorageService
|
||||||
|
import com.r3corda.core.testing.*
|
||||||
|
import org.junit.Test
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
|
class BillOfLadingAgreementTests {
|
||||||
|
val pros = BillOfLadingAgreement.BillOfLadingProperties(
|
||||||
|
billOfLadingID = "billOfLadingID",
|
||||||
|
issueDate = LocalDate.now(),
|
||||||
|
carrierOwner = ALICE,
|
||||||
|
nameOfVessel = "Karaboudjan",
|
||||||
|
descriptionOfGoods = listOf(LocDataStructures.Good(description="Crab meet cans",quantity = 10000,grossWeight = null)),
|
||||||
|
dateOfShipment = LocalDate.now(),
|
||||||
|
portOfLoading = LocDataStructures.Port(country = "Morokko",city = "Larache",address = null,state = null,name=null),
|
||||||
|
portOfDischarge = LocDataStructures.Port(country = "Belgium",city = "Antwerpen",address = null,state = null,name=null),
|
||||||
|
shipper = null,
|
||||||
|
notify = LocDataStructures.Person(
|
||||||
|
name = "Some guy",
|
||||||
|
address = "Some address",
|
||||||
|
phone = "+11 23456789"
|
||||||
|
),
|
||||||
|
consignee = LocDataStructures.Company(
|
||||||
|
name = "Some company",
|
||||||
|
address = "Some other address",
|
||||||
|
phone = "+11 12345678"
|
||||||
|
),
|
||||||
|
grossWeight = LocDataStructures.Weight(
|
||||||
|
quantity = 2500.0,
|
||||||
|
unit = LocDataStructures.WeightUnit.KG
|
||||||
|
)
|
||||||
|
);
|
||||||
|
val Bill = BillOfLadingAgreement.State(
|
||||||
|
owner = MEGA_CORP_PUBKEY,
|
||||||
|
beneficiary = BOB,
|
||||||
|
props =pros
|
||||||
|
)
|
||||||
|
|
||||||
|
val attachments = MockStorageService().attachments
|
||||||
|
|
||||||
|
//Generation method tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun issueGenerationMethod() {
|
||||||
|
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary,Bill.props, notary = DUMMY_NOTARY).apply {
|
||||||
|
signWith(ALICE_KEY)
|
||||||
|
}
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun issueGenerationMethod_Unsigned() {
|
||||||
|
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props)
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun issueGenerationMethod_KeyMismatch() {
|
||||||
|
val ptx = BillOfLadingAgreement().generateIssue(Bill.owner, Bill.beneficiary, Bill.props).apply {
|
||||||
|
signWith(BOB_KEY)
|
||||||
|
}
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transferAndEndorseGenerationMethod() {
|
||||||
|
|
||||||
|
val ptx:TransactionBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY)
|
||||||
|
val sr = StateAndRef(
|
||||||
|
TransactionState(Bill, DUMMY_NOTARY),
|
||||||
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
|
)
|
||||||
|
BillOfLadingAgreement().generateTransferAndEndorse(ptx,sr,CHARLIE_PUBKEY, CHARLIE)
|
||||||
|
ptx.signWith(MEGA_CORP_KEY) //Signed by owner
|
||||||
|
ptx.signWith(BOB_KEY) //and beneficiary
|
||||||
|
// ptx.signWith(CHARLIE_KEY) // ??????
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun transferAndEndorseGenerationMethod_MissingBeneficiarySignature() {
|
||||||
|
val ptx:TransactionBuilder = TransactionType.General.Builder()
|
||||||
|
val sr = StateAndRef(
|
||||||
|
TransactionState(Bill, DUMMY_NOTARY),
|
||||||
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
|
)
|
||||||
|
BillOfLadingAgreement().generateTransferAndEndorse(ptx,sr,CHARLIE_PUBKEY, CHARLIE)
|
||||||
|
ptx.signWith(MEGA_CORP_KEY) //Signed by owner
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun transferAndEndorseGenerationMethod_MissingOwnerSignature() {
|
||||||
|
val ptx:TransactionBuilder = TransactionType.General.Builder()
|
||||||
|
val sr = StateAndRef(
|
||||||
|
TransactionState(Bill, DUMMY_NOTARY),
|
||||||
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
|
)
|
||||||
|
BillOfLadingAgreement().generateTransferAndEndorse(ptx,sr,CHARLIE_PUBKEY, CHARLIE)
|
||||||
|
ptx.signWith(BOB_KEY) //Signed by beneficiary
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transferPossessionGenerationMethod() {
|
||||||
|
val ptx:TransactionBuilder = TransactionType.General.Builder(notary = DUMMY_NOTARY)
|
||||||
|
val sr = StateAndRef(
|
||||||
|
TransactionState(Bill, DUMMY_NOTARY),
|
||||||
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
|
)
|
||||||
|
BillOfLadingAgreement().generateTransferPossession(ptx,sr,CHARLIE_PUBKEY)
|
||||||
|
ptx.signWith(MEGA_CORP_KEY) //Signed by owner
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun transferPossessionGenerationMethod_Unsigned() {
|
||||||
|
val ptx:TransactionBuilder = TransactionType.General.Builder()
|
||||||
|
val sr = StateAndRef(
|
||||||
|
TransactionState(Bill, DUMMY_NOTARY),
|
||||||
|
StateRef(SecureHash.randomSHA256(), Random().nextInt(32))
|
||||||
|
)
|
||||||
|
BillOfLadingAgreement().generateTransferPossession(ptx,sr,CHARLIE_PUBKEY)
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verifyToLedgerTransaction(MOCK_IDENTITY_SERVICE,attachments)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Custom transaction tests
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun generalConsistencyTests() {
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are multiple commands
|
||||||
|
this.`fails requirement`("List has more than one element.");
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are no commands
|
||||||
|
this.`fails requirement`("Required ${BillOfLadingAgreement.Commands::class.qualifiedName} command");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun issueTests() {
|
||||||
|
transaction {
|
||||||
|
output { Bill }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.IssueBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.accepts();
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.IssueBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("there is no input state");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { Bill }
|
||||||
|
arg(BOB_PUBKEY) { BillOfLadingAgreement.Commands.IssueBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the carrier");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transferAndEndorseTests() {
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.accepts();
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
//There is no timestamp
|
||||||
|
this.`fails requirement`("must be timestamped");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
input { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are two inputs
|
||||||
|
this.`fails requirement`("List has more than one element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are no inputs
|
||||||
|
this.`fails requirement`("List is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
output { Bill }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are two outputs
|
||||||
|
this.`fails requirement`("List has more than one element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are no outputs
|
||||||
|
this.`fails requirement`("List is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the beneficiary");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE) }
|
||||||
|
arg(BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the state object owner");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, beneficiary = CHARLIE, props = pros.copy(nameOfVessel = "Svet")) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, BOB_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the bill of lading agreement properties are unchanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun transferPossessionTests() {
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.accepts();
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
//There is no timestamp
|
||||||
|
this.`fails requirement`("must be timestamped");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
input { Bill.copy(owner = BOB_PUBKEY) }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are two inputs
|
||||||
|
this.`fails requirement`("List has more than one element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are no inputs
|
||||||
|
this.`fails requirement`("List is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { Bill.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are two outputs
|
||||||
|
this.`fails requirement`("List has more than one element.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
//There are no outputs
|
||||||
|
this.`fails requirement`("List is empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the state object owner");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY,beneficiary = CHARLIE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the beneficiary is unchanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { Bill }
|
||||||
|
output { Bill.copy(owner = CHARLIE_PUBKEY, props = pros.copy(nameOfVessel = "Svet")) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { BillOfLadingAgreement.Commands.TransferPossession() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the bill of lading agreement properties are unchanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,251 @@
|
|||||||
|
package com.r3corda.contracts
|
||||||
|
|
||||||
|
import com.r3corda.core.contracts.DOLLARS
|
||||||
|
import com.r3corda.core.contracts.`issued by`
|
||||||
|
import com.r3corda.core.serialization.OpaqueBytes
|
||||||
|
import com.r3corda.core.testing.*
|
||||||
|
import org.junit.Test
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.util.*
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
class InvoiceTests {
|
||||||
|
|
||||||
|
val defaultRef = OpaqueBytes(ByteArray(1, { 1 }))
|
||||||
|
val defaultIssuer = MEGA_CORP.ref(defaultRef)
|
||||||
|
|
||||||
|
val invoiceProperties = Invoice.InvoiceProperties(
|
||||||
|
invoiceID = "123",
|
||||||
|
seller = LocDataStructures.Company(
|
||||||
|
name = "Mega Corp LTD.",
|
||||||
|
address = "123 Main St. Awesome Town, ZZ 11111",
|
||||||
|
phone = null
|
||||||
|
),
|
||||||
|
buyer = LocDataStructures.Company(
|
||||||
|
name = "Sandworm Imports",
|
||||||
|
address = "555 Elm St. Little Town, VV, 22222",
|
||||||
|
phone = null
|
||||||
|
),
|
||||||
|
invoiceDate = LocalDate.now(),
|
||||||
|
term = 60,
|
||||||
|
goods = arrayListOf<LocDataStructures.PricedGood>(
|
||||||
|
LocDataStructures.PricedGood(
|
||||||
|
description = "Salt",
|
||||||
|
purchaseOrderRef = null,
|
||||||
|
quantity = 10,
|
||||||
|
unitPrice = 3.DOLLARS `issued by` defaultIssuer,
|
||||||
|
grossWeight = null
|
||||||
|
),
|
||||||
|
LocDataStructures.PricedGood(
|
||||||
|
description = "Pepper",
|
||||||
|
purchaseOrderRef = null,
|
||||||
|
quantity = 20,
|
||||||
|
unitPrice = 4.DOLLARS `issued by` defaultIssuer,
|
||||||
|
grossWeight = null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val initialInvoiceState = Invoice.State(MEGA_CORP, ALICE, false,invoiceProperties)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Issue - requireThat Tests`() {
|
||||||
|
|
||||||
|
//Happy Path Issue
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
accepts()
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "there is no input state"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState }
|
||||||
|
arg(DUMMY_PUBKEY_1) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the transaction is signed by the invoice owner"
|
||||||
|
}
|
||||||
|
|
||||||
|
var props = invoiceProperties.copy(seller = invoiceProperties.buyer);
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState.copy(props = props) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the buyer and seller must be different"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState.copy(assigned = true) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the invoice must not be assigned"
|
||||||
|
}
|
||||||
|
|
||||||
|
props = invoiceProperties.copy(invoiceID = "");
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState.copy(props = props) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the invoice ID must not be blank"
|
||||||
|
}
|
||||||
|
|
||||||
|
val withMessage = "the term must be a positive number"
|
||||||
|
val r = try {
|
||||||
|
props = invoiceProperties.copy(term = 0);
|
||||||
|
false
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val m = e.message
|
||||||
|
if (m == null)
|
||||||
|
fail("Threw exception without a message")
|
||||||
|
else
|
||||||
|
if (!m.toLowerCase().contains(withMessage.toLowerCase())) throw AssertionError("Error was actually: $m", e)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
if (!r) throw AssertionError("Expected exception but didn't get one")
|
||||||
|
|
||||||
|
props = invoiceProperties.copy(invoiceDate = LocalDate.now().minusDays(invoiceProperties.term + 1))
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState.copy(props = props) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(java.time.Instant.now())
|
||||||
|
this `fails requirement` "the payment date must be in the future"
|
||||||
|
}
|
||||||
|
|
||||||
|
val withMessage2 = "there must be goods assigned to the invoice"
|
||||||
|
val r2 = try {
|
||||||
|
props = invoiceProperties.copy(goods = Collections.emptyList())
|
||||||
|
false
|
||||||
|
} catch (e: Exception) {
|
||||||
|
val m = e.message
|
||||||
|
if (m == null)
|
||||||
|
fail("Threw exception without a message")
|
||||||
|
else
|
||||||
|
if (!m.toLowerCase().contains(withMessage2.toLowerCase())) {
|
||||||
|
throw AssertionError("Error was actually: $m expected $withMessage2", e)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
if (!r2) throw AssertionError("Expected exception but didn't get one")
|
||||||
|
|
||||||
|
val goods = arrayListOf<LocDataStructures.PricedGood>( LocDataStructures.PricedGood(
|
||||||
|
description = "Salt",
|
||||||
|
purchaseOrderRef = null,
|
||||||
|
quantity = 10,
|
||||||
|
unitPrice = 0.DOLLARS `issued by` defaultIssuer,
|
||||||
|
grossWeight = null
|
||||||
|
))
|
||||||
|
|
||||||
|
props = invoiceProperties.copy(goods = goods)
|
||||||
|
transaction {
|
||||||
|
output { initialInvoiceState.copy(props = props) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Issue() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the invoice amount must be non-zero"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Assign - requireThat Tests`() {
|
||||||
|
|
||||||
|
//Happy Path Assign
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState.copy(assigned = true) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
accepts()
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState.copy(owner = ALICE) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "input state owner must be the same as the output state owner"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState }
|
||||||
|
arg(DUMMY_PUBKEY_1) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the transaction must be signed by the owner"
|
||||||
|
}
|
||||||
|
|
||||||
|
var props = invoiceProperties.copy(seller = invoiceProperties.buyer);
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState.copy(props = props) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the invoice properties must remain unchanged"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState.copy(assigned = true) }
|
||||||
|
output { initialInvoiceState }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the input invoice must not be assigned"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState.copy(assigned = false) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(TEST_TX_TIME)
|
||||||
|
this `fails requirement` "the output invoice must be assigned"
|
||||||
|
}
|
||||||
|
|
||||||
|
props = invoiceProperties.copy(invoiceDate = LocalDate.now().minusDays(invoiceProperties.term + 1))
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState.copy(props = props) }
|
||||||
|
output { initialInvoiceState.copy(props = props, assigned = true) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Assign() }
|
||||||
|
timestamp(java.time.Instant.now())
|
||||||
|
this `fails requirement` "the payment date must be in the future"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Extinguish - requireThat Tests`() {
|
||||||
|
|
||||||
|
//Happy Path Extinguish
|
||||||
|
val props = invoiceProperties.copy(invoiceDate = LocalDate.now().minusDays(invoiceProperties.term + 1))
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState.copy(props = props) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Extinguish() }
|
||||||
|
timestamp(java.time.Instant.now())
|
||||||
|
accepts()
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
output { initialInvoiceState }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Extinguish() }
|
||||||
|
timestamp(java.time.Instant.now())
|
||||||
|
this `fails requirement` "there shouldn't be an output state"
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { initialInvoiceState }
|
||||||
|
arg(DUMMY_PUBKEY_1) { Invoice.Commands.Extinguish() }
|
||||||
|
timestamp(java.time.Instant.now())
|
||||||
|
this `fails requirement` "the transaction must be signed by the owner"
|
||||||
|
}
|
||||||
|
|
||||||
|
// transaction {
|
||||||
|
// input { initialInvoiceState }
|
||||||
|
// arg(MEGA_CORP_PUBKEY) { Invoice.Commands.Extinguish() }
|
||||||
|
// timestamp(java.time.Instant.now())
|
||||||
|
// this `fails requirement` "the payment date must be today or in the past"
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
464
experimental/src/test/kotlin/com/r3corda/contracts/LOCTests.kt
Normal file
464
experimental/src/test/kotlin/com/r3corda/contracts/LOCTests.kt
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
package com.r3corda.contracts
|
||||||
|
|
||||||
|
import com.r3corda.contracts.asset.Cash
|
||||||
|
import com.r3corda.core.contracts.*
|
||||||
|
import com.r3corda.core.crypto.SecureHash
|
||||||
|
import com.r3corda.core.serialization.OpaqueBytes
|
||||||
|
import com.r3corda.core.testing.*
|
||||||
|
import org.junit.Test
|
||||||
|
import java.time.Instant
|
||||||
|
import java.time.LocalDate
|
||||||
|
import java.time.Period
|
||||||
|
|
||||||
|
|
||||||
|
class LOCTests {
|
||||||
|
|
||||||
|
val defaultRef = OpaqueBytes(ByteArray(1, { 1 }))
|
||||||
|
val defaultIssuer = MEGA_CORP.ref(defaultRef)
|
||||||
|
|
||||||
|
val pros = LOC.LOCProperties(
|
||||||
|
letterOfCreditID = "letterOfCreditID",
|
||||||
|
applicationDate = LocalDate.of(2016,5,15),
|
||||||
|
issueDate = LocalDate.now().minusDays(30),
|
||||||
|
typeCredit = LocDataStructures.CreditType.SIGHT,
|
||||||
|
amount = 100000.DOLLARS `issued by` defaultIssuer,
|
||||||
|
expiryDate = LocalDate.now().plusDays(1),
|
||||||
|
portLoading = LocDataStructures.Port("SG","Singapore",null,null,null),
|
||||||
|
portDischarge = LocDataStructures.Port("US","Oakland",null,null,null),
|
||||||
|
descriptionGoods = listOf(LocDataStructures.PricedGood(description="Tiger balm",
|
||||||
|
quantity = 10000,
|
||||||
|
grossWeight = null,
|
||||||
|
unitPrice = 1.DOLLARS `issued by` defaultIssuer,
|
||||||
|
purchaseOrderRef = null
|
||||||
|
)),
|
||||||
|
placePresentation = LocDataStructures.Location("US","California","Oakland"),
|
||||||
|
latestShip = LocalDate.of(2016,6,12),
|
||||||
|
periodPresentation = Period.ofDays(31),
|
||||||
|
beneficiary = ALICE,
|
||||||
|
issuingbank = MEGA_CORP,
|
||||||
|
appplicant = CHARLIE,
|
||||||
|
invoiceRef = StateRef(SecureHash.randomSHA256(),0)
|
||||||
|
)
|
||||||
|
|
||||||
|
val LOCstate = LOC.State(
|
||||||
|
beneficiaryPaid = false,
|
||||||
|
issued = false,
|
||||||
|
terminated = false,
|
||||||
|
props =pros
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
val Billpros = BillOfLadingAgreement.BillOfLadingProperties(
|
||||||
|
billOfLadingID = "billOfLadingID",
|
||||||
|
issueDate = LocalDate.of(2016,6,1),
|
||||||
|
carrierOwner = BOB,
|
||||||
|
nameOfVessel = "Karaboudjan",
|
||||||
|
descriptionOfGoods = listOf(LocDataStructures.Good(description="Crab meet cans",quantity = 10000,grossWeight = null)),
|
||||||
|
dateOfShipment = null,
|
||||||
|
portOfLoading = LocDataStructures.Port(country = "Morokko",city = "Larache",address = null,name = null,state = null),
|
||||||
|
portOfDischarge = LocDataStructures.Port(country = "Belgium",city = "Antwerpen",address = null, name = null, state = null),
|
||||||
|
shipper = null,
|
||||||
|
notify = LocDataStructures.Person(
|
||||||
|
name = "Some guy",
|
||||||
|
address = "Some address",
|
||||||
|
phone = "+11 23456789"
|
||||||
|
),
|
||||||
|
consignee = LocDataStructures.Company(
|
||||||
|
name = "Some company",
|
||||||
|
address = "Some other address",
|
||||||
|
phone = "+11 12345678"
|
||||||
|
),
|
||||||
|
grossWeight = LocDataStructures.Weight(
|
||||||
|
quantity = 2500.0,
|
||||||
|
unit = LocDataStructures.WeightUnit.KG
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val Billstate = BillOfLadingAgreement.State(
|
||||||
|
owner = ALICE_PUBKEY,
|
||||||
|
beneficiary = ALICE,
|
||||||
|
props =Billpros
|
||||||
|
)
|
||||||
|
|
||||||
|
val Cashstate = Cash.State(
|
||||||
|
deposit = MEGA_CORP.ref(1),
|
||||||
|
amount = 100000.DOLLARS,
|
||||||
|
owner = MEGA_CORP_PUBKEY
|
||||||
|
)
|
||||||
|
|
||||||
|
val invoiceState = Invoice.State(
|
||||||
|
owner = ALICE,
|
||||||
|
buyer = BOB,
|
||||||
|
assigned = true,
|
||||||
|
props = Invoice.InvoiceProperties(
|
||||||
|
invoiceID = "test",
|
||||||
|
seller = LocDataStructures.Company(
|
||||||
|
name = "Alice",
|
||||||
|
address = "",
|
||||||
|
phone = null
|
||||||
|
),
|
||||||
|
buyer = LocDataStructures.Company(
|
||||||
|
name = "Charlie",
|
||||||
|
address = "",
|
||||||
|
phone = null
|
||||||
|
),
|
||||||
|
invoiceDate = LocalDate.now().minusDays(1),
|
||||||
|
term = 1,
|
||||||
|
goods = listOf(LocDataStructures.PricedGood(
|
||||||
|
description = "Test good",
|
||||||
|
purchaseOrderRef = null,
|
||||||
|
quantity = 1000,
|
||||||
|
unitPrice = 100.DOLLARS `issued by` defaultIssuer,
|
||||||
|
grossWeight = null)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun issueSignedByBank() {
|
||||||
|
val ptx = LOC().generateIssue(LOCstate.beneficiaryPaid, LOCstate.issued, LOCstate.terminated, LOCstate.props, DUMMY_NOTARY).apply {
|
||||||
|
signWith(MEGA_CORP_KEY)
|
||||||
|
}
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verify()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun issueUnsigned() {
|
||||||
|
val ptx = LOC().generateIssue(LOCstate.beneficiaryPaid, LOCstate.issued, LOCstate.terminated, LOCstate.props, DUMMY_NOTARY)
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verify()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException::class)
|
||||||
|
fun issueKeyMismatch() {
|
||||||
|
val ptx = LOC().generateIssue(LOCstate.beneficiaryPaid, LOCstate.issued, LOCstate.terminated, LOCstate.props, DUMMY_NOTARY).apply {
|
||||||
|
signWith(BOB_KEY)
|
||||||
|
}
|
||||||
|
val stx = ptx.toSignedTransaction()
|
||||||
|
stx.verify()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun issueStatusTests() {
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
output { LOCstate.copy(issued = false) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { LOC.Commands.Issuance() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the LOC must be Issued");
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { LOC.Commands.Issuance() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("Demand Presentation must not be preformed successfully");
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
output { LOCstate.copy(terminated = true, issued = true) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { LOC.Commands.Issuance() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("LOC must not be terminated");
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
output { LOCstate.copy(issued = true) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { LOC.Commands.Issuance() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.accepts()
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
output { LOCstate.copy(issued = true, props = pros.copy(periodPresentation = Period.ofDays(0))) }
|
||||||
|
// output { LOCstate.copy() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { LOC.Commands.Issuance() }
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the period of presentation must be a positive number");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun demandPresentaionTests() {
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true)}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.accepts();
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true)}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the issuing bank");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true)}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the Beneficiary");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, props = pros.copy(amount = 1.POUNDS `issued by` defaultIssuer ))}
|
||||||
|
output { Billstate.copy(owner = CHARLIE_PUBKEY)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the LOC properties do not remain the same");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, props = pros.copy(latestShip = Billstate.props.issueDate.minusDays(1))) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, props = pros.copy(latestShip = Billstate.props.issueDate.minusDays(1)))}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the shipment is late");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate.copy(amount = 99000.DOLLARS `issued by` defaultIssuer) }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true)}
|
||||||
|
output { Billstate.copy(owner = CHARLIE_PUBKEY)}
|
||||||
|
output { Cashstate.copy(amount = 99000.DOLLARS `issued by` defaultIssuer ).copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the cash state has not been transferred");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true)}
|
||||||
|
output { Billstate.copy(beneficiary = ALICE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the bill of lading has not been transferred");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transaction {
|
||||||
|
input { LOCstate.copy(issued = true, props = pros.copy(issueDate = LocalDate.now().minusDays(32))) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, props = pros.copy(issueDate =LocalDate.now().minusDays(32)))}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the presentation is late");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = false, issued = true)}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the beneficiary has not been paid, status not changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = false) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = false)}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the LOC must be Issued");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true) }
|
||||||
|
input { Billstate }
|
||||||
|
input { Cashstate }
|
||||||
|
input { invoiceState }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Billstate.copy(beneficiary = CHARLIE)}
|
||||||
|
output { Cashstate.copy(owner = ALICE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.DemandPresentation() }
|
||||||
|
arg(ALICE_PUBKEY) { BillOfLadingAgreement.Commands.TransferAndEndorseBL() }
|
||||||
|
arg(ALICE_PUBKEY) { Invoice.Commands.Extinguish()}
|
||||||
|
arg(MEGA_CORP_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("LOC must not be terminated");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun terminationTests() {
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.accepts();
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(ALICE_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the issuing bank");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, ALICE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the transaction is signed by the applicant");
|
||||||
|
}*/
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(amount = Cashstate.amount.minus(Amount(10,Cashstate.amount.token))) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the cash state has not been transferred");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("Empty collection can't be reduced");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = false) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = false, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the beneficiary has not been paid, status not changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = false, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = false, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the LOC must be Issued");
|
||||||
|
}
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = false)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("LOC should be terminated");
|
||||||
|
}
|
||||||
|
|
||||||
|
transaction {
|
||||||
|
input { LOCstate.copy(issued = true, beneficiaryPaid = true, props = pros.copy(amount = 1.POUNDS `issued by` defaultIssuer)) }
|
||||||
|
input { Cashstate.copy(owner = CHARLIE_PUBKEY) }
|
||||||
|
output { LOCstate.copy(beneficiaryPaid = true, issued = true, terminated = true)}
|
||||||
|
output { Cashstate.copy(owner = MEGA_CORP_PUBKEY) }
|
||||||
|
arg(MEGA_CORP_PUBKEY, CHARLIE_PUBKEY) { LOC.Commands.Termination() }
|
||||||
|
arg(CHARLIE_PUBKEY) {Cash.Commands.Move()}
|
||||||
|
timestamp(Instant.now())
|
||||||
|
this.`fails requirement`("the LOC properties do not remain the same");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user