StrategyExecutor implements Supplier<Collection<R>> { @Override public Collection<R> get() { if (!operationDescriptor.isEnabled(tenant)) { logger.debug("Access to marketplace disabled by Zonky."); return Collections.emptyList(); } final Money currentBalance = tenant.getKnownBalanceUpperBound(); final Money minimum = operationDescriptor.getMinimumBalance(tenant); if (currentBalance.compareTo(minimum) < 0) { logger.debug("Asleep due to balance estimated below minimum. ({} < {})", currentBalance, minimum); return Collections.emptyList(); } return operationDescriptor.getStrategy(tenant) .map(this::invest) .orElseGet(() -> { logger.debug("Asleep as there is no strategy."); return Collections.emptyList(); }); } StrategyExecutor(final PowerTenant tenant, final OperationDescriptor<T, S, R> operationDescriptor); static StrategyExecutor<LoanDescriptor, InvestmentStrategy, Loan> forInvesting(final PowerTenant tenant); static StrategyExecutor<ParticipationDescriptor, PurchaseStrategy, Participation> forPurchasing(
            final PowerTenant tenant); @Override Collection<R> get();  }
@Test void purchasingNoStrategy() { final Participation mock = mock(ParticipationImpl.class); final ParticipationDescriptor pd = new ParticipationDescriptor(mock, () -> MockLoanBuilder.fresh()); final PowerTenant tenant = mockTenant(); final PurchasingOperationDescriptor d = mockPurchasingOperationDescriptor(pd); final StrategyExecutor<ParticipationDescriptor, PurchaseStrategy, Participation> exec = new StrategyExecutor<>( tenant, d); assertThat(exec.get()).isEmpty(); final List<Event> events = getEventsRequested(); assertThat(events).isEmpty(); }
@Test void purchasingNoneAccepted() { final Zonky zonky = harmlessZonky(); final Loan loan = new MockLoanBuilder() .set(LoanImpl::setAmount, Money.from(200)) .build(); when(zonky.getLoan(eq(loan.getId()))).thenReturn(loan); final Participation mock = mock(ParticipationImpl.class); when(mock.getRemainingPrincipal()).thenReturn(Money.from(250)); final ParticipationDescriptor pd = new ParticipationDescriptor(mock, () -> loan); final PowerTenant tenant = mockTenant(zonky); when(tenant.getPurchaseStrategy()).thenReturn(Optional.of(NONE_ACCEPTING_PURCHASE_STRATEGY)); final PurchasingOperationDescriptor d = mockPurchasingOperationDescriptor(pd); final StrategyExecutor<ParticipationDescriptor, PurchaseStrategy, Participation> exec = new StrategyExecutor<>( tenant, d); assertThat(exec.get()).isEmpty(); final List<Event> e = getEventsRequested(); assertThat(e).hasSize(2); assertSoftly(softly -> { softly.assertThat(e) .first() .isInstanceOf(PurchasingStartedEvent.class); softly.assertThat(e) .last() .isInstanceOf(PurchasingCompletedEvent.class); }); }
@Test void purchasingSomeAccepted() { final Loan loan = new MockLoanBuilder() .set(LoanImpl::setAmount, Money.from(100_000)) .set(LoanImpl::setRating, Rating.D) .set(LoanImpl::setRemainingInvestment, Money.from(1_000)) .set(LoanImpl::setReservedAmount, Money.from(0)) .set(LoanImpl::setMyInvestment, mockMyInvestment()) .set(LoanImpl::setDatePublished, OffsetDateTime.now()) .build(); final int loanId = loan.getId(); final Rating rating = loan.getRating(); final Zonky zonky = harmlessZonky(); when(zonky.getLoan(eq(loanId))).thenReturn(loan); final Participation mock = mock(ParticipationImpl.class); when(mock.getId()).thenReturn(1L); when(mock.getLoanId()).thenReturn(loanId); when(mock.getRemainingPrincipal()).thenReturn(Money.from(250)); when(mock.getRating()).thenReturn(rating); final ParticipationDescriptor pd = new ParticipationDescriptor(mock, () -> loan); final PowerTenant tenant = mockTenant(zonky); when(tenant.getPurchaseStrategy()).thenReturn(Optional.of(ALL_ACCEPTING_PURCHASE_STRATEGY)); final PurchasingOperationDescriptor d = mockPurchasingOperationDescriptor(pd); final StrategyExecutor<ParticipationDescriptor, PurchaseStrategy, Participation> exec = new StrategyExecutor<>( tenant, d); assertThat(exec.get()).isNotEmpty(); verify(zonky, never()).purchase(eq(mock)); final List<Event> e = getEventsRequested(); assertThat(e).hasSize(4); assertSoftly(softly -> { softly.assertThat(e) .first() .isInstanceOf(PurchasingStartedEvent.class); softly.assertThat(e.get(1)) .isInstanceOf(PurchaseRecommendedEvent.class); softly.assertThat(e.get(2)) .isInstanceOf(InvestmentPurchasedEvent.class); softly.assertThat(e) .last() .isInstanceOf(PurchasingCompletedEvent.class); }); assertThat(exec.get()).isEmpty(); }
@Test void tryPurchaseButZonkyFail() { final Loan loan = new MockLoanBuilder() .set(LoanImpl::setAmount, Money.from(100_000)) .set(LoanImpl::setRating, Rating.D) .set(LoanImpl::setRemainingInvestment, Money.from(1_000)) .set(LoanImpl::setReservedAmount, Money.from(0)) .set(LoanImpl::setMyInvestment, mockMyInvestment()) .set(LoanImpl::setDatePublished, OffsetDateTime.now()) .build(); final int loanId = loan.getId(); final Rating rating = loan.getRating(); final Zonky zonky = harmlessZonky(); when(zonky.getLoan(eq(loanId))).thenReturn(loan); doReturn(PurchaseResult.failure(new ClientErrorException(410))).when(zonky) .purchase(any()); final Participation mock = mock(ParticipationImpl.class); when(mock.getId()).thenReturn(1L); when(mock.getLoanId()).thenReturn(loanId); when(mock.getRemainingPrincipal()).thenReturn(Money.from(250)); when(mock.getRating()).thenReturn(rating); final ParticipationDescriptor pd = new ParticipationDescriptor(mock, () -> loan); final PowerTenant tenant = mockTenant(zonky, false); when(tenant.getPurchaseStrategy()).thenReturn(Optional.of(ALL_ACCEPTING_PURCHASE_STRATEGY)); final PurchasingOperationDescriptor d = mockPurchasingOperationDescriptor(pd); final StrategyExecutor<ParticipationDescriptor, PurchaseStrategy, Participation> exec = new StrategyExecutor<>( tenant, d); assertThat(exec.get()).isEmpty(); }
@Test void purchasingNoItems() { final Zonky zonky = harmlessZonky(); final PowerTenant tenant = mockTenant(zonky); final PurchasingOperationDescriptor d = mockPurchasingOperationDescriptor(); final StrategyExecutor<ParticipationDescriptor, PurchaseStrategy, Participation> exec = new StrategyExecutor<>( tenant, d); when(tenant.getPurchaseStrategy()).thenReturn(Optional.of(ALL_ACCEPTING_PURCHASE_STRATEGY)); assertThat(exec.get()).isEmpty(); final List<Event> e = getEventsRequested(); assertThat(e).isEmpty(); }
@Test void investingNoStrategy() { final Loan loan = new MockLoanBuilder() .set(LoanImpl::setAmount, Money.from(2)) .build(); final LoanDescriptor ld = new LoanDescriptor(loan); final Zonky z = AbstractZonkyLeveragingTest.harmlessZonky(); final PowerTenant tenant = mockTenant(z); final InvestingOperationDescriptor d = mockInvestingOperationDescriptor(ld); final StrategyExecutor<LoanDescriptor, InvestmentStrategy, Loan> exec = new StrategyExecutor<>(tenant, d); assertThat(exec.get()).isEmpty(); final List<Event> events = getEventsRequested(); assertThat(events).isEmpty(); }
@Test void investingNoItems() { final Zonky z = AbstractZonkyLeveragingTest.harmlessZonky(); final PowerTenant tenant = mockTenant(z); when(tenant.getInvestmentStrategy()).thenReturn(Optional.of(ALL_ACCEPTING_INVESTMENT_STRATEGY)); final InvestingOperationDescriptor d = mockInvestingOperationDescriptor(); final StrategyExecutor<LoanDescriptor, InvestmentStrategy, Loan> exec = new StrategyExecutor<>(tenant, d); assertThat(exec.get()).isEmpty(); }
@Test void investingNoneAccepted() { final Loan loan = new MockLoanBuilder() .set(LoanImpl::setAmount, Money.from(100_000)) .set(LoanImpl::setRating, Rating.D) .set(LoanImpl::setMyInvestment, mockMyInvestment()) .set(LoanImpl::setDatePublished, OffsetDateTime.now()) .build(); final int loanId = loan.getId(); final LoanDescriptor ld = new LoanDescriptor(loan); final Zonky z = harmlessZonky(); final PowerTenant tenant = mockTenant(z); when(tenant.getInvestmentStrategy()).thenReturn(Optional.of(NONE_ACCEPTING_INVESTMENT_STRATEGY)); when(z.getLoan(eq(loanId))).thenReturn(loan); final InvestingOperationDescriptor d = mockInvestingOperationDescriptor(ld); final StrategyExecutor<LoanDescriptor, InvestmentStrategy, Loan> exec = new StrategyExecutor<>(tenant, d); assertThat(exec.get()).isEmpty(); }
@Test void doesNotInvestWhenDisabled() { final Zonky zonky = harmlessZonky(); final PowerTenant tenant = mockTenant(zonky); final OperationDescriptor<LoanDescriptor, InvestmentStrategy, Loan> d = mock(OperationDescriptor.class); when(d.getLogger()).thenReturn(LogManager.getLogger()); when(d.isEnabled(any())).thenReturn(false); final StrategyExecutor<LoanDescriptor, InvestmentStrategy, Loan> e = new StrategyExecutor<>(tenant, d); assertThat(e.get()).isEmpty(); final List<Event> evt = getEventsRequested(); assertThat(evt).isEmpty(); }
@Test void forcesMarketplaceCheck() { final Instant now = Instant.now(); setClock(Clock.fixed(now, Defaults.ZONE_ID)); final Zonky zonky = harmlessZonky(); final Loan loan = new MockLoanBuilder() .set(LoanImpl::setRating, Rating.D) .build(); when(zonky.getAvailableLoans(any())).thenAnswer(i -> Stream.of(loan)); final PowerTenant tenant = mockTenant(zonky); when(tenant.getAvailability() .isAvailable()).thenReturn(true); when(tenant.getInvestmentStrategy()) .thenReturn(Optional.of((available, portfolio, restrictions) -> Stream.empty())); final OperationDescriptor<LoanDescriptor, InvestmentStrategy, Loan> d = new InvestingOperationDescriptor(); final StrategyExecutor<LoanDescriptor, InvestmentStrategy, Loan> e = new StrategyExecutor<>(tenant, d); assertThat(e.get()).isEmpty(); verify(zonky, times(1)).getLastPublishedLoanInfo(); verify(zonky, times(1)).getAvailableLoans(any()); assertThat(e.get()).isEmpty(); verify(zonky, times(2)).getLastPublishedLoanInfo(); verify(zonky, times(1)).getAvailableLoans(any()); setClock(Clock.fixed(now.plus(Duration.ofMinutes(61)), Defaults.ZONE_ID)); assertThat(e.get()).isEmpty(); verify(zonky, times(3)).getLastPublishedLoanInfo(); verify(zonky, times(2)).getAvailableLoans(any()); }