def test_parallel_transaction(account1, account2, bucket):
    if not have_freezetime:
        return

    zero = create_decimal(0)

    # test lots of transactions all happening in parallel
    total1 = zero
    total2 = zero

    start1 = account1.balance()
    start2 = account2.balance()

    rlock = RLock()

    def perform_transaction(key, result):
        delta1 = zero
        delta2 = zero
        auth = Authorisation()

        # need to work with thread-local copies of the accounts
        my_account1 = Account(uid=account1.uid())
        my_account2 = Account(uid=account2.uid())

        for i in range(0, 5):
            transaction = Transaction(value=create_decimal(random.random()),
                                      description="Transaction %d" % i)

            if random.randint(0, 1):
                Ledger.perform(transaction, my_account1, my_account2, auth)
                delta1 -= transaction.value()
                delta2 += transaction.value()
            else:
                Ledger.perform(transaction, my_account2, my_account1, auth)
                delta1 += transaction.value()
                delta2 -= transaction.value()

        with rlock:
            result[key] = (delta1, delta2)

    threads = []

    result = {}

    for i in range(0, 5):
        t = Thread(target=perform_transaction, args=[i, result])
        t.start()
        threads.append(t)

    total1 = zero
    total2 = zero

    for i, thread in enumerate(threads):
        thread.join()
        total1 += result[i][0]
        total2 += result[i][1]

    assert (account1.balance() == start1 + total1)
    assert (account2.balance() == start2 + total2)
Esempio n. 2
0
def random_transaction():
    value = create_decimal(1000.0 * random.random())
    description = "%s transaction" % value
    transaction = Transaction(value, description)

    assert (transaction.value() == value)
    assert (transaction.description() == description)

    return transaction
Esempio n. 3
0
def random_transaction(account1, account2):
    value = create_decimal(1000.0 * random.random())
    description = "%s transaction" % value
    transaction = Transaction(value, description)

    assert (transaction.value() == value)
    assert (transaction.description() == description)

    assert (account1.get_overdraft_limit() == account1_overdraft_limit)
    assert (account2.get_overdraft_limit() == account2_overdraft_limit)

    if random.randint(0, 1):
        return (transaction, account1, account2)
    else:
        return (transaction, account2, account1)
    def perform_transaction(key, result):
        delta1 = zero
        delta2 = zero

        # need to work with thread-local copies of the accounts
        my_account1 = Account(uid=account1.uid())
        my_account2 = Account(uid=account2.uid())

        for i in range(0, 10):
            transaction = Transaction(value=create_decimal(random.random()),
                                      description="Transaction %d" % i)

            if random.randint(0, 1):
                auth = Authorisation(
                    resource=transaction.fingerprint(),
                    testing_key=testing_key,
                    testing_user_guid=my_account1.group_name())

                Ledger.perform(transaction=transaction,
                               debit_account=my_account1,
                               credit_account=my_account2,
                               authorisation=auth)
                delta1 -= transaction.value()
                delta2 += transaction.value()
            else:
                auth = Authorisation(
                    resource=transaction.fingerprint(),
                    testing_key=testing_key,
                    testing_user_guid=my_account2.group_name())

                Ledger.perform(transaction=transaction,
                               debit_account=my_account2,
                               credit_account=my_account1,
                               authorisation=auth)
                delta1 += transaction.value()
                delta2 -= transaction.value()

        with rlock:
            result[key] = (delta1, delta2)
    def perform_transaction(key, result):
        delta1 = zero
        delta2 = zero
        auth = Authorisation()

        # need to work with thread-local copies of the accounts
        my_account1 = Account(uid=account1.uid())
        my_account2 = Account(uid=account2.uid())

        for i in range(0, 5):
            transaction = Transaction(value=create_decimal(random.random()),
                                      description="Transaction %d" % i)

            if random.randint(0, 1):
                Ledger.perform(transaction, my_account1, my_account2, auth)
                delta1 -= transaction.value()
                delta2 += transaction.value()
            else:
                Ledger.perform(transaction, my_account2, my_account1, auth)
                delta1 += transaction.value()
                delta2 -= transaction.value()

        with rlock:
            result[key] = (delta1, delta2)
