feat: campaign draft, wip
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
5788d59516
commit
d93fe8d3b9
|
@ -0,0 +1,60 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm.model;
|
||||||
|
|
||||||
|
import io.ebean.annotation.Length;
|
||||||
|
import io.ebean.annotation.NotNull;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.persistence.*;
|
||||||
|
import javax.validation.constraints.Null;
|
||||||
|
|
||||||
|
@Entity
|
||||||
|
public class Campaign extends Base {
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Length(64)
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
@Length(256)
|
||||||
|
@NotNull
|
||||||
|
private String campaignName;
|
||||||
|
|
||||||
|
@Nullable @Null private String description;
|
||||||
|
|
||||||
|
@ManyToMany(fetch = FetchType.LAZY)
|
||||||
|
private List<User> users;
|
||||||
|
|
||||||
|
public Campaign(String id, String campaignName, @Nullable String description) {
|
||||||
|
this.id = id;
|
||||||
|
this.campaignName = campaignName;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCampaignName() {
|
||||||
|
return campaignName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCampaignName(String campaignName) {
|
||||||
|
this.campaignName = campaignName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public String getDescription() {
|
||||||
|
return description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDescription(@Nullable String description) {
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<User> getUsers() {
|
||||||
|
return users;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUsers(List<User> users) {
|
||||||
|
this.users = users;
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import io.ebean.annotation.ConstraintMode;
|
||||||
import io.ebean.annotation.DbForeignKey;
|
import io.ebean.annotation.DbForeignKey;
|
||||||
import io.ebean.annotation.Length;
|
import io.ebean.annotation.Length;
|
||||||
import io.ebean.annotation.NotNull;
|
import io.ebean.annotation.NotNull;
|
||||||
|
import java.util.List;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.persistence.*;
|
import javax.persistence.*;
|
||||||
|
|
||||||
|
@ -22,17 +23,20 @@ public class User extends Base {
|
||||||
@OneToOne(fetch = FetchType.LAZY, optional = true)
|
@OneToOne(fetch = FetchType.LAZY, optional = true)
|
||||||
@DbForeignKey(onDelete = ConstraintMode.CASCADE)
|
@DbForeignKey(onDelete = ConstraintMode.CASCADE)
|
||||||
@JoinColumn(name = "telegram_id", referencedColumnName = "id")
|
@JoinColumn(name = "telegram_id", referencedColumnName = "id")
|
||||||
private TgChat telegramId;
|
private TgChat telegramChat;
|
||||||
|
|
||||||
|
@ManyToMany(fetch = FetchType.LAZY)
|
||||||
|
private List<Campaign> campaigns;
|
||||||
|
|
||||||
@Length(256)
|
@Length(256)
|
||||||
@Nullable
|
@Nullable
|
||||||
private String emailAddress;
|
private String emailAddress;
|
||||||
|
|
||||||
public User(String id, String emailAddress, Boolean isActive, TgChat telegramId) {
|
public User(String id, String emailAddress, Boolean isActive, TgChat telegramChat) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.emailAddress = emailAddress;
|
this.emailAddress = emailAddress;
|
||||||
this.isActive = isActive;
|
this.isActive = isActive;
|
||||||
this.telegramId = telegramId;
|
this.telegramChat = telegramChat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getId() {
|
public String getId() {
|
||||||
|
@ -47,19 +51,28 @@ public class User extends Base {
|
||||||
isActive = active;
|
isActive = active;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TgChat getTelegramId() {
|
public TgChat getTelegramChat() {
|
||||||
return telegramId;
|
return telegramChat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTelegramId(TgChat telegramId) {
|
public void setTelegramChat(TgChat telegramChat) {
|
||||||
this.telegramId = telegramId;
|
this.telegramChat = telegramChat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getEmailAddress() {
|
public String getEmailAddress() {
|
||||||
return emailAddress;
|
return emailAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmailAddress(String emailAddress) {
|
public void setEmailAddress(@Nullable String emailAddress) {
|
||||||
this.emailAddress = emailAddress;
|
this.emailAddress = emailAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Campaign> getCampaigns() {
|
||||||
|
return campaigns;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCampaigns(List<Campaign> campaigns) {
|
||||||
|
this.campaigns = campaigns;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.github.polpetta.mezzotre.orm.telegram;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.Campaign;
|
||||||
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
public class CampaignUtil {
|
||||||
|
|
||||||
|
private final UUIDGenerator uuidGenerator;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CampaignUtil(UUIDGenerator uuidGenerator) {
|
||||||
|
this.uuidGenerator = uuidGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Campaign insertNewCampaign(String name, String description) {
|
||||||
|
final Campaign campaign = new Campaign(uuidGenerator.generateAsString(), name, description);
|
||||||
|
|
||||||
|
campaign.save();
|
||||||
|
return campaign;
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,18 +34,18 @@ public class ChatUtil {
|
||||||
* then saved in the database
|
* then saved in the database
|
||||||
*
|
*
|
||||||
* @param chat the chat that will be updated with the new {@link ChatContext} values
|
* @param chat the chat that will be updated with the new {@link ChatContext} values
|
||||||
* @param stepName the step name to set
|
* @param stageName the stage name to set
|
||||||
* @param stageNumber the stage number to set
|
* @param stepNumber the step number to set
|
||||||
* @param additionalFields if there are, additional custom fields that will be added to {@link
|
* @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
|
* 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
|
* available for custom entries. Use {@link Collections#emptyMap()} if you don't wish to add
|
||||||
* any additional field
|
* any additional field
|
||||||
*/
|
*/
|
||||||
public void updateChatContext(
|
public void updateChatContext(
|
||||||
TgChat chat, String stepName, int stageNumber, Map<String, Object> additionalFields) {
|
TgChat chat, String stageName, int stepNumber, Map<String, Object> additionalFields) {
|
||||||
final ChatContext chatContext = chat.getChatContext();
|
final ChatContext chatContext = chat.getChatContext();
|
||||||
chatContext.setStage(stepName);
|
chatContext.setStage(stageName);
|
||||||
chatContext.setStep(stageNumber);
|
chatContext.setStep(stepNumber);
|
||||||
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
chatContext.setPreviousMessageUnixTimestampInSeconds(clock.now());
|
||||||
additionalFields.forEach(chatContext::setAdditionalProperty);
|
additionalFields.forEach(chatContext::setAdditionalProperty);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Optional;
|
||||||
* @author Davide Polonio
|
* @author Davide Polonio
|
||||||
* @since 1.0
|
* @since 1.0
|
||||||
*/
|
*/
|
||||||
public class Util {
|
class Util {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the message id of the given {@link Update}
|
* Extract the message id of the given {@link Update}
|
||||||
|
@ -19,7 +19,7 @@ public class Util {
|
||||||
* @return an {@link Optional} containing a {@link Integer} with the message id if it is present,
|
* @return an {@link Optional} containing a {@link Integer} with the message id if it is present,
|
||||||
* otherwise a {@link Optional#empty()} if it is not found.
|
* otherwise a {@link Optional#empty()} if it is not found.
|
||||||
*/
|
*/
|
||||||
public static Optional<Integer> extractMessageId(Update update) {
|
static Optional<Integer> extractMessageId(Update update) {
|
||||||
return Optional.ofNullable(update.callbackQuery().message()).map(Message::messageId);
|
return Optional.ofNullable(update.callbackQuery().message()).map(Message::messageId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,10 +37,12 @@ public class CommandDI extends AbstractModule {
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
@Named("commandProcessor")
|
@Named("commandProcessor")
|
||||||
public Map<String, Processor> getCommandProcessor(Start start, Help help) {
|
public Map<String, Processor> getCommandProcessor(
|
||||||
|
Start start, Help help, CreateCampaign createCampaign) {
|
||||||
final HashMap<String, Processor> commandMap = new HashMap<>();
|
final HashMap<String, Processor> commandMap = new HashMap<>();
|
||||||
commandMap.putAll(mapForProcessor(start));
|
commandMap.putAll(mapForProcessor(start));
|
||||||
commandMap.putAll(mapForProcessor(help));
|
commandMap.putAll(mapForProcessor(help));
|
||||||
|
commandMap.putAll(mapForProcessor(createCampaign));
|
||||||
|
|
||||||
return commandMap;
|
return commandMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.CampaignUtil;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
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.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
// FIXME tests, doc
|
||||||
|
public class CreateCampaign implements Processor {
|
||||||
|
|
||||||
|
private static final String TRIGGERING_KEYWORD = "/createCampaign";
|
||||||
|
// FIXME create the label
|
||||||
|
private static final String BUTTON_CREATE_IT_LOCALE_KEY = "createCampaign.createItButton";
|
||||||
|
private static final String CAMPAIGN_NAME_CHAT_CTX_FIELD = "campaign_name";
|
||||||
|
private static final String CAMPAIGN_NAME_TEMPLATE_CTX_FIELD = "campaignName";
|
||||||
|
private static final int MAX_CAMPAIGN_NAME_LENGTH = 256;
|
||||||
|
private static final int MAX_CAMPAIGN_DESCRIPTION_LENGTH = 4096;
|
||||||
|
private final TemplateContentGenerator templateContentGenerator;
|
||||||
|
private final Executor threadPool;
|
||||||
|
private final UUIDGenerator uuidGenerator;
|
||||||
|
private final ChatUtil chatUtil;
|
||||||
|
private final CampaignUtil campaignUtil;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public CreateCampaign(
|
||||||
|
TemplateContentGenerator templateContentGenerator,
|
||||||
|
@Named("eventThreadPool") Executor threadPool,
|
||||||
|
UUIDGenerator uuidGenerator,
|
||||||
|
ChatUtil chatUtil,
|
||||||
|
CampaignUtil campaignUtil) {
|
||||||
|
|
||||||
|
this.templateContentGenerator = templateContentGenerator;
|
||||||
|
this.threadPool = threadPool;
|
||||||
|
this.uuidGenerator = uuidGenerator;
|
||||||
|
this.chatUtil = chatUtil;
|
||||||
|
this.campaignUtil = campaignUtil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getTriggerKeywords() {
|
||||||
|
return Set.of(TRIGGERING_KEYWORD, "/newCampaign");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Optional<BaseRequest<?, ?>>> process(TgChat chat, Update update) {
|
||||||
|
// There are multiple steps, we have to route the incoming campaign creation to the right one
|
||||||
|
return switch ((int) chat.getChatContext().getStep()) {
|
||||||
|
default -> promptCampaignName(chat);
|
||||||
|
case 1 -> verifyCampaignNameAndPromptCampaignDescription(chat, update);
|
||||||
|
case 2 -> verifyDescriptionAndCompleteCreation(chat, update);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Optional<BaseRequest<?, ?>>> promptCampaignName(TgChat chat) {
|
||||||
|
return CompletableFuture.supplyAsync(
|
||||||
|
() ->
|
||||||
|
templateContentGenerator.mergeTemplate(
|
||||||
|
ctx -> {}, chat.getLocale(), "template/telegram/createCampaign.0.vm"),
|
||||||
|
threadPool)
|
||||||
|
.thenApplyAsync(
|
||||||
|
message -> {
|
||||||
|
chatUtil.updateChatContext(chat, TRIGGERING_KEYWORD, 1, Collections.emptyMap());
|
||||||
|
|
||||||
|
return Optional.of(
|
||||||
|
new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown));
|
||||||
|
},
|
||||||
|
threadPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Optional<BaseRequest<?, ?>>>
|
||||||
|
verifyCampaignNameAndPromptCampaignDescription(TgChat chat, Update update) {
|
||||||
|
/*
|
||||||
|
Flow:
|
||||||
|
1 - Input validation (name length, etc...)
|
||||||
|
2 - Build message according to validation output
|
||||||
|
3 - Send message back
|
||||||
|
4 - ???
|
||||||
|
5 - Profit
|
||||||
|
*/
|
||||||
|
return CompletableFuture.completedFuture(chat)
|
||||||
|
.thenApply(
|
||||||
|
// 1 - Perform input validation
|
||||||
|
ignored ->
|
||||||
|
Util.extractText(update)
|
||||||
|
.filter(campaignName -> campaignName.length() <= MAX_CAMPAIGN_NAME_LENGTH))
|
||||||
|
.thenApplyAsync(
|
||||||
|
nameOpt -> {
|
||||||
|
// 2 - create appropriate message and update chat context
|
||||||
|
return nameOpt
|
||||||
|
.map(
|
||||||
|
campaignName -> {
|
||||||
|
chatUtil.updateChatContext(
|
||||||
|
chat,
|
||||||
|
TRIGGERING_KEYWORD,
|
||||||
|
2,
|
||||||
|
Collections.singletonMap(CAMPAIGN_NAME_CHAT_CTX_FIELD, campaignName));
|
||||||
|
return templateContentGenerator.mergeTemplate(
|
||||||
|
ctx -> ctx.put(CAMPAIGN_NAME_TEMPLATE_CTX_FIELD, campaignName),
|
||||||
|
chat.getLocale(),
|
||||||
|
"template/telegram/createCampaign.2.vm");
|
||||||
|
})
|
||||||
|
// We don't update the context here on purpose. If the user wants to try another
|
||||||
|
// campaign name is free to do so!
|
||||||
|
.orElseGet(
|
||||||
|
() ->
|
||||||
|
templateContentGenerator.mergeTemplate(
|
||||||
|
ctx -> {},
|
||||||
|
chat.getLocale(),
|
||||||
|
"template/telegram/createCampaign.nameNotValid.vm"));
|
||||||
|
})
|
||||||
|
.thenApply(
|
||||||
|
// 3 - send message back
|
||||||
|
message ->
|
||||||
|
Optional.of(new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Optional<BaseRequest<?, ?>>> verifyDescriptionAndCompleteCreation(
|
||||||
|
TgChat chat, Update update) {
|
||||||
|
/*
|
||||||
|
Flow:
|
||||||
|
1 - Input validation (description length, etc...)
|
||||||
|
2 - Build message according to validation output
|
||||||
|
3 - Send message back
|
||||||
|
4 - ???
|
||||||
|
5 - Profit
|
||||||
|
*/
|
||||||
|
return CompletableFuture.completedFuture(chat)
|
||||||
|
.thenApply(
|
||||||
|
ignored ->
|
||||||
|
Util.extractText(update)
|
||||||
|
.filter(description -> description.length() <= MAX_CAMPAIGN_DESCRIPTION_LENGTH))
|
||||||
|
.thenApplyAsync(
|
||||||
|
descriptionOpt ->
|
||||||
|
descriptionOpt
|
||||||
|
.map(
|
||||||
|
campaignDescription -> {
|
||||||
|
// We go full beans here because we know that the property will never be
|
||||||
|
// null at this point - if it is null, then there are bigger problems, and
|
||||||
|
// we should start questioning our life (and our programming skills
|
||||||
|
// really)
|
||||||
|
final String campaignName =
|
||||||
|
(String)
|
||||||
|
chat.getChatContext()
|
||||||
|
.getAdditionalProperties()
|
||||||
|
.get(CAMPAIGN_NAME_CHAT_CTX_FIELD);
|
||||||
|
campaignUtil.insertNewCampaign(campaignName, campaignDescription);
|
||||||
|
chatUtil.updateChatContext(
|
||||||
|
chat, TRIGGERING_KEYWORD, 3, Collections.emptyMap());
|
||||||
|
return templateContentGenerator.mergeTemplate(
|
||||||
|
ctx -> {}, chat.getLocale(), "template/telegram/createCampaign.3.vm");
|
||||||
|
})
|
||||||
|
.orElseGet(
|
||||||
|
() ->
|
||||||
|
templateContentGenerator.mergeTemplate(
|
||||||
|
ctx -> {},
|
||||||
|
chat.getLocale(),
|
||||||
|
"template/telegram/createCampaign.descriptionNotValid.vm")))
|
||||||
|
.thenApply(
|
||||||
|
message ->
|
||||||
|
Optional.of(new SendMessage(chat.getId(), message).parseMode(ParseMode.Markdown)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
import com.pengrad.telegrambot.model.Message;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
import com.pengrad.telegrambot.model.User;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
class Util {
|
||||||
|
|
||||||
|
static Optional<Long> extractSenderId(Update update) {
|
||||||
|
return Optional.ofNullable(update).map(Update::message).map(Message::from).map(User::id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Optional<String> extractText(Update update) {
|
||||||
|
return Optional.ofNullable(update).map(Update::message).map(Message::text);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
${i18n.createCampaign.letsStartName}
|
|
@ -0,0 +1 @@
|
||||||
|
${i18n.createCampaign.optionalDescription}
|
|
@ -0,0 +1 @@
|
||||||
|
${i18n.createCampaign.done}
|
|
@ -0,0 +1 @@
|
||||||
|
${i18n.createCampaign.descriptionNotValid}
|
|
@ -0,0 +1 @@
|
||||||
|
${i18n.createCampaign.nameNotValid}
|
|
@ -69,6 +69,6 @@ public class UserIntegrationTest {
|
||||||
final User id123 = new QUser().id.eq("id123").findOne();
|
final User id123 = new QUser().id.eq("id123").findOne();
|
||||||
assertNotNull(id123);
|
assertNotNull(id123);
|
||||||
assertTrue(id123.isActive());
|
assertTrue(id123.isActive());
|
||||||
assertNull(id123.getTelegramId());
|
assertNull(id123.getTelegramChat());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,223 @@
|
||||||
|
package com.github.polpetta.mezzotre.telegram.command;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.github.polpetta.mezzotre.i18n.TemplateContentGenerator;
|
||||||
|
import com.github.polpetta.mezzotre.orm.model.TgChat;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.CampaignUtil;
|
||||||
|
import com.github.polpetta.mezzotre.orm.telegram.ChatUtil;
|
||||||
|
import com.github.polpetta.mezzotre.util.UUIDGenerator;
|
||||||
|
import com.github.polpetta.types.json.ChatContext;
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.pengrad.telegrambot.model.Update;
|
||||||
|
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 java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Timeout;
|
||||||
|
import org.junit.jupiter.api.parallel.Execution;
|
||||||
|
import org.junit.jupiter.api.parallel.ExecutionMode;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
|
@Execution(ExecutionMode.CONCURRENT)
|
||||||
|
class CreateCampaignTest {
|
||||||
|
|
||||||
|
private static Gson gson;
|
||||||
|
private TemplateContentGenerator fakeTemplateContentGenerator;
|
||||||
|
private UUIDGenerator fakeUUIDGenerator;
|
||||||
|
private ChatUtil fakeChatUtil;
|
||||||
|
private CampaignUtil fakeCampaignUtil;
|
||||||
|
private CreateCampaign createCampaign;
|
||||||
|
|
||||||
|
@BeforeAll
|
||||||
|
static void beforeAll() {
|
||||||
|
gson = new Gson();
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
|
||||||
|
fakeTemplateContentGenerator = mock(TemplateContentGenerator.class);
|
||||||
|
fakeUUIDGenerator = mock(UUIDGenerator.class);
|
||||||
|
fakeChatUtil = mock(ChatUtil.class);
|
||||||
|
fakeCampaignUtil = mock(CampaignUtil.class);
|
||||||
|
|
||||||
|
createCampaign =
|
||||||
|
new CreateCampaign(
|
||||||
|
fakeTemplateContentGenerator,
|
||||||
|
Executors.newSingleThreadExecutor(),
|
||||||
|
fakeUUIDGenerator,
|
||||||
|
fakeChatUtil,
|
||||||
|
fakeCampaignUtil);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stream<Arguments> getMultipleValidCampaignEntries() {
|
||||||
|
return Stream.of(
|
||||||
|
Arguments.of("a simple name", "a simple description"),
|
||||||
|
Arguments.of("a simple name", "a simple\nmultiline\n description"),
|
||||||
|
Arguments.of(
|
||||||
|
"an \uD83E\uDD21 emoji \uD83D\uDCAF name \uD83D\uDD25",
|
||||||
|
"a very \uD83E\uDDE8 campaign description \uD83E\uDD16"),
|
||||||
|
Arguments.of("一个简单的名字", "一个简单的描述"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@MethodSource("getMultipleValidCampaignEntries")
|
||||||
|
@Timeout(value = 1, unit = TimeUnit.MINUTES)
|
||||||
|
void shouldGenerateANewCampaign(String name, String description) throws Exception {
|
||||||
|
|
||||||
|
final TgChat fakeTgChat = mock(TgChat.class);
|
||||||
|
final ChatContext initialChatContext = new ChatContext();
|
||||||
|
initialChatContext.setStep(0); // implicit but better specify just for clarity
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(initialChatContext);
|
||||||
|
when(fakeTgChat.getLocale()).thenReturn("en-US");
|
||||||
|
|
||||||
|
{
|
||||||
|
when(fakeTemplateContentGenerator.mergeTemplate(
|
||||||
|
any(), eq("en-US"), eq("template/telegram/createCampaign.0.vm")))
|
||||||
|
.thenReturn("a string");
|
||||||
|
final Update firstMessage =
|
||||||
|
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\":\"/createCampaign\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> got =
|
||||||
|
createCampaign.process(fakeTgChat, firstMessage);
|
||||||
|
final Optional<BaseRequest<?, ?>> baseRequestOptional = got.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = baseRequestOptional.get();
|
||||||
|
assertEquals("a string", gotResponse.getParameters().get("text"));
|
||||||
|
|
||||||
|
verify(fakeChatUtil, times(1))
|
||||||
|
.updateChatContext(fakeTgChat, "/createCampaign", 1, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
when(fakeTemplateContentGenerator.mergeTemplate(
|
||||||
|
any(), eq("en-US"), eq("template/telegram/createCampaign.2.vm")))
|
||||||
|
.thenReturn("a second string");
|
||||||
|
final ChatContext chatContext = new ChatContext();
|
||||||
|
chatContext.setStep(1);
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(chatContext);
|
||||||
|
|
||||||
|
final Update secondMessageName =
|
||||||
|
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\":\""
|
||||||
|
+ name
|
||||||
|
+ "\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> got =
|
||||||
|
createCampaign.process(fakeTgChat, secondMessageName);
|
||||||
|
final Optional<BaseRequest<?, ?>> baseRequestOptional = got.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = baseRequestOptional.get();
|
||||||
|
assertEquals("a second string", gotResponse.getParameters().get("text"));
|
||||||
|
|
||||||
|
final ArgumentCaptor<Map<String, Object>> capturedChatCtx =
|
||||||
|
ArgumentCaptor.forClass(Map.class);
|
||||||
|
verify(fakeChatUtil, times(1))
|
||||||
|
.updateChatContext(
|
||||||
|
eq(fakeTgChat), eq("/createCampaign"), eq(2), capturedChatCtx.capture());
|
||||||
|
|
||||||
|
final Map<String, Object> chatCtxValue = capturedChatCtx.getValue();
|
||||||
|
assertEquals(name, chatCtxValue.get("campaign_name"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
when(fakeTemplateContentGenerator.mergeTemplate(
|
||||||
|
any(), eq("en-US"), eq("template/telegram/createCampaign.3.vm")))
|
||||||
|
.thenReturn("a third string");
|
||||||
|
final ChatContext chatContext = new ChatContext();
|
||||||
|
chatContext.setStep(2);
|
||||||
|
chatContext.setAdditionalProperty("campaign_name", name);
|
||||||
|
when(fakeTgChat.getChatContext()).thenReturn(chatContext);
|
||||||
|
|
||||||
|
final Update thirdMessageDescription =
|
||||||
|
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\":\""
|
||||||
|
+ description
|
||||||
|
+ "\"\n"
|
||||||
|
+ "}\n"
|
||||||
|
+ "}",
|
||||||
|
Update.class);
|
||||||
|
|
||||||
|
final CompletableFuture<Optional<BaseRequest<?, ?>>> got =
|
||||||
|
createCampaign.process(fakeTgChat, thirdMessageDescription);
|
||||||
|
final Optional<BaseRequest<?, ?>> baseRequestOptional = got.get();
|
||||||
|
final BaseRequest<?, ?> gotResponse = baseRequestOptional.get();
|
||||||
|
assertEquals("a third string", gotResponse.getParameters().get("text"));
|
||||||
|
|
||||||
|
verify(fakeCampaignUtil, times(1)).insertNewCampaign(name, description);
|
||||||
|
verify(fakeChatUtil, times(1))
|
||||||
|
.updateChatContext(fakeTgChat, "/createCampaign", 3, Collections.emptyMap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue