diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..02e6602ce6e4217e09c914d52e42bdd171aa624a --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,49 @@ +name: Cpp + +on: + push: + branches: [ "dev", "main" ] + pull_request: + branches: [ "dev", "main" ] + +defaults: + run: + shell: bash + +jobs: + build: + runs-on: ubuntu-latest + container: raiden454/cpp-app + steps: + - uses: actions/checkout@v3 + - name: Installing + run: | + apt update + git clone https://github.com/google/googletest.git -b release-1.11.0 + cd googletest + mkdir build + cd build + cmake .. -DBUILD_GMOCK=ON + make + make install + cd ../.. + - name: Building + run: | + mkdir build + cd build + cmake .. + make + - name: Run-Tests + run: | + ./build/server/internal/service/tests/test_service + linters: + runs-on: ubuntu-latest + container: raiden454/cpp-app + steps: + - uses: actions/checkout@v3 + - name: Cppcheck + run: | + cppcheck server --std=c++17 --enable=all + - name: Cpplint + run: | + cpplint --extensions=cpp,hpp,h --recursive ./server/* diff --git a/.gitignore b/.gitignore index ff16b4fa8dbae03cd112cd35af40b655a8b5147e..6baac201cbb815ea31f8520cb6b597ac1743af67 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ /cmake-build-debug/ /build/ +.vscode .idea +.vscode server/build/ log.txt CMakeUserPresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 90ae8b6e9170d48b324111d6381ffd7baa7196b2..e51f2bfe1b5440809734dbcce45c2ac87f01a54f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ set(CMAKE_CXX_STANDARD 20) -cmake_minimum_required(VERSION 3.19) +cmake_minimum_required(VERSION 3.16) set(PROJECT_NAME "SourcedOut") diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..fd9957b926a8c123392a74501b8176fc2f51c710 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,44 @@ +FROM ubuntu:20.04 AS base + +ENV TZ=Europe/Moscow +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +RUN apt update -y +RUN apt install -y gcc +RUN apt install -y libpqxx-dev +RUN apt install -y clang-tidy +RUN apt install -y nlohmann-json3-dev +RUN apt install -y python3-pip +RUN apt install -y cppcheck +RUN apt install -y git +RUN apt-get update -y +RUN apt install -y xvfb +RUN pip install gcovr +RUN pip install cpplint + +RUN apt-get install wget +RUN apt-get install libssl-dev + +RUN wget https://github.com/Kitware/CMake/releases/download/v3.26.3/cmake-3.26.3.tar.gz +RUN tar -zxvf cmake-3.26.3.tar.gz +WORKDIR cmake-3.26.3 +RUN ./bootstrap +RUN make +RUN make install +RUN cd .. + +RUN wget https://boostorg.jfrog.io/artifactory/main/release/1.82.0/source/boost_1_82_0.tar.gz +RUN tar xvf boost_1_82_0.tar.gz +WORKDIR boost_1_82_0 +RUN ./bootstrap.sh --prefix=/usr/ +RUN ./b2 install + +RUN git clone https://github.com/google/googletest.git -b release-1.11.0 +WORKDIR googletest/build +RUN cmake .. -DBUILD_GMOCK=OFF +RUN make +RUN make install + +WORKDIR /project + +COPY . . \ No newline at end of file diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index e7bc4e06ed8f34b20499ab54431bdcc11c11d804..2e5c3dc9fcef8d100ad122beedaf7d1ddaf37e57 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,15 +1,22 @@ -set(CMAKE_CXX_STANDARD 20) -cmake_minimum_required(VERSION 3.19) set(CMAKE_PREFIX_PATH build) project(SourcedOut CXX) -find_package(antlr4-runtime REQUIRED) +# find_package(antlr4-runtime REQUIRED) find_package(Boost 1.8.1 REQUIRED) -find_package(libpqxx REQUIRED) +# find_package(libpqxx REQUIRED) +find_library(PQXX_LIB pqxx) find_package(GTest REQUIRED) find_package(nlohmann_json REQUIRED) message(STATUS ${nlohmann_json_LIBRARIES}) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + add_subdirectory(internal) add_subdirectory(cmd) + +message(STATUS ${Boost_LIBRARIES}) +set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-lpthread -pthread") + #add_subdirectory(cpp-dotenv) #add_executable(${PROJECT_NAME} text-basic-metrics/tbm_main.cpp text-basic-metrics/tbm_main.cpp) строка для запуска моей части в text-basic-metrics -#target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${antlr4-runtime_LIBRARIES} ${libpqxx_LIBRARIES} ${GTest_LIBRARIES} nlohmann_json::nlohmann_json) +# target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${antlr4-runtime_LIBRARIES} ${libpqxx_LIBRARIES} ${GTest_LIBRARIES}) \ No newline at end of file diff --git a/server/cmd/CMakeLists.txt b/server/cmd/CMakeLists.txt index 7b7c4687a15ec3d3cada06753b2b9f102b3bf47a..446f2a8e6f3113d0433e9a8e9540cb15711dc13a 100644 --- a/server/cmd/CMakeLists.txt +++ b/server/cmd/CMakeLists.txt @@ -1,7 +1,3 @@ -cmake_minimum_required(VERSION 3.19) - -set(CMAKE_CXX_STANDARD 20) - set(PROJECT_NAME "Server") project(${PROJECT_NAME}) diff --git a/server/internal/CMakeLists.txt b/server/internal/CMakeLists.txt index 930ea1a897b5a3c2e1c1d1640ee66489ac4ef175..f23cb6bf4ee5ceddca8379d76e7412c294e919dc 100644 --- a/server/internal/CMakeLists.txt +++ b/server/internal/CMakeLists.txt @@ -1,16 +1,19 @@ -set(CMAKE_CXX_STANDARD 20) -cmake_minimum_required(VERSION 3.19) add_subdirectory(src) add_subdirectory(entities) add_subdirectory(dbManager) add_subdirectory(repository) +add_subdirectory(service) + set(libEntities_LIB ${libEntities_LIB} PARENT_SCOPE) set(libEntities_INCLUDE_DIRS ${libEntities_INCLUDE_DIRS} PARENT_SCOPE) set(libRepository_LIB ${libRepository_LIB} PARENT_SCOPE) set(libRepository_INCLUDE_DIRS ${libRepository_INCLUDE_DIRS} PARENT_SCOPE) +set(SERVICE_LIB ${SERVICE_lib_LIB} PARENT_SCOPE) +set(SERVICE_INCLUDE_DIRS ${SERVICE_lib_INCLUDE_DIRS} PARENT_SCOPE) + set(libDbManager_LIB ${libDbManager_LIB} PARENT_SCOPE) set(libDbManager_INCLUDE_DIRS ${libDbManager_INCLUDE_DIRS} PARENT_SCOPE) -message("DbManager = ${libDbManager_LIB}") \ No newline at end of file +message("DbManager = ${libDbManager_LIB}") diff --git a/server/internal/dbManager/CMakeLists.txt b/server/internal/dbManager/CMakeLists.txt index 432484efd9dd9147e4e617c339d6bc084cb07ec6..3299f969567d69d641e4ea3d86ae4edc9a6bca1e 100644 --- a/server/internal/dbManager/CMakeLists.txt +++ b/server/internal/dbManager/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.19) - project("DbManagerLib") set(LIB_NAME libDbManager) diff --git a/server/internal/entities/CMakeLists.txt b/server/internal/entities/CMakeLists.txt index 08858c7e652e13d26b92360cf52bc6e7511ffd54..7b6ad66178ab491134855e3b0327e11e56c73072 100644 --- a/server/internal/entities/CMakeLists.txt +++ b/server/internal/entities/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.19) - project("EntitiesLib") set(LIB_NAME libEntities) diff --git a/server/internal/entities/include/Solution.hpp b/server/internal/entities/include/Solution.hpp index dcfa255607b382b079f22877977fa8ff54c24cee..5c39ef5e7a2593035c2eb313f0e0635f053dc3e5 100644 --- a/server/internal/entities/include/Solution.hpp +++ b/server/internal/entities/include/Solution.hpp @@ -7,6 +7,7 @@ class Solution { public: + Solution() =default; Solution(size_t id, std::string sendDate, size_t senderId, std::string source, std::string tokens, std::string astTree, size_t taskId, std::string result); diff --git a/server/internal/entities/include/Task.hpp b/server/internal/entities/include/Task.hpp index a9832ec37d3ce44954f833b50abe00d9232adb93..6ad362fc839c3dc314b302617a349e086274c726 100644 --- a/server/internal/entities/include/Task.hpp +++ b/server/internal/entities/include/Task.hpp @@ -8,6 +8,8 @@ private: std::string description; public: + Task()=default; + ~Task() = default; Task(size_t id, std::string description); explicit Task(std::string description); diff --git a/server/internal/entities/include/User.hpp b/server/internal/entities/include/User.hpp index 23a2f137e9ff5c1c5c68c3cd54854f02e7a63af0..a597e6926f09b3bf457b087aca388d907d76dc36 100644 --- a/server/internal/entities/include/User.hpp +++ b/server/internal/entities/include/User.hpp @@ -13,6 +13,7 @@ private: std::string username; public: + User()=default; User(size_t id_, std::string login_, std::string password_, std::string username_); User(std::string login_, std::string password_, std::string username_); diff --git a/server/internal/repository/CMakeLists.txt b/server/internal/repository/CMakeLists.txt index c3eeeee670b165d7fbdcf9958dc2e6b7992079f7..67bead16b45306754c0cd277a2a2d2f10bc14df0 100644 --- a/server/internal/repository/CMakeLists.txt +++ b/server/internal/repository/CMakeLists.txt @@ -1,11 +1,9 @@ -cmake_minimum_required(VERSION 3.19) - project("RepositoryLib") set(LIB_NAME libRepository) file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) -file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) +file(GLOB_RECURSE HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h ${CMAKE_CURRENT_SOURCE_DIR}/virtual/*.hpp) message("SOURCES = ${SOURCES}") message("HEADERS = ${HEADERS}") @@ -18,13 +16,13 @@ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -lboost_filesystem") add_library(${LIB_NAME} ${SOURCES} ${HEADERS}) -target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) +target_include_directories(${LIB_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) target_link_libraries(${LIB_NAME} ${Boost_LIBRARIES} ${libpqxx_LIBRARIES} ${libEntities_LIB} ${libDbManager_LIB}) set(libRepository_LIB ${LIB_NAME}) set(libRepository_LIB ${libRepository_LIB} PARENT_SCOPE) -set(libRepository_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include) +set(libRepository_INCLUDE_DIRS ${LIB_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) set(libRepository_INCLUDE_DIRS ${libRepository_INCLUDE_DIRS} PARENT_SCOPE) diff --git a/server/internal/repository/include/SolutionRepository.hpp b/server/internal/repository/include/SolutionRepository.hpp index 04034d52ef8734bed542ca7d5626b5620404a7be..8b4d326cebf27acef9dd1a7bec0937b1d0513a56 100644 --- a/server/internal/repository/include/SolutionRepository.hpp +++ b/server/internal/repository/include/SolutionRepository.hpp @@ -11,13 +11,15 @@ using namespace pqxx; class SolutionRepository : ISolutionRepository { + // ~SolutionRepository()override = default; Solution getSolutionById(size_t id) override; std::vector getSolutionsBySenderId(size_t sender_id) override; std::vector getSolutionsByTaskId(size_t task_id) override; + std::vector getSolutions(size_t sender_id, size_t task_id) override; - void storeSolution(Solution solution) override; + size_t storeSolution(Solution solution) override; void updateSolution(Solution solution) override; diff --git a/server/internal/repository/include/TaskRepository.hpp b/server/internal/repository/include/TaskRepository.hpp index 793ea2f319c527a5ea7267cd6f9f121954b0cc76..11d2f3efdb8d5dfdf3e73df01491b9b80f192257 100644 --- a/server/internal/repository/include/TaskRepository.hpp +++ b/server/internal/repository/include/TaskRepository.hpp @@ -9,13 +9,13 @@ using namespace pqxx; -class TaskRepository : ITaskRepository { +class TaskRepository : public ITaskRepository { public: Task getTaskById(size_t id) override; void updateTask(Task task) override; - void storeTask(Task task) override; + int storeTask(Task task) override; void deleteTask(Task task) override; diff --git a/server/internal/repository/src/SolutionRepository.cpp b/server/internal/repository/src/SolutionRepository.cpp index 4e21f053d4c63fe7b5ac8c78eb12e7d6defb1ad8..a33884bf0d1a8d66901fe317664f21f0d33851c9 100644 --- a/server/internal/repository/src/SolutionRepository.cpp +++ b/server/internal/repository/src/SolutionRepository.cpp @@ -5,6 +5,10 @@ #include "SolutionRepository.hpp" using namespace pqxx; +std::vector SolutionRepository::getSolutions(size_t sender_id, size_t task_id){ + return std::vector(); +} + Solution SolutionRepository::getSolutionById(size_t id) { try { auto c = manager->connection(); @@ -53,7 +57,7 @@ std::vector SolutionRepository::getSolutionsByTaskId(size_t task_id) } } -void SolutionRepository::storeSolution(Solution solution) { +size_t SolutionRepository::storeSolution(Solution solution) { try { auto c = manager->connection(); diff --git a/server/internal/repository/src/TaskRepository.cpp b/server/internal/repository/src/TaskRepository.cpp index 58725b51cc34a0a3c7df71f726ba54ec86454fdf..ea434516697ba493acc42116eba2c00148d6d53f 100644 --- a/server/internal/repository/src/TaskRepository.cpp +++ b/server/internal/repository/src/TaskRepository.cpp @@ -32,7 +32,7 @@ void TaskRepository::updateTask(Task task) { } } -void TaskRepository::storeTask(Task task) { +int TaskRepository::storeTask(Task task) { try { auto c = manager->connection(); std::string sql = (boost::format("INSERT INTO tasks (description) " \ @@ -45,6 +45,7 @@ void TaskRepository::storeTask(Task task) { std::cerr << e.what() << std::endl; throw e; } + return 0; } void TaskRepository::deleteTask(Task task) { diff --git a/server/internal/repository/src/UserRepository.cpp b/server/internal/repository/src/UserRepository.cpp index b4962862093b054d81fb091a38ccdd564c15080a..2ba6ee04aaca76b2695487cd4694dc60658abb16 100644 --- a/server/internal/repository/src/UserRepository.cpp +++ b/server/internal/repository/src/UserRepository.cpp @@ -43,7 +43,6 @@ size_t UserRepository::makeUser(User user) { w.exec(sql); w.commit(); manager->freeConnection(c); - return getUserByLogin(user.getLogin()).getId(); } catch (const std::exception &e) { std::cerr << e.what() << std::endl; diff --git a/server/internal/repository/virtual/ISolutionRepository.hpp b/server/internal/repository/virtual/ISolutionRepository.hpp index c64a1b6c15767892048b8a2b69cd0f1c3de362e0..8a7b03532869bcbc96bad16cfd680a593ccbf884 100644 --- a/server/internal/repository/virtual/ISolutionRepository.hpp +++ b/server/internal/repository/virtual/ISolutionRepository.hpp @@ -6,13 +6,16 @@ #include "../../entities/include/Solution.hpp" class ISolutionRepository { + public: + virtual ~ISolutionRepository() = default; virtual Solution getSolutionById(size_t id) = 0; virtual std::vector getSolutionsBySenderId(size_t sender_id) = 0; + virtual std::vector getSolutions(size_t sender_id, size_t task_id) = 0; virtual std::vector getSolutionsByTaskId(size_t task_id) = 0; - virtual void storeSolution(Solution solution) = 0; + virtual size_t storeSolution(Solution solution) = 0; virtual void updateSolution(Solution solution) = 0; diff --git a/server/internal/repository/virtual/ITaskRepository.hpp b/server/internal/repository/virtual/ITaskRepository.hpp index 26d32d23017abec52275cea7883d0c215f199243..e367c21d528da5a084b1a2ac02410fea9a6ebc05 100644 --- a/server/internal/repository/virtual/ITaskRepository.hpp +++ b/server/internal/repository/virtual/ITaskRepository.hpp @@ -5,11 +5,13 @@ #include "../../entities/include/Task.hpp" class ITaskRepository { + public: + virtual ~ITaskRepository() = default; virtual Task getTaskById(size_t id) = 0; virtual void updateTask(Task task) = 0; - virtual void storeTask(Task task) = 0; + virtual int storeTask(Task task) = 0; virtual void deleteTask(Task task) = 0; diff --git a/server/internal/repository/virtual/IUserRepository.hpp b/server/internal/repository/virtual/IUserRepository.hpp index 8b3f7ab20af9f478fc68b318ada1a3d3d594696e..14d7a50ee86692f3768460ce520b78a50cf82f94 100644 --- a/server/internal/repository/virtual/IUserRepository.hpp +++ b/server/internal/repository/virtual/IUserRepository.hpp @@ -5,6 +5,7 @@ #include "../../entities/include/User.hpp" class IUserRepository { public: + virtual ~IUserRepository() = default; virtual User getUserById(size_t id) = 0; virtual User getUserByLogin(std::string login) = 0; diff --git a/server/internal/service/CMakeLists.txt b/server/internal/service/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..1fd20be844a7b9bb8361879a84f447bdab20de5b --- /dev/null +++ b/server/internal/service/CMakeLists.txt @@ -0,0 +1,23 @@ +project("ServiceLib") + +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +file(GLOB INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/virtual) + + +include_directories(${INCLUDE_DIRS}) +add_library(${PROJECT_NAME} ${SOURCES}) + +message("libRepository_LIB = ${libRepository_LIB}") +message("libRepository_INCLUDE_DIRS = ${libRepository_INCLUDE_DIRS}") + +# target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIRS} ${libEntities_INCLUDE_DIRS} ${libRepository_INCLUDE_DIRS}) +target_link_libraries(${PROJECT_NAME} ${libRepository_LIB} ${ibEntities_LIB}) + +set(SERVICE_lib_LIBRARY ${PROJECT_NAME}) +set(SERVICE_lib_LIBRARY ${SERVICE_lib_LIBRARY} PARENT_SCOPE) + +set(SERVICE_lib_INCLUDE_DIRS ${INCLUDE_DIRS}) +set(SERVICE_lib_INCLUDE_DIRS ${SERVICE_lib_INCLUDE_DIRS} PARENT_SCOPE) + +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/server/internal/service/include/Exceptions.h b/server/internal/service/include/Exceptions.h new file mode 100644 index 0000000000000000000000000000000000000000..a032c21f5db1fa1cd4376dca67e295819a91d665 --- /dev/null +++ b/server/internal/service/include/Exceptions.h @@ -0,0 +1,9 @@ +#pragma once + +class ValidateException : public std::exception { + std::string _msg; + + public: + ValidateException(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; \ No newline at end of file diff --git a/server/internal/service/include/SolutionService.h b/server/internal/service/include/SolutionService.h new file mode 100644 index 0000000000000000000000000000000000000000..86e432bc31c68676e19247e59e063fb8bca7cade --- /dev/null +++ b/server/internal/service/include/SolutionService.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "ISolutionRepository.hpp" +#include "ISolutionService.h" + +class SolutionService : ISolutionService { + private: + std::unique_ptr solutionRepo; + // std::unique_ptr antlr + public: + explicit SolutionService(std::unique_ptr solutionRepo); + Solution createSolution(size_t userId, size_t taskId, + std::string source) override; + std::vector getSolutionsByUserAndTaskId(size_t userId, + size_t taskId) override; + void deleteSolutionById(size_t solId) override; + std::pair getMetrics(size_t solId) override; +}; diff --git a/server/internal/service/include/TaskService.h b/server/internal/service/include/TaskService.h new file mode 100644 index 0000000000000000000000000000000000000000..bf04d3e472f736a8c24bd1d753cd5ed5a3577636 --- /dev/null +++ b/server/internal/service/include/TaskService.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include "ITaskRepository.hpp" +#include "ITaskService.h" + +class TaskService : public ITaskService { + private: + std::unique_ptr taskRepo; + + public: + TaskService(std::unique_ptr taskRepo); + ~TaskService() override = default; + Task createTask(std::string desc) override; + Task getTask(size_t id) override; + std::vector getAllTasks() override; + void deleteTask(size_t id) override; +}; diff --git a/server/internal/service/include/UserService.h b/server/internal/service/include/UserService.h new file mode 100644 index 0000000000000000000000000000000000000000..719aa02fbcc1f38035bf4cf134085fdcc11af00f --- /dev/null +++ b/server/internal/service/include/UserService.h @@ -0,0 +1,19 @@ +#pragma once +#include + +#include "IUserRepository.hpp" +#include "IUserService.h" +#include "UserValidator.h" + +class UserService : IUserService { + private: + std::unique_ptr userRepo; + + public: + ~UserService() override = default; + explicit UserService(std::unique_ptr userRepo); + User createUser(std::string login, std::string username, + std::string password) override; + User getUserById(size_t id) override; + void deleteUser(size_t id) override; +}; diff --git a/server/internal/service/include/UserValidator.h b/server/internal/service/include/UserValidator.h new file mode 100644 index 0000000000000000000000000000000000000000..002f4503d5a1aaeaef870a5034b29879bfdd2b91 --- /dev/null +++ b/server/internal/service/include/UserValidator.h @@ -0,0 +1,16 @@ +#pragma once + +#include "User.hpp" + +class UserValidator { + private: + User user; + bool validateLogin(); + bool validatePassword(); + bool validateUsername(); + + public: + explicit UserValidator(User user); + bool validateUser(); + ~UserValidator() = default; +}; diff --git a/server/internal/service/src/SolutionService.cpp b/server/internal/service/src/SolutionService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..216b76c52f7e5810d5479751207d5c5a79e08198 --- /dev/null +++ b/server/internal/service/src/SolutionService.cpp @@ -0,0 +1,40 @@ +#include "SolutionService.h" + +SolutionService::SolutionService( + std::unique_ptr solutionRepo) + : solutionRepo(std::move(solutionRepo)) {} + +Solution SolutionService::createSolution(size_t userId, size_t taskId, + std::string source) { + size_t id = solutionRepo->storeSolution( + Solution(0, "", userId, "", "", "", taskId, "")); + return Solution(id, "", userId, source, "", "", taskId, ""); +} + +std::vector SolutionService::getSolutionsByUserAndTaskId( + size_t userId, size_t taskId) { + try { + return solutionRepo->getSolutions(userId, taskId); + } catch (std::exception& e) { + throw e; + } +} + +void SolutionService::deleteSolutionById(size_t solId) { + try { + solutionRepo->deleteSolutionById(solId); + } catch (std::exception& e) { + throw e; + } +} + +std::pair SolutionService::getMetrics(size_t solId) { + try { + Solution sol = solutionRepo->getSolutionById(solId); + std::string tokens = sol.getTokens(); + std::string astTree = sol.getAstTree(); + return std::make_pair(tokens, astTree); + } catch (std::exception& e) { + throw e; + } +} diff --git a/server/internal/service/src/TaskService.cpp b/server/internal/service/src/TaskService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a3ce5ef9b3baadb7902fbdf0f2cca6f9d0229a7a --- /dev/null +++ b/server/internal/service/src/TaskService.cpp @@ -0,0 +1,15 @@ +#include "TaskService.h" + +TaskService::TaskService(std::unique_ptr taskRepo) + : taskRepo(std::move(taskRepo)) {} + +Task TaskService::createTask(std::string desc) { + size_t id = taskRepo->storeTask(Task(desc)); + return Task(id, desc); +} + +std::vector TaskService::getAllTasks() { return std::vector(); } + +Task TaskService::getTask(size_t id) { return taskRepo->getTaskById(id); } + +void TaskService::deleteTask(size_t id) { taskRepo->deleteTaskById(id); } diff --git a/server/internal/service/src/UserService.cpp b/server/internal/service/src/UserService.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b44ecf333aaab2c52a757505646371a8bfd83f48 --- /dev/null +++ b/server/internal/service/src/UserService.cpp @@ -0,0 +1,19 @@ +#include "UserService.h" + +#include "Exceptions.h" + +UserService::UserService(std::unique_ptr userRepo) + : userRepo(std::move(userRepo)) {} + +User UserService::createUser(std::string login, std::string username, + std::string password) { + if (login == "") { + throw ValidateException("invalid login"); + } + size_t id = userRepo->makeUser(User(login, password, username)); + return User(id, login, password, username); +} + +User UserService::getUserById(size_t id) { return userRepo->getUserById(id); } + +void UserService::deleteUser(size_t id) { userRepo->deleteByUserId(id); } diff --git a/server/internal/service/src/UserValidator.cpp b/server/internal/service/src/UserValidator.cpp new file mode 100644 index 0000000000000000000000000000000000000000..66666414ca86a6efd9e4e4e7ca113b9b38e9a5f4 --- /dev/null +++ b/server/internal/service/src/UserValidator.cpp @@ -0,0 +1,36 @@ +#include "UserValidator.h" + +#include + +UserValidator::UserValidator(User user) : user(user) {} + +bool UserValidator::validateUser() { + if (validateLogin() && validatePassword() && validateUsername()) { + return true; + } + return false; +} + +bool UserValidator::validateLogin() { + std::string login = user.getLogin(); + if (login.length() < 3 || login.length() > 30) { + return false; + } + return true; +} + +bool UserValidator::validatePassword() { + std::string password = user.getPassword(); + if (password.length() < 8 || password.length() > 30) { + return false; + } + return true; +} + +bool UserValidator::validateUsername() { + std::string username = user.getUsername(); + if (username.length() < 3 || username.length() > 20) { + return false; + } + return true; +} diff --git a/server/internal/service/tests/CMakeLists.txt b/server/internal/service/tests/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5dc8740fa010b643d39406582be5b37b0e1a5c09 --- /dev/null +++ b/server/internal/service/tests/CMakeLists.txt @@ -0,0 +1,17 @@ +project(test_service) + +set(CMAKE_CXX_STANDARD 20) +add_compile_options(-coverage) + +file(GLOB SOURCES *.cpp) + +enable_testing() +find_package(GTest REQUIRED) + +add_executable(${PROJECT_NAME} ${SOURCES}) + +target_link_libraries(${PROJECT_NAME} ${SERVICE_lib_LIBRARY} GTest::GTest gmock) +target_include_directories(${PROJECT_NAME} PUBLIC ${SERVICE_lib_INCLUDE_DIRS}) + + +add_test(test_service test_service) \ No newline at end of file diff --git a/server/internal/service/tests/SolutionServiceTest.cpp b/server/internal/service/tests/SolutionServiceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2dcb91ffdb91f2fc6741c0f6bdca1fecd037c03f --- /dev/null +++ b/server/internal/service/tests/SolutionServiceTest.cpp @@ -0,0 +1,96 @@ +#include +#include + +#include "SolutionService.h" + +class Exception : public std::exception { + std::string _msg; + + public: + Exception(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; + +bool operator==(Solution s1, Solution s2) { return s1.getId() == s2.getId(); } + +class SolutionRepositoryMock : public ISolutionRepository { + public: + ~SolutionRepositoryMock() override = default; + MOCK_METHOD(Solution, getSolutionById, (size_t id), (override)); + MOCK_METHOD(std::vector, getSolutionsBySenderId, (size_t sender_id), + (override)); + MOCK_METHOD(std::vector, getSolutionsByTaskId, (size_t task_id), + (override)); + MOCK_METHOD(std::vector, getSolutions, + (size_t sender_id, size_t task_id), (override)); + MOCK_METHOD(size_t, storeSolution, (Solution solution), (override)); + MOCK_METHOD(void, updateSolution, (Solution solution), (override)); + MOCK_METHOD(void, deleteSolutionById, (size_t id), (override)); + MOCK_METHOD(void, deleteSolution, (Solution solution), (override)); +}; + +struct SolutionServiceTest : public testing::Test { + SolutionService* ss; + SolutionRepositoryMock* mock_ptr; + + void SetUp() { + auto mock = std::make_unique(); + mock_ptr = mock.get(); + ss = new SolutionService(std::move(mock)); + } + void TearDown() { delete ss; } +}; + +ACTION(NoSolutionException) { + throw Exception("no solution with this id in db"); +} + +TEST_F(SolutionServiceTest, getSolutionsByUserAndTaskId) { + std::vector solutions; + solutions.push_back(Solution(0, "", 1, "", "", "", 1, "")); + solutions.push_back(Solution(1, "", 1, "", "", "", 1, "")); + EXPECT_CALL(*mock_ptr, getSolutions(1, 1)) + .Times(1) + .WillOnce(::testing::Return(solutions)); + std::vector sols = ss->getSolutionsByUserAndTaskId(1, 1); + EXPECT_EQ(sols.size(), 2); +} + +TEST_F(SolutionServiceTest, deleteSolution) { + EXPECT_CALL(*mock_ptr, deleteSolutionById(1)).Times(1); + ss->deleteSolutionById(1); +} + +TEST_F(SolutionServiceTest, deleteSolutionException) { + EXPECT_CALL(*mock_ptr, deleteSolutionById(-1)) + .Times(1) + .WillRepeatedly(NoSolutionException()); + EXPECT_THROW(ss->deleteSolutionById(-1), std::exception); +} + +TEST_F(SolutionServiceTest, getMetrics) { + EXPECT_CALL(*mock_ptr, getSolutionById(1)) + .Times(1) + .WillOnce(::testing::Return( + Solution(1, "", 1, "", "tokens", "astTree", 1, ""))); + std::pair metrics = ss->getMetrics(1); + EXPECT_EQ(metrics.first, "tokens"); + EXPECT_EQ(metrics.second, "astTree"); +} + +TEST_F(SolutionServiceTest, getMetricsException) { + EXPECT_CALL(*mock_ptr, getSolutionById(-1)) + .Times(1) + .WillRepeatedly(NoSolutionException()); + EXPECT_THROW(ss->getMetrics(-1), std::exception); +} + +TEST_F(SolutionServiceTest, createSolution) { + EXPECT_CALL(*mock_ptr, + storeSolution(Solution(0, "", 2, "source", "", "", 1, ""))) + .Times(1) + .WillRepeatedly(::testing::Return(1)); + Solution sol = ss->createSolution(2, 1, "source"); + EXPECT_EQ(sol.getId(), 1); + EXPECT_EQ(sol.getSource(), "source"); +} \ No newline at end of file diff --git a/server/internal/service/tests/TaskServiceTest.cpp b/server/internal/service/tests/TaskServiceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5a450d201f578fa2c5f58817c42ebcd6165438ca --- /dev/null +++ b/server/internal/service/tests/TaskServiceTest.cpp @@ -0,0 +1,82 @@ +#include +#include + +#include "TaskService.h" + +bool operator==(Task t1, Task t2) { return t1.getId() == t2.getId(); } + +class Exception : public std::exception { + std::string _msg; + + public: + Exception(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; + +class TaskRepositoryMock : public ITaskRepository { + public: + ~TaskRepositoryMock() override = default; + MOCK_METHOD(Task, getTaskById, (size_t id), (override)); + MOCK_METHOD(void, updateTask, (Task task), (override)); + MOCK_METHOD(int, storeTask, (Task task), (override)); + MOCK_METHOD(void, deleteTask, (Task task), (override)); + MOCK_METHOD(void, deleteTaskById, (size_t id), (override)); +}; + +struct TaskServiceTest : public testing::Test { + TaskService* ts; + TaskRepositoryMock* mock_ptr; + + void SetUp() { + auto mock = std::make_unique(); + mock_ptr = mock.get(); + ts = new TaskService(std::move(mock)); + } + void TearDown() { delete ts; } +}; + +ACTION(NoTaskException) { throw Exception("no task with this id in db"); } + +TEST_F(TaskServiceTest, deleteTaskById) { + EXPECT_CALL(*mock_ptr, deleteTaskById(1)).Times(1); + ts->deleteTask(1); +} + +TEST_F(TaskServiceTest, deleteTasWithInvalidId) { + EXPECT_CALL(*mock_ptr, deleteTaskById(1)) + .Times(1) + .WillRepeatedly(NoTaskException()); + EXPECT_THROW(ts->deleteTask(1), Exception); +} + +TEST_F(TaskServiceTest, GetTaskByIdOK) { + EXPECT_CALL(*mock_ptr, getTaskById(1)) + .Times(1) + .WillOnce(::testing::Return(Task(1, "desription"))); + Task t = ts->getTask(1); + EXPECT_EQ(t.getId(), 1); + EXPECT_EQ(t.getDescription(), "desription"); +} + +TEST_F(TaskServiceTest, GetTaskByIdEXEPTION) { + EXPECT_CALL(*mock_ptr, getTaskById(-1)) + .Times(1) + .WillRepeatedly(NoTaskException()); + EXPECT_THROW(ts->getTask(-1), Exception); +} + +TEST_F(TaskServiceTest, CreateTask) { + EXPECT_CALL(*mock_ptr, storeTask(Task("desc"))) + .Times(1) + .WillOnce(::testing::Return(1)); + Task t = ts->createTask("desc"); + EXPECT_EQ(t.getId(), 1); + EXPECT_EQ(t.getDescription(), "desc"); + + EXPECT_CALL(*mock_ptr, storeTask(Task("desc2"))) + .Times(1) + .WillOnce(::testing::Return(2)); + t = ts->createTask("desc2"); + EXPECT_EQ(t.getId(), 2); + EXPECT_EQ(t.getDescription(), "desc2"); +} diff --git a/server/internal/service/tests/UserServiceTest.cpp b/server/internal/service/tests/UserServiceTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a7423515f22b208ac3cbbf11a1947010b8d66fd2 --- /dev/null +++ b/server/internal/service/tests/UserServiceTest.cpp @@ -0,0 +1,85 @@ +#include +#include + +#include "Exceptions.h" +#include "UserService.h" + +bool operator==(User u1, User u2) { return u1.getId() == u2.getId(); } + +class Exception : public std::exception { + std::string _msg; + + public: + Exception(const std::string& msg) : _msg(msg) {} + virtual const char* what() const noexcept override { return _msg.c_str(); } +}; + +class UserRepositoryMock : public IUserRepository { + public: + ~UserRepositoryMock() override = default; + MOCK_METHOD(User, getUserById, (size_t id), (override)); + MOCK_METHOD(User, getUserByLogin, (std::string login), (override)); + MOCK_METHOD(size_t, makeUser, (User user), (override)); + MOCK_METHOD(void, deleteUser, (User user), (override)); + MOCK_METHOD(void, deleteByUserId, (size_t id), (override)); + MOCK_METHOD(std::vector, getAllUsers, (), (override)); +}; + +struct UserServiceTest : public testing::Test { + UserService* us; + UserRepositoryMock* mock_ptr; + + void SetUp() { + auto mock = std::make_unique(); + mock_ptr = mock.get(); + us = new UserService(std::move(mock)); + } + void TearDown() { delete us; } +}; + +ACTION(NoUserException) { throw Exception("no user with this id in db"); } + +TEST_F(UserServiceTest, deleteUser) { + EXPECT_CALL(*mock_ptr, deleteByUserId(1)).Times(1); + us->deleteUser(1); +} + +TEST_F(UserServiceTest, deleteUserWithInvalidId) { + EXPECT_CALL(*mock_ptr, deleteByUserId(1)) + .Times(1) + .WillRepeatedly(NoUserException()); + EXPECT_THROW(us->deleteUser(1), Exception); +} + +TEST_F(UserServiceTest, getUserOk) { + EXPECT_CALL(*mock_ptr, getUserById(1)) + .Times(1) + .WillOnce(::testing::Return(User(1, "login", "password", "username"))); + User u = us->getUserById(1); + EXPECT_EQ(u.getLogin(), "login"); + EXPECT_EQ(u.getId(), 1); + EXPECT_EQ(u.getPassword(), "password"); + EXPECT_EQ(u.getUsername(), "username"); +} + +TEST_F(UserServiceTest, getUserEXEPTION) { + EXPECT_CALL(*mock_ptr, getUserById(-1)).Times(1).WillOnce(NoUserException()); + EXPECT_THROW(us->getUserById(-1), Exception); +} + +TEST_F(UserServiceTest, makeUserOk) { + EXPECT_CALL(*mock_ptr, makeUser(User("login", "password", "username"))) + .Times(1) + .WillOnce(::testing::Return(1)); + User u = us->createUser("login", "username", "password"); + EXPECT_EQ(u.getLogin(), "login"); + EXPECT_EQ(u.getId(), 1); + EXPECT_EQ(u.getPassword(), "password"); + EXPECT_EQ(u.getUsername(), "username"); +} + +TEST_F(UserServiceTest, makeUserInvalidData) { + EXPECT_CALL(*mock_ptr, makeUser(User("login", "password", "username"))) + .Times(0); + EXPECT_THROW(us->createUser("", "", ""), ValidateException); +} \ No newline at end of file diff --git a/server/internal/service/tests/UserValidatorTest.cpp b/server/internal/service/tests/UserValidatorTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..802b2f03b5eb3368a223abbb1a4269a30c555a55 --- /dev/null +++ b/server/internal/service/tests/UserValidatorTest.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include "UserValidator.h" + +TEST(UserValidatorTest, validateOK) { + UserValidator uv(User("login", "password", "username")); + EXPECT_TRUE(uv.validateUser()); +} + +TEST(UserValidatorTest, invalidLogin) { + UserValidator uv(User("", "password", "username")); + EXPECT_FALSE(uv.validateUser()); +} + +TEST(UserValidatorTest, invalidPassword) { + UserValidator uv(User("login", "", "username")); + EXPECT_FALSE(uv.validateUser()); +} + +TEST(UserValidatorTest, invalidUsername) { + UserValidator uv(User("login", "password", "")); + EXPECT_FALSE(uv.validateUser()); +} + +TEST(UserValidatorTest, invalidUserFields) { + UserValidator uv(User("", "", "")); + EXPECT_FALSE(uv.validateUser()); +} \ No newline at end of file diff --git a/server/internal/service/tests/main.cpp b/server/internal/service/tests/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4ccb03731487f11418e7471cd8342ac0e4f81ae8 --- /dev/null +++ b/server/internal/service/tests/main.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main(int argc, char** argv) { + ::testing::InitGoogleMock(&argc, argv); + + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/server/internal/service/virtual/ISolutionService.h b/server/internal/service/virtual/ISolutionService.h new file mode 100644 index 0000000000000000000000000000000000000000..a9ec5483b64e2d9737b725ee17b6dc4afad3c078 --- /dev/null +++ b/server/internal/service/virtual/ISolutionService.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +#include "Solution.hpp" + +class ISolutionService { + public: + virtual ~ISolutionService() = default; + virtual Solution createSolution(size_t userId, size_t taskId, + std::string source) = 0; + virtual std::vector getSolutionsByUserAndTaskId(size_t userId, + size_t taskId) = 0; + virtual void deleteSolutionById(size_t solId) = 0; + + virtual std::pair getMetrics(size_t solId) = 0; +}; diff --git a/server/internal/service/virtual/ITaskService.h b/server/internal/service/virtual/ITaskService.h new file mode 100644 index 0000000000000000000000000000000000000000..731fb0f99bb7056d3d87ced20038cfb26cbd27f5 --- /dev/null +++ b/server/internal/service/virtual/ITaskService.h @@ -0,0 +1,13 @@ +#pragma once +#include + +#include "Task.hpp" + +class ITaskService { + public: + virtual ~ITaskService() = default; + virtual Task createTask(std::string desc) = 0; + virtual Task getTask(size_t id) = 0; + virtual std::vector getAllTasks() = 0; + virtual void deleteTask(size_t id) = 0; +}; diff --git a/server/internal/service/virtual/IUserService.h b/server/internal/service/virtual/IUserService.h new file mode 100644 index 0000000000000000000000000000000000000000..06d79720920e538ce1a8bfa24079953cc04b05e7 --- /dev/null +++ b/server/internal/service/virtual/IUserService.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "User.hpp" + +class IUserService { + public: + virtual ~IUserService() = default; + virtual User createUser(std::string login, std::string username, + std::string password) = 0; + virtual User getUserById(size_t id) = 0; + virtual void deleteUser(size_t id) = 0; +}; diff --git a/server/internal/src/CMakeLists.txt b/server/internal/src/CMakeLists.txt index 72e4c50a529e11df35b7cb5041f8f92700da291b..58510fa62873ba76091356ff991847fa7b7fa67e 100644 --- a/server/internal/src/CMakeLists.txt +++ b/server/internal/src/CMakeLists.txt @@ -1,5 +1,3 @@ -set(CMAKE_CXX_STANDARD 20) -cmake_minimum_required(VERSION 3.19) add_subdirectory(db) set(DB_Lib_LIB ${DB_Lib_LIB} PARENT_SCOPE) set(DB_Lib_INCLUDE_DIRS ${DB_Lib_INCLUDE_DIRS} PARENT_SCOPE) \ No newline at end of file diff --git a/server/internal/src/db/CMakeLists.txt b/server/internal/src/db/CMakeLists.txt index df02471d1e844d4f84ebe571d568da19adc26cad..9d4549e5ebc4401f147910d871f3d941fcac6c3a 100644 --- a/server/internal/src/db/CMakeLists.txt +++ b/server/internal/src/db/CMakeLists.txt @@ -1,5 +1,3 @@ -cmake_minimum_required(VERSION 3.10) - project("DBLib") set(LIB_NAME DB_Lib)