Баарына салам. Эске сала кетейин: биринчи бөлүктө биз Flywayди коштук. уланталы.
docker-compose.yml маалымат базасын кошуу
Кийинки этап - негизги docker-compose.yml ичинде маалымат базасы менен иштөө. Келгиле, маалымат базасын docker-compose файлына кошолу:version: '3.1'
services:
jrtb-bot:
depends_on:
- jrtb-db
build:
context: .
environment:
BOT_NAME: ${BOT_NAME}
BOT_TOKEN: ${BOT_TOKEN}
BOT_DB_USERNAME: ${BOT_DB_USERNAME}
BOT_DB_PASSWORD: ${BOT_DB_PASSWORD}
restart: always
jrtb-db:
image: mysql:5.7
restart: always
environment:
MYSQL_USER: ${BOT_DB_USERNAME}
MYSQL_PASSWORD: ${BOT_DB_PASSWORD}
MYSQL_DATABASE: 'jrtb_db'
MYSQL_ROOT_PASSWORD: 'root'
ports:
- '3306:3306'
expose:
- '3306'
Мен дагы бул сапты биздин арызга коштум:
depends_on:
- jrtb-db
Бул тиркемени баштоодон мурун базанын башталышын күтөбүз дегенди билдирет. Андан кийин, маалымат базасы менен иштешибиз керек болгон дагы эки өзгөрмө кошулганын байкай аласыз:
${BOT_DB_USERNAME}
${BOT_DB_PASSWORD}
Биз аларды docker-compose программасына телеграмма ботундагыдай эле - айлана-чөйрө өзгөрмөлөрү аркылуу алабыз. Мен муну бизде маалымат базасынын колдонуучу аты менен анын сырсөзүнүн баалуулуктарын койгон бир гана жерибиз болушу үчүн жасадым. Биз аларды колдонмобуздун докер сүрөтүнө жана маалымат базабыздын докер контейнерине өткөрүп беребиз. Андан кийин SpringBoot программасын маалымат базасы үчүн өзгөрмөлөрдү кабыл алууга үйрөтүү үчүн Dockerfileди жаңыртышыбыз керек.
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
ENV BOT_NAME=test.javarush_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}", "-Dspring.datasource.username=${BOT_DB_USERNAME}", "-jar", "app.jar"]
Эми биз Dockerfileге маалымат базасынын өзгөрмөлөрүн кошобуз:
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
Өзгөрмө маанилери ар кандай болот. Биз Dockerfileге өтө тургандар демейки маанилерди талап кылат, ошондуктан мен кээ бирлерин киргиздим. Биз акыркы сапты эки элемент менен кеңейтебиз, анын жардамы менен биз DB колдонуучу атын жана паролду тиркемени ишке киргизебиз:
"-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}"
Докер файлындагы акыркы сап (ENTRYPOINT менен башталат) оролбостон болушу керек. Эгер сиз которууну жасасаңыз, бул code иштебейт. Акыркы кадам өзгөрмөлөрдү маалымат базасына өткөрүү үчүн start.sh файлын жаңыртуу.
#!/bin/bash
# Pull new changes
git pull
# Prepare Jar
mvn clean
mvn package
# Ensure, that docker-compose stopped
docker-compose stop
# Add environment variables
export BOT_NAME=$1
export BOT_TOKEN=$2
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
# Start new deployment
docker-compose up --build -d
Биз docker-compose иштетүүдөн мурун чөйрө өзгөрмөлөрүн кантип кошууну билебиз. Бул үчүн, сиз жөн гана var_name=var_value экспортун аткарышыңыз керек.. Ошондуктан, биз эки гана сапты кошобуз:
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
Бул жерде биз маалымат базасынын колдонуучу атын жана паролду орнотобуз. Албетте, биз боттун аталышы жана энбелгиси үчүн кылгандай, bash скриптин иштеткенде бул өзгөрмөлөрдү өткөрүүгө болот. Бирок мага мунун кереги жоктой сезилет. Маалыматтар базасына чындап кирүү үчүн, сиз маалымат базасы жайгаштырыла турган serverдин IP дарегин бorп, суроо-талапка уруксат берилген IP даректердин тизмесинде болушуңуз керек. Мага келсек, бул жетиштүү. Фундамент түптөлдү: эми сиз иштеп чыгуучуга түшүнүктүү болгон нерселерди жасай аласыз - code жазыңыз. Ага чейин биз DevOps инженерлери кылган иштерди аткарып жатканбыз - чөйрөнү орнотуу.
Репозиторий катмарын кошуу
Адатта, колдонмо үч катмардан турат:- Контроллерлор колдонмого кирүү чекиттери болуп саналат.
- Кызматтар бизнес логикасы иштейт. Бизде буга чейин жарым-жартылай бар: SendMessageService бизнес логикасынын ачык өкүлү.
- Репозиторийлер маалымат базасы менен иштөө үчүн жер. Биздин учурда, бул телеграмма боту.
- Биз JDBC менен иштешпейбиз: биз түздөн-түз жогорку абстракциялар менен иштейбиз. Башкача айтканда, маалымат базасындагы tableларга туура келген POJOларды сактаңыз. Биз мындай класстарды entity деп атайбыз , анткени алар расмий түрдө Java Persistence API'де аталат (бул ORM аркылуу маалымат базасы менен иштөө үчүн интерфейстердин жалпы жыйындысы, башкача айтканда, JDBC менен иштөө боюнча абстракция). Бизде маалымат базасында сактай турган an object классы болот жана алар бизге керектүү tableга жазылат. Маалыматтар базасынан издөөдө биз ошол эле an objectтерди алабыз.
- Spring Data өз интерфейстеринин топтомун колдонууну сунуш кылат: JpaRepository , CrudRepository , ж.б.... Башка интерфейстер бар: толук тизмени бул жерден тапса болот . Сулуулук, алардын ыкмаларын ишке ашырбай эле колдоно аласың(!). Мындан тышкары, интерфейсте жаңы ыкмаларды жазууга мүмкүн болгон белгилүү бир шаблон бар жана алар автоматтык түрдө ишке ашырылат.
- Жаз биздин өнүгүүбүздү мүмкүн болушунча жеңилдетет. Бул үчүн биз өзүбүздүн интерфейсибизди түзүп, жогоруда сүрөттөлгөндөрдөн мурасташыбыз керек. Жаз бул интерфейсти колдонуу керек экенин бorши үчүн, Репозиторий annotationсын кошуңуз.
- Эгерде бизде жок маалымат базасы менен иштөө ыкмасын жазуу керек болсо, анда бул да көйгөй эмес - биз аны жазабыз. Мен сага ал жерде эмне кылуу керектигин жана кантип көрсөтөм.
package com.github.javarushcommunity.jrtb.repository.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* Telegram User entity.
*/
@Data
@Entity
@Table(name = "tg_user")
public class TelegramUser {
@Id
@Column(name = "chat_id")
private String chatId;
@Column(name = "active")
private boolean active;
}
Бул жерде сиз бизде javax.persistence пакетинин бардык annotationлары бар экенин көрө аласыз. Бул бардык ORM ишке ашыруу үчүн колдонулган жалпы annotationлар. Демейки боюнча, Spring Data Jpa Hibernate режимин колдонот, бирок башка ишке ашыруулар колдонулушу мүмкүн. Бул жерде биз колдонгон annotationлардын тизмеси:
- Объект - бул маалымат базасы менен иштөө үчүн an object экендигин көрсөтөт;
- Таблица – бул жерде tableнын атын аныктайбыз;
- Id - annotation tableда кайсы талаа Негизги ачкыч болоорун айтат;
- Мамыча - tableдан талаанын атын аныктайт.
package com.github.javarushcommunity.jrtb.repository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, String> {
List<TelegramUser> findAllByActiveTrue();
}
Бул жерде сиз мен эч жерде ишке ашырбаган findAllByActiveTrue() ыкмасын кантип кошконумду көрө аласыз . Бирок бул анын иштешине тоскоол болбойт. Spring Data tg_user tableсынан бардык жазууларды алуу керек экенин түшүнөт, анын активдүү талаасы = true . Биз TelegramUser an objectи менен иштөө кызматын кошобуз (биз SOLIDден көз карандылыктын инversionсын башка субъекттердин кызматтары башка an objectтин репозиторийлери менен түздөн-түз байланыша албаган контекстте колдонобуз - ошол субъекттин кызматы аркылуу гана). Биз пакетте TelegramUserService кызматын түзөбүз, анын азырынча бир нече ыкмалары болот: колдонуучуну сактап, колдонуучуну анын идентификатору боюнча алуу жана активдүү колдонуучулардын тизмесин көрсөтүү. Алгач TelegramUserService интерфейсин түзөбүз:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* {@link Service} for handling {@link TelegramUser} entity.
*/
public interface TelegramUserService {
/**
* Save provided {@link TelegramUser} entity.
*
* @param telegramUser provided telegram user.
*/
void save(TelegramUser telegramUser);
/**
* Retrieve all active {@link TelegramUser}.
*
* @return the collection of the active {@link TelegramUser} objects.
*/
List<TelegramUser> retrieveAllActiveUsers();
/**
* Find {@link TelegramUser} by chatId.
*
* @param chatId provided Chat ID
* @return {@link TelegramUser} with provided chat ID or null otherwise.
*/
Optional<TelegramUser> findByChatId(String chatId);
}
Жана, чындыгында, TelegramUserServiceImpl ишке ашыруу:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.repository.TelegramUserRepository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* Implementation of {@link TelegramUserService}.
*/
@Service
public class TelegramUserServiceImpl implements TelegramUserService {
private final TelegramUserRepository telegramUserRepository;
@Autowired
public TelegramUserServiceImpl(TelegramUserRepository telegramUserRepository) {
this.telegramUserRepository = telegramUserRepository;
}
@Override
public void save(TelegramUser telegramUser) {
telegramUserRepository.save(telegramUser);
}
@Override
public List<TelegramUser> retrieveAllActiveUsers() {
return telegramUserRepository.findAllByActiveTrue();
}
@Override
public Optional<TelegramUser> findByChatId(String chatId) {
return telegramUserRepository.findById(chatId);
}
}
Бул жерде биз Autowired annotationсын колдонуу менен TelegramuserRepository an objectинин көз карандылык инъекциясын (класстын инстанциясын киргизебиз) жана конструктордо колдонгонубузду белгилей кетүү керек. Сиз муну өзгөрмө үчүн жасай аласыз, бирок бул Spring Framework командасы бизге сунуш кылган ыкма.
Боттун статистикасын кошуу
Андан кийин / баштоо жана / токтотуу буйруктарын жаңыртышыңыз керек. /start буйругу колдонулганда, сиз жаңы колдонуучуну маалымат базасына сактап, аны активдүү = чындык кылып коюшуңуз керек. Жана /stop болгондо, колдонуучунун маалыматтарын жаңыртыңыз: активдүү = false орнотуңуз. StartCommand классын оңдойлу :package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;
/**
* Start {@link Command}.
*/
public class StartCommand implements Command {
private final SendBotMessageService sendBotMessageService;
private final TelegramUserService telegramUserService;
public final static String START_MESSAGE = "Привет. Я Javarush Telegram Bot. Я помогу тебе быть в курсе последних " +
"статей тех авторов, котрые тебе интересны. Я еще маленький и только учусь.";
public StartCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
String chatId = update.getMessage().getChatId().toString();
telegramUserService.findByChatId(chatId).ifPresentOrElse(
user -> {
user.setActive(true);
telegramUserService.save(user);
},
() -> {
TelegramUser telegramUser = new TelegramUser();
telegramUser.setActive(true);
telegramUser.setChatId(chatId);
telegramUserService.save(telegramUser);
});
sendBotMessageService.sendMessage(chatId, START_MESSAGE);
}
}
Бул жерде биз TelegramuserService an objectисин конструкторго өткөрүп беребиз, анын жардамы менен жаңы колдонуучуну сактап калабыз. Андан тышкары, Java'дагы Optional'дын ырахаттарын колдонуу менен, төмөнкү логика иштейт: эгерде бизде маалымат базасында колдонуучу болсо, биз аны жөн гана активдүү кылабыз, эгерде жок болсо, жаңы активдүүнү түзөбүз. StopCommand:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;
import java.util.Optional;
/**
* Stop {@link Command}.
*/
public class StopCommand implements Command {
private final SendBotMessageService sendBotMessageService;
private final TelegramUserService telegramUserService;
public static final String STOP_MESSAGE = "Деактивировал все ваши подписки \uD83D\uDE1F.";
public StopCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
telegramUserService.findByChatId(update.getMessage().getChatId().toString())
.ifPresent(it -> {
it.setActive(false);
telegramUserService.save(it);
});
}
}
Биз TelegramServiceTestти StopCommand'га ушундай эле жол менен өткөрүп беребиз. Кошумча логика мындай: эгерде бизде ушундай чат ID бар колдонуучу болсо, биз аны өчүрөбүз, башкача айтканда, биз активдүү = жалган деп коёбуз. Муну өз көзүң менен кантип көрө аласың? Боттун статистикасын көрсөтө турган жаңы /stat буйругун түзөлү. Бул этапта, бул бардык колдонуучулар үчүн жеткorктүү жөнөкөй статистика болот. Келечекте биз аны чектеп, администраторлор үчүн гана мүмкүнчүлүк беребиз. Статистикада бир жазуу болот: активдүү бот колдонуучуларынын саны. Бул үчүн, CommandNameге STAT("/stat") маанисин кошуңуз . Андан кийин, StatCommand классын түзүңүз :
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.telegram.telegrambots.meta.api.objects.Update;
/**
* Statistics {@link Command}.
*/
public class StatCommand implements Command {
private final TelegramUserService telegramUserService;
private final SendBotMessageService sendBotMessageService;
public final static String STAT_MESSAGE = "Javarush Telegram Bot использует %s человек.";
@Autowired
public StatCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
int activeUserCount = telegramUserService.retrieveAllActiveUsers().size();
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), String.format(STAT_MESSAGE, activeUserCount));
}
}
Бул жерде бардыгы жөнөкөй: биз retrieveAllActiveUsers ыкмасын колдонуу менен бардык активдүү колдонуучулардын тизмесин алабыз жана коллекциянын көлөмүн алабыз. Ошондой эле, биз азыр өсүүчү класстарды жаңыртышыбыз керек: CommandContainer жана JavarushTelegramBot , алар бизге керектүү жаңы кызматты өткөрүүгө үйрөнүшөт. CommandContainer:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import com.google.common.collect.ImmutableMap;
import static com.github.javarushcommunity.jrtb.command.CommandName.*;
/**
* Container of the {@link Command}s, which are using for handling telegram commands.
*/
public class CommandContainer {
private final ImmutableMap<String, Command> commandMap;
private final Command unknownCommand;
public CommandContainer(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
commandMap = ImmutableMap.<String, Command>builder()
.put(START.getCommandName(), new StartCommand(sendBotMessageService, telegramUserService))
.put(STOP.getCommandName(), new StopCommand(sendBotMessageService, telegramUserService))
.put(HELP.getCommandName(), new HelpCommand(sendBotMessageService))
.put(NO.getCommandName(), new NoCommand(sendBotMessageService))
.put(STAT.getCommandName(), new StatCommand(sendBotMessageService, telegramUserService))
.build();
unknownCommand = new UnknownCommand(sendBotMessageService);
}
public Command retrieveCommand(String commandIdentifier) {
return commandMap.getOrDefault(commandIdentifier, unknownCommand);
}
}
Бул жерде биз картага жаңы буйрукту кошуп, аны TelegramUserService конструктору аркылуу өткөрдүк. Бирок боттун өзүндө конструктор гана өзгөрөт:
@Autowired
public JavarushTelegramBot(TelegramUserService telegramUserService) {
this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this), telegramUserService);
}
Эми биз Autowired annotationсын кошуп, аргумент катары TelegramUserService тапшырабыз. Бул биз аны Колдонмо контекстинен алабыз дегенди билдирет. Биз ошондой эле HelpCommand классын жаңыртабыз , ошентип жаңы статистика буйругу сыпаттамада пайда болот.
Кол менен сыноо
Келгиле, маалымат базасын docker-compose-test.yml жана JavarushTelegramBotApplication классындагы негизги ыкманы ишке киргизели. Андан кийин биз буйруктардын топтомун жазабыз:- /stat - биз маалымат базасы бош болсо, бул ботту колдонгон адам нөл болот деп күтөбүз;
- /старт - ботту баштоо;
- /stat - азыр биз ботту 1 адам колдонот деп күтөбүз;
- /стоп - ботту токтотуу;
- /stat - биз аны кайрадан 0 адам колдонот деп күтөбүз.
Биз тесттерди жазып, жаңыртабыз
Биз конструкторлорду алмаштыргандыктан, тест класстарын да жаңыртышыбыз керек болот. AbstractCommandTest классында биз дагы бир талааны кошушубуз керек - үч буйрук үчүн зарыл болгон TelegramUserService классы:protected TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
Андан кийин, CommandContainer ичиндеги init() ыкмасын жаңырталы :
@BeforeEach
public void init() {
SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
commandContainer = new CommandContainer(sendBotMessageService, telegramUserService);
}
StartCommand ичинде getCommand() ыкмасын жаңыртышыңыз керек :
@Override
Command getCommand() {
return new StartCommand(sendBotMessageService, telegramUserService);
}
Ошондой эле StopCommand ичинде:
@Override
Command getCommand() {
return new StopCommand(sendBotMessageService, telegramUserService);
}
Кийинки, келгиле, жаңы тесттерди карап көрөлү. StatCommand үчүн типтүү тест түзөлү :
package com.github.javarushcommunity.jrtb.command;
import static com.github.javarushcommunity.jrtb.command.CommandName.STAT;
import static com.github.javarushcommunity.jrtb.command.StatCommand.STAT_MESSAGE;
public class StatCommandTest extends AbstractCommandTest {
@Override
String getCommandName() {
return STAT.getCommandName();
}
@Override
String getCommandMessage() {
return String.format(STAT_MESSAGE, 0);
}
@Override
Command getCommand() {
return new StatCommand(sendBotMessageService, telegramUserService);
}
}
Бул жөнөкөй. Эми маалымат базасы менен иштөөнү кантип сынай турганыбыз жөнүндө сүйлөшөлү. Биз буга чейин бирдик сыноолорун гана жасаганбыз. Интеграция тести колдонмонун бир нече бөлүктөрү ортосундагы интеграцияны текшерет. Мисалы, тиркемелер жана маалымат базалары. Бул жерде баары татаалыраак болот, анткени тестирлөө үчүн бизге жайгаштырылган маалымат базасы керек. Ошондуктан, биз өзүбүздүн тесттерибизди локалдуу иштеткенде, бизде docker-compose-test.yml менен иштеген маалымат базасы болушу керек. Бул тестти иштетүү үчүн сиз SpringBoot тиркемесин толугу менен иштетишиңиз керек. Сыноо классында SpringBootTest annotationсы бар , ал колдонмону баштайт. Бирок бул ыкма биз үчүн иштебейт, анткени тиркеме ишке киргенде телеграмма боту да ишке кирет. Бирок бул жерде карама-каршылык бар. Сыноолор биздин машинада жергorктүү түрдө жана GitHub Actions аркылуу жалпыга ачык жүргүзүлөт. Тесттер бүт тиркемени ишке киргизүү менен өтүшү үчүн, биз аларды телеграмма ботундагы жарактуу маалыматтар менен иштетишибиз керек: башкача айтканда, анын аты жана белгиси боюнча... Ошондуктан, бизде эки вариант бар:
- Андыктан боттун атын жана токенин жалпыга ачык кылып, баары жакшы болот, аны эч ким пайдаланып, бизге кийлигишпейт деп ишениңиз.
- Башка жол менен кел.
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
Биз үчүн алар ./src/test/resources/ + annotationда көрсөтүлгөн жолдун боюнда жайгашат. Бул жерде алар кандай көрүнөт:
clearDbs.sql:
DELETE FROM tg_user;
telegram_users.sql:
INSERT INTO tg_user VALUES ("123456789", 1);
INSERT INTO tg_user VALUES ("123456788", 1);
INSERT INTO tg_user VALUES ("123456787", 1);
INSERT INTO tg_user VALUES ("123456786", 1);
INSERT INTO tg_user VALUES ("123456785", 1);
INSERT INTO tg_user VALUES ("123456784", 0);
INSERT INTO tg_user VALUES ("123456782", 0);
INSERT INTO tg_user VALUES ("123456781", 0);
Натыйжада биздин TelegramUserRepositoryIT тестибиз ушундай болот (көрүп тургандай, интеграциялык тестирлөөнүн аталышы башкача болот - биз Тестти эмес, ITти кошобуз):
package com.github.javarushcommunity.jrtb.repository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;
import java.util.List;
import java.util.Optional;
import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;
/**
* Integration-level testing for {@link TelegramUserRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class TelegramUserRepositoryIT {
@Autowired
private TelegramUserRepository telegramUserRepository;
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
@Test
public void shouldProperlyFindAllActiveUsers() {
//when
List<TelegramUser> users = telegramUserRepository.findAllByActiveTrue();
//then
Assertions.assertEquals(5, users.size());
}
@Sql(scripts = {"/sql/clearDbs.sql"})
@Test
public void shouldProperlySaveTelegramUser() {
//given
TelegramUser telegramUser = new TelegramUser();
telegramUser.setChatId("1234567890");
telegramUser.setActive(false);
telegramUserRepository.save(telegramUser);
//when
Optional<TelegramUser> saved = telegramUserRepository.findById(telegramUser.getChatId());
//then
Assertions.assertTrue(saved.isPresent());
Assertions.assertEquals(telegramUser, saved.get());
}
}
Биз тесттерди жаздык, бирок суроо туулат: GitHub'та биздин CI процессибизди ишке киргизүү менен эмне болот? Анын маалымат базасы болбойт. Азырынча чындап эле жөн гана кызыл курулуш болот. Бул үчүн, бизде GitHub аракеттери бар, анда биз курууну ишке киргизүүнү конфигурациялай алабыз. Сыноолорду жүргүзүүдөн мурун, керектүү орнотуулар менен маалымат базасын ишке киргизишиңиз керек. Көрсө, Интернетте мисалдар көп эмес, ошондуктан мен сизге муну бир жерге сактап коюуну кеңеш кылам. .github/workflows/maven.yml файлын жаңырталы:
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI with Maven
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up MySQL
uses: mirromutth/mysql-action@v1.1
with:
mysql version: '5.7'
mysql database: 'dev_jrtb_db'
mysql root password: 'root'
mysql user: 'dev_jrtb_db_user'
mysql password: 'dev_jrtb_db_password'
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
- name: Build with Maven
run: mvn -B package --file pom.xml
Азыр жаңы MySQL орнотуу блогу бар . Анда биз MySQLди CI процессибизге кошобуз, ошол эле учурда бизге керектүү өзгөрмөлөрдү аныктайбыз. Азыр биз каалаган нерсенин бардыгын коштук. Акыркы этап өзгөрүүлөрдү түртүп, куруу өтүп, жашыл болорун көрүүгө болот.
Документти жаңылоо
Келгиле, долбоордун versionсын pom.xml ичиндеги 0.3.0-SNAPSHOTтен 0.4.0-SNAPSHOTка чейин жаңырталы, ошондой эле RELEASE_NOTESге кошолу:## 0.4.0-SNAPSHOT
* JRTB-1: added repository layer.
Ушунун бардыгынан кийин, биз милдеттенме, түртүү жана тартуу өтүнүчүн түзөбүз. Эң негизгиси биздин курулушубуз жашыл!
Пайдалуу шилтемелер:
- Биздин телеграмма ботунун репозиторийси
- Макалада сүрөттөлгөн бардык өзгөртүүлөр менен өтүнүчтү тартыңыз
- SpringBoot + Flyway макаласы
- DockerHub'тан MySQL сүрөтү
- Орто: Docker Compose менен MySql инстанциясын кантип түзүү керек
- Habr: Жазгы маалыматтар Jpa
- Менин телеграм каналым
GO TO FULL VERSION