chore: perform refactor for db-cleaning and more testing
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
* Add Javadoc where missing * Solve some FIXMEs here and therepull/3/head
parent
3e094ec72a
commit
9ff5748964
|
@ -5,7 +5,8 @@ import com.github.polpetta.mezzotre.route.RouteDI;
|
|||
import com.github.polpetta.mezzotre.route.Telegram;
|
||||
import com.github.polpetta.mezzotre.telegram.callbackquery.CallbackQueryDI;
|
||||
import com.github.polpetta.mezzotre.telegram.command.CommandDI;
|
||||
import com.github.polpetta.mezzotre.util.di.ThreadPool;
|
||||
import com.github.polpetta.mezzotre.util.ServiceModule;
|
||||
import com.github.polpetta.mezzotre.util.UtilDI;
|
||||
import com.google.inject.*;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.name.Names;
|
||||
|
@ -24,7 +25,7 @@ public class App extends Jooby {
|
|||
(jooby) -> {
|
||||
final HashSet<Module> modules = new HashSet<>();
|
||||
modules.add(new OrmDI());
|
||||
modules.add(new ThreadPool());
|
||||
modules.add(new UtilDI());
|
||||
modules.add(new RouteDI());
|
||||
modules.add(new CommandDI());
|
||||
modules.add(new CallbackQueryDI());
|
||||
|
@ -55,6 +56,7 @@ public class App extends Jooby {
|
|||
install(
|
||||
injector.getInstance(Key.get(Extension.class, Names.named("flyWayMigrationExtension"))));
|
||||
install(injector.getInstance(Key.get(Extension.class, Names.named("ebeanExtension"))));
|
||||
install(injector.getInstance(Key.get(ServiceModule.class, Names.named("serviceModule"))));
|
||||
|
||||
decorator(new AccessLogHandler());
|
||||
decorator(new TransactionalRequest());
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
package com.github.polpetta.mezzotre;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.BatchBeanCleanerService;
|
||||
import com.google.common.util.concurrent.Service;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import io.jooby.Jooby;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class AppDI extends AbstractModule {
|
||||
|
@ -28,4 +33,13 @@ public class AppDI extends AbstractModule {
|
|||
public String getAppName() {
|
||||
return APPLICATION_NAME;
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("services")
|
||||
public List<Service> getApplicationServices(
|
||||
Logger logger,
|
||||
@Named("serviceRunningCheckTime") Pair<Integer, TimeUnit> serviceRunningCheckTime) {
|
||||
return List.of(new BatchBeanCleanerService(logger, serviceRunningCheckTime));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package com.github.polpetta.mezzotre.orm;
|
||||
|
||||
import com.google.common.util.concurrent.AbstractExecutionThreadService;
|
||||
import io.ebean.typequery.PString;
|
||||
import io.ebean.typequery.TQRootBean;
|
||||
import io.vavr.Tuple;
|
||||
import io.vavr.Tuple3;
|
||||
import io.vavr.control.Try;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class BatchBeanCleanerService extends AbstractExecutionThreadService {
|
||||
|
||||
private final LinkedBlockingDeque<
|
||||
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||
entriesToRemove;
|
||||
private final Logger log;
|
||||
private final Pair<Integer, TimeUnit> serviceRunningCheckTime;
|
||||
private final LinkedList<
|
||||
Consumer<Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>>
|
||||
deletionListener;
|
||||
|
||||
@Inject
|
||||
public BatchBeanCleanerService(
|
||||
Logger logger,
|
||||
@Named("serviceRunningCheckTime") Pair<Integer, TimeUnit> serviceRunningCheckTime) {
|
||||
this.log = logger;
|
||||
this.serviceRunningCheckTime = serviceRunningCheckTime;
|
||||
this.entriesToRemove = new LinkedBlockingDeque<>();
|
||||
this.deletionListener = new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() throws Exception {
|
||||
while (isRunning()) {
|
||||
// This statement is blocking for 1 sec if the queue is empty, and then it goes on - this way
|
||||
// we check if the service is still supposed to be up or what
|
||||
Optional.ofNullable(
|
||||
entriesToRemove.poll(
|
||||
serviceRunningCheckTime.getLeft(), serviceRunningCheckTime.getRight()))
|
||||
.ifPresent(
|
||||
entryToRemove -> {
|
||||
Try.of(
|
||||
() -> {
|
||||
final int deleted = entryToRemove._2().eq(entryToRemove._1()).delete();
|
||||
if (deleted > 0) {
|
||||
log.trace(
|
||||
"Bean with id " + entryToRemove._1() + " removed successfully ");
|
||||
} else {
|
||||
log.warn(
|
||||
"Bean(s) with id "
|
||||
+ entryToRemove._1()
|
||||
+ " for query "
|
||||
+ entryToRemove._2()
|
||||
+ " was not removed from the database because it was not"
|
||||
+ " found");
|
||||
}
|
||||
entryToRemove._3().complete(deleted);
|
||||
deletionListener.parallelStream().forEach(l -> l.accept(entryToRemove));
|
||||
return null;
|
||||
})
|
||||
.onFailure(ex -> entryToRemove._3().completeExceptionally(ex));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown() throws Exception {
|
||||
super.shutDown();
|
||||
entriesToRemove.clear();
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> removeAsync(
|
||||
String id, PString<? extends TQRootBean<?, ?>> row) {
|
||||
final CompletableFuture<Integer> jobExecution = new CompletableFuture<>();
|
||||
entriesToRemove.offer(Tuple.of(id, row, jobExecution));
|
||||
return jobExecution;
|
||||
}
|
||||
|
||||
public void addListener(
|
||||
Consumer<Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||
listener) {
|
||||
deletionListener.add(listener);
|
||||
}
|
||||
|
||||
public void removeListener(
|
||||
Consumer<Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||
listener) {
|
||||
deletionListener.remove(listener);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.github.polpetta.mezzotre.orm;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import io.ebean.typequery.PString;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.Supplier;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@Singleton
|
||||
public class CallbackQueryContextCleaner {
|
||||
|
||||
private static final Supplier<PString<QCallbackQueryContext>> ENTRY_GROUP =
|
||||
() -> new QCallbackQueryContext().entryGroup;
|
||||
private static final Supplier<PString<QCallbackQueryContext>> SINGLE_ENTRY =
|
||||
() -> new QCallbackQueryContext().id;
|
||||
|
||||
private final BatchBeanCleanerService batchBeanCleanerService;
|
||||
private final Logger log;
|
||||
|
||||
@Inject
|
||||
public CallbackQueryContextCleaner(BatchBeanCleanerService batchBeanCleanerService, Logger log) {
|
||||
this.batchBeanCleanerService = batchBeanCleanerService;
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> removeGroupAsync(String id) {
|
||||
log.trace("CallbackQueryContext entry group " + id + " queued for removal");
|
||||
return batchBeanCleanerService.removeAsync(id, ENTRY_GROUP.get());
|
||||
}
|
||||
|
||||
public CompletableFuture<Integer> removeIdAsync(String id) {
|
||||
log.trace("CallbackQueryContext single entity " + id + " queued for removal");
|
||||
return batchBeanCleanerService.removeAsync(id, SINGLE_ENTRY.get());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package com.github.polpetta.mezzotre.orm.telegram;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.pengrad.telegrambot.model.Message;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* ChatUtil provides utilities for interacting in an easier way when manipulating chat related data.
|
||||
* In particular, it provides an easy and DRY way to modify a set of frequently-accessed fields, or
|
||||
* simply to extract data performing all the necessary steps
|
||||
*
|
||||
* @author Davide Polonio
|
||||
* @since 1.0
|
||||
*/
|
||||
public class ChatUtil {
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
@Inject
|
||||
public ChatUtil(Clock clock) {
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the {@link ChatContext} with the values passed to the method. The updated values are
|
||||
* then saved in the database
|
||||
*
|
||||
* @param chat the chat that will be updated with the new {@link ChatContext} values
|
||||
* @param stepName the step name to set
|
||||
* @param stageNumber the stage number to set
|
||||
* @param additionalFields if there are, additional custom fields that will be added to {@link
|
||||
* ChatContext}. Note that these values will have to be manually retrieved since no method is
|
||||
* available for custom entries. Use {@link Collections#emptyMap()} if you don't wish to add
|
||||
* any additional field
|
||||
*/
|
||||
public void updateChatContext(
|
||||
TgChat chat, String stepName, int stageNumber, Map<String, Object> additionalFields) {
|
||||
final ChatContext chatContext = chat.getChatContext();
|
||||
chatContext.setStage(stepName);
|
||||
chatContext.setStep(stageNumber);
|
||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
||||
additionalFields.forEach(chatContext::setAdditionalProperty);
|
||||
|
||||
chat.setChatContext(chatContext);
|
||||
chat.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a possible {@link TgChat} from the given {@link CallbackQueryContext} or the {@link
|
||||
* Update}. Note that first the {@link CallbackQueryContext} is checked, then {@link Update} is
|
||||
* used as fallback. Once the Telegram chat id is retrieved from one of these two, the persistence
|
||||
* layer is queried and if there is any {@link TgChat} which the corresponding ID it is returned
|
||||
* to the caller.
|
||||
*
|
||||
* @param callbackQueryContext the {@link CallbackQueryContext} coming from a Telegram callback
|
||||
* query
|
||||
* @param update the whole {@link Update} object that is sent from Telegram servers
|
||||
* @return an {@link Optional} that may contain a {@link TgChat} if the ID is found in the
|
||||
* persistence layer, otherwise {@link Optional#empty()} is given back either if the id is not
|
||||
* present or if the chat is not present in the persistence layer.
|
||||
*/
|
||||
public Optional<TgChat> extractChat(CallbackQueryContext callbackQueryContext, Update update) {
|
||||
return Optional.of(callbackQueryContext.getFields().getTelegramChatId())
|
||||
.map(Double::longValue)
|
||||
// If we're desperate, search in the message for the chat id
|
||||
.filter(chatId -> chatId != 0L && chatId != Long.MIN_VALUE)
|
||||
.or(
|
||||
() ->
|
||||
Optional.ofNullable(update.callbackQuery().message())
|
||||
.map(Message::messageId)
|
||||
.map(Long::valueOf))
|
||||
.filter(chatId -> chatId != 0L && chatId != Long.MIN_VALUE)
|
||||
.flatMap(chatId -> new QTgChat().id.eq(chatId).findOneOrEmpty());
|
||||
}
|
||||
|
||||
public <T> T cleanCallbackQuery(T toReturn, CallbackQueryContext callbackQueryContext) {
|
||||
new QCallbackQueryContext().entryGroup.eq(callbackQueryContext.getId()).delete();
|
||||
return toReturn;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.google.inject.assistedinject.Assisted;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
|
@ -12,11 +13,16 @@ import org.slf4j.Logger;
|
|||
public class NotFound implements Processor {
|
||||
|
||||
private final Logger log;
|
||||
private final CallbackQueryContextCleaner callbackQueryContextCleaner;
|
||||
private final String eventName;
|
||||
|
||||
@Inject
|
||||
public NotFound(Logger logger, @Assisted String eventName) {
|
||||
public NotFound(
|
||||
Logger logger,
|
||||
CallbackQueryContextCleaner callbackQueryContextCleaner,
|
||||
@Assisted String eventName) {
|
||||
this.log = logger;
|
||||
this.callbackQueryContextCleaner = callbackQueryContextCleaner;
|
||||
this.eventName = eventName;
|
||||
}
|
||||
|
||||
|
@ -33,6 +39,7 @@ public class NotFound implements Processor {
|
|||
+ callbackQueryContext.getId()
|
||||
+ " event name "
|
||||
+ eventName);
|
||||
callbackQueryContextCleaner.removeGroupAsync(callbackQueryContext.getEntryGroup());
|
||||
return CompletableFuture.completedFuture(Optional.empty());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
|
@ -36,17 +37,23 @@ public class SelectLanguageTutorial implements Processor {
|
|||
private final TemplateContentGenerator templateContentGenerator;
|
||||
private final Logger log;
|
||||
private final UUIDGenerator uuidGenerator;
|
||||
private final ChatUtil chatUtil;
|
||||
private final CallbackQueryContextCleaner callbackQueryContextCleaner;
|
||||
|
||||
@Inject
|
||||
public SelectLanguageTutorial(
|
||||
@Named("eventThreadPool") Executor threadPool,
|
||||
TemplateContentGenerator templateContentGenerator,
|
||||
Logger log,
|
||||
UUIDGenerator uuidGenerator) {
|
||||
UUIDGenerator uuidGenerator,
|
||||
ChatUtil chatUtil,
|
||||
CallbackQueryContextCleaner callbackQueryContextCleaner) {
|
||||
this.threadPool = threadPool;
|
||||
this.templateContentGenerator = templateContentGenerator;
|
||||
this.log = log;
|
||||
this.uuidGenerator = uuidGenerator;
|
||||
this.chatUtil = chatUtil;
|
||||
this.callbackQueryContextCleaner = callbackQueryContextCleaner;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -59,7 +66,8 @@ public class SelectLanguageTutorial implements Processor {
|
|||
CallbackQueryContext callbackQueryContext, Update update) {
|
||||
return CompletableFuture.supplyAsync(
|
||||
() ->
|
||||
Util.extractChat(callbackQueryContext, update)
|
||||
chatUtil
|
||||
.extractChat(callbackQueryContext, update)
|
||||
.map(
|
||||
tgChat -> {
|
||||
tgChat.setLocale(
|
||||
|
@ -90,6 +98,14 @@ public class SelectLanguageTutorial implements Processor {
|
|||
.thenApplyAsync(
|
||||
// If we are here then we're sure there is at least a chat associated with this callback
|
||||
tgChat -> {
|
||||
if (tgChat.getHasHelpBeenShown()) {
|
||||
log.trace(
|
||||
"Help message has already been shown for this user - no help button will be"
|
||||
+ " present");
|
||||
} else {
|
||||
log.trace("No help message shown yet - the help button will be added");
|
||||
}
|
||||
|
||||
final String message =
|
||||
templateContentGenerator.mergeTemplate(
|
||||
velocityContext ->
|
||||
|
@ -99,14 +115,7 @@ public class SelectLanguageTutorial implements Processor {
|
|||
|
||||
log.trace("SelectLanguageTutorial event - message to send back: " + message);
|
||||
|
||||
final String callBackGroupToDelete = callbackQueryContext.getEntryGroup();
|
||||
final int delete =
|
||||
new QCallbackQueryContext().entryGroup.eq(callBackGroupToDelete).delete();
|
||||
log.trace(
|
||||
"Deleted "
|
||||
+ delete
|
||||
+ " entries regarding callback group "
|
||||
+ callBackGroupToDelete);
|
||||
callbackQueryContextCleaner.removeGroupAsync(callbackQueryContext.getEntryGroup());
|
||||
|
||||
final Optional<Integer> messageId = Util.extractMessageId(update);
|
||||
BaseRequest<?, ?> baseRequest;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.telegram.model.Help;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
|
@ -9,13 +11,28 @@ import com.pengrad.telegrambot.model.request.ParseMode;
|
|||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import com.pengrad.telegrambot.request.EditMessageText;
|
||||
import com.pengrad.telegrambot.request.SendMessage;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
/**
|
||||
* ShowHelp callback query event edits a previous message (if available), otherwise it sends a new
|
||||
* message with a list of available commands (the one implemented by {@link
|
||||
* com.github.polpetta.mezzotre.telegram.command.Processor}), and a list of buttons that are
|
||||
* callback queries (the ones implemented by {@link Processor}). The class is a Singleton since it
|
||||
* is stateless and having multiple instances would only be a waste of memory.
|
||||
*
|
||||
* @author Davide Polonio
|
||||
* @since 1.0
|
||||
* @see com.github.polpetta.mezzotre.telegram.command.Help same functionality but as {@link
|
||||
* com.github.polpetta.mezzotre.telegram.command.Processor} command
|
||||
*/
|
||||
@Singleton
|
||||
class ShowHelp implements Processor {
|
||||
|
||||
public static String EVENT_NAME = "showHelp";
|
||||
|
@ -24,8 +41,9 @@ class ShowHelp implements Processor {
|
|||
private final Map<String, com.github.polpetta.mezzotre.telegram.command.Processor>
|
||||
tgCommandProcessors;
|
||||
private final Map<String, Processor> eventProcessor;
|
||||
private final ChatUtil chatUtil;
|
||||
private final CallbackQueryContextCleaner callbackQueryContextCleaner;
|
||||
|
||||
// FIXME tests
|
||||
@Inject
|
||||
public ShowHelp(
|
||||
@Named("eventThreadPool") Executor threadPool,
|
||||
|
@ -33,12 +51,15 @@ class ShowHelp implements Processor {
|
|||
@Named("commandProcessor")
|
||||
Map<String, com.github.polpetta.mezzotre.telegram.command.Processor> tgCommandProcessors,
|
||||
@Named("eventProcessors")
|
||||
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor>
|
||||
eventProcessor) {
|
||||
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> eventProcessor,
|
||||
ChatUtil chatUtil,
|
||||
CallbackQueryContextCleaner callbackQueryContextCleaner) {
|
||||
this.threadPool = threadPool;
|
||||
this.modelHelp = modelHelp;
|
||||
this.tgCommandProcessors = tgCommandProcessors;
|
||||
this.eventProcessor = eventProcessor;
|
||||
this.chatUtil = chatUtil;
|
||||
this.callbackQueryContextCleaner = callbackQueryContextCleaner;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,26 +72,40 @@ class ShowHelp implements Processor {
|
|||
CallbackQueryContext callbackQueryContext, Update update) {
|
||||
return CompletableFuture.supplyAsync(
|
||||
() ->
|
||||
Util.extractChat(callbackQueryContext, update)
|
||||
chatUtil
|
||||
.extractChat(callbackQueryContext, update) // FIXME callbackquerycontext removal?
|
||||
.map(
|
||||
chat -> {
|
||||
final String message = modelHelp.getMessage(chat, tgCommandProcessors);
|
||||
chatUtil.updateChatContext(chat, EVENT_NAME, 0, Collections.emptyMap());
|
||||
chat.setHasHelpBeenShown(true);
|
||||
chat.save();
|
||||
final Optional<Integer> messageId = Util.extractMessageId(update);
|
||||
final InlineKeyboardButton[] buttons =
|
||||
modelHelp.generateInlineKeyBoardButton(chat, eventProcessor);
|
||||
BaseRequest<?, ?> request;
|
||||
if (messageId.isPresent()) {
|
||||
final EditMessageText editMessageText =
|
||||
new EditMessageText(chat.getId(), messageId.get(), message);
|
||||
new EditMessageText(chat.getId(), messageId.get(), message)
|
||||
.parseMode(ParseMode.Markdown);
|
||||
if (buttons.length > 0) {
|
||||
editMessageText.replyMarkup(new InlineKeyboardMarkup(buttons));
|
||||
}
|
||||
request = editMessageText;
|
||||
} else {
|
||||
final SendMessage sendMessage =
|
||||
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown);
|
||||
if (buttons.length > 0) {
|
||||
sendMessage.replyMarkup(new InlineKeyboardMarkup(buttons));
|
||||
}
|
||||
request = sendMessage;
|
||||
}
|
||||
|
||||
// We don't check if the element is removed or what - we just schedule its
|
||||
// removal, then it is someone else problem
|
||||
callbackQueryContextCleaner.removeGroupAsync(
|
||||
callbackQueryContext.getEntryGroup());
|
||||
|
||||
return request;
|
||||
}),
|
||||
threadPool);
|
||||
|
|
|
@ -1,28 +1,12 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||
import com.pengrad.telegrambot.model.Message;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import java.util.Optional;
|
||||
|
||||
public class Util {
|
||||
|
||||
public static Optional<TgChat> extractChat(
|
||||
CallbackQueryContext callbackQueryContext, Update update) {
|
||||
return Optional.of(callbackQueryContext.getFields().getTelegramChatId())
|
||||
.map(Double::longValue)
|
||||
// If we're desperate, search in the message for the chat id
|
||||
.or(
|
||||
() ->
|
||||
Optional.ofNullable(update.callbackQuery().message())
|
||||
.map(Message::messageId)
|
||||
.map(Long::valueOf))
|
||||
.filter(chatId -> chatId != 0L && chatId != Long.MIN_VALUE)
|
||||
.flatMap(chatId -> new QTgChat().id.eq(chatId).findOneOrEmpty());
|
||||
}
|
||||
|
||||
// FIXME tests, doc
|
||||
public static Optional<Integer> extractMessageId(Update update) {
|
||||
return Optional.ofNullable(update.callbackQuery().message()).map(Message::messageId);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
package com.github.polpetta.mezzotre.telegram.command;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||
import com.pengrad.telegrambot.model.request.ParseMode;
|
||||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import com.pengrad.telegrambot.request.SendMessage;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
@ -14,8 +16,19 @@ import java.util.concurrent.CompletableFuture;
|
|||
import java.util.concurrent.Executor;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
public class Help implements Processor {
|
||||
/**
|
||||
* The Help command class allows the user to be informed in any moment of commands that are
|
||||
* available in the system. If possible, it also provides a {@code reply_markup} keyboard ({@link
|
||||
* InlineKeyboardMarkup} for easier bot interactions
|
||||
*
|
||||
* @author Davide Polonio
|
||||
* @since 1.0
|
||||
* @see com.github.polpetta.mezzotre.telegram.callbackquery.ShowHelp for event-based help
|
||||
*/
|
||||
@Singleton
|
||||
class Help implements Processor {
|
||||
|
||||
private static final String TRIGGERING_STAGING_NAME = "/help";
|
||||
|
||||
|
@ -24,6 +37,7 @@ public class Help implements Processor {
|
|||
private final Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor>
|
||||
eventProcessor;
|
||||
private final com.github.polpetta.mezzotre.telegram.model.Help modelHelp;
|
||||
private final ChatUtil chatUtil;
|
||||
|
||||
@Inject
|
||||
public Help(
|
||||
|
@ -31,11 +45,13 @@ public class Help implements Processor {
|
|||
@Named("commandProcessor") Map<String, Processor> tgCommandProcessors,
|
||||
@Named("eventProcessors")
|
||||
Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> eventProcessor,
|
||||
com.github.polpetta.mezzotre.telegram.model.Help modelHelp) {
|
||||
com.github.polpetta.mezzotre.telegram.model.Help modelHelp,
|
||||
ChatUtil chatUtil) {
|
||||
this.threadPool = threadPool;
|
||||
this.tgCommandProcessors = tgCommandProcessors;
|
||||
this.eventProcessor = eventProcessor;
|
||||
this.modelHelp = modelHelp;
|
||||
this.chatUtil = chatUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -48,6 +64,9 @@ public class Help implements Processor {
|
|||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
final String message = modelHelp.getMessage(chat, tgCommandProcessors);
|
||||
chatUtil.updateChatContext(chat, TRIGGERING_STAGING_NAME, 0, Collections.emptyMap());
|
||||
chat.setHasHelpBeenShown(true);
|
||||
chat.save();
|
||||
|
||||
final SendMessage sendMessage =
|
||||
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown);
|
||||
|
|
|
@ -11,7 +11,14 @@ import java.util.Set;
|
|||
import java.util.concurrent.CompletableFuture;
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class NotFound implements Processor {
|
||||
/**
|
||||
* Generates a "Command not found" message
|
||||
*
|
||||
* @author Davide Polonio
|
||||
* @since 1.0
|
||||
* @see com.github.polpetta.mezzotre.telegram.callbackquery.NotFound for the event-based version
|
||||
*/
|
||||
class NotFound implements Processor {
|
||||
|
||||
private final String commandName;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
package com.github.polpetta.mezzotre.telegram.command;
|
||||
|
||||
public interface NotFoundFactory {
|
||||
interface NotFoundFactory {
|
||||
NotFound create(String commandName);
|
||||
}
|
||||
|
|
|
@ -3,12 +3,11 @@ package com.github.polpetta.mezzotre.telegram.command;
|
|||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.telegram.callbackquery.Field;
|
||||
import com.github.polpetta.mezzotre.telegram.callbackquery.Value;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.google.inject.Singleton;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
|
@ -16,6 +15,7 @@ import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
|||
import com.pengrad.telegrambot.model.request.ParseMode;
|
||||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import com.pengrad.telegrambot.request.SendMessage;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
@ -32,15 +32,15 @@ import org.slf4j.Logger;
|
|||
* @since 1.0
|
||||
*/
|
||||
@Singleton
|
||||
public class Start implements Processor {
|
||||
class Start implements Processor {
|
||||
|
||||
private static final String TRIGGERING_STAGING_NAME = "/start";
|
||||
|
||||
private final Executor threadPool;
|
||||
private final Logger log;
|
||||
private final UUIDGenerator uuidGenerator;
|
||||
private final Clock clock;
|
||||
private final String applicationName;
|
||||
private final ChatUtil chatUtil;
|
||||
|
||||
private final TemplateContentGenerator templateContentGenerator;
|
||||
|
||||
|
@ -50,14 +50,14 @@ public class Start implements Processor {
|
|||
@Named("eventThreadPool") Executor threadPool,
|
||||
Logger log,
|
||||
UUIDGenerator uuidGenerator,
|
||||
Clock clock,
|
||||
@Named("applicationName") String applicationName) {
|
||||
@Named("applicationName") String applicationName,
|
||||
ChatUtil chatUtil) {
|
||||
this.templateContentGenerator = templateContentGenerator;
|
||||
this.threadPool = threadPool;
|
||||
this.log = log;
|
||||
this.uuidGenerator = uuidGenerator;
|
||||
this.clock = clock;
|
||||
this.applicationName = applicationName;
|
||||
this.chatUtil = chatUtil;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -87,12 +87,8 @@ public class Start implements Processor {
|
|||
"template/telegram/start.vm");
|
||||
log.trace("Start command - message to send back: " + message);
|
||||
|
||||
final ChatContext chatContext = chat.getChatContext();
|
||||
chatContext.setStage(TRIGGERING_STAGING_NAME);
|
||||
chatContext.setStep(0);
|
||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
||||
chat.setChatContext(chatContext);
|
||||
chat.save();
|
||||
// FIXME bug!! Show help button set to true but its fake news
|
||||
chatUtil.updateChatContext(chat, TRIGGERING_STAGING_NAME, 0, Collections.emptyMap());
|
||||
|
||||
// To get the messageId we should send the message first, then save it in the database!
|
||||
final String groupId = uuidGenerator.generateAsString();
|
||||
|
|
|
@ -5,10 +5,8 @@ import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
|||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.telegram.callbackquery.Field;
|
||||
import com.github.polpetta.mezzotre.telegram.command.Processor;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -21,21 +19,16 @@ public class Help {
|
|||
private static final String TRIGGERING_STAGING_NAME = "/help";
|
||||
|
||||
private final TemplateContentGenerator templateContentGenerator;
|
||||
private final Clock clock;
|
||||
private final UUIDGenerator uuidGenerator;
|
||||
|
||||
@Inject
|
||||
public Help(
|
||||
TemplateContentGenerator templateContentGenerator, Clock clock, UUIDGenerator uuidGenerator) {
|
||||
|
||||
public Help(TemplateContentGenerator templateContentGenerator, UUIDGenerator uuidGenerator) {
|
||||
this.templateContentGenerator = templateContentGenerator;
|
||||
this.clock = clock;
|
||||
this.uuidGenerator = uuidGenerator;
|
||||
}
|
||||
|
||||
public String getMessage(TgChat chat, Map<String, Processor> tgCommandProcessors) {
|
||||
final String message =
|
||||
templateContentGenerator.mergeTemplate(
|
||||
return templateContentGenerator.mergeTemplate(
|
||||
velocityContext -> {
|
||||
velocityContext.put(
|
||||
"commands",
|
||||
|
@ -44,24 +37,12 @@ public class Help {
|
|||
.map(
|
||||
p ->
|
||||
Pair.of(
|
||||
p.getTriggerKeywords().stream()
|
||||
.sorted()
|
||||
.collect(Collectors.toList()),
|
||||
p.getTriggerKeywords().stream().sorted().collect(Collectors.toList()),
|
||||
p.getLocaleDescriptionKeyword()))
|
||||
.collect(Collectors.toList()));
|
||||
},
|
||||
chat.getLocale(),
|
||||
"template/telegram/help.vm");
|
||||
|
||||
// FIXME this shouldn't stay here. We need to move it into another method
|
||||
final ChatContext chatContext = chat.getChatContext();
|
||||
chatContext.setStage(TRIGGERING_STAGING_NAME);
|
||||
chatContext.setStep(0);
|
||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
||||
chat.setChatContext(chatContext);
|
||||
chat.setHasHelpBeenShown(true);
|
||||
chat.save();
|
||||
return message;
|
||||
}
|
||||
|
||||
public InlineKeyboardButton[] generateInlineKeyBoardButton(
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
package com.github.polpetta.mezzotre.util;
|
||||
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.google.common.util.concurrent.Service;
|
||||
import com.google.common.util.concurrent.ServiceManager;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.Jooby;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ServiceModule implements Extension {
|
||||
|
||||
private final ServiceManager serviceManager;
|
||||
|
||||
@Inject
|
||||
public ServiceModule(@Named("services") List<Service> services) {
|
||||
serviceManager = new ServiceManager(services);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean lateinit() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void install(@NotNull Jooby application) throws Exception {
|
||||
final CompletableFuture<Void> initialization = new CompletableFuture<>();
|
||||
serviceManager.addListener(
|
||||
new ServiceManager.Listener() {
|
||||
@Override
|
||||
public void healthy() {
|
||||
super.healthy();
|
||||
application.getLog().info("All internal application services are up and running");
|
||||
initialization.complete(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failure(@NotNull Service service) {
|
||||
super.failure(service);
|
||||
initialization.completeExceptionally(service.failureCause());
|
||||
}
|
||||
},
|
||||
MoreExecutors.directExecutor());
|
||||
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(
|
||||
new Thread(
|
||||
() -> {
|
||||
try {
|
||||
serviceManager.stopAsync().awaitStopped(Duration.ofSeconds(15));
|
||||
} catch (TimeoutException ex) {
|
||||
application
|
||||
.getLog()
|
||||
.warn(
|
||||
"Unable to correctly stop all the services, got the following error"
|
||||
+ " while stopping: "
|
||||
+ ex.getMessage());
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}));
|
||||
|
||||
// Blocking call to wait for all the services to be up and running
|
||||
serviceManager.startAsync();
|
||||
initialization.get();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package com.github.polpetta.mezzotre.util;
|
||||
|
||||
import com.google.common.util.concurrent.Service;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
public class UtilDI extends AbstractModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("eventThreadPool")
|
||||
public Executor getEventExecutor() {
|
||||
return ForkJoinPool.commonPool();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("longJobThreadPool")
|
||||
public Executor getLongJobExecutor() {
|
||||
return Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("serviceModule")
|
||||
public ServiceModule getServiceModule(@Named("services") List<Service> moduleList) {
|
||||
return new ServiceModule(moduleList);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("serviceRunningCheckTime")
|
||||
public Pair<Integer, TimeUnit> getTime() {
|
||||
return Pair.of(5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
package com.github.polpetta.mezzotre.util.di;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ForkJoinPool;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
public class ThreadPool extends AbstractModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("eventThreadPool")
|
||||
public Executor getEventExecutor() {
|
||||
return ForkJoinPool.commonPool();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("longJobThreadPool")
|
||||
public Executor getLongJobExecutor() {
|
||||
return Executors.newCachedThreadPool();
|
||||
}
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
${i18n.help.description}:
|
||||
|
||||
#foreach(${command} in ${commands})
|
||||
## FIXME this is not displayed correctly in telegram!
|
||||
*#foreach(${key} in ${command.left}) ${key}#end: ${i18n.get(${command.right})}
|
||||
-#foreach(${key} in ${command.left}) ${key}#end: ${i18n.get(${command.right})}
|
||||
#end
|
||||
|
||||
${i18n.help.buttonsToo}
|
|
@ -1,3 +1,3 @@
|
|||
**${i18n.start.helloFirstName.insert(${firstName})}**
|
||||
*${i18n.start.helloFirstName.insert(${firstName})}*
|
||||
|
||||
${i18n.start.description.insert(${programName})}
|
|
@ -3,15 +3,21 @@ package com.github.polpetta.mezzotre;
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.google.inject.*;
|
||||
import com.github.polpetta.mezzotre.util.ServiceModule;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Module;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Stage;
|
||||
import io.jooby.*;
|
||||
import io.jooby.ebean.EbeanModule;
|
||||
import io.jooby.flyway.FlywayModule;
|
||||
import io.jooby.hikari.HikariModule;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class UnitTest {
|
||||
|
@ -37,6 +43,20 @@ public class UnitTest {
|
|||
public Extension getEbeanExtension() {
|
||||
return mock(EbeanModule.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("serviceModule")
|
||||
public ServiceModule getServiceModule() {
|
||||
return mock(ServiceModule.class);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("serviceRunningCheckTime")
|
||||
public Pair<Integer, TimeUnit> getTime() {
|
||||
return Pair.of(0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -2,18 +2,22 @@ package com.github.polpetta.mezzotre.helper;
|
|||
|
||||
import com.github.polpetta.mezzotre.App;
|
||||
import com.github.polpetta.mezzotre.route.RouteDI;
|
||||
import com.github.polpetta.mezzotre.util.ServiceModule;
|
||||
import com.google.common.util.concurrent.Service;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Provides;
|
||||
import com.google.inject.Singleton;
|
||||
import com.google.inject.Stage;
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import io.jooby.Extension;
|
||||
import io.jooby.ebean.EbeanModule;
|
||||
import io.jooby.flyway.FlywayModule;
|
||||
import io.jooby.hikari.HikariModule;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Named;
|
||||
import javax.inject.Singleton;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
|
||||
|
@ -50,6 +54,20 @@ public class IntegrationAppFactory {
|
|||
public Extension getEbeanExtension() {
|
||||
return new EbeanModule();
|
||||
}
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
@Named("serviceModule")
|
||||
public ServiceModule getServiceModule(@Named("services") List<Service> moduleList) {
|
||||
return new ServiceModule(moduleList);
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("serviceRunningCheckTime")
|
||||
public Pair<Integer, TimeUnit> getTime() {
|
||||
return Pair.of(0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public static App loadCustomDbApplication() {
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package com.github.polpetta.mezzotre.orm;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import com.github.polpetta.mezzotre.helper.Loader;
|
||||
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import io.ebean.Database;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.jupiter.api.*;
|
||||
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 BatchBeanCleanerServiceIntegrationTest {
|
||||
|
||||
@Container
|
||||
private final PostgreSQLContainer<?> postgresServer =
|
||||
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||
|
||||
private Database database;
|
||||
private BatchBeanCleanerService batchBeanCleanerService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
database =
|
||||
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||
batchBeanCleanerService =
|
||||
new BatchBeanCleanerService(
|
||||
LoggerFactory.getLogger(BatchBeanCleanerService.class),
|
||||
Pair.of(0, TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
if (batchBeanCleanerService != null) {
|
||||
batchBeanCleanerService.stopAsync().awaitTerminated(Duration.ofSeconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||
void shouldRemoveEntryFromDatabase() throws Exception {
|
||||
final CallbackQueryContext callbackQueryContext =
|
||||
new CallbackQueryContext("1234", "4567", new CallbackQueryMetadata());
|
||||
callbackQueryContext.save();
|
||||
|
||||
batchBeanCleanerService.startAsync();
|
||||
batchBeanCleanerService.awaitRunning();
|
||||
|
||||
final CompletableFuture<Integer> integerCompletableFuture =
|
||||
batchBeanCleanerService.removeAsync("1234", new QCallbackQueryContext().id);
|
||||
|
||||
final Integer gotDeletion = integerCompletableFuture.get();
|
||||
|
||||
assertEquals(1, gotDeletion);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||
void shouldRemoveMultipleEntriesFromDatabase() throws Exception {
|
||||
final CallbackQueryContext callbackQueryContext =
|
||||
new CallbackQueryContext("1234", "4567", new CallbackQueryMetadata());
|
||||
callbackQueryContext.save();
|
||||
final CallbackQueryContext callbackQueryContext2 =
|
||||
new CallbackQueryContext("4321", "4567", new CallbackQueryMetadata());
|
||||
callbackQueryContext2.save();
|
||||
|
||||
batchBeanCleanerService.startAsync();
|
||||
batchBeanCleanerService.awaitRunning();
|
||||
|
||||
final CompletableFuture<Integer> integerCompletableFuture =
|
||||
batchBeanCleanerService.removeAsync("4567", new QCallbackQueryContext().entryGroup);
|
||||
final CompletableFuture<Integer> integerCompletableFuture2 =
|
||||
batchBeanCleanerService.removeAsync("4567", new QCallbackQueryContext().entryGroup);
|
||||
|
||||
final Integer gotDeletion1 = integerCompletableFuture.get();
|
||||
final Integer gotDeletion2 = integerCompletableFuture2.get();
|
||||
|
||||
assertEquals(2, gotDeletion1);
|
||||
assertEquals(0, gotDeletion2);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package com.github.polpetta.mezzotre.orm.telegram;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import com.github.polpetta.mezzotre.helper.Loader;
|
||||
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.google.gson.Gson;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import io.ebean.Database;
|
||||
import java.util.Optional;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Tag;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.testcontainers.containers.PostgreSQLContainer;
|
||||
import org.testcontainers.junit.jupiter.Container;
|
||||
import org.testcontainers.junit.jupiter.Testcontainers;
|
||||
|
||||
@Tag("slow")
|
||||
@Tag("database")
|
||||
@Testcontainers
|
||||
class ChatUtilIntegrationTest {
|
||||
|
||||
private static Gson gson;
|
||||
|
||||
@Container
|
||||
private final PostgreSQLContainer<?> postgresServer =
|
||||
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||
|
||||
private Database database;
|
||||
private Clock fakeClock;
|
||||
private ChatUtil chatUtil;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
database =
|
||||
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||
|
||||
fakeClock = mock(Clock.class);
|
||||
chatUtil = new ChatUtil(fakeClock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExtractChatFromCallBackQueryContext() {
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ "\"update_id\":10000,\n"
|
||||
+ "\"message\":{\n"
|
||||
+ " \"date\":1441645532,\n"
|
||||
+ " \"chat\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message_id\":1365,\n"
|
||||
+ " \"from\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\":\"/help\"\n"
|
||||
+ "}\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
final CallbackQueryContext callbackQueryContext =
|
||||
new CallbackQueryContext(
|
||||
"123",
|
||||
"456",
|
||||
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||
.withTelegramChatId(69420L)
|
||||
.build());
|
||||
|
||||
final TgChat expectedChat = new TgChat(69420L, new ChatContext());
|
||||
expectedChat.save();
|
||||
|
||||
final Optional<TgChat> tgChatOptional =
|
||||
chatUtil.extractChat(
|
||||
callbackQueryContext, null); // null just for test purposes, not usually expected
|
||||
|
||||
assertEquals(expectedChat, tgChatOptional.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExtractChatFromUpdateIfCallbackQueryContextIsEmpty() {
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message\": {\n"
|
||||
+ " \"message_id\": 69420,\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 244745330,\n"
|
||||
+ " \"is_bot\": true,\n"
|
||||
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||
+ " \"username\": \"devdavidebot\"\n"
|
||||
+ " },\n"
|
||||
+ " \"date\": 1681218838,\n"
|
||||
+ " \"chat\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||
+ "\\n"
|
||||
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||
+ " \"entities\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"bold\",\n"
|
||||
+ " \"offset\": 0,\n"
|
||||
+ " \"length\": 16\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"italic\",\n"
|
||||
+ " \"offset\": 26,\n"
|
||||
+ " \"length\": 8\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"reply_markup\": {\n"
|
||||
+ " \"inline_keyboard\": [\n"
|
||||
+ " [\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"English\",\n"
|
||||
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"Italian\",\n"
|
||||
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
final CallbackQueryContext callbackQueryContext =
|
||||
new CallbackQueryContext("123", "456", new CallbackQueryMetadata());
|
||||
|
||||
final TgChat expectedChat = new TgChat(69420L, new ChatContext());
|
||||
expectedChat.save();
|
||||
|
||||
final Optional<TgChat> tgChatOptional =
|
||||
chatUtil.extractChat(
|
||||
callbackQueryContext, update); // null just for test purposes, not usually expected
|
||||
|
||||
assertEquals(expectedChat, tgChatOptional.get());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.github.polpetta.mezzotre.orm.telegram;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
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 ChatUtilTest {
|
||||
private Clock fakeClock;
|
||||
private ChatUtil chatUtil;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
fakeClock = mock(Clock.class);
|
||||
chatUtil = new ChatUtil(fakeClock);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateChatContext() {
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
final ChatContext chatContext = new ChatContext();
|
||||
when(fakeTgChat.getChatContext()).thenReturn(chatContext);
|
||||
when(fakeClock.now()).thenReturn(69420L);
|
||||
chatUtil.updateChatContext(fakeTgChat, "/test1", 42, Collections.emptyMap());
|
||||
|
||||
verify(fakeTgChat, times(1)).getChatContext();
|
||||
final ArgumentCaptor<ChatContext> chatContextArgumentCaptor =
|
||||
ArgumentCaptor.forClass(ChatContext.class);
|
||||
verify(fakeTgChat, times(1)).setChatContext(chatContextArgumentCaptor.capture());
|
||||
verify(fakeTgChat, times(0)).setHasHelpBeenShown(eq(true));
|
||||
final ChatContext capturedChatContextValue = chatContextArgumentCaptor.getValue();
|
||||
assertEquals("/test1", capturedChatContextValue.getStage());
|
||||
assertEquals(42, capturedChatContextValue.getStep());
|
||||
assertEquals(69420L, capturedChatContextValue.getPreviousMessageUnixTimestampInSeconds());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddMapElementsToChatContextToo() {
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
final ChatContext chatContext = new ChatContext();
|
||||
when(fakeTgChat.getChatContext()).thenReturn(chatContext);
|
||||
when(fakeClock.now()).thenReturn(69420L);
|
||||
|
||||
final Object obj1 = new Object();
|
||||
final Object obj2 = new Object();
|
||||
|
||||
chatUtil.updateChatContext(fakeTgChat, "/test1", 42, Map.of("field1", obj1, "field2", obj2));
|
||||
|
||||
verify(fakeTgChat, times(1)).getChatContext();
|
||||
final ArgumentCaptor<ChatContext> chatContextArgumentCaptor =
|
||||
ArgumentCaptor.forClass(ChatContext.class);
|
||||
verify(fakeTgChat, times(1)).setChatContext(chatContextArgumentCaptor.capture());
|
||||
final ChatContext capturedChatContextValue = chatContextArgumentCaptor.getValue();
|
||||
final Map<String, Object> gotAdditionalProperties =
|
||||
capturedChatContextValue.getAdditionalProperties();
|
||||
assertEquals(2, gotAdditionalProperties.size());
|
||||
assertEquals(obj1, gotAdditionalProperties.get("field1"));
|
||||
assertEquals(obj2, gotAdditionalProperties.get("field2"));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
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.slf4j.LoggerFactory;
|
||||
|
||||
@Execution(ExecutionMode.CONCURRENT)
|
||||
class NotFoundTest {
|
||||
|
||||
private CallbackQueryContextCleaner fakeCallbackQueryCleaner;
|
||||
private NotFound notFound;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
fakeCallbackQueryCleaner = mock(CallbackQueryContextCleaner.class);
|
||||
|
||||
notFound =
|
||||
new NotFound(LoggerFactory.getLogger(NotFound.class), fakeCallbackQueryCleaner, "anEvent");
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCallCallbackQueryContextCleaner() throws Exception {
|
||||
final CallbackQueryContext fakeCallbackQuery = mock(CallbackQueryContext.class);
|
||||
when(fakeCallbackQuery.getId()).thenReturn("anId");
|
||||
when(fakeCallbackQuery.getEntryGroup()).thenReturn("123");
|
||||
final Update fakeUpdate = mock(Update.class);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResult =
|
||||
notFound.process(fakeCallbackQuery, fakeUpdate);
|
||||
|
||||
verify(fakeCallbackQueryCleaner, times(1)).removeGroupAsync(eq("123"));
|
||||
assertEquals(Optional.empty(), gotResult.get());
|
||||
}
|
||||
}
|
|
@ -7,10 +7,14 @@ import com.github.polpetta.mezzotre.helper.Loader;
|
|||
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
||||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||
import com.github.polpetta.mezzotre.orm.BatchBeanCleanerService;
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
|
@ -22,12 +26,17 @@ import com.pengrad.telegrambot.request.BaseRequest;
|
|||
import com.pengrad.telegrambot.request.EditMessageText;
|
||||
import com.pengrad.telegrambot.request.SendMessage;
|
||||
import io.ebean.Database;
|
||||
import io.ebean.typequery.PString;
|
||||
import io.ebean.typequery.TQRootBean;
|
||||
import io.vavr.Tuple3;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
|
@ -52,6 +61,8 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
private Database database;
|
||||
private SelectLanguageTutorial selectLanguageTutorial;
|
||||
private UUIDGenerator fakeUUIDGenerator;
|
||||
private ChatUtil chatUtil;
|
||||
private BatchBeanCleanerService batchBeanCleanerService;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
|
@ -64,6 +75,12 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||
|
||||
fakeUUIDGenerator = mock(UUIDGenerator.class);
|
||||
chatUtil = new ChatUtil(new Clock());
|
||||
batchBeanCleanerService =
|
||||
new BatchBeanCleanerService(
|
||||
LoggerFactory.getLogger(BatchBeanCleanerService.class),
|
||||
Pair.of(0, TimeUnit.MILLISECONDS));
|
||||
batchBeanCleanerService.startAsync().awaitRunning(Duration.ofSeconds(10));
|
||||
|
||||
selectLanguageTutorial =
|
||||
new SelectLanguageTutorial(
|
||||
|
@ -71,7 +88,16 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
new TemplateContentGenerator(
|
||||
new LocalizedMessageFactory(Loader.defaultVelocityEngine())),
|
||||
LoggerFactory.getLogger(SelectLanguageTutorial.class),
|
||||
fakeUUIDGenerator);
|
||||
fakeUUIDGenerator,
|
||||
chatUtil,
|
||||
new CallbackQueryContextCleaner(
|
||||
batchBeanCleanerService,
|
||||
LoggerFactory.getLogger(CallbackQueryContextCleaner.class)));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
batchBeanCleanerService.stopAsync().awaitTerminated(Duration.ofSeconds(10));
|
||||
}
|
||||
|
||||
private static Stream<Arguments> getTestLocales() {
|
||||
|
@ -193,6 +219,11 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
"c018108f-6612-4848-8fca-cf301460d4eb", entryGroupId, callbackQueryMetadata);
|
||||
changeLanguageCallbackQueryContext.save();
|
||||
|
||||
final CompletableFuture<
|
||||
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||
callBackFuture = new CompletableFuture<>();
|
||||
batchBeanCleanerService.addListener(callBackFuture::complete);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> processFuture =
|
||||
this.selectLanguageTutorial.process(changeLanguageCallbackQueryContext, update);
|
||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = processFuture.get();
|
||||
|
@ -213,6 +244,7 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
assertEquals(
|
||||
1, new QCallbackQueryContext().id.eq("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5").findCount());
|
||||
} else {
|
||||
callBackFuture.get(); // Await that callback are cleaned out first
|
||||
assertEquals(0, keyboardButtons.size());
|
||||
assertEquals(0, new QCallbackQueryContext().findCount());
|
||||
}
|
||||
|
@ -221,6 +253,11 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
assertNotNull(retrievedTgChat);
|
||||
assertEquals(selectLanguageTutorial.getLocale(), retrievedTgChat.getLocale());
|
||||
|
||||
final Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>
|
||||
deletionCallback = callBackFuture.get();
|
||||
|
||||
assertEquals(entryGroupId, deletionCallback._1());
|
||||
assertEquals(1, deletionCallback._3().get());
|
||||
assertEquals(0, new QCallbackQueryContext().entryGroup.eq(entryGroupId).findCount());
|
||||
}
|
||||
|
||||
|
@ -308,6 +345,10 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
new CallbackQueryContext(
|
||||
"c018108f-6612-4848-8fca-cf301460d4eb", entryGroupId, callbackQueryMetadata);
|
||||
changeLanguageCallbackQueryContext.save();
|
||||
final CompletableFuture<
|
||||
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||
callBackFuture = new CompletableFuture<>();
|
||||
batchBeanCleanerService.addListener(callBackFuture::complete);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> processFuture =
|
||||
this.selectLanguageTutorial.process(changeLanguageCallbackQueryContext, update);
|
||||
|
@ -329,6 +370,7 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
assertEquals(
|
||||
1, new QCallbackQueryContext().id.eq("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5").findCount());
|
||||
} else {
|
||||
callBackFuture.get(); // Await that callback are cleaned out first
|
||||
assertEquals(0, keyboardButtons.size());
|
||||
assertEquals(0, new QCallbackQueryContext().findCount());
|
||||
}
|
||||
|
@ -337,6 +379,11 @@ class SelectLanguageTutorialIntegrationTest {
|
|||
assertNotNull(retrievedTgChat);
|
||||
assertEquals(selectLanguageTutorial.getLocale(), retrievedTgChat.getLocale());
|
||||
|
||||
final Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>
|
||||
deletionCallback = callBackFuture.get();
|
||||
|
||||
assertEquals(entryGroupId, deletionCallback._1());
|
||||
assertEquals(1, deletionCallback._3().get());
|
||||
assertEquals(0, new QCallbackQueryContext().entryGroup.eq(entryGroupId).findCount());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,457 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.github.polpetta.mezzotre.helper.Loader;
|
||||
import com.github.polpetta.mezzotre.helper.TestConfig;
|
||||
import com.github.polpetta.mezzotre.i18n.LocalizedMessageFactory;
|
||||
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||
import com.github.polpetta.mezzotre.orm.BatchBeanCleanerService;
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.telegram.model.Help;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.CallbackQueryMetadata;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.google.gson.Gson;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import com.pengrad.telegrambot.request.EditMessageText;
|
||||
import com.pengrad.telegrambot.request.SendMessage;
|
||||
import io.ebean.Database;
|
||||
import io.ebean.typequery.PString;
|
||||
import io.ebean.typequery.TQRootBean;
|
||||
import io.vavr.Tuple3;
|
||||
import java.time.Duration;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.junit.jupiter.api.*;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.slf4j.Logger;
|
||||
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 ShowHelpIntegrationTest {
|
||||
|
||||
private static Gson gson;
|
||||
private static Logger testLog;
|
||||
|
||||
@Container
|
||||
private final PostgreSQLContainer<?> postgresServer =
|
||||
new PostgreSQLContainer<>(TestConfig.POSTGRES_DOCKER_IMAGE);
|
||||
|
||||
private Database database;
|
||||
private VelocityEngine velocityEngine;
|
||||
private UUIDGenerator fakeUUIDGenerator;
|
||||
private Clock fakeClock;
|
||||
private BatchBeanCleanerService batchBeanCleanerService;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
gson = new Gson();
|
||||
testLog = LoggerFactory.getLogger(ShowHelpIntegrationTest.class);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws Exception {
|
||||
database =
|
||||
Loader.connectToDatabase(Loader.loadDefaultEbeanConfigWithPostgresSettings(postgresServer));
|
||||
velocityEngine = Loader.defaultVelocityEngine();
|
||||
|
||||
final Logger log = LoggerFactory.getLogger(ShowHelp.class);
|
||||
fakeUUIDGenerator = mock(UUIDGenerator.class);
|
||||
fakeClock = mock(Clock.class);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws Exception {
|
||||
if (batchBeanCleanerService != null) {
|
||||
batchBeanCleanerService.stopAsync().awaitTerminated(Duration.ofSeconds(10));
|
||||
}
|
||||
}
|
||||
|
||||
private ShowHelp generateShowHelpWithCustomCommands(
|
||||
Map<String, Processor> eventProcessors,
|
||||
Map<String, com.github.polpetta.mezzotre.telegram.command.Processor> commandProcessors)
|
||||
throws TimeoutException {
|
||||
|
||||
batchBeanCleanerService =
|
||||
new BatchBeanCleanerService(
|
||||
LoggerFactory.getLogger(CallbackQueryContextCleaner.class),
|
||||
Pair.of(0, TimeUnit.MILLISECONDS));
|
||||
batchBeanCleanerService.startAsync().awaitRunning(Duration.ofSeconds(10));
|
||||
|
||||
return new ShowHelp(
|
||||
Executors.newSingleThreadExecutor(),
|
||||
new Help(
|
||||
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
||||
fakeUUIDGenerator),
|
||||
commandProcessors,
|
||||
eventProcessors,
|
||||
// We need to implement a service that is sync - no thread creation!
|
||||
new ChatUtil(fakeClock),
|
||||
new CallbackQueryContextCleaner(
|
||||
batchBeanCleanerService, LoggerFactory.getLogger(CallbackQueryContextCleaner.class)));
|
||||
}
|
||||
|
||||
public static Stream<Arguments> getMessageIdOrNot() {
|
||||
return Stream.of(
|
||||
Arguments.of(
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message\": {\n"
|
||||
+ " \"message_id\": 2723,\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 244745330,\n"
|
||||
+ " \"is_bot\": true,\n"
|
||||
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||
+ " \"username\": \"devdavidebot\"\n"
|
||||
+ " },\n"
|
||||
+ " \"date\": 1681218838,\n"
|
||||
+ " \"chat\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\": \"a message\",\n"
|
||||
+ " \"reply_markup\": {\n"
|
||||
+ " \"inline_keyboard\": [\n"
|
||||
+ " [\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"English\",\n"
|
||||
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"Italian\",\n"
|
||||
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class),
|
||||
EditMessageText.class),
|
||||
Arguments.of(
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class),
|
||||
SendMessage.class));
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||
@MethodSource("getMessageIdOrNot")
|
||||
void shouldShowHelpMessageWithButtons(Update update, Class<?> typeOfMessage) throws Exception {
|
||||
// we have 2 events + group uuid
|
||||
when(fakeUUIDGenerator.generateAsString())
|
||||
.thenReturn("e86e6fa1-fdd4-4120-b85d-a5482db2e8b5")
|
||||
.thenReturn("16507fbd-9f28-48a8-9de1-3ea1c943af67")
|
||||
.thenReturn("0b0ac18e-f621-484e-aa8d-9b176be5b930");
|
||||
when(fakeClock.now()).thenReturn(42L);
|
||||
|
||||
final HashMap<String, com.github.polpetta.mezzotre.telegram.command.Processor> commands =
|
||||
new HashMap<>();
|
||||
final com.github.polpetta.mezzotre.telegram.command.Processor dummy1 =
|
||||
new com.github.polpetta.mezzotre.telegram.command.Processor() {
|
||||
@Override
|
||||
public Set<String> getTriggerKeywords() {
|
||||
return Set.of("/a", "/b");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||
TgChat chat, Update update) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocaleDescriptionKeyword() {
|
||||
return "start.cmdDescription";
|
||||
}
|
||||
};
|
||||
final com.github.polpetta.mezzotre.telegram.command.Processor dummy2 =
|
||||
new com.github.polpetta.mezzotre.telegram.command.Processor() {
|
||||
@Override
|
||||
public Set<String> getTriggerKeywords() {
|
||||
return Set.of("/different");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||
TgChat chat, Update update) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLocaleDescriptionKeyword() {
|
||||
return "help.cmdDescription";
|
||||
}
|
||||
};
|
||||
commands.put("/a", dummy1);
|
||||
commands.put("/b", dummy1);
|
||||
commands.put("/different", dummy2);
|
||||
|
||||
final Map<String, Processor> events = new HashMap<>();
|
||||
|
||||
final Processor dummyEvent1 =
|
||||
new Processor() {
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return "exampleEvent";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeDirectlyInvokedByTheUser() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getPrettyPrintLocaleKeyName() {
|
||||
return Optional.of("changeLanguage.inlineKeyboardButtonName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||
CallbackQueryContext callbackQueryContext, Update update) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
final Processor dummyEvent2 =
|
||||
new com.github.polpetta.mezzotre.telegram.callbackquery.Processor() {
|
||||
@Override
|
||||
public String getEventName() {
|
||||
return "secondExampleEvent";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeDirectlyInvokedByTheUser() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getPrettyPrintLocaleKeyName() {
|
||||
return Optional.of("selectLanguageTutorial.inlineKeyboardButtonName");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(
|
||||
CallbackQueryContext callbackQueryContext, Update update) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
events.put(dummyEvent1.getEventName(), dummyEvent1);
|
||||
events.put(dummyEvent2.getEventName(), dummyEvent2);
|
||||
|
||||
final ShowHelp showHelp = generateShowHelpWithCustomCommands(events, commands);
|
||||
final TgChat tgChat = new TgChat(1111111L, new ChatContext());
|
||||
tgChat.save();
|
||||
final CallbackQueryContext callbackQueryContext =
|
||||
new CallbackQueryContext(
|
||||
"1234",
|
||||
"5678",
|
||||
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||
.withTelegramChatId(1111111L)
|
||||
.build());
|
||||
callbackQueryContext.save();
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResultFuture =
|
||||
showHelp.process(callbackQueryContext, update);
|
||||
|
||||
final Optional<BaseRequest<?, ?>> baseRequestOptional = gotResultFuture.get();
|
||||
final BaseRequest<?, ?> gotResponse = baseRequestOptional.get();
|
||||
assertInstanceOf(typeOfMessage, gotResponse);
|
||||
final String message = (String) gotResponse.getParameters().get("text");
|
||||
assertEquals(
|
||||
"Here is a list of what I can do:\n"
|
||||
+ "\n"
|
||||
+ "- /a /b: Trigger this very bot\n"
|
||||
+ "- /different: Print the help message\n"
|
||||
+ "\n"
|
||||
+ "You can do the same operations you'd do with the commands aforementioned by"
|
||||
+ " selecting the corresponding button below \uD83D\uDC47",
|
||||
message);
|
||||
final InlineKeyboardButton[][] keyboard =
|
||||
((InlineKeyboardMarkup)
|
||||
gotResponse
|
||||
.getParameters()
|
||||
.getOrDefault("reply_markup", new InlineKeyboardMarkup()))
|
||||
.inlineKeyboard();
|
||||
|
||||
final List<InlineKeyboardButton> keyboardButtons =
|
||||
Stream.of(keyboard).flatMap(Stream::of).toList();
|
||||
|
||||
assertEquals(2, keyboardButtons.size());
|
||||
|
||||
assertFalse(keyboardButtons.get(0).callbackData().isBlank());
|
||||
assertFalse(keyboardButtons.get(1).callbackData().isBlank());
|
||||
|
||||
assertTrue(
|
||||
keyboardButtons.stream()
|
||||
.map(InlineKeyboardButton::text)
|
||||
.anyMatch("Select language"::equals));
|
||||
|
||||
assertTrue(
|
||||
keyboardButtons.stream()
|
||||
.map(InlineKeyboardButton::text)
|
||||
.anyMatch("Change language"::equals));
|
||||
|
||||
final TgChat gotChat = new QTgChat().id.eq(1111111L).findOne();
|
||||
assertNotNull(gotChat);
|
||||
assertTrue(gotChat.getHasHelpBeenShown());
|
||||
final ChatContext gotChatChatContext = gotChat.getChatContext();
|
||||
assertEquals(0, gotChatChatContext.getStep());
|
||||
assertEquals("showHelp", gotChatChatContext.getStage());
|
||||
assertEquals(42, gotChatChatContext.getPreviousMessageUnixTimestampInSeconds());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||
void shouldCleanUpCallbackQueryContextAfterRun() throws Exception {
|
||||
when(fakeClock.now()).thenReturn(42L);
|
||||
|
||||
final ShowHelp showHelp =
|
||||
generateShowHelpWithCustomCommands(Collections.emptyMap(), Collections.emptyMap());
|
||||
final TgChat tgChat = new TgChat(1111111L, new ChatContext());
|
||||
tgChat.save();
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message\": {\n"
|
||||
+ " \"message_id\": 2723,\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 244745330,\n"
|
||||
+ " \"is_bot\": true,\n"
|
||||
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||
+ " \"username\": \"devdavidebot\"\n"
|
||||
+ " },\n"
|
||||
+ " \"date\": 1681218838,\n"
|
||||
+ " \"chat\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||
+ "\\n"
|
||||
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||
+ " \"entities\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"bold\",\n"
|
||||
+ " \"offset\": 0,\n"
|
||||
+ " \"length\": 16\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"italic\",\n"
|
||||
+ " \"offset\": 26,\n"
|
||||
+ " \"length\": 8\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
final CallbackQueryContext callbackQueryContext =
|
||||
new CallbackQueryContext(
|
||||
"1234",
|
||||
"5678",
|
||||
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||
.withTelegramChatId(1111111L)
|
||||
.build());
|
||||
callbackQueryContext.save();
|
||||
// Create another CallbackQuery context of the same group
|
||||
final CallbackQueryContext callbackQueryContext2 =
|
||||
new CallbackQueryContext(
|
||||
"7891",
|
||||
"5678",
|
||||
new CallbackQueryMetadata.CallbackQueryMetadataBuilder()
|
||||
.withTelegramChatId(1111111L)
|
||||
.build());
|
||||
callbackQueryContext2.save();
|
||||
|
||||
// I know I know, a future containing a future...but it is for testing purposes, otherwise we'd
|
||||
// have to do some while/sleep thread hack which I find horrible to do
|
||||
final CompletableFuture<
|
||||
Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>>
|
||||
callBackFuture = new CompletableFuture<>();
|
||||
batchBeanCleanerService.addListener(callBackFuture::complete);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResultFuture =
|
||||
showHelp.process(callbackQueryContext, update);
|
||||
|
||||
final Optional<BaseRequest<?, ?>> baseRequestOptional = gotResultFuture.get();
|
||||
assertDoesNotThrow(baseRequestOptional::get);
|
||||
|
||||
final Tuple3<String, PString<? extends TQRootBean<?, ?>>, CompletableFuture<Integer>>
|
||||
deletionCallback = callBackFuture.get();
|
||||
|
||||
assertEquals("5678", deletionCallback._1());
|
||||
assertEquals(2, deletionCallback._3().get());
|
||||
assertEquals(
|
||||
0,
|
||||
new QCallbackQueryContext().findCount(),
|
||||
"The CallbackQuery context group has not been cleaned properly!");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,314 @@
|
|||
package com.github.polpetta.mezzotre.telegram.callbackquery;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.CallbackQueryContextCleaner;
|
||||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.telegram.model.Help;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.google.gson.Gson;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
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;
|
||||
|
||||
@Execution(ExecutionMode.CONCURRENT)
|
||||
class ShowHelpTest {
|
||||
|
||||
private static Gson gson;
|
||||
private ShowHelp showHelp;
|
||||
private Help fakeModelHelp;
|
||||
private ChatUtil fakeChatUtil;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
fakeModelHelp = mock(Help.class);
|
||||
fakeChatUtil = mock(ChatUtil.class);
|
||||
final CallbackQueryContextCleaner fakeCallbackQueryContextCleaner =
|
||||
mock(CallbackQueryContextCleaner.class);
|
||||
|
||||
showHelp =
|
||||
new ShowHelp(
|
||||
Executors.newSingleThreadExecutor(),
|
||||
fakeModelHelp,
|
||||
Collections.emptyMap(),
|
||||
Collections.emptyMap(),
|
||||
fakeChatUtil,
|
||||
fakeCallbackQueryContextCleaner);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateChatContext() throws Exception {
|
||||
final CallbackQueryContext fakeCallbackQueryContext = mock(CallbackQueryContext.class);
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message\": {\n"
|
||||
+ " \"message_id\": 2723,\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 244745330,\n"
|
||||
+ " \"is_bot\": true,\n"
|
||||
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||
+ " \"username\": \"devdavidebot\"\n"
|
||||
+ " },\n"
|
||||
+ " \"date\": 1681218838,\n"
|
||||
+ " \"chat\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||
+ "\\n"
|
||||
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||
+ " \"entities\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"bold\",\n"
|
||||
+ " \"offset\": 0,\n"
|
||||
+ " \"length\": 16\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"italic\",\n"
|
||||
+ " \"offset\": 26,\n"
|
||||
+ " \"length\": 8\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"reply_markup\": {\n"
|
||||
+ " \"inline_keyboard\": [\n"
|
||||
+ " [\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"English\",\n"
|
||||
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"Italian\",\n"
|
||||
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||
when(fakeModelHelp.getMessage(eq(fakeTgChat), any())).thenReturn("doesn't matter");
|
||||
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn(new InlineKeyboardButton[] {});
|
||||
when(fakeChatUtil.extractChat(eq(fakeCallbackQueryContext), eq(update)))
|
||||
.thenReturn(Optional.of(fakeTgChat));
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||
showHelp.process(fakeCallbackQueryContext, update);
|
||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||
|
||||
verify(fakeChatUtil, times(1))
|
||||
.updateChatContext(eq(fakeTgChat), eq("showHelp"), eq(0), eq(Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAddInlineKeyboardIfButtonsAreReturned() throws Exception {
|
||||
final CallbackQueryContext fakeCallbackQueryContext = mock(CallbackQueryContext.class);
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message\": {\n"
|
||||
+ " \"message_id\": 2723,\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 244745330,\n"
|
||||
+ " \"is_bot\": true,\n"
|
||||
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||
+ " \"username\": \"devdavidebot\"\n"
|
||||
+ " },\n"
|
||||
+ " \"date\": 1681218838,\n"
|
||||
+ " \"chat\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||
+ "\\n"
|
||||
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||
+ " \"entities\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"bold\",\n"
|
||||
+ " \"offset\": 0,\n"
|
||||
+ " \"length\": 16\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"italic\",\n"
|
||||
+ " \"offset\": 26,\n"
|
||||
+ " \"length\": 8\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"reply_markup\": {\n"
|
||||
+ " \"inline_keyboard\": [\n"
|
||||
+ " [\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"English\",\n"
|
||||
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"Italian\",\n"
|
||||
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||
when(fakeModelHelp.getMessage(eq(fakeTgChat), any())).thenReturn("doesn't matter");
|
||||
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn(new InlineKeyboardButton[] {new InlineKeyboardButton("example1")});
|
||||
when(fakeChatUtil.extractChat(eq(fakeCallbackQueryContext), eq(update)))
|
||||
.thenReturn(Optional.of(fakeTgChat));
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||
showHelp.process(fakeCallbackQueryContext, update);
|
||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||
|
||||
final InlineKeyboardMarkup replyMarkup =
|
||||
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||
assertEquals(1, replyMarkup.inlineKeyboard()[0].length);
|
||||
assertEquals("example1", replyMarkup.inlineKeyboard()[0][0].text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAddAnyKeyboardIfThereAreNoButtons() throws Exception {
|
||||
final CallbackQueryContext fakeCallbackQueryContext = mock(CallbackQueryContext.class);
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ " \"update_id\": 158712614,\n"
|
||||
+ " \"callback_query\": {\n"
|
||||
+ " \"id\": \"20496049451114620\",\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"is_bot\": false,\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"language_code\": \"en\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message\": {\n"
|
||||
+ " \"message_id\": 2723,\n"
|
||||
+ " \"from\": {\n"
|
||||
+ " \"id\": 244745330,\n"
|
||||
+ " \"is_bot\": true,\n"
|
||||
+ " \"first_name\": \"Dev - DavideBot\",\n"
|
||||
+ " \"username\": \"devdavidebot\"\n"
|
||||
+ " },\n"
|
||||
+ " \"date\": 1681218838,\n"
|
||||
+ " \"chat\": {\n"
|
||||
+ " \"id\": 1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"username\": \"Testusername\",\n"
|
||||
+ " \"first_name\": \"Test Firstname\",\n"
|
||||
+ " \"last_name\": \"Test Lastname\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\": \"Hello xxxxxx! \uD83D\uDC4B\\n"
|
||||
+ "\\n"
|
||||
+ "This is Mezzotre, a simple bot focused on DnD content management! Please start"
|
||||
+ " by choosing a language down below \uD83D\uDC47\",\n"
|
||||
+ " \"entities\": [\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"bold\",\n"
|
||||
+ " \"offset\": 0,\n"
|
||||
+ " \"length\": 16\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"type\": \"italic\",\n"
|
||||
+ " \"offset\": 26,\n"
|
||||
+ " \"length\": 8\n"
|
||||
+ " }\n"
|
||||
+ " ],\n"
|
||||
+ " \"reply_markup\": {\n"
|
||||
+ " \"inline_keyboard\": [\n"
|
||||
+ " [\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"English\",\n"
|
||||
+ " \"callback_data\": \"9a64be11-d086-4bd9-859f-720c43dedcb5\"\n"
|
||||
+ " },\n"
|
||||
+ " {\n"
|
||||
+ " \"text\": \"Italian\",\n"
|
||||
+ " \"callback_data\": \"8768d660-f05f-4f4b-bda5-3451ab573d56\"\n"
|
||||
+ " }\n"
|
||||
+ " ]\n"
|
||||
+ " ]\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ " }\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||
when(fakeModelHelp.getMessage(eq(fakeTgChat), any())).thenReturn("doesn't matter");
|
||||
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn(new InlineKeyboardButton[] {});
|
||||
when(fakeChatUtil.extractChat(eq(fakeCallbackQueryContext), eq(update)))
|
||||
.thenReturn(Optional.of(fakeTgChat));
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||
showHelp.process(fakeCallbackQueryContext, update);
|
||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||
final InlineKeyboardMarkup replyMarkup =
|
||||
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||
assertNull(replyMarkup);
|
||||
}
|
||||
}
|
|
@ -11,6 +11,7 @@ import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
|||
import com.github.polpetta.mezzotre.orm.model.CallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
|
@ -172,10 +173,15 @@ class HelpIntegrationTest {
|
|||
final com.github.polpetta.mezzotre.telegram.model.Help modelHelp =
|
||||
new com.github.polpetta.mezzotre.telegram.model.Help(
|
||||
new TemplateContentGenerator(new LocalizedMessageFactory(velocityEngine)),
|
||||
fakeClock,
|
||||
new UUIDGenerator());
|
||||
|
||||
help = new Help(Executors.newSingleThreadExecutor(), commands, events, modelHelp);
|
||||
help =
|
||||
new Help(
|
||||
Executors.newSingleThreadExecutor(),
|
||||
commands,
|
||||
events,
|
||||
modelHelp,
|
||||
new ChatUtil(fakeClock));
|
||||
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
|
@ -210,8 +216,8 @@ class HelpIntegrationTest {
|
|||
assertEquals(
|
||||
"Here is a list of what I can do:\n"
|
||||
+ "\n"
|
||||
+ "* /a /b: Trigger this very bot\n"
|
||||
+ "* /different: Print the help message\n"
|
||||
+ "- /a /b: Trigger this very bot\n"
|
||||
+ "- /different: Print the help message\n"
|
||||
+ "\n"
|
||||
+ "You can do the same operations you'd do with the commands aforementioned by"
|
||||
+ " selecting the corresponding button below \uD83D\uDC47",
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
package com.github.polpetta.mezzotre.telegram.command;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
import com.google.gson.Gson;
|
||||
import com.pengrad.telegrambot.model.Update;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardButton;
|
||||
import com.pengrad.telegrambot.model.request.InlineKeyboardMarkup;
|
||||
import com.pengrad.telegrambot.request.BaseRequest;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
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;
|
||||
|
||||
@Execution(ExecutionMode.CONCURRENT)
|
||||
class HelpTest {
|
||||
|
||||
private static Gson gson;
|
||||
private com.github.polpetta.mezzotre.telegram.model.Help fakeModelHelp;
|
||||
private ChatUtil fakeChatUtil;
|
||||
private Help help;
|
||||
|
||||
@BeforeAll
|
||||
static void beforeAll() {
|
||||
gson = new Gson();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
|
||||
final Map<String, Processor> commands = Collections.emptyMap();
|
||||
final Map<String, com.github.polpetta.mezzotre.telegram.callbackquery.Processor> events =
|
||||
Collections.emptyMap();
|
||||
|
||||
fakeModelHelp = mock(com.github.polpetta.mezzotre.telegram.model.Help.class);
|
||||
fakeChatUtil = mock(ChatUtil.class);
|
||||
|
||||
help =
|
||||
new Help(
|
||||
Executors.newSingleThreadExecutor(), commands, events, fakeModelHelp, fakeChatUtil);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldUpdateChatContext() throws Exception {
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||
when(fakeModelHelp.getMessage(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn("doesn't matter");
|
||||
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn(new InlineKeyboardButton[] {});
|
||||
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ "\"update_id\":10000,\n"
|
||||
+ "\"message\":{\n"
|
||||
+ " \"date\":1441645532,\n"
|
||||
+ " \"chat\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message_id\":1365,\n"
|
||||
+ " \"from\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\":\"/help\"\n"
|
||||
+ "}\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||
help.process(fakeTgChat, update);
|
||||
assertDoesNotThrow(() -> gotResponseFuture.get());
|
||||
verify(fakeChatUtil, times(1))
|
||||
.updateChatContext(eq(fakeTgChat), eq("/help"), eq(0), eq(Collections.emptyMap()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldAppendKeyboardIfButtonsAreReturned() throws Exception {
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||
when(fakeModelHelp.getMessage(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn("doesn't matter");
|
||||
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn(new InlineKeyboardButton[] {new InlineKeyboardButton("example1")});
|
||||
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ "\"update_id\":10000,\n"
|
||||
+ "\"message\":{\n"
|
||||
+ " \"date\":1441645532,\n"
|
||||
+ " \"chat\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message_id\":1365,\n"
|
||||
+ " \"from\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\":\"/help\"\n"
|
||||
+ "}\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||
help.process(fakeTgChat, update);
|
||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||
final InlineKeyboardMarkup replyMarkup =
|
||||
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||
assertEquals(1, replyMarkup.inlineKeyboard()[0].length);
|
||||
assertEquals("example1", replyMarkup.inlineKeyboard()[0][0].text());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldNotAppendKeyboardIfNoButtonIsPresent() throws Exception {
|
||||
final TgChat fakeTgChat = mock(TgChat.class);
|
||||
when(fakeTgChat.getId()).thenReturn(1111111L);
|
||||
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||
when(fakeTgChat.getChatContext()).thenReturn(new ChatContext());
|
||||
when(fakeModelHelp.getMessage(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn("doesn't matter");
|
||||
when(fakeModelHelp.generateInlineKeyBoardButton(eq(fakeTgChat), eq(Collections.emptyMap())))
|
||||
.thenReturn(new InlineKeyboardButton[] {});
|
||||
|
||||
final Update update =
|
||||
gson.fromJson(
|
||||
"{\n"
|
||||
+ "\"update_id\":10000,\n"
|
||||
+ "\"message\":{\n"
|
||||
+ " \"date\":1441645532,\n"
|
||||
+ " \"chat\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"type\": \"private\",\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"message_id\":1365,\n"
|
||||
+ " \"from\":{\n"
|
||||
+ " \"last_name\":\"Test Lastname\",\n"
|
||||
+ " \"id\":1111111,\n"
|
||||
+ " \"first_name\":\"Test Firstname\",\n"
|
||||
+ " \"username\":\"Testusername\"\n"
|
||||
+ " },\n"
|
||||
+ " \"text\":\"/help\"\n"
|
||||
+ "}\n"
|
||||
+ "}",
|
||||
Update.class);
|
||||
|
||||
final CompletableFuture<Optional<BaseRequest<?, ?>>> gotResponseFuture =
|
||||
help.process(fakeTgChat, update);
|
||||
final Optional<BaseRequest<?, ?>> gotResponseOpt = gotResponseFuture.get();
|
||||
final BaseRequest<?, ?> gotResponse = gotResponseOpt.get();
|
||||
final InlineKeyboardMarkup replyMarkup =
|
||||
(InlineKeyboardMarkup) gotResponse.getParameters().get("reply_markup");
|
||||
assertNull(replyMarkup);
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
|||
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QCallbackQueryContext;
|
||||
import com.github.polpetta.mezzotre.orm.model.query.QTgChat;
|
||||
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||
import com.github.polpetta.mezzotre.util.Clock;
|
||||
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||
import com.github.polpetta.types.json.ChatContext;
|
||||
|
@ -74,8 +75,8 @@ class StartIntegrationTest {
|
|||
Executors.newSingleThreadExecutor(),
|
||||
log,
|
||||
fakeUUIDGenerator,
|
||||
fakeClock,
|
||||
"Mezzotre");
|
||||
"Mezzotre",
|
||||
new ChatUtil(fakeClock));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -122,7 +123,7 @@ class StartIntegrationTest {
|
|||
assertInstanceOf(SendMessage.class, gotMessage);
|
||||
final String message = (String) gotMessage.getParameters().get("text");
|
||||
assertEquals(
|
||||
"**Hello Test Firstname! \uD83D\uDC4B**\n\n"
|
||||
"*Hello Test Firstname! \uD83D\uDC4B*\n\n"
|
||||
+ "This is _Mezzotre_, a simple bot focused on DnD content management! Please start by"
|
||||
+ " choosing a language down below \uD83D\uDC47",
|
||||
message);
|
||||
|
@ -180,7 +181,7 @@ class StartIntegrationTest {
|
|||
assertInstanceOf(SendMessage.class, gotMessage);
|
||||
final String message = (String) gotMessage.getParameters().get("text");
|
||||
assertEquals(
|
||||
"**Hello Test Firstname! \uD83D\uDC4B**\n\n"
|
||||
"*Hello Test Firstname! \uD83D\uDC4B*\n\n"
|
||||
+ "This is _Mezzotre_, a simple bot focused on DnD content management! Please start by"
|
||||
+ " choosing a language down below \uD83D\uDC47",
|
||||
message);
|
||||
|
|
Loading…
Reference in New Issue