def test_temporal_transactions(account1, account2, bucket):
    if not have_freezetime:
        return

    zero = create_decimal(0)

    balance1 = zero
    balance2 = zero
    final_balance1 = zero
    final_balance2 = zero
    liability1 = zero
    liability2 = zero
    receivable1 = zero
    receivable2 = zero

    # generate some random times for the transactions
    random_dates = []
    now = get_datetime_now()
    for i in range(0, 50):
        if i == 0:
            # this is an evil edge-case datetime
            s = "2019-01-20 20:59:59.092627+00:00"
            r = datetime.datetime.fromisoformat(s)
        else:
            r = start_time + random.random() * (now - start_time)

        while (r.minute == 59 and r.second >= 58) or \
              (r.minute == 0 and r.second == 0 and r.microsecond < 10):
            r = r + datetime.timedelta(seconds=1)

        random_dates.append(r)

    random_dates.sort()

    # (which must be applied in time order!)
    random_dates.sort()

    provisionals = []

    for (i, transaction_time) in enumerate(random_dates):
        with freeze_time(transaction_time) as _frozen_datetime:
            now = get_datetime_now()
            assert (transaction_time == now)

            is_provisional = (random.randint(0, 3) <= 2)

            # check search for transaction is not O(n^2) lookup scanning
            # through the keys...

            transaction = Transaction(25 * random.random(),
                                      "test transaction %d" % i)

            if random.randint(0, 1):
                debit_account = account1
                credit_account = account2

                if is_provisional:
                    liability1 += transaction.value()
                    receivable2 += transaction.value()
                else:
                    balance1 -= transaction.value()
                    balance2 += transaction.value()

                final_balance1 -= transaction.value()
                final_balance2 += transaction.value()
            else:
                debit_account = account2
                credit_account = account1

                if is_provisional:
                    receivable1 += transaction.value()
                    liability2 += transaction.value()
                else:
                    balance1 += transaction.value()
                    balance2 -= transaction.value()

                final_balance1 += transaction.value()
                final_balance2 -= transaction.value()

            auth = Authorisation(resource=transaction.fingerprint(),
                                 testing_key=testing_key,
                                 testing_user_guid=debit_account.group_name())

            records = Ledger.perform(transaction=transaction,
                                     debit_account=debit_account,
                                     credit_account=credit_account,
                                     authorisation=auth,
                                     is_provisional=is_provisional,
                                     bucket=bucket)

            for record in records:
                assert (record.datetime() == now)

            if is_provisional:
                for record in records:
                    provisionals.append((credit_account, record))
            elif (random.randint(0, 3) <= 2):
                # receipt pending transactions
                balance1 = Balance(balance=balance1,
                                   liability=liability1,
                                   receivable=receivable1)

                balance2 = Balance(balance=balance2,
                                   liability=liability2,
                                   receivable=receivable2)

                assert (account1.balance() == balance1)
                assert (account2.balance() == balance2)

                for (credit_account, record) in provisionals:
                    credit_note = record.credit_note()
                    auth = Authorisation(
                        resource=credit_note.fingerprint(),
                        testing_key=testing_key,
                        testing_user_guid=credit_account.group_name())

                    receipted_value = create_decimal(
                        random.random() * float(credit_note.value()))

                    delta_value = credit_note.value() - receipted_value

                    Ledger.receipt(Receipt(credit_note=credit_note,
                                           receipted_value=receipted_value,
                                           authorisation=auth),
                                   bucket=bucket)

                    if credit_note.debit_account_uid() == account1.uid():
                        final_balance1 += delta_value
                        final_balance2 -= delta_value
                    else:
                        final_balance2 += delta_value
                        final_balance1 -= delta_value

                assert (account1.balance() == Balance(balance=final_balance1))
                assert (account2.balance() == Balance(balance=final_balance2))

                provisionals = []
                balance1 = final_balance1
                balance2 = final_balance2
                liability1 = zero
                liability2 = zero
                receivable1 = zero
                receivable2 = zero

    balance1 = Balance(balance=balance1,
                       liability=liability1,
                       receivable=receivable1)

    balance2 = Balance(balance=balance2,
                       liability=liability2,
                       receivable=receivable2)

    assert (account1.balance() == balance1)
    assert (account2.balance() == balance2)

    for (credit_account, record) in provisionals:
        credit_note = record.credit_note()
        auth = Authorisation(resource=record.credit_note().fingerprint(),
                             testing_key=testing_key,
                             testing_user_guid=credit_account.group_name())

        receipted_value = create_decimal(random.random() *
                                         float(credit_note.value()))

        delta_value = credit_note.value() - receipted_value

        Ledger.receipt(Receipt(credit_note=credit_note,
                               authorisation=auth,
                               receipted_value=receipted_value),
                       bucket=bucket)

        if credit_note.debit_account_uid() == account1.uid():
            final_balance1 += delta_value
            final_balance2 -= delta_value
        else:
            final_balance2 += delta_value
            final_balance1 -= delta_value

    assert (account1.balance() == Balance(balance=final_balance1))
    assert (account2.balance() == Balance(balance=final_balance2))
