From 97c3d50e6b750c89c254cc2686f7def708c30865 Mon Sep 17 00:00:00 2001 From: Davide Polonio Date: Tue, 18 Apr 2023 17:59:56 +0200 Subject: [PATCH] fix: bunch of TODOs --- .../i18n/TemplateContentGenerator.java | 2 - .../orm/CallbackQueryContextCleaner.java | 28 ++++- .../telegram/callbackquery/Field.java | 11 +- .../telegram/callbackquery/NotFound.java | 7 +- .../telegram/callbackquery/Processor.java | 12 +- .../i18n/TemplateContentGeneratorTest.java | 103 ++++++++++++++++++ ...ackQueryContextCleanerIntegrationTest.java | 81 ++++++++++++++ 7 files changed, 238 insertions(+), 6 deletions(-) create mode 100644 src/test/java/com/github/polpetta/mezzotre/i18n/TemplateContentGeneratorTest.java create mode 100644 src/test/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleanerIntegrationTest.java diff --git a/src/main/java/com/github/polpetta/mezzotre/i18n/TemplateContentGenerator.java b/src/main/java/com/github/polpetta/mezzotre/i18n/TemplateContentGenerator.java index 33438eb..ab8f959 100644 --- a/src/main/java/com/github/polpetta/mezzotre/i18n/TemplateContentGenerator.java +++ b/src/main/java/com/github/polpetta/mezzotre/i18n/TemplateContentGenerator.java @@ -8,8 +8,6 @@ import org.apache.velocity.VelocityContext; import org.apache.velocity.tools.ToolManager; import org.apache.velocity.util.StringBuilderWriter; -// FIXME tests - /** * This class aims to generate localized messages or strings either merging Velocity templates or * taking directly the keys from {@link java.util.ResourceBundle} diff --git a/src/main/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleaner.java b/src/main/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleaner.java index 190e664..677a6b1 100644 --- a/src/main/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleaner.java +++ b/src/main/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleaner.java @@ -1,5 +1,6 @@ package com.github.polpetta.mezzotre.orm; +import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext; import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext; import io.ebean.typequery.PString; import java.util.concurrent.CompletableFuture; @@ -8,7 +9,14 @@ import javax.inject.Inject; import javax.inject.Singleton; import org.slf4j.Logger; -// FIXME tests, doc +/** + * This class allows the system to lazily remove {@link CallbackQueryContextCleaner} entries that + * are no more needed, using the {@link BatchBeanCleanerService} service under the hood. + * + * @author Davide Polonio + * @see BatchBeanCleanerService + * @since 1.0 + */ @Singleton public class CallbackQueryContextCleaner { @@ -26,11 +34,29 @@ public class CallbackQueryContextCleaner { this.log = log; } + /** + * Add a group of {@link com.github.polpetta.mezzotre.orm.model.CallbackQueryContext} to be + * removed + * + * @param id the group id of the {@link + * com.github.polpetta.mezzotre.orm.model.CallbackQueryContext} + * @return a {@link CompletableFuture} with an {@link Integer} indicating how many entries have + * been deleted from the persistence layer + * @see CallbackQueryContext#getEntryGroup() + */ public CompletableFuture removeGroupAsync(String id) { log.trace("CallbackQueryContext entry group " + id + " queued for removal"); return batchBeanCleanerService.removeAsync(id, ENTRY_GROUP.get()); } + /** + * Add a single {@link com.github.polpetta.mezzotre.orm.model.CallbackQueryContext} to be removed + * + * @param id the id of the {@link CallbackQueryContext} to remove + * @return a {@link CompletableFuture} with an {@link Integer} indicating how many entries have + * been deleted from the persistence layer. Can be 0 or 1. + * @see CallbackQueryContext#getId() + */ public CompletableFuture removeIdAsync(String id) { log.trace("CallbackQueryContext single entity " + id + " queued for removal"); return batchBeanCleanerService.removeAsync(id, SINGLE_ENTRY.get()); diff --git a/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Field.java b/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Field.java index f66a1eb..14d6968 100644 --- a/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Field.java +++ b/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Field.java @@ -1,6 +1,11 @@ package com.github.polpetta.mezzotre.telegram.callbackquery; -// FIXME doc +/** + * This interface is a placeholder to keep all fields in one place + * + * @author Davide Polonio + * @since 1.0 + */ public interface Field { /** * Additional fields that are related to {@code changeLanguage} event @@ -22,6 +27,10 @@ public interface Field { } } + /** + * Enumerator with custom fields for {@link + * com.github.polpetta.mezzotre.telegram.callbackquery.ShowHelp} callback query + */ enum ShowHelp { InvokedFromHelpMessage("invokedFromHelpMessage"); diff --git a/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/NotFound.java b/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/NotFound.java index a35720d..901d6f9 100644 --- a/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/NotFound.java +++ b/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/NotFound.java @@ -10,7 +10,12 @@ import java.util.concurrent.CompletableFuture; import javax.inject.Inject; import org.slf4j.Logger; -// FIXME doc +/** + * Callback query event that is triggered only when a corresponding event is not found in the system + * + * @author Davide Polonio + * @since 1.0 + */ public class NotFound implements Processor { private final Logger log; diff --git a/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Processor.java b/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Processor.java index ecba2a4..0707a50 100644 --- a/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Processor.java +++ b/src/main/java/com/github/polpetta/mezzotre/telegram/callbackquery/Processor.java @@ -23,11 +23,21 @@ public interface Processor { */ String getEventName(); - // FIXME javadoc + /** + * Determines if the Callback Query can directly be invoked by the user, such as starting an + * interaction from this point or invoking it from an inline query button + * + * @return true if it is, false otherwise + */ default boolean canBeDirectlyInvokedByTheUser() { return false; } + /** + * Gives the ability to print a localized version of the event, in a pretty way + * + * @return the key for the corresponding localization string + */ default Optional getPrettyPrintLocaleKeyName() { return Optional.empty(); } diff --git a/src/test/java/com/github/polpetta/mezzotre/i18n/TemplateContentGeneratorTest.java b/src/test/java/com/github/polpetta/mezzotre/i18n/TemplateContentGeneratorTest.java new file mode 100644 index 0000000..2d8df23 --- /dev/null +++ b/src/test/java/com/github/polpetta/mezzotre/i18n/TemplateContentGeneratorTest.java @@ -0,0 +1,103 @@ +package com.github.polpetta.mezzotre.i18n; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.Locale; +import java.util.ResourceBundle; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.app.VelocityEngine; +import org.apache.velocity.tools.ToolContext; +import org.apache.velocity.tools.ToolManager; +import org.apache.velocity.util.StringBuilderWriter; +import org.jetbrains.annotations.NotNull; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.junit.jupiter.api.parallel.ExecutionMode; +import org.mockito.ArgumentCaptor; + +@Execution(ExecutionMode.CONCURRENT) +class TemplateContentGeneratorTest { + + private LocalizedMessageFactory fakeLocalizedMessageFactory; + private TemplateContentGenerator templateContentGenerator; + + @BeforeEach + void setUp() { + fakeLocalizedMessageFactory = mock(LocalizedMessageFactory.class); + templateContentGenerator = new TemplateContentGenerator(fakeLocalizedMessageFactory); + } + + @Test + void shouldCallMergeTemplateWithRightArguments() { + final VelocityEngine fakeVelocityEngine = mock(VelocityEngine.class); + final ToolManager fakeToolManager = mock(ToolManager.class); + when(fakeToolManager.getVelocityEngine()).thenReturn(fakeVelocityEngine); + final ToolContext fakeToolContext = mock(ToolContext.class); + when(fakeToolManager.createContext()).thenReturn(fakeToolContext); + when(fakeLocalizedMessageFactory.createVelocityToolManager(Locale.forLanguageTag("en-US"))) + .thenReturn(fakeToolManager); + + templateContentGenerator.mergeTemplate( + ctx -> { + ctx.put("testtesttest", "value"); + }, + "en-US", + "a/fake/path.vm"); + + final ArgumentCaptor velocityContextArgumentCaptor = + ArgumentCaptor.forClass(VelocityContext.class); + verify(fakeVelocityEngine, times(1)) + .mergeTemplate( + eq("a/fake/path.vm"), + eq(StandardCharsets.UTF_8.name()), + velocityContextArgumentCaptor.capture(), + any(StringBuilderWriter.class)); + + final VelocityContext gotVelocityContext = velocityContextArgumentCaptor.getValue(); + assertEquals("value", gotVelocityContext.get("testtesttest")); + } + + @Test + void shouldCallLocaleFactoryWhenRetrievingKey() { + final Locale enUS = Locale.forLanguageTag("en-US"); + + class StubRB extends ResourceBundle { + + @Override + protected Object handleGetObject(@NotNull String s) { + return "testtest123"; + } + + @NotNull + @Override + public Enumeration getKeys() { + return new Enumeration() { + + private boolean next = true; + + @Override + public boolean hasMoreElements() { + final boolean toRet = next; + next = !next; + return toRet; + } + + @Override + public String nextElement() { + return "doens't matter"; + } + }; + } + } + + final StubRB stubRB = new StubRB(); + when(fakeLocalizedMessageFactory.createResourceBundle(any())).thenReturn(stubRB); + + final String got = templateContentGenerator.getString(enUS, "a.string"); + assertEquals("testtest123", got); + } +} diff --git a/src/test/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleanerIntegrationTest.java b/src/test/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleanerIntegrationTest.java new file mode 100644 index 0000000..de487f2 --- /dev/null +++ b/src/test/java/com/github/polpetta/mezzotre/orm/CallbackQueryContextCleanerIntegrationTest.java @@ -0,0 +1,81 @@ +package com.github.polpetta.mezzotre.orm; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +import com.github.polpetta.mezzotre.helper.Loader; +import com.github.polpetta.mezzotre.helper.TestConfig; +import io.ebean.Database; +import io.ebean.typequery.PString; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.mockito.ArgumentCaptor; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +@Tag("slow") +@Tag("database") +@Tag("velocity") +@Testcontainers +class CallbackQueryContextCleanerIntegrationTest { + + @Container + private final PostgreSQLContainer postgresServer = + new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE); + + private Database database; + + private BatchBeanCleanerService fakeBatchBeanCleanerService; + private CallbackQueryContextCleaner callbackQueryContextCleaner; + + @BeforeEach + void setUp() throws Exception { + database = + Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer)); + + fakeBatchBeanCleanerService = mock(BatchBeanCleanerService.class); + + callbackQueryContextCleaner = + new CallbackQueryContextCleaner( + fakeBatchBeanCleanerService, + LoggerFactory.getLogger(CallbackQueryContextCleaner.class)); + } + + @Test + @Timeout(value = 1, unit = TimeUnit.MINUTES) + void shouldDeleteByGroupId() throws Exception { + when(fakeBatchBeanCleanerService.removeAsync(eq("an id"), any())) + .thenReturn(CompletableFuture.completedFuture(1)); + + final Integer got = callbackQueryContextCleaner.removeGroupAsync("an id").get(); + + final ArgumentCaptor pStringArgumentCaptor = ArgumentCaptor.forClass(PString.class); + verify(fakeBatchBeanCleanerService, times(1)) + .removeAsync(eq("an id"), pStringArgumentCaptor.capture()); + assertEquals(1, got); + assertEquals("entryGroup", pStringArgumentCaptor.getValue().toString()); + } + + @Test + @Timeout(value = 1, unit = TimeUnit.MINUTES) + void shouldDeleteById() throws Exception { + when(fakeBatchBeanCleanerService.removeAsync(eq("an id"), any())) + .thenReturn(CompletableFuture.completedFuture(1)); + + final Integer got = callbackQueryContextCleaner.removeIdAsync("an id").get(); + + final ArgumentCaptor pStringArgumentCaptor = ArgumentCaptor.forClass(PString.class); + verify(fakeBatchBeanCleanerService, times(1)) + .removeAsync(eq("an id"), pStringArgumentCaptor.capture()); + assertEquals(1, got); + assertEquals("id", pStringArgumentCaptor.getValue().toString()); + } +}