Пример #1
0
def test_process_rejected_config_signal(account):
    c = p.get_account_config(C_ID, D_ID)
    assert c.config_error is None
    p.process_pending_log_entries(C_ID)
    ple_count = len(models.LogEntry.query.all())

    params = {
        'debtor_id': D_ID,
        'creditor_id': C_ID,
        'config_ts': c.last_config_ts,
        'config_seqnum': c.last_config_seqnum,
        'negligible_amount': c.negligible_amount,
        'config_data': '',
        'config_flags': c.config_flags,
        'rejection_code': 'TEST_CODE',
    }
    p.process_rejected_config_signal(**{**params, 'config_data': 'UNEXPECTED'})
    p.process_rejected_config_signal(**{**params, 'negligible_amount': c.negligible_amount * 1.0001})
    p.process_rejected_config_signal(**{**params, 'config_flags': c.config_flags ^ 1})
    p.process_rejected_config_signal(**{**params, 'config_seqnum': c.last_config_seqnum - 1})
    p.process_rejected_config_signal(**{**params, 'config_seqnum': c.last_config_seqnum + 1})
    p.process_rejected_config_signal(**{**params, 'config_ts': c.last_config_ts + timedelta(seconds=-1)})
    p.process_rejected_config_signal(**{**params, 'config_ts': c.last_config_ts + timedelta(seconds=1)})
    c = p.get_account_config(C_ID, D_ID)
    info_latest_update_id = c.info_latest_update_id
    info_latest_update_ts = c.info_latest_update_ts
    assert c.config_error is None
    assert len(models.PendingLogEntry.query.all()) == 0

    p.process_rejected_config_signal(**params)
    c = p.get_account_config(C_ID, D_ID)
    assert c.config_error == 'TEST_CODE'
    assert c.info_latest_update_id == info_latest_update_id + 1
    assert c.info_latest_update_ts >= info_latest_update_ts

    p.process_pending_log_entries(C_ID)
    assert len(LogEntry.query.all()) == ple_count + 1
    ple = LogEntry.query.filter_by(object_type='AccountInfo', object_update_id=info_latest_update_id + 1).one()
    assert ple.creditor_id == C_ID
    assert '/info' in ple.object_uri

    p.process_rejected_config_signal(**params)
    assert len(LogEntry.query.all()) == ple_count + 1
Пример #2
0
def test_process_account_purge_signal(db_session, creditor, account, current_ts):
    AccountData.query.filter_by(debtor_id=D_ID, creditor_id=C_ID).update({
        AccountData.creation_date: date(2020, 1, 2),
        AccountData.has_server_account: True,
        AccountData.principal: 1000,
        AccountData.interest: 15.0,
    }, synchronize_session=False)
    db_session.commit()
    data = AccountData.query.one()
    assert data.debtor_id == D_ID
    assert data.creditor_id == C_ID
    assert data.has_server_account
    assert data.principal == 1000
    assert data.interest == 15.0

    p.process_account_purge_signal(debtor_id=1111, creditor_id=2222, creation_date=date(2020, 1, 2))
    p.process_account_purge_signal(debtor_id=D_ID, creditor_id=C_ID, creation_date=date(2020, 1, 1))
    data = AccountData.query.one()
    assert data.has_server_account
    assert data.principal == 1000
    assert data.interest == 15.0
    p.process_pending_log_entries(C_ID)
    assert len(LogEntry.query.all()) == 2

    p.process_account_purge_signal(debtor_id=D_ID, creditor_id=C_ID, creation_date=date(2020, 1, 2))
    data = AccountData.query.one()
    assert not data.has_server_account
    assert data.principal == 0
    assert data.interest == 0.0

    p.process_pending_log_entries(C_ID)
    assert len(LogEntry.query.all()) == 3
    entry = LogEntry.query.filter_by(object_type='AccountInfo').one()
    assert entry.object_uri == f'/creditors/{i64_to_u64(C_ID)}/accounts/{i64_to_u64(D_ID)}/info'
    assert not entry.is_deleted

    p.process_account_purge_signal(debtor_id=D_ID, creditor_id=C_ID, creation_date=date(2020, 1, 2))
    p.process_pending_log_entries(C_ID)
    assert len(LogEntry.query.all()) == 3
Пример #3
0
def test_successful_transfer(db_session, account, current_ts):
    rt = p.initiate_running_transfer(
        creditor_id=C_ID,
        transfer_uuid=TEST_UUID,
        debtor_id=D_ID,
        amount=1000,
        recipient_uri='swpt:18446744073709551615/666',
        recipient='666',
        transfer_note_format='json',
        transfer_note='{}',
        deadline=current_ts + timedelta(seconds=1000),
        min_interest_rate=10.0,
    )
    p.process_prepared_direct_transfer_signal(
        debtor_id=D_ID,
        creditor_id=C_ID,
        transfer_id=123,
        coordinator_id=C_ID,
        coordinator_request_id=rt.coordinator_request_id + 1,
        locked_amount=0,
        recipient='666',
    )
    assert len(FinalizeTransferSignal.query.all()) == 1
    fts = FinalizeTransferSignal.query.filter_by(coordinator_request_id=rt.coordinator_request_id + 1).one()
    assert fts.creditor_id == C_ID
    assert fts.debtor_id == D_ID
    assert fts.transfer_id == 123
    assert fts.coordinator_id == C_ID
    assert fts.committed_amount == 0
    assert fts.transfer_note_format == ''
    assert fts.transfer_note == ''
    p.process_prepared_direct_transfer_signal(
        debtor_id=D_ID,
        creditor_id=C_ID,
        transfer_id=123,
        coordinator_id=C_ID,
        coordinator_request_id=rt.coordinator_request_id,
        locked_amount=0,
        recipient='666',
    )
    assert len(FinalizeTransferSignal.query.all()) == 2
    fts = FinalizeTransferSignal.query.filter_by(coordinator_request_id=rt.coordinator_request_id).one()
    assert fts.creditor_id == C_ID
    assert fts.debtor_id == D_ID
    assert fts.transfer_id == 123
    assert fts.coordinator_id == C_ID
    assert fts.committed_amount == 1000
    assert fts.transfer_note_format == 'json'
    assert fts.transfer_note == '{}'
    rt = RunningTransfer.query.one()
    assert rt.finalized_at is None
    assert rt.transfer_id == 123
    assert rt.error_code is None
    assert rt.total_locked_amount is None
    p.process_finalized_direct_transfer_signal(
        debtor_id=D_ID,
        creditor_id=C_ID,
        transfer_id=123,
        coordinator_id=C_ID,
        coordinator_request_id=rt.coordinator_request_id,
        committed_amount=1000,
        status_code='OK',
        total_locked_amount=100,
    )
    assert rt.finalized_at is not None
    assert rt.transfer_id == 123
    assert rt.error_code is None
    assert rt.total_locked_amount is None

    p.process_pending_log_entries(C_ID)
    le = LogEntry.query.filter_by(object_type_hint=LogEntry.OTH_TRANSFER).filter(LogEntry.object_update_id > 1).one()
    assert le.data is None
    assert le.data_finalized_at == rt.finalized_at
    assert le.data_error_code is None
Пример #4
0
def test_process_rejected_direct_transfer_signal(db_session, account, current_ts):
    rt = p.initiate_running_transfer(
        creditor_id=C_ID,
        transfer_uuid=TEST_UUID,
        debtor_id=D_ID,
        amount=1000,
        recipient_uri='swpt:18446744073709551615/666',
        recipient='666',
        transfer_note_format='json',
        transfer_note='{}',
        deadline=current_ts + timedelta(seconds=1000),
        min_interest_rate=10.0,
    )
    assert rt.creditor_id == C_ID
    assert rt.transfer_uuid == TEST_UUID
    assert rt.debtor_id == D_ID
    assert rt.amount == 1000
    assert rt.recipient_uri == 'swpt:18446744073709551615/666'
    assert rt.recipient == '666'
    assert rt.transfer_note_format == 'json'
    assert rt.transfer_note == '{}'
    assert isinstance(rt.initiated_at, datetime)
    assert rt.finalized_at is None
    assert rt.error_code is None
    assert rt.total_locked_amount is None
    assert rt.deadline == current_ts + timedelta(seconds=1000)
    assert rt.min_interest_rate == 10.0
    assert rt.coordinator_request_id is not None
    assert rt.transfer_id is None
    assert rt.latest_update_id == 1
    assert isinstance(rt.latest_update_ts, datetime)

    pts = PrepareTransferSignal.query.one()
    assert pts.creditor_id == C_ID
    assert pts.debtor_id == D_ID
    assert pts.coordinator_request_id == rt.coordinator_request_id
    assert pts.recipient == rt.recipient
    assert pts.min_interest_rate == rt.min_interest_rate
    assert 500 <= pts.max_commit_delay <= 1500

    p.process_rejected_direct_transfer_signal(
        coordinator_id=C_ID,
        coordinator_request_id=rt.coordinator_request_id,
        status_code='TEST_ERROR',
        total_locked_amount=600,
        debtor_id=D_ID,
        creditor_id=C_ID,
    )
    rt = RunningTransfer.query.one()
    assert rt.creditor_id == C_ID
    assert rt.transfer_uuid == TEST_UUID
    assert rt.debtor_id == D_ID
    assert rt.amount == 1000
    assert rt.recipient_uri == 'swpt:18446744073709551615/666'
    assert rt.recipient == '666'
    assert rt.transfer_note_format == 'json'
    assert rt.transfer_note == '{}'
    assert isinstance(rt.initiated_at, datetime)
    assert rt.finalized_at is not None
    assert rt.error_code == 'TEST_ERROR'
    assert rt.total_locked_amount == 600
    assert rt.deadline == current_ts + timedelta(seconds=1000)
    assert rt.min_interest_rate == 10.0
    assert rt.coordinator_request_id is not None
    assert rt.transfer_id is None
    assert rt.latest_update_id == 2
    assert isinstance(rt.latest_update_ts, datetime)

    p.process_pending_log_entries(C_ID)
    le = LogEntry.query.filter_by(object_type_hint=LogEntry.OTH_TRANSFER).filter(LogEntry.object_update_id > 1).one()
    assert le.data is None
    assert le.data_finalized_at == rt.finalized_at
    assert le.data_error_code == rt.error_code
Пример #5
0
 def get_ledger_update_entries_count():
     p.process_pending_log_entries(C_ID)
     return len(LogEntry.query.filter_by(object_type_hint=LogEntry.OTH_ACCOUNT_LEDGER).all())
Пример #6
0
 def get_committed_tranfer_entries_count():
     p.process_pending_log_entries(C_ID)
     return len(LogEntry.query.filter_by(object_type_hint=LogEntry.OTH_COMMITTED_TRANSFER).all())
Пример #7
0
 def get_info_entries_count():
     p.process_pending_log_entries(C_ID)
     return len(LogEntry.query.filter_by(object_type='AccountInfo').all())
Пример #8
0
def test_process_account_update_signal(db_session, account):
    AccountData.query.filter_by(creditor_id=C_ID, debtor_id=D_ID).update({
        'ledger_principal': 1001,
        'ledger_last_entry_id': 88,
        'ledger_last_transfer_number': 888,
    })

    def get_data():
        return AccountData.query.filter_by(creditor_id=C_ID, debtor_id=D_ID).one()

    ad = get_data()
    assert not ad.is_config_effectual
    assert ad.ledger_principal == 1001
    assert ad.ledger_last_entry_id == 88
    assert ad.negligible_amount == models.DEFAULT_NEGLIGIBLE_AMOUNT
    assert ad.config_flags == models.DEFAULT_CONFIG_FLAGS
    assert ad.last_change_seqnum == 0
    assert ad.config_error is None
    last_ts = ad.last_config_ts
    last_seqnum = ad.last_config_seqnum
    negligible_amount = ad.negligible_amount
    config_flags = ad.config_flags
    last_heartbeat_ts = ad.last_heartbeat_ts
    creation_date = date(2020, 1, 15)

    time.sleep(0.1)
    current_ts = datetime.now(tz=timezone.utc)
    assert last_heartbeat_ts < current_ts

    params = {
        'debtor_id': D_ID,
        'creditor_id': C_ID,
        'creation_date': creation_date,
        'last_change_ts': current_ts,
        'last_change_seqnum': 1,
        'principal': 1000,
        'interest': 12.0,
        'interest_rate': 5.0,
        'last_interest_rate_change_ts': current_ts - timedelta(days=1),
        'transfer_note_max_bytes': 500,
        'last_config_ts': last_ts,
        'last_config_seqnum': last_seqnum,
        'negligible_amount': negligible_amount,
        'config_flags': config_flags,
        'config_data': '',
        'account_id': str(C_ID),
        'debtor_info_iri': 'http://example.com',
        'debtor_info_content_type': 'text/plain',
        'debtor_info_sha256': 32 * b'\xff',
        'last_transfer_number': 22,
        'last_transfer_committed_at': current_ts - timedelta(days=2),
        'ts': current_ts,
        'ttl': 0,
    }

    p.process_account_update_signal(**params)
    ad = get_data()
    assert last_heartbeat_ts == ad.last_heartbeat_ts
    assert ad.last_change_seqnum == 0
    assert not ad.is_config_effectual
    assert ad.config_error is None

    params['ttl'] = 10000
    p.process_account_update_signal(**params)
    ad = get_data()
    assert ad.last_heartbeat_ts > last_heartbeat_ts
    assert ad.is_config_effectual
    assert ad.creation_date == creation_date
    assert ad.last_change_ts == current_ts
    assert ad.last_change_seqnum == 1
    assert ad.principal == 1000
    assert ad.interest == 12.0
    assert ad.interest_rate == 5.0
    assert ad.last_interest_rate_change_ts == current_ts - timedelta(days=1)
    assert ad.transfer_note_max_bytes == 500
    assert ad.last_config_ts == last_ts
    assert ad.last_config_seqnum == last_seqnum
    assert ad.account_id == str(C_ID)
    assert ad.debtor_info_iri == 'http://example.com'
    assert ad.debtor_info_content_type == 'text/plain'
    assert ad.debtor_info_sha256 == 32 * b'\xff'
    assert ad.last_transfer_number == 22
    assert ad.last_transfer_committed_at == current_ts - timedelta(days=2)
    assert ad.config_error is None

    p.process_account_update_signal(**params)
    assert ad.last_change_seqnum == 1

    p.process_rejected_config_signal(
        debtor_id=D_ID,
        creditor_id=C_ID,
        config_ts=last_ts,
        config_seqnum=last_seqnum,
        negligible_amount=negligible_amount,
        config_data='',
        config_flags=config_flags,
        rejection_code='TEST_CONFIG_ERROR',
    )
    ad = get_data()
    assert ad.config_error == 'TEST_CONFIG_ERROR'

    params['last_change_seqnum'] = 2
    params['principal'] = 1100
    params['negligible_amount'] = 3.33
    params['config_flags'] = 77
    p.process_account_update_signal(**params)
    ad = get_data()
    assert ad.last_change_seqnum == 2
    assert ad.principal == 1100
    assert ad.negligible_amount == negligible_amount
    assert ad.config_flags == config_flags
    assert not ad.is_config_effectual
    assert ad.config_error == 'TEST_CONFIG_ERROR'

    params['last_change_seqnum'] = 3
    params['negligible_amount'] = negligible_amount
    params['config_flags'] = config_flags
    p.process_account_update_signal(**params)
    ad = get_data()
    assert ad.last_change_seqnum == 3
    assert ad.is_config_effectual
    assert ad.config_error is None

    params['creation_date'] = creation_date + timedelta(days=2)
    p.process_account_update_signal(**params)
    ad = get_data()
    assert ad.last_change_seqnum == 3
    assert ad.is_config_effectual
    assert ad.creation_date == creation_date + timedelta(days=2)
    assert ad.config_error is None
    assert ad.ledger_principal == 0
    assert ad.ledger_last_entry_id == 89
    assert ad.ledger_last_transfer_number == 0

    # Discard orphaned account.
    params['debtor_id'] = 1235
    params['last_change_seqnum'] = 1
    params['negligible_amount'] = 2.0
    p.process_account_update_signal(**params)
    cas = ConfigureAccountSignal.query.filter_by(creditor_id=C_ID, debtor_id=1235).one()
    assert cas.negligible_amount > 1e22
    assert cas.config_flags & AccountData.CONFIG_SCHEDULED_FOR_DELETION_FLAG

    params['last_change_seqnum'] = 2
    params['negligible_amount'] = models.DEFAULT_NEGLIGIBLE_AMOUNT
    params['config_flags'] = AccountData.CONFIG_SCHEDULED_FOR_DELETION_FLAG
    p.process_account_update_signal(**params)
    assert ConfigureAccountSignal.query.filter_by(creditor_id=C_ID, debtor_id=1235).one()

    assert list(p.get_creditors_with_pending_log_entries()) == [(C_ID,)]
    p.process_pending_log_entries(1235)
    p.process_pending_log_entries(C_ID)
    assert len(models.LogEntry.query.filter_by(object_type='AccountInfo').all()) == 3
    assert len(models.LogEntry.query.filter_by(object_type_hint=LogEntry.OTH_ACCOUNT_LEDGER).all()) == 1