Esempio n. 7
0
def test_pending_transactions(random_transaction):
    (transaction, account1, account2) = random_transaction

    starting_balance1 = account1.balance()
    starting_balance2 = account2.balance()

    authorisation = Authorisation(resource=transaction.fingerprint(),
                                  testing_key=testing_key,
                                  testing_user_guid=account1.group_name())

    records = Ledger.perform(transactions=transaction,
                             debit_account=account1,
                             credit_account=account2,
                             authorisation=authorisation,
                             is_provisional=True)

    assert (len(records) == 1)
    record = records[0]

    ending_balance1 = account1.balance()
    ending_balance2 = account2.balance()

    assert (ending_balance1.liability() == starting_balance1.liability() +
            transaction.value())
    assert (ending_balance2.receivable() == starting_balance2.receivable() +
            transaction.value())
    assert (starting_balance1.balance() == ending_balance1.balance())
    assert (starting_balance2.balance() == ending_balance2.balance())
    assert (starting_balance2.liability() == ending_balance2.liability())
    assert (starting_balance1.receivable() == ending_balance1.receivable())

    assert (record.debit_account_uid() == account1.uid())
    assert (record.credit_account_uid() == account2.uid())

    debit_note = record.debit_note()
    credit_note = record.credit_note()

    assert (not debit_note.is_null())
    assert (not credit_note.is_null())

    assert (debit_note.account_uid() == account1.uid())
    assert (credit_note.account_uid() == account2.uid())

    assert (debit_note.is_provisional())
    assert (credit_note.is_provisional())

    assert (debit_note.value() == transaction.value())
    assert (credit_note.value() == transaction.value())

    now = get_datetime_now()

    assert (debit_note.datetime() < now)
    assert (credit_note.datetime() < now)
    assert (debit_note.datetime() <= credit_note.datetime())

    assert_packable(debit_note)
    assert_packable(credit_note)

    # now receipt a random amount of the transaction
    authorisation = Authorisation(resource=credit_note.fingerprint(),
                                  testing_key=testing_key,
                                  testing_user_guid=account2.group_name())

    with pytest.raises(ValueError):
        receipt = Receipt(
            credit_note, authorisation,
            create_decimal(random.random()) + credit_note.value())

    if random.randint(0, 1):
        value = credit_note.value()
        receipt = Receipt(credit_note, authorisation)
    else:
        value = create_decimal(
            create_decimal(random.random()) * credit_note.value())
        receipt = Receipt(credit_note, authorisation, value)

    assert (not receipt.is_null())
    assert (receipt.authorisation() == authorisation)
    assert (receipt.receipted_value() == value)
    assert (receipt.credit_note() == credit_note)
    assert_packable(receipt)

    rrecords = Ledger.receipt(receipt)

    assert (len(rrecords) == 1)
    rrecord = rrecords[0]

    assert (not rrecord.is_null())
    assert_packable(rrecord)

    assert (not rrecord.is_provisional())
    assert (rrecord.is_direct())
    assert (rrecord.get_receipt_info() == receipt)
    assert (rrecord.is_receipt())
    assert (rrecord.original_transaction() == transaction)

    # the original transaction record has now been updated to
    # say that it has been receipted...
    assert (record.is_provisional())
    record.reload()
    assert (record.is_receipted())

    assert (rrecord.original_transaction_record() == record)

    ending_balance1 = account1.balance()
    ending_balance2 = account2.balance()

    assert (ending_balance1.liability() == starting_balance1.liability())
    assert (ending_balance2.receivable() == starting_balance2.receivable())
    assert (starting_balance1.balance() - value == ending_balance1.balance())
    assert (starting_balance2.balance() + value == ending_balance2.balance())
    assert (starting_balance2.liability() == ending_balance2.liability())
    assert (starting_balance1.receivable() == ending_balance1.receivable())
def test_temporal_transactions(account1, account2, bucket):
    if not have_freezetime:
        return

    zero = create_decimal(0)

    balance1 = zero
    balance2 = zero
    final_balance1 = zero
    final_balance2 = zero
    liability1 = zero
    liability2 = zero
    receivable1 = zero
    receivable2 = zero

    # generate some random times for the transactions
    random_dates = []
    now = datetime.datetime.now()
    for i in range(0, 100):
        random_dates.append(start_time + random.random() * (now - start_time))

    # (which must be applied in time order!)
    random_dates.sort()

    records = []

    for (i, transaction_time) in enumerate(random_dates):
        with freeze_time(transaction_time) as frozen_datetime:
            now = datetime.datetime.now()
            assert (frozen_datetime() == now)

            is_provisional = random.randint(0, 5)

            transaction = Transaction(25 * random.random(),
                                      "test transaction %d" % i)
            auth = Authorisation()

            if random.randint(0, 10):
                record = Ledger.perform(transaction,
                                        account1,
                                        account2,
                                        auth,
                                        is_provisional,
                                        bucket=bucket)

                if is_provisional:
                    liability1 += transaction.value()
                    receivable2 += transaction.value()
                else:
                    balance1 -= transaction.value()
                    balance2 += transaction.value()

                final_balance1 -= transaction.value()
                final_balance2 += transaction.value()
            else:
                record = Ledger.perform(transaction,
                                        account2,
                                        account1,
                                        auth,
                                        is_provisional,
                                        bucket=bucket)

                if is_provisional:
                    receivable1 += transaction.value()
                    liability2 += transaction.value()
                else:
                    balance1 += transaction.value()
                    balance2 -= transaction.value()

                final_balance1 += transaction.value()
                final_balance2 -= transaction.value()

            if is_provisional:
                records.append(record)

            assert (record.timestamp() == now.timestamp())

    assert (account1.balance() == balance1)
    assert (account2.balance() == balance2)
    assert (account1.liability() == liability1)
    assert (account1.receivable() == receivable1)
    assert (account2.liability() == liability2)
    assert (account2.receivable() == receivable2)

    for record in records:
        Ledger.receipt(Receipt(record.credit_note(), Authorisation()),
                       bucket=bucket)

    assert (account1.balance() == final_balance1)
    assert (account2.balance() == final_balance2)

    assert (account1.liability() == zero)
    assert (account1.receivable() == zero)
    assert (account2.liability() == zero)
    assert (account2.receivable() == zero)
Esempio n. 9
0
def test_pending_transactions(random_transaction):
    (transaction, account1, account2) = random_transaction

    starting_balance1 = account1.balance()
    starting_liability1 = account1.liability()
    starting_receivable1 = account1.receivable()

    starting_balance2 = account2.balance()
    starting_liability2 = account2.liability()
    starting_receivable2 = account2.receivable()

    record = Ledger.perform(transaction,
                            account1,
                            account2,
                            Authorisation(),
                            is_provisional=True)

    ending_balance1 = account1.balance()
    ending_liability1 = account1.liability()
    ending_receivable1 = account1.receivable()

    ending_balance2 = account2.balance()
    ending_liability2 = account2.liability()
    ending_receivable2 = account2.receivable()

    assert (ending_liability1 == starting_liability1 + transaction.value())
    assert (ending_receivable2 == starting_receivable2 + transaction.value())
    assert (starting_balance1 == ending_balance1)
    assert (starting_balance2 == ending_balance2)
    assert (starting_liability2 == ending_liability2)
    assert (starting_receivable1 == ending_receivable1)

    assert (record.debit_account_uid() == account1.uid())
    assert (record.credit_account_uid() == account2.uid())

    debit_note = record.debit_note()
    credit_note = record.credit_note()

    assert (not debit_note.is_null())
    assert (not credit_note.is_null())

    assert (debit_note.account_uid() == account1.uid())
    assert (credit_note.account_uid() == account2.uid())

    assert (debit_note.is_provisional())
    assert (credit_note.is_provisional())

    assert (debit_note.value() == transaction.value())
    assert (credit_note.value() == transaction.value())

    now = datetime.datetime.now()

    assert (debit_note.timestamp() < now.timestamp())
    assert (credit_note.timestamp() < now.timestamp())
    assert (debit_note.timestamp() <= credit_note.timestamp())

    assert_packable(debit_note)
    assert_packable(credit_note)

    # now receipt a random amount of the transaction
    auth = Authorisation()

    with pytest.raises(ValueError):
        receipt = Receipt(
            credit_note, auth,
            create_decimal(random.random()) + credit_note.value())

    if random.randint(0, 1):
        value = credit_note.value()
        receipt = Receipt(credit_note, auth)
    else:
        value = create_decimal(
            create_decimal(random.random()) * credit_note.value())
        receipt = Receipt(credit_note, auth, value)

    assert (not receipt.is_null())
    assert (receipt.authorisation() == auth)
    assert (receipt.receipted_value() == value)
    assert (receipt.credit_note() == credit_note)
    assert_packable(receipt)

    rrecord = Ledger.receipt(receipt)

    assert (not rrecord.is_null())
    assert_packable(rrecord)

    assert (not rrecord.is_provisional())
    assert (rrecord.is_direct())
    assert (rrecord.get_receipt_info() == receipt)
    assert (rrecord.is_receipt())
    assert (rrecord.original_transaction() == transaction)

    # the original transaction record has now been updated to
    # say that it has been receipted...
    assert (record.is_provisional())
    record.reload()
    assert (record.is_receipted())

    assert (rrecord.original_transaction_record() == record)

    ending_balance1 = account1.balance()
    ending_liability1 = account1.liability()
    ending_receivable1 = account1.receivable()

    ending_balance2 = account2.balance()
    ending_liability2 = account2.liability()
    ending_receivable2 = account2.receivable()

    assert (ending_liability1 == starting_liability1)
    assert (ending_receivable2 == starting_receivable2)
    assert (starting_balance1 - value == ending_balance1)
    assert (starting_balance2 + value == ending_balance2)
    assert (starting_liability2 == ending_liability2)
    assert (starting_receivable1 == ending_receivable1)