From 818f507aa4d8bd7a04545bc66b4fd7cc4d53b4a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Wed, 8 Mar 2017 19:26:37 +0100 Subject: [PATCH] Skip investments until CAPTCHA is gone (fixes #103) The seemingly unrelated changes in this commit are there to increase mutation coverage in robozonky-app over the threshold. --- .../notifications/InvestmentSkippedEvent.java | 37 +++++++++++++ .../app/investing/InvestmentTracker.java | 4 ++ .../robozonky/app/investing/Investor.java | 19 +++++-- .../app/version/VersionRetriever.java | 4 +- .../app/investing/WireInvestorTest.java | 52 ++++++++++++++++++ .../app/version/VersionRetrieverTest.java | 38 +++++++++++++ .../robozonky/installer/panels/Util.java | 8 +-- .../email/InvestmentSkippedEventListener.java | 53 +++++++++++++++++++ .../email/SupportedListener.java | 16 ++++++ .../files/FileStoringListenerService.java | 3 ++ .../files/InvestmentSkippedEventListener.java | 37 +++++++++++++ .../email/investment-skipped.ftl | 13 +++++ .../email/AbstractListenerTest.java | 2 + .../email/notifications-enabled.cfg | 1 + 14 files changed, 278 insertions(+), 9 deletions(-) create mode 100644 robozonky-api/src/main/java/com/github/triceo/robozonky/api/notifications/InvestmentSkippedEvent.java create mode 100644 robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/InvestmentSkippedEventListener.java create mode 100644 robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/InvestmentSkippedEventListener.java create mode 100644 robozonky-notifications/src/main/resources/com/github/triceo/robozonky/notifications/email/investment-skipped.ftl diff --git a/robozonky-api/src/main/java/com/github/triceo/robozonky/api/notifications/InvestmentSkippedEvent.java b/robozonky-api/src/main/java/com/github/triceo/robozonky/api/notifications/InvestmentSkippedEvent.java new file mode 100644 index 000000000..7f26e4c02 --- /dev/null +++ b/robozonky-api/src/main/java/com/github/triceo/robozonky/api/notifications/InvestmentSkippedEvent.java @@ -0,0 +1,37 @@ +/* + * Copyright 2017 Lukáš Petrovický + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.triceo.robozonky.api.notifications; + +import com.github.triceo.robozonky.api.strategies.Recommendation; + +/** + * Fired when an event was skipped by the investment algorithm due to CAPTCHA, to be evaluated later after CAPTCHA + * expires. + */ +public final class InvestmentSkippedEvent extends Event { + + private final Recommendation recommendation; + + public InvestmentSkippedEvent(final Recommendation recommendation) { + this.recommendation = recommendation; + } + + public Recommendation getRecommendation() { + return recommendation; + } + +} diff --git a/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/InvestmentTracker.java b/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/InvestmentTracker.java index ef7b86862..6f509cc26 100644 --- a/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/InvestmentTracker.java +++ b/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/InvestmentTracker.java @@ -128,6 +128,10 @@ public synchronized boolean isSeenBefore(final int loanId) { return this.seenLoans.contains(loanId); } + public synchronized boolean isDiscarded(final int loanId) { + return this.discardedLoans.contains(loanId); + } + /** * Mark a given loan as no longer relevant for this session. * diff --git a/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/Investor.java b/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/Investor.java index 888c88565..b917928e0 100644 --- a/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/Investor.java +++ b/robozonky-app/src/main/java/com/github/triceo/robozonky/app/investing/Investor.java @@ -31,6 +31,7 @@ import com.github.triceo.robozonky.api.notifications.InvestmentMadeEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRejectedEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRequestedEvent; +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; import com.github.triceo.robozonky.api.notifications.LoanRecommendedEvent; import com.github.triceo.robozonky.api.notifications.StrategyCompletedEvent; import com.github.triceo.robozonky.api.notifications.StrategyStartedEvent; @@ -81,9 +82,18 @@ static Optional actuallyInvest(final Recommendation recommendation, final String providerId = api.getConfirmationProviderId().orElse("-"); switch (response.getType()) { case REJECTED: - Events.fire(new InvestmentRejectedEvent(recommendation, balance.intValue(), providerId)); - tracker.discardLoan(loanId); - return Optional.empty(); + return api.getConfirmationProviderId().map(c -> { + Events.fire(new InvestmentRejectedEvent(recommendation, balance.intValue(), providerId)); + // rejected through a confirmation provider => forget + tracker.discardLoan(loanId); + return Optional.empty(); + }).orElseGet(() -> { + // rejected due to no confirmation provider => make available for direct investment later + Events.fire(new InvestmentSkippedEvent(recommendation)); + Investor.LOGGER.debug("Loan #{} protected by CAPTCHA, will check back later.", loanId); + tracker.ignoreLoan(loanId); + return Optional.empty(); + }); case DELEGATED: Events.fire(new InvestmentDelegatedEvent(recommendation, balance.intValue(), providerId)); if (recommendation.isConfirmationRequired()) { @@ -101,9 +111,10 @@ static Optional actuallyInvest(final Recommendation recommendation, tracker.makeInvestment(i); return Optional.of(i); case SEEN_BEFORE: + Events.fire(new InvestmentSkippedEvent(recommendation)); return Optional.empty(); default: - throw new IllegalStateException("Not possible. "); + throw new IllegalStateException("Not possible."); } } diff --git a/robozonky-app/src/main/java/com/github/triceo/robozonky/app/version/VersionRetriever.java b/robozonky-app/src/main/java/com/github/triceo/robozonky/app/version/VersionRetriever.java index 10cec14a6..c7ea6c83f 100644 --- a/robozonky-app/src/main/java/com/github/triceo/robozonky/app/version/VersionRetriever.java +++ b/robozonky-app/src/main/java/com/github/triceo/robozonky/app/version/VersionRetriever.java @@ -93,7 +93,7 @@ static String findLastStable(final Set versions) { .orElseThrow(() -> new IllegalStateException("Impossible.")); } - private static VersionIdentifier parseNodeList(final NodeList nodeList) { + static VersionIdentifier parseNodeList(final NodeList nodeList) { final SortedSet versions = new TreeSet<>(new VersionComparator()); for (int i = 0; i < nodeList.getLength(); i++) { final String version = nodeList.item(i).getTextContent(); @@ -103,7 +103,7 @@ private static VersionIdentifier parseNodeList(final NodeList nodeList) { final String stable = VersionRetriever.findLastStable(versions); // and check if it is followed by any other versions final SortedSet tail = versions.tailSet(stable); - if (tail.isEmpty()) { + if (tail.size() == 1) { return new VersionIdentifier(stable); } else { return new VersionIdentifier(stable, tail.last()); diff --git a/robozonky-app/src/test/java/com/github/triceo/robozonky/app/investing/WireInvestorTest.java b/robozonky-app/src/test/java/com/github/triceo/robozonky/app/investing/WireInvestorTest.java index 460f05abe..5b30233e8 100644 --- a/robozonky-app/src/test/java/com/github/triceo/robozonky/app/investing/WireInvestorTest.java +++ b/robozonky-app/src/test/java/com/github/triceo/robozonky/app/investing/WireInvestorTest.java @@ -28,6 +28,7 @@ import com.github.triceo.robozonky.api.notifications.InvestmentMadeEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRejectedEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRequestedEvent; +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; import com.github.triceo.robozonky.api.remote.entities.Investment; import com.github.triceo.robozonky.api.strategies.LoanDescriptor; import com.github.triceo.robozonky.api.strategies.Recommendation; @@ -130,6 +131,57 @@ public void investmentDelegated() { Assertions.assertThat(t2.isSeenBefore(loanId)).isTrue(); } + @Test + public void investmentDelegatedButExpectedConfirmed() { + final LoanDescriptor ld = AbstractInvestingTest.mockLoanDescriptor(); + final Collection availableLoans = Collections.singletonList(ld); + final InvestmentTracker t = new InvestmentTracker(availableLoans, BigDecimal.valueOf(10000)); + final Recommendation r = ld.recommend(200, true).get(); + final int loanId = ld.getLoan().getId(); + final ZonkyProxy api = Mockito.mock(ZonkyProxy.class); + Mockito.when(api.invest(ArgumentMatchers.eq(r), ArgumentMatchers.anyBoolean())) + .thenReturn(new ZonkyResponse(ZonkyResponseType.DELEGATED)); + Mockito.when(api.getConfirmationProviderId()).thenReturn(Optional.of("something")); + Assertions.assertThat(t.isDiscarded(loanId)).isFalse(); + final Optional result = Investor.actuallyInvest(r, api, t); + Assertions.assertThat(t.isDiscarded(loanId)).isTrue(); + Assertions.assertThat(result).isEmpty(); + // validate event + final List newEvents = this.getNewEvents(); + Assertions.assertThat(newEvents).hasSize(2); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(newEvents.get(0)).isInstanceOf(InvestmentRequestedEvent.class); + softly.assertThat(newEvents.get(1)).isInstanceOf(InvestmentDelegatedEvent.class); + }); + // check that discard information is persisted + final InvestmentTracker t2 = new InvestmentTracker(availableLoans, BigDecimal.valueOf(10000)); + Assertions.assertThat(t2.isDiscarded(loanId)).isTrue(); + } + + @Test + public void investmentIgnoredWhenNoConfirmationProviderAndCaptcha() { + final LoanDescriptor ld = AbstractInvestingTest.mockLoanDescriptor(); + final Collection availableLoans = Collections.singletonList(ld); + final InvestmentTracker t = new InvestmentTracker(availableLoans, BigDecimal.valueOf(10000)); + final Recommendation r = ld.recommend(200).get(); + final int loanId = ld.getLoan().getId(); + final ZonkyProxy api = Mockito.mock(ZonkyProxy.class); + Mockito.when(api.invest(ArgumentMatchers.eq(r), ArgumentMatchers.anyBoolean())) + .thenReturn(new ZonkyResponse(ZonkyResponseType.REJECTED)); + Mockito.when(api.getConfirmationProviderId()).thenReturn(Optional.empty()); + Assertions.assertThat(t.isSeenBefore(loanId)).isFalse(); + final Optional result = Investor.actuallyInvest(r, api, t); + Assertions.assertThat(t.isSeenBefore(loanId)).isTrue(); + Assertions.assertThat(result).isEmpty(); + // validate event + final List newEvents = this.getNewEvents(); + Assertions.assertThat(newEvents).hasSize(2); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(newEvents.get(0)).isInstanceOf(InvestmentRequestedEvent.class); + softly.assertThat(newEvents.get(1)).isInstanceOf(InvestmentSkippedEvent.class); + }); + } + @Test public void investmentSuccessful() { final BigDecimal oldBalance = BigDecimal.valueOf(10000); diff --git a/robozonky-app/src/test/java/com/github/triceo/robozonky/app/version/VersionRetrieverTest.java b/robozonky-app/src/test/java/com/github/triceo/robozonky/app/version/VersionRetrieverTest.java index c7c9333b7..5474bc566 100644 --- a/robozonky-app/src/test/java/com/github/triceo/robozonky/app/version/VersionRetrieverTest.java +++ b/robozonky-app/src/test/java/com/github/triceo/robozonky/app/version/VersionRetrieverTest.java @@ -19,7 +19,12 @@ import java.util.Collections; import org.assertj.core.api.Assertions; +import org.assertj.core.api.SoftAssertions; import org.junit.Test; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; public class VersionRetrieverTest { @@ -43,4 +48,37 @@ public void checkNoStable() { .isInstanceOf(IllegalStateException.class); } + @Test + public void parseSingleNodeList() { + final String version = "1.2.3"; + final Node n = Mockito.mock(Node.class); + Mockito.when(n.getTextContent()).thenReturn(version); + final NodeList l = Mockito.mock(NodeList.class); + Mockito.when(l.getLength()).thenReturn(1); + Mockito.when(l.item(ArgumentMatchers.eq(0))).thenReturn(n); + final VersionIdentifier actual = VersionRetriever.parseNodeList(l); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(actual.getLatestStable()).isEqualTo(version); + softly.assertThat(actual.getLatestUnstable()).isEmpty(); + }); + } + + @Test + public void parseLongerNodeList() { + final String version = "1.2.3", version2 = "1.2.4-SNAPSHOT"; + final Node n1 = Mockito.mock(Node.class); + Mockito.when(n1.getTextContent()).thenReturn(version); + final Node n2 = Mockito.mock(Node.class); + Mockito.when(n2.getTextContent()).thenReturn(version2); + final NodeList l = Mockito.mock(NodeList.class); + Mockito.when(l.getLength()).thenReturn(2); + Mockito.when(l.item(ArgumentMatchers.eq(0))).thenReturn(n1); + Mockito.when(l.item(ArgumentMatchers.eq(1))).thenReturn(n2); + final VersionIdentifier actual = VersionRetriever.parseNodeList(l); + SoftAssertions.assertSoftly(softly -> { + softly.assertThat(actual.getLatestStable()).isEqualTo(version); + softly.assertThat(actual.getLatestUnstable()).contains(version2); + }); + } + } diff --git a/robozonky-installer/robozonky-installer-panels/src/main/java/com/github/triceo/robozonky/installer/panels/Util.java b/robozonky-installer/robozonky-installer-panels/src/main/java/com/github/triceo/robozonky/installer/panels/Util.java index 6f5920272..55634ee86 100644 --- a/robozonky-installer/robozonky-installer-panels/src/main/java/com/github/triceo/robozonky/installer/panels/Util.java +++ b/robozonky-installer/robozonky-installer-panels/src/main/java/com/github/triceo/robozonky/installer/panels/Util.java @@ -55,9 +55,11 @@ public static Properties configureEmailNotifications(final InstallData data) { p.setProperty("smtp.port", toInt(Variables.SMTP_PORT.getValue(data))); p.setProperty("smtp.requiresStartTLS", toBoolean(Variables.SMTP_IS_TLS.getValue(data))); p.setProperty("smtp.requiresSslOnConnect", toBoolean(Variables.SMTP_IS_SSL.getValue(data))); - p.setProperty("investmentRejected.enabled", toBoolean(Variables.EMAIL_IS_INVESTMENT.getValue(data))); - p.setProperty("investmentMade.enabled", toBoolean(Variables.EMAIL_IS_INVESTMENT.getValue(data))); - p.setProperty("investmentDelegated.enabled", toBoolean(Variables.EMAIL_IS_INVESTMENT.getValue(data))); + final String isInvestmentEmailEnabled = toBoolean(Variables.EMAIL_IS_INVESTMENT.getValue(data)); + p.setProperty("investmentSkipped.enabled", isInvestmentEmailEnabled); + p.setProperty("investmentRejected.enabled", isInvestmentEmailEnabled); + p.setProperty("investmentMade.enabled", isInvestmentEmailEnabled); + p.setProperty("investmentDelegated.enabled", isInvestmentEmailEnabled); p.setProperty("balanceTracker.enabled", toBoolean(Variables.EMAIL_IS_BALANCE_OVER_200.getValue(data))); p.setProperty("balanceTracker.targetBalance", "200"); p.setProperty("roboZonkyDaemonFailed.enabled", toBoolean(Variables.EMAIL_IS_FAILURE.getValue(data))); diff --git a/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/InvestmentSkippedEventListener.java b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/InvestmentSkippedEventListener.java new file mode 100644 index 000000000..9bb41137e --- /dev/null +++ b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/InvestmentSkippedEventListener.java @@ -0,0 +1,53 @@ +/* + * Copyright 2017 Lukáš Petrovický + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.triceo.robozonky.notifications.email; + +import java.util.HashMap; +import java.util.Map; + +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; +import com.github.triceo.robozonky.api.remote.entities.Loan; + +final class InvestmentSkippedEventListener extends AbstractEmailingListener { + + public InvestmentSkippedEventListener(final ListenerSpecificNotificationProperties properties) { + super(properties); + } + + @Override + String getSubject(final InvestmentSkippedEvent event) { + return "Půjčka č. " + event.getRecommendation().getLoanDescriptor().getLoan().getId() + " dočasně přeskočena"; + } + + @Override + String getTemplateFileName() { + return "investment-skipped.ftl"; + } + + @Override + Map getData(final InvestmentSkippedEvent event) { + final Loan loan = event.getRecommendation().getLoanDescriptor().getLoan(); + final Map result = new HashMap<>(); + result.put("loanId", loan.getId()); + result.put("loanRecommendation", event.getRecommendation().getRecommendedInvestmentAmount()); + result.put("loanAmount", loan.getAmount()); + result.put("loanRating", loan.getRating().getCode()); + result.put("loanTerm", loan.getTermInMonths()); + return result; + } + +} diff --git a/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/SupportedListener.java b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/SupportedListener.java index 303f92214..751d30c9e 100644 --- a/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/SupportedListener.java +++ b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/email/SupportedListener.java @@ -22,6 +22,7 @@ import com.github.triceo.robozonky.api.notifications.InvestmentDelegatedEvent; import com.github.triceo.robozonky.api.notifications.InvestmentMadeEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRejectedEvent; +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; import com.github.triceo.robozonky.api.notifications.RemoteOperationFailedEvent; import com.github.triceo.robozonky.api.notifications.RoboZonkyCrashedEvent; import com.github.triceo.robozonky.api.notifications.RoboZonkyDaemonFailedEvent; @@ -46,6 +47,21 @@ public Class getEventType() { protected EventListener newListener(final ListenerSpecificNotificationProperties properties) { return new InvestmentMadeEventListener(properties); } + }, INVESTMENT_SKIPPED { + @Override + public String getId() { + return "investmentSkipped"; + } + + @Override + public Class getEventType() { + return InvestmentSkippedEvent.class; + } + + @Override + protected EventListener newListener(final ListenerSpecificNotificationProperties properties) { + return new InvestmentSkippedEventListener(properties); + } }, INVESTMENT_DELEGATED { @Override public String getId() { diff --git a/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/FileStoringListenerService.java b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/FileStoringListenerService.java index 4eb7f43a0..bd7a7e609 100644 --- a/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/FileStoringListenerService.java +++ b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/FileStoringListenerService.java @@ -24,6 +24,7 @@ import com.github.triceo.robozonky.api.notifications.InvestmentDelegatedEvent; import com.github.triceo.robozonky.api.notifications.InvestmentMadeEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRejectedEvent; +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; import com.github.triceo.robozonky.api.notifications.ListenerService; public final class FileStoringListenerService implements ListenerService { @@ -36,6 +37,8 @@ public Refreshable> findListener(final Class< return Refreshable.createImmutable((EventListener) new InvestmentRejectedEventListener()); } else if (Objects.equals(eventType, InvestmentDelegatedEvent.class)) { return Refreshable.createImmutable((EventListener) new InvestmentDelegatedEventListener()); + } else if (Objects.equals(eventType, InvestmentSkippedEvent.class)) { + return Refreshable.createImmutable((EventListener) new InvestmentSkippedEventListener()); } return Refreshable.createImmutable(null); } diff --git a/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/InvestmentSkippedEventListener.java b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/InvestmentSkippedEventListener.java new file mode 100644 index 000000000..441ac16b1 --- /dev/null +++ b/robozonky-notifications/src/main/java/com/github/triceo/robozonky/notifications/files/InvestmentSkippedEventListener.java @@ -0,0 +1,37 @@ +/* + * Copyright 2017 Lukáš Petrovický + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.github.triceo.robozonky.notifications.files; + +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; + +final class InvestmentSkippedEventListener extends AbstractFileStoringListener { + + @Override + int getLoanId(final InvestmentSkippedEvent event) { + return event.getRecommendation().getLoanDescriptor().getLoan().getId(); + } + + @Override + int getAmount(final InvestmentSkippedEvent event) { + return event.getRecommendation().getRecommendedInvestmentAmount(); + } + + @Override + String getSuffix() { + return "skipped"; + } +} diff --git a/robozonky-notifications/src/main/resources/com/github/triceo/robozonky/notifications/email/investment-skipped.ftl b/robozonky-notifications/src/main/resources/com/github/triceo/robozonky/notifications/email/investment-skipped.ftl new file mode 100644 index 000000000..03a373f60 --- /dev/null +++ b/robozonky-notifications/src/main/resources/com/github/triceo/robozonky/notifications/email/investment-skipped.ftl @@ -0,0 +1,13 @@ +Půjčka s následujícími parametry byla přeskočena: + +- Číslo půjčky: ${data.loanId?c} +- Rating: ${data.loanRating} +- Délka splácení: ${data.loanTerm?c} měsíců +- Požadovaná částka: ${data.loanAmount?c},- Kč +- Navržená výše investice: ${data.loanRecommendation?c},- Kč + +Informace o této půjčce jsou dostupné na následující adrese: +https://app.zonky.cz/#/marketplace/detail/${data.loanId?c}/ + +Pokud tato půjčka vydrží na tržišti až do vypršení CAPTCHA, robot se k ní vrátí. + diff --git a/robozonky-notifications/src/test/java/com/github/triceo/robozonky/notifications/email/AbstractListenerTest.java b/robozonky-notifications/src/test/java/com/github/triceo/robozonky/notifications/email/AbstractListenerTest.java index 169cd29f0..fc319ee64 100644 --- a/robozonky-notifications/src/test/java/com/github/triceo/robozonky/notifications/email/AbstractListenerTest.java +++ b/robozonky-notifications/src/test/java/com/github/triceo/robozonky/notifications/email/AbstractListenerTest.java @@ -32,6 +32,7 @@ import com.github.triceo.robozonky.api.notifications.InvestmentDelegatedEvent; import com.github.triceo.robozonky.api.notifications.InvestmentMadeEvent; import com.github.triceo.robozonky.api.notifications.InvestmentRejectedEvent; +import com.github.triceo.robozonky.api.notifications.InvestmentSkippedEvent; import com.github.triceo.robozonky.api.notifications.RemoteOperationFailedEvent; import com.github.triceo.robozonky.api.notifications.RoboZonkyCrashedEvent; import com.github.triceo.robozonky.api.notifications.RoboZonkyDaemonFailedEvent; @@ -78,6 +79,7 @@ public static Collection getListeners() { events.put(SupportedListener.INVESTMENT_DELEGATED.getEventType(), new InvestmentDelegatedEvent(recommendation, 200, "random")); events.put(SupportedListener.INVESTMENT_MADE.getEventType(), new InvestmentMadeEvent(i, 200)); + events.put(SupportedListener.INVESTMENT_SKIPPED.getEventType(), new InvestmentSkippedEvent(recommendation)); events.put(SupportedListener.INVESTMENT_REJECTED.getEventType(), new InvestmentRejectedEvent(recommendation, 200, "random")); events.put(SupportedListener.EXECUTION_STARTED.getEventType(), diff --git a/robozonky-notifications/src/test/resources/com/github/triceo/robozonky/notifications/email/notifications-enabled.cfg b/robozonky-notifications/src/test/resources/com/github/triceo/robozonky/notifications/email/notifications-enabled.cfg index c71f645f4..aab723d1b 100644 --- a/robozonky-notifications/src/test/resources/com/github/triceo/robozonky/notifications/email/notifications-enabled.cfg +++ b/robozonky-notifications/src/test/resources/com/github/triceo/robozonky/notifications/email/notifications-enabled.cfg @@ -7,6 +7,7 @@ smtp.username = user@seznam.cz smtp.password = pass investmentRejected.enabled = true +investmentSkipped.enabled = true investmentDelegated.enabled = true investmentMade.enabled = true balanceTracker.enabled = true