From 162004976917bc391c97e52f06012313edcd822c Mon Sep 17 00:00:00 2001 From: Ilia Egorov Date: Sat, 17 Jan 2026 09:53:55 +0300 Subject: [PATCH 1/3] feat: add compilations --- .../CompilationsAdminController.java | 41 ++++ .../CompilationsPublicController.java | 35 ++++ .../mapper/CompilationsMapper.java | 51 +++++ .../repository/CompilationsRepository.java | 19 ++ .../service/CompilationsPublicGetRequest.java | 12 ++ .../service/CompilationsService.java | 20 ++ .../service/CompilationsServiceImpl.java | 195 ++++++++++++++++++ .../event/repository/EventRepository.java | 4 + .../handler/GlobalExceptionHandler.java | 2 +- 9 files changed, 378 insertions(+), 1 deletion(-) create mode 100644 main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java create mode 100644 main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java create mode 100644 main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java create mode 100644 main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java create mode 100644 main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java create mode 100644 main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java create mode 100644 main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java diff --git a/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java new file mode 100644 index 0000000..85872e1 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java @@ -0,0 +1,41 @@ +package ru.practicum.compilation.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; +import ru.practicum.compilation.service.CompilationsService; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin/compilations") +public class CompilationsAdminController { + private final CompilationsService compService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public CompilationDto saveCompilation( + @RequestBody @Valid NewCompilationDto newCompilationDto) { + log.info("Admin save compilation requested with body={}", newCompilationDto); + return compService.save(newCompilationDto); + } + + @DeleteMapping("/{compId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCompilation(@PathVariable long compId) { + log.info("Admin delete compilation requested with id={}", compId); + compService.deleteById(compId); + } + + @PatchMapping("/{compId}") + public CompilationDto updateCompilation(@PathVariable long compId, + @RequestBody @Valid UpdateCompilationRequest updateRequest) { + log.info("Admin update compilation requested with id={}, body={}", compId, updateRequest); + return compService.update(compId, updateRequest); + } +} diff --git a/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java new file mode 100644 index 0000000..fc70542 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java @@ -0,0 +1,35 @@ +package ru.practicum.compilation.controller; + + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.service.CompilationsPublicGetRequest; +import ru.practicum.compilation.service.CompilationsService; + +import java.util.Collection; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/compilations") +public class CompilationsPublicController { + private final CompilationsService compService; + + @GetMapping + public Collection getCompilations(@RequestParam boolean pinned, + @RequestParam(defaultValue = "0") int from, + @RequestParam(defaultValue = "10") int size + ) { + CompilationsPublicGetRequest getRequest = new CompilationsPublicGetRequest(pinned, from, size); + log.info("Public get compilations requested with params= {}", getRequest); + return compService.findAll(getRequest); + } + + @GetMapping("/{compId}") + public CompilationDto getById(@PathVariable long compId) { + log.info("Public get compilation by id requested with id={}", compId); + return compService.findById(compId); + } +} diff --git a/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java new file mode 100644 index 0000000..e395cbd --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java @@ -0,0 +1,51 @@ +package ru.practicum.compilation.mapper; + +import lombok.experimental.UtilityClass; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; +import ru.practicum.compilation.model.Compilation; +import ru.practicum.event.dto.EventShortDto; +import ru.practicum.event.model.Event; + +import java.util.List; +import java.util.Set; + +@UtilityClass +public class CompilationsMapper { + + public Compilation mapToEntity(NewCompilationDto newCompilationDto, Set events) { + return new Compilation( + null, + newCompilationDto.title(), + newCompilationDto.pinned(), + events + ); + } + + public CompilationDto mapToDto(Compilation compilation, List events) { + return new CompilationDto( + events, + compilation.getId(), + Boolean.TRUE.equals(compilation.getPinned()), + compilation.getTitle() + ); + } + + public void updateEntity(Compilation compilation, + UpdateCompilationRequest updateRequest, + Set events) { + + if (updateRequest.hasTitle()) { + compilation.setTitle(updateRequest.title()); + } + + if (updateRequest.hasPinned()) { + compilation.setPinned(updateRequest.pinned()); + } + + if (updateRequest.hasEvents()) { + compilation.setEvents(events); + } + } +} diff --git a/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java new file mode 100644 index 0000000..31804fd --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java @@ -0,0 +1,19 @@ +package ru.practicum.compilation.repository; + +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.compilation.model.Compilation; + +import java.util.Optional; + +public interface CompilationsRepository extends JpaRepository { + + Page findAllByPinned(boolean pinned, Pageable pageable); + + @EntityGraph(attributePaths = "events") + Optional findWithEventsById(Long id); + + boolean existsByTitle(String title); +} diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java new file mode 100644 index 0000000..09a0827 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java @@ -0,0 +1,12 @@ +package ru.practicum.compilation.service; + +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +public record CompilationsPublicGetRequest(boolean pinned, int from, int size) { + + public Pageable getPageable() { + int page = from / size; + return PageRequest.of(page, size); + } +} diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java new file mode 100644 index 0000000..98a36ad --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java @@ -0,0 +1,20 @@ +package ru.practicum.compilation.service; + +import jakarta.validation.Valid; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; + +import java.util.Collection; + +public interface CompilationsService { + Collection findAll(CompilationsPublicGetRequest getRequest); + + CompilationDto findById(long compId); + + CompilationDto save(@Valid NewCompilationDto newCompilationDto); + + void deleteById(long compId); + + CompilationDto update(long compId, UpdateCompilationRequest updateRequest); +} diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java new file mode 100644 index 0000000..e4ef380 --- /dev/null +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java @@ -0,0 +1,195 @@ +package ru.practicum.compilation.service; + + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.client.StatsClient; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; +import ru.practicum.compilation.mapper.CompilationsMapper; +import ru.practicum.compilation.model.Compilation; +import ru.practicum.compilation.repository.CompilationsRepository; +import ru.practicum.event.dto.EventShortDto; +import ru.practicum.event.mapper.EventMapper; +import ru.practicum.event.model.Event; +import ru.practicum.event.repository.EventRepository; +import ru.practicum.exception.ConflictException; +import ru.practicum.exception.NotFoundException; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class CompilationsServiceImpl implements CompilationsService { + + private final CompilationsRepository compRepository; + private final EventRepository eventRepository; + private final RequestRepository requestRepository; + private final StatsClient statsClient; + + @Override + public Collection findAll(CompilationsPublicGetRequest getRequest) { + + Page page = compRepository.findAllByPinned( + getRequest.pinned(), + getRequest.getPageable() + ); + + Set events = page.stream() + .flatMap(c -> c.getEvents().stream()) + .collect(Collectors.toSet()); + + Map confirmedRequests = getConfirmedRequests(events); + Map views = getViews(events); + + return page.stream() + .map(c -> toDto(c, confirmedRequests, views)) + .toList(); + } + + @Override + public CompilationDto findById(long compId) { + + Compilation compilation = compRepository.findWithEventsById(compId) + .orElseThrow( + NotFoundException.supplier( + "Compilation with id=%d was not found", compId + ) + ); + + Set events = compilation.getEvents(); + + Map confirmedRequests = getConfirmedRequests(events); + Map views = getViews(events); + + return toDto(compilation, confirmedRequests, views); + } + + @Override + @Transactional + public CompilationDto save(NewCompilationDto newCompilationDto) { + + if (compRepository.existsByTitle(newCompilationDto.title())) { + throw new ConflictException( + "Compilation with title=" + newCompilationDto.title() + " already exists" + ); + } + + Set events = getEvents(newCompilationDto.events()); + + Compilation compilation = CompilationsMapper.mapToEntity( + newCompilationDto, + events + ); + + Compilation saved = compRepository.save(compilation); + + Map confirmedRequests = getConfirmedRequests(events); + Map views = getViews(events); + + return toDto(saved, confirmedRequests, views); + } + + @Override + @Transactional + public void deleteById(long compId) { + if (!compRepository.existsById(compId)) { + throw new NotFoundException( + "Compilation with id=" + compId + " was not found" + ); + } + + compRepository.deleteById(compId); + } + + @Override + @Transactional + public CompilationDto update(long compId, UpdateCompilationRequest updateRequest) { + + Compilation compilation = compRepository.findWithEventsById(compId) + .orElseThrow( + NotFoundException.supplier( + "Compilation with id=%d was not found", compId + ) + ); + + Set events = null; + if (updateRequest.hasEvents()) { + events = getEvents(updateRequest.events()); + } + + CompilationsMapper.updateEntity(compilation, updateRequest, events); + + Compilation updated = compRepository.save(compilation); + + Set actualEvents = updated.getEvents(); + + Map confirmedRequests = getConfirmedRequests(actualEvents); + Map views = getViews(actualEvents); + + return toDto(updated, confirmedRequests, views); + } + + private CompilationDto toDto( + Compilation compilation, + Map confirmedRequests, + Map views + ) { + List events = compilation.getEvents().stream() + .map(event -> EventMapper.mapToShortDto( + event, + confirmedRequests.getOrDefault(event.getId(), 0L), + views.get(event.getId()) + )) + .toList(); + + return CompilationsMapper.mapToDto(compilation, events); + } + + private Set getEvents(Collection eventIds) { + if (eventIds == null || eventIds.isEmpty()) { + return Set.of(); + } + + Set events = eventRepository.findAllByIdIn(eventIds); + + if (events.size() != eventIds.size()) { + throw new NotFoundException("One or more events were not found"); + } + + return events; + } + + private Map getConfirmedRequests(Set events) { + if (events.isEmpty()) { + return Map.of(); + } + + List eventIds = events.stream() + .map(Event::getId) + .toList(); + + return requestRepository.countConfirmedByEventIds(eventIds); + } + + private Map getViews(Set events) { + if (events.isEmpty()) { + return Map.of(); + } + + List eventIds = events.stream() + .map(Event::getId) + .toList(); + + return statsClient.getViews(eventIds); + } +} diff --git a/main-service/src/main/java/ru/practicum/event/repository/EventRepository.java b/main-service/src/main/java/ru/practicum/event/repository/EventRepository.java index c9917e8..9c07c7a 100644 --- a/main-service/src/main/java/ru/practicum/event/repository/EventRepository.java +++ b/main-service/src/main/java/ru/practicum/event/repository/EventRepository.java @@ -1,7 +1,9 @@ package ru.practicum.event.repository; import java.time.LocalDateTime; +import java.util.Collection; import java.util.Optional; +import java.util.Set; import ru.practicum.event.model.Event; import ru.practicum.event.model.EventState; @@ -86,4 +88,6 @@ static Predicate createPredicate(EventsPublicGetRequest request) { Optional findByIdAndState(Long id, EventState state); Page findByInitiator_Id(Long initiatorId, Pageable pageable); + + Set findAllByIdIn(Collection ids); } diff --git a/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java b/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java index 7a51bc4..1641496 100644 --- a/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java +++ b/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java @@ -8,9 +8,9 @@ import jakarta.validation.ConstraintViolationException; +import ru.practicum.exception.*; import ru.practicum.exception.dto.ApiError; import ru.practicum.exception.dto.Violation; -import ru.practicum.exception.*; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; From 3b57211de8f922f619352730beb24c2088ef0d3b Mon Sep 17 00:00:00 2001 From: Ilia Egorov Date: Sat, 17 Jan 2026 10:12:47 +0300 Subject: [PATCH 2/3] feat: add all postman tests --- postman/ewm-main-service.json | 5751 +++++++++++++++++++++++++++------ 1 file changed, 4692 insertions(+), 1059 deletions(-) diff --git a/postman/ewm-main-service.json b/postman/ewm-main-service.json index 9c84a28..62b600e 100644 --- a/postman/ewm-main-service.json +++ b/postman/ewm-main-service.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "788f4375-147e-4e45-ab6d-7baa8e542674", - "name": "Test Explore With Me - Main service Copy", + "_postman_id": "4f622f31-328a-4506-95bd-66359cfbe749", + "name": "Test Explore With Me - Main service", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "27311667", - "_collection_link": "https://api555-4802.postman.co/workspace/explore-with-me~26332bb9-fe8e-403d-8af2-e0d94c187194/collection/27311667-788f4375-147e-4e45-ab6d-7baa8e542674?action=share&source=collection_link&creator=27311667" + "_exporter_id": "23073145", + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-4f622f31-328a-4506-95bd-66359cfbe749?action=share&source=collection_link&creator=23073145" }, "item": [ { @@ -13,6 +13,103 @@ { "name": "Event", "item": [ + { + "name": "Required query params", + "item": [ + { + "name": "Добавление запроса от текущего пользователя на участие в событии без обязательного query params", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события", + "disabled": true + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + } + ] + }, { "name": "Unrequired query params", "item": [ @@ -1337,6 +1434,169 @@ }, "response": [] }, + { + "name": "Поиск событий с проверкой параметров", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const user2 = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = rnd.getEvent(category.id)\r", + " event.requestModeration = true;\r", + " event.participantLimit = 2;\r", + " event = await api.addEvent(user.id, event);\r", + " event = await api.publishEvent(event.id);\r", + " pm.request.removeQueryParams(['users', 'categories']);\r", + " pm.request.addQueryParams([`users=` + user.id, 'categories=' + category.id]);\r", + " pm.collectionVariables.set('response', event);\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/events?users=\" + user.id +\"&states=PUBLISHED&categories=\" + category.id + \"&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {\r", + " pm.collectionVariables.set('confirmedRequests', response.json()[0].confirmedRequests)\r", + " });\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, user2.id);\r", + " const confirmedRequest = await api.acceptParticipationRequest(event.id, user.id, requestToJoin.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Идентификатор категории должен соответствовать искомой категории');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость посещения события должна соответствовать искомому событию');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате искомого события');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Число участников события должно соответствовать искомому событию');\r", + " pm.expect(pm.collectionVariables.get('confirmedRequests')).equal(0);\r", + " pm.expect(target.confirmedRequests).equal(1);\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/events?users=0&states=PUBLISHED&categories=0&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events" + ], + "query": [ + { + "key": "users", + "value": "0", + "description": "список id пользователей, чьи события нужно найти" + }, + { + "key": "states", + "value": "PUBLISHED", + "description": "список состояний в которых находятся искомые события" + }, + { + "key": "categories", + "value": "0", + "description": "список id категорий в которых будет вестись поиск" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе" + } + ] + }, + "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" + }, + "response": [] + }, { "name": "Получение событий с возможностью фильтрации и проверкой на валидацию", "event": [ @@ -1570,7 +1830,7 @@ "response": [] }, { - "name": "Изменение даты события на уже наступившую", + "name": "Добавление запроса на участие при participantLimit == 0", "event": [ { "listen": "prerequest", @@ -1583,15 +1843,15 @@ " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " eventDate : \"2020-10-11 23:10:05\"\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " eventBody['participantLimit'] = 0\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1620,30 +1880,148 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", - " pm.response.to.have.status(400);\r", + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201); \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", "});\r", "\r", - "" + "const target = pm.response.json();\r", + "var query = {};\r", + "pm.request.url.query.all().forEach((param) => { query[param.key] = param.value});\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"При создании у запроса на участие должен быть статус CONFIRMED\", function () {\r", + " pm.expect(target.status).equal(\"CONFIRMED\");\r", + "});\r", + "\r", + "pm.test(\"Id ивента в запросе и в ответе должны совпадать\", function () {\r", + " pm.expect(target.event.toString()).equal(query['eventId'].toString());\r", + "});" ], "type": "text/javascript" } } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}" - }, + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + }, + { + "name": "Изменение даты события на уже наступившую", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " eventDate : \"2020-10-11 23:10:05\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.have.status(400);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, "url": { "raw": "{{baseUrl}}/admin/events/:eventId", "host": [ @@ -7980,165 +8358,3099 @@ ] } ] - } - ] - }, - { - "name": "409 Conflict", - "item": [ - { - "name": "Попытка изменения имени категории на уже существующее", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - " let category1, category2\r", - " try {\r", - " category1 = await api.addCategory(rnd.getCategory());\r", - " category2 = await api.addCategory(rnd.getCategory());\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - " pm.collectionVariables.set(\"catid\", category2.id)\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " name : category1.name\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript" - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "{{baseUrl}}/admin/categories/:catId", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "admin", - "categories", - ":catId" - ], - "variable": [ - { - "key": "catId", - "value": "{{catid}}" - } - ] - }, - "description": "Обратите внимание: имя категории должно быть уникальным" - }, - "response": [] }, { - "name": "Добавление новой категории с занятым именем", - "event": [ + "name": "Compilation", + "item": [ { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " let category;\r", - " try {\r", - " category = rnd.getCategory();\r", - " await api.addCategory(category);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify(category),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript" - } + "name": "Required query params", + "item": [] }, { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "name": "Unreqired params in body", + "item": [ + { + "name": "Получение подборок событий без нескольких Query params", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "compilations" + ], + "query": [ + { + "key": "pinned", + "value": "true", + "description": "искать только закрепленные/не закрепленные подборки", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + }, + { + "name": "Добавление новой подборки без параметра pinned", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " compilation = rnd.getCompilation(event.id);\r", + " delete compilation['pinned'];\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + " pm.expect(target.pinned).equal(false);\r", + "\r", + " pm.expect(source.events[0]).equal(target.events[0].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Required params in body", + "item": [ + { + "name": "Добавление подборки без поля title", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = {\r", + " \"pinned\":\"true\"};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки с пустым полем title", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = {\r", + " \"pinned\":\"true\",\r", + " \"title\": \"\"};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки с пустой строкой в качестве названия", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = {\r", + " \"pinned\":\"true\",\r", + " \"title\": \" \"};\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Misc tests", + "item": [ + { + "name": "Добавление подборки с проверкой связей многие-ко-многим", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " let compilation2;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " const event2 = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " compilation = rnd.getCompilation(event.id, event2.id);\r", + " compilation2 = rnd.getCompilation(event.id, event2.id);\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/admin/compilations\",\r", + " method : \"POST\",\r", + " header: { \"Content-Type\": \"application/json\" },\r", + " body: JSON.stringify({\r", + " events: compilation2.events,\r", + " title: compilation2.title\r", + " })\r", + " }, (error, response) => {\r", + "\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + " if (target.events[0].id < target.events[1].id){\r", + " pm.expect(source.events[0]).equal(target.events[0].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " } else {\r", + " pm.expect(source.events[0]).equal(target.events[1].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " }\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки без событий", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " compilation = rnd.getCompilation();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", + " pm.expect(target).to.have.property('id');\r", + " pm.expect(target).to.have.property('title');\r", + " pm.expect(target).to.have.property('pinned');\r", + " pm.expect(target).to.have.property('events');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + "\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "String length restrictions", + "item": [ + { + "name": "Добавление подборки с title.length > 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({title: rnd.getWord(51)}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Добавление подборки с title.length == 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let compilation;\r", + " try {\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({title: rnd.getWord(50)}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Обновить названия подборки с title.length > 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " title: rnd.getWord(51)\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r", + " pm.response.to.be.badRequest; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Обновить названия подборки с title.length == 50", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " title: rnd.getWord(50)\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.have.status(200);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations/:compId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations", + ":compId" + ], + "variable": [ + { + "key": "compId", + "value": "{{compid}}" + } + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Default values check", + "item": [ + { + "name": "Добавление подборки без pinned", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let cArr = Array.from({length: 10}, () => rnd.getCompilation());\r", + " let responseArr = [];\r", + " try {\r", + " cArr.forEach(function(x){ delete x.pinned });\r", + " for (const c of cArr){\r", + " responseArr.push(await api.addCompilation(c));\r", + " }\r", + " pm.collectionVariables.set('responseArr', responseArr);\r", + " compilation = rnd.getCompilation();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('responseArr');\r", + "\r", + "\r", + "pm.test(\"У каждой из созданных подборок pinned должно принять значение по умолчанию(false)\", function () {\r", + " source.forEach(function(x){pm.expect(x.pinned).to.be.equal(false)});\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "compilations" + ] + } + }, + "response": [] + }, + { + "name": "Проверка на значения по-умолчанию from и size(compilation)", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " for (let i = 0; i < 11; i++){\r", + " await api.addCompilation(rnd.getCompilation());\r", + " }\r", + " await pm.sendRequest({\r", + " url : \"http://localhost:8080/compilations?from=0\",\r", + " method : \"GET\",\r", + " header: { \"Content-Type\": \"application/json\" }\r", + " }, (error, response) => {pm.collectionVariables.set('source', response.json())});\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const target = pm.response.json();\r", + "const source = pm.collectionVariables.get('source');\r", + "\r", + "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r", + " pm.expect(target[0].id).to.be.equal(source[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r", + "});\r", + "\r", + "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r", + " pm.expect(target.length).to.be.equal(10);\r", + "});\r", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/compilations", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "compilations" + ], + "query": [ + { + "key": "pinned", + "value": "true", + "description": "искать только закрепленные/не закрепленные подборки", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе", + "disabled": true + } + ] + } + }, + "response": [] + } + ] + } + ] + } + ] + }, + { + "name": "409 Conflict", + "item": [ + { + "name": "Попытка изменения имени категории на уже существующее", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category1, category2\r", + " try {\r", + " category1 = await api.addCategory(rnd.getCategory());\r", + " category2 = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", category2.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : category1.name\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Добавление новой категории с занятым именем", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = rnd.getCategory();\r", + " await api.addCategory(category);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Добавление пользователя с занятым именем почты", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let user;\r", + " try {\r", + " user = rnd.getUser();\r", + " user.name = rnd.getWord(10);\r", + " await api.addUser(user);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(user),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/users", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "users" + ] + } + }, + "response": [] + }, + { + "name": "Удаление категории с привязанными событиями", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const user = await api.addUser(rnd.getUser());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set('catid', category.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + }, + "description": "Обратите внимание: с категорий не должно быть связано ни одного события." + }, + "response": [] + }, + { + "name": "Изменение имени категории на уже занятое", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + " let category1, category2\r", + " try {\r", + " category1 = await api.addCategory(rnd.getCategory());\r", + " category2 = await api.addCategory(rnd.getCategory());\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + " pm.collectionVariables.set(\"catid\", Number(category1.id))\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " name : category2.name\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Публикация уже опубликованного события", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction : \"PUBLISH_EVENT\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Публикация отмененного события", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.rejectEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction : \"PUBLISH_EVENT\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Отмена опубликованного события", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction : \"REJECT_EVENT\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}" + }, + "url": { + "raw": "{{baseUrl}}/admin/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } + ] + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + }, + "response": [] + }, + { + "name": "Добавление повторного запроса от пользователя на участие в событии", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + }, + { + "name": "Добавление запроса от инициатора события на участие в нём", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + }, + { + "name": "Добавление запроса на участие в неопубликованном событии", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + }, + { + "name": "Добавление запроса на участие в событии, у которого заполнен лимит участников", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody.participantLimit = 1;\r", + " eventBody.requestModeration = false;\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser1 = await api.addUser(rnd.getUser());\r", + " const submittedUser2 = await api.addUser(rnd.getUser());\r", + " await api.publishParticipationRequest(event.id, submittedUser1.id);\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser2.id);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "requests" + ], + "query": [ + { + "key": "eventId", + "value": "0", + "description": "(Required) id события" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" + }, + "response": [] + }, + { + "name": "Изменение опубликованного события от имени пользователя", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " eventDate : rnd.getFutureDateTime(6)\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" + } + ] + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + }, + "response": [] + }, + { + "name": "Попытка принять заявку на участие в событии, когда лимит уже достигнут", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true;\r", + " eventBody['participantLimit'] = 1;\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser1 = await api.addUser(rnd.getUser());\r", + " const submittedUser2 = await api.addUser(rnd.getUser());\r", + " const requestToParticipate = await api.publishParticipationRequest(event.id, submittedUser1.id);\r", + " const requestToParticipate2 = await api.publishParticipationRequest(event.id, submittedUser2.id);\r", + " await api.acceptParticipationRequest(event.id, user.id, requestToParticipate.id);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToParticipate2.id],\r", + " status:\"CONFIRMED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" + }, + "response": [] + }, + { + "name": "Попытка отменить уже принятую заявку на участие в событии", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true;\r", + " eventBody['participantLimit'] = 1;\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToParticipate = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " await api.acceptParticipationRequest(event.id, user.id, requestToParticipate.id);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToParticipate.id],\r", + " status:\"REJECTED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " // выполняем наш скрипт\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", + " pm.response.to.have.status(409);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + ":userId", + "events", + ":eventId", + "requests" + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" + } + ] + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" + }, + "response": [] + } + ] + }, + { + "name": "Category", + "item": [ + { + "name": "Добавление новой категории", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " let category;\r", + " try {\r", + " category = rnd.getCategory();\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(category),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}/admin/categories", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "admin", + "categories" + ] + }, + "description": "Обратите внимание: имя категории должно быть уникальным" + }, + "response": [] + }, + { + "name": "Получение категорий", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " pm.collectionVariables.set(\"response\", category)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "let founded;\r", + "target.forEach(function(element){if (element.id == source.id) founded = element});\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target[0]).to.have.property('id');\r", + "pm.expect(target[0]).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(founded.id, 'Идентификатор категории должен соответствовать идентификатору категории добавленной ранее');\r", + " pm.expect(source.name).equal(founded.name, 'Название категории должно соответствовать названию категории добавленной ранее');\r", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/categories?from=0&size=1000", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "categories" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество категорий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество категорий в наборе" + } + ] + } + }, + "response": [] + }, + { + "name": "Получение информации о категории по её идентификатору", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " pm.collectionVariables.set(\"response\", category)\r", + " pm.collectionVariables.set(\"catid\", category.id)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор категории должен соответствовать идентификатору в запросе');\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно соответствовать названию категории с указанным идентификатором');\r", "});" ], "type": "text/javascript" @@ -8146,42 +11458,131 @@ } ], "request": { - "method": "POST", + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}/categories/:catId", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}", + "description": "(Required) id категории" + } + ] + } + }, + "response": [] + }, + { + "name": "Удаление категории", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const findedCategory = await api.findCategory(category.id);\r", + " pm.collectionVariables.set(\"catid\", category.id)\r", + " pm.collectionVariables.set(\"response\", findedCategory)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript" + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "\r", + "source = pm.collectionVariables.get('response');\r", + "catId = pm.collectionVariables.get('catid');\r", + "\r", + "pm.test(\"Категория должна быть найдена до удаления\", function () {\r", + " pm.expect(source.id).equal(catId, 'Идентификтор категории должен совпадать с удаляемым');\r", + "});\r", + "\r", + "pm.sendRequest({\r", + " url: pm.collectionVariables.get(\"baseUrl\") + \"/categories/\" + catId,\r", + " method: 'GET',\r", + " }, (error, response) => {\r", + " pm.test(\"Категория не должна быть найдена после удаления\", function () {\r", + " pm.expect(response.code).to.eql(404);\r", + " });\r", + " });" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{baseUrl}}/admin/categories", + "raw": "{{baseUrl}}/admin/categories/:catId", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "categories" + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } ] }, - "description": "Обратите внимание: имя категории должно быть уникальным" + "description": "Обратите внимание: с категорий не должно быть связано ни одного события." }, "response": [] }, { - "name": "Добавление пользователя с занятым именем почты", + "name": "Изменение категории", "event": [ { "listen": "prerequest", @@ -8190,19 +11591,18 @@ "const main = async () => {\r", " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", - "\r", - " let user;\r", + " let category\r", " try {\r", - " user = rnd.getUser();\r", - " user.name = rnd.getWord(10);\r", - " await api.addUser(user);\r", + " category = await api.addCategory(rnd.getCategory());\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", - "\r", + " pm.collectionVariables.set(\"catid\", Number(category.id))\r", " pm.request.body.update({\r", " mode: 'raw',\r", - " raw: JSON.stringify(user),\r", + " raw: JSON.stringify({\r", + " name : rnd.getCategory().name\r", + " }),\r", " options: { raw: { language: 'json' } }\r", " });\r", "};\r", @@ -8229,10 +11629,23 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", "});" ], "type": "text/javascript" @@ -8240,41 +11653,39 @@ } ], "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], + "method": "PATCH", + "header": [], "body": { "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{{request_body}}" }, "url": { - "raw": "{{baseUrl}}/admin/users", + "raw": "{{baseUrl}}/admin/categories/:catId", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "users" + "categories", + ":catId" + ], + "variable": [ + { + "key": "catId", + "value": "{{catid}}" + } ] } }, "response": [] - }, + } + ] + }, + { + "name": "Users", + "item": [ { - "name": "Удаление категории с привязанными событиями", + "name": "Поиск пользователей", "event": [ { "listen": "prerequest", @@ -8284,11 +11695,10 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", + " let compilation;\r", " try {\r", - " const category = await api.addCategory(rnd.getCategory());\r", " const user = await api.addUser(rnd.getUser());\r", - " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " pm.collectionVariables.set('catid', category.id);\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -8316,10 +11726,26 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Пользователи должны содержать поля: id, name, email\", function () {\r", + " pm.expect(target[0]).to.have.property('id');\r", + " pm.expect(target[0]).to.have.property('name');\r", + " pm.expect(target[0]).to.have.property('email');\r", + "});\r", + "\r", + "pm.test(\"Должен быть найден только один пользователь по заданному фильтру\", function () {\r", + " pm.expect(target.length).to.eql(1);\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target[0].id).equal(pm.collectionVariables.get(\"uid\"));\r", "});" ], "type": "text/javascript" @@ -8327,7 +11753,7 @@ } ], "request": { - "method": "DELETE", + "method": "GET", "header": [ { "key": "Accept", @@ -8335,28 +11761,45 @@ } ], "url": { - "raw": "{{baseUrl}}/admin/categories/:catId", + "raw": "{{baseUrl}}/admin/users?ids={{uid}}", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "categories", - ":catId" + "users" ], - "variable": [ + "query": [ { - "key": "catId", - "value": "{{catid}}" + "key": "ids", + "value": "{{uid}}", + "description": "id пользователей" + }, + { + "key": "ids", + "value": "-10833646", + "description": "id пользователей", + "disabled": true + }, + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора", + "disabled": true + }, + { + "key": "size", + "value": "10", + "description": "количество элементов в наборе", + "disabled": true } ] - }, - "description": "Обратите внимание: с категорий не должно быть связано ни одного события." + } }, "response": [] }, { - "name": "Изменение имени категории на уже занятое", + "name": "Добавление нового пользователя", "event": [ { "listen": "prerequest", @@ -8365,19 +11808,17 @@ "const main = async () => {\r", " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", - " let category1, category2\r", + "\r", + " let user;\r", " try {\r", - " category1 = await api.addCategory(rnd.getCategory());\r", - " category2 = await api.addCategory(rnd.getCategory());\r", + " user = rnd.getUser();\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", - " pm.collectionVariables.set(\"catid\", Number(category1.id))\r", + "\r", " pm.request.body.update({\r", " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " name : category2.name\r", - " }),\r", + " raw: JSON.stringify(user),\r", " options: { raw: { language: 'json' } }\r", " });\r", "};\r", @@ -8404,10 +11845,25 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Пользователь должен содержать поля: id, name, email\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('name');\r", + "pm.expect(target).to.have.property('email');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.name).equal(target.name, 'Имя пользователя должно соответствовать отправленному в запросе');\r", + " pm.expect(source.email).equal(target.email, 'Почта пользователя должна соответствовать отправленной в запросе');\r", "});" ], "type": "text/javascript" @@ -8415,35 +11871,72 @@ } ], "request": { - "method": "PATCH", - "header": [], + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Accept", + "value": "application/json" + } + ], "body": { "mode": "raw", - "raw": "{{request_body}}" + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } }, "url": { - "raw": "{{baseUrl}}/admin/categories/:catId", + "raw": "{{baseUrl}}/admin/users", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "categories", - ":catId" - ], - "variable": [ - { - "key": "catId", - "value": "{{catid}}" - } + "users" ] } }, "response": [] }, { - "name": "Публикация уже опубликованного события", + "name": "Удаление пользователя", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "const source = pm.collectionVariables.get('response');\r", + "const userId = pm.collectionVariables.get('uid');\r", + "\r", + "pm.test(\"Пользователь должен быть найден до выполнения запроса\", function(){\r", + " pm.expect(source.length).to.eql(1);\r", + " pm.expect(source[0].id).to.eql(userId);\r", + "});\r", + "let body\r", + "const req = {\r", + " url: \"http://localhost:8080/admin/users?ids=\" + pm.collectionVariables.get(\"uid\"),\r", + " method: \"GET\",\r", + " body: body == null ? \"\" : JSON.stringify(body),\r", + " header: { \"Content-Type\": \"application/json\" },\r", + " };\r", + "pm.sendRequest(req, (error, response) => {\r", + " pm.test(\"Пользователь должен быть удалён после выполнения запроса\", function(){\r", + " pm.expect(response.json().length).to.eql(0);\r", + " });\r", + "})" + ], + "type": "text/javascript" + } + }, { "listen": "prerequest", "script": { @@ -8452,19 +11945,12 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", + " let compilation;\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", - " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " event = await api.publishEvent(event.id);\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " stateAction : \"PUBLISH_EVENT\"\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", + " const foundedUser = await api.findUser(user.id);\r", + " pm.collectionVariables.set(\"uid\", user.id);\r", + " pm.collectionVariables.set(\"response\", foundedUser)\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -8475,7 +11961,6 @@ "setTimeout(async () => \r", " {\r", " try {\r", - " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -8488,57 +11973,44 @@ ], "type": "text/javascript" } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});" - ], - "type": "text/javascript" - } } ], "request": { - "method": "PATCH", + "method": "DELETE", "header": [ { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}" - }, "url": { - "raw": "{{baseUrl}}/admin/events/:eventId", + "raw": "{{baseUrl}}/admin/users/:userId", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "events", - ":eventId" + "users", + ":userId" ], "variable": [ { - "key": "eventId", - "value": "{{eid}}", - "description": "(Required) id события" + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id пользователя" } ] - }, - "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + } }, "response": [] - }, + } + ] + }, + { + "name": "Event", + "item": [ { - "name": "Публикация отмененного события", + "name": "Добавление нового события", "event": [ { "listen": "prerequest", @@ -8548,22 +12020,21 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", + " let event;\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " event = await api.rejectEvent(event.id);\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " stateAction : \"PUBLISH_EVENT\"\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", + " event = rnd.getEvent(category.id);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(event),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", "};\r", "\r", "const interval = setInterval(() => {}, 1000);\r", @@ -8571,7 +12042,6 @@ "setTimeout(async () => \r", " {\r", " try {\r", - " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -8589,10 +12059,42 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201); \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).equal(source.title, 'Название события должно соответствовать названию события в запросе');\r", + " pm.expect(target.annotation).equal(source.annotation, 'Аннотация события должна соответствовать аннотации события в запросе');\r", + " pm.expect(target.paid.toString()).equal(source.paid.toString(), 'Стоимость события должна соответствовать стоимости события в запросе');\r", + " pm.expect(target.eventDate).equal(source.eventDate, 'Дата проведения события должна соответствовать дате проведения события в запросе');\r", + " pm.expect(target.description).equal(source.description, 'Описание события должно соответствовать описание события в запросе');\r", + " pm.expect(target.participantLimit.toString()).equal(source.participantLimit.toString(), 'Лимит участников события должно соответствовать лимиту участников события в запросе');\r", + " pm.expect(target.location.lat.toString()).equal(source.location.lat.toString(), 'Широта локации проведения события должна соответствовать широте локации проведения события в запросе');\r", + " pm.expect(target.location.lon.toString()).equal(source.location.lon.toString(), 'Долгота локации проведения события должна соответствовать долготе локации проведения события в запросе');\r", + " pm.expect(target.requestModeration.toString()).equal(source.requestModeration.toString(), 'Необходимость модерации события должна соответствовать необходимости модерации события в запросе');\r", "});" ], "type": "text/javascript" @@ -8600,8 +12102,12 @@ } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, { "key": "Accept", "value": "application/json" @@ -8609,32 +12115,37 @@ ], "body": { "mode": "raw", - "raw": "{{request_body}}" + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } }, "url": { - "raw": "{{baseUrl}}/admin/events/:eventId", + "raw": "{{baseUrl}}/users/:userId/events", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "events", - ":eventId" + "users", + ":userId", + "events" ], "variable": [ { - "key": "eventId", - "value": "{{eid}}", - "description": "(Required) id события" + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" } ] }, - "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" }, "response": [] }, { - "name": "Отмена опубликованного события", + "name": "Добавление запроса от текущего пользователя на участие в событии", "event": [ { "listen": "prerequest", @@ -8647,16 +12158,14 @@ " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", " event = await api.publishEvent(event.id);\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " stateAction : \"REJECT_EVENT\"\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " pm.request.removeQueryParams(['eventId']);\r", + " pm.request.addQueryParams([`eventId=` + event.id]);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -8685,10 +12194,30 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201); \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const target = pm.response.json();\r", + "var query = {};\r", + "pm.request.url.query.all().forEach((param) => { query[param.key] = param.value});\r", + "\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"При создании у запроса на участие должен быть статус PENDING\", function () {\r", + " pm.expect(target.status).equal(\"PENDING\");\r", + "});\r", + "\r", + "pm.test(\"Id ивента в запросе и в ответе должны совпадать\", function () {\r", + " pm.expect(target.event.toString()).equal(query['eventId'].toString());\r", "});" ], "type": "text/javascript" @@ -8696,41 +12225,44 @@ } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}" - }, "url": { - "raw": "{{baseUrl}}/admin/events/:eventId", + "raw": "{{baseUrl}}/users/:userId/requests?eventId=0", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "events", - ":eventId" + "users", + ":userId", + "requests" ], - "variable": [ + "query": [ { "key": "eventId", - "value": "{{eid}}", + "value": "0", "description": "(Required) id события" } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } ] }, - "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + "description": "Обратите внимание:\n- нельзя добавить повторный запрос\n- инициатор события не может добавить запрос на участие в своём событии\n- нельзя участвовать в неопубликованном событии\n- если у события достигнут лимит запросов на участие - необходимо вернуть ошибку\n- если для события отключена пре-модерация запросов на участие, то запрос должен автоматически перейти в состояние подтвержденного" }, "response": [] }, { - "name": "Изменение опубликованного события от имени пользователя", + "name": "Поиск событий", "event": [ { "listen": "prerequest", @@ -8745,16 +12277,9 @@ " const category = await api.addCategory(rnd.getCategory());\r", " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", " event = await api.publishEvent(event.id);\r", - " pm.collectionVariables.set(\"uid\", user.id);\r", - " pm.collectionVariables.set(\"eid\", event.id);\r", - " pm.collectionVariables.set(\"response\", event);\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " eventDate : rnd.getFutureDateTime(6)\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", + " pm.request.removeQueryParams(['users', 'categories']);\r", + " pm.request.addQueryParams([`users=` + user.id, 'categories=' + category.id]);\r", + " pm.collectionVariables.set('response', event);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -8765,6 +12290,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -8781,11 +12307,43 @@ { "listen": "test", "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 409 и данные в формате json\", function () {\r", - " pm.response.to.have.status(409);\r", + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Идентификатор категории должен соответствовать искомой категории');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость посещения события должна соответствовать искомому событию');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате искомого события');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Число участников события должно соответствовать искомому событию');\r", "});" ], "type": "text/javascript" @@ -8793,61 +12351,66 @@ } ], "request": { - "method": "PATCH", + "method": "GET", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "raw": "{{baseUrl}}/admin/events?users=0&states=PUBLISHED&categories=0&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000", "host": [ "{{baseUrl}}" ], "path": [ - "users", - ":userId", - "events", - ":eventId" + "admin", + "events" ], - "variable": [ + "query": [ { - "key": "userId", - "value": "{{uid}}", - "description": "(Required) id текущего пользователя" + "key": "users", + "value": "0", + "description": "список id пользователей, чьи события нужно найти" }, { - "key": "eventId", - "value": "{{eid}}", - "description": "(Required) id отменяемого события" + "key": "states", + "value": "PUBLISHED", + "description": "список состояний в которых находятся искомые события" + }, + { + "key": "categories", + "value": "0", + "description": "список id категорий в которых будет вестись поиск" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "from", + "value": "0", + "description": "количество событий, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество событий в наборе" } ] }, - "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" }, "response": [] - } - ] - }, - { - "name": "Category", - "item": [ + }, { - "name": "Добавление новой категории", + "name": "Получение событий, добавленных текущим пользователем", "event": [ { "listen": "prerequest", @@ -8857,18 +12420,14 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", - " let category;\r", " try {\r", - " category = rnd.getCategory();\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", - "\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify(category),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", "};\r", "\r", "const interval = setInterval(() => {}, 1000);\r", @@ -8893,23 +12452,20 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", - " pm.response.to.have.status(201);\r", + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", "});\r", "\r", - "const source = JSON.parse(pm.request.body.raw);\r", - "const target = pm.response.json();\r", + "const target = pm.response.json()[0];\r", "\r", - "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", - "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('name');\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate\", function () {\r", + " pm.expect(target).to.contain.keys('id', 'title', 'annotation', 'category', 'paid', 'eventDate');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", " pm.expect(target.id).to.not.be.null;\r", - " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", "});" ], "type": "text/javascript" @@ -8917,42 +12473,48 @@ } ], "request": { - "method": "POST", + "method": "GET", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{baseUrl}}/admin/categories", + "raw": "{{baseUrl}}/users/:userId/events?from=0&size=1000", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "categories" + "users", + ":userId", + "events" + ], + "query": [ + { + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе" + } + ], + "variable": [ + { + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + } ] - }, - "description": "Обратите внимание: имя категории должно быть уникальным" + } }, "response": [] }, { - "name": "Получение категорий", + "name": "Получение событий с возможностью фильтрации", "event": [ { "listen": "prerequest", @@ -8963,8 +12525,13 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " pm.collectionVariables.set(\"response\", category)\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.request.removeQueryParams(['text', 'categories', 'paid']);\r", + " pm.request.addQueryParams([`text=` + event.annotation, 'categories=' + category.id, 'paid=' + event.paid]);\r", + " pm.collectionVariables.set('response', event);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -8975,6 +12542,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -8999,18 +12567,26 @@ "});\r", "\r", "const source = pm.collectionVariables.get('response');\r", - "const target = pm.response.json();\r", - "let founded;\r", - "target.forEach(function(element){if (element.id == source.id) founded = element});\r", + "const target = pm.response.json()[0];\r", "\r", - "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", - "pm.expect(target[0]).to.have.property('id');\r", - "pm.expect(target[0]).to.have.property('name');\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.id).equal(founded.id, 'Идентификатор категории должен соответствовать идентификатору категории добавленной ранее');\r", - " pm.expect(source.name).equal(founded.name, 'Название категории должно соответствовать названию категории добавленной ранее');\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать аннотации события с указанным идентификатором');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория события должна соответствовать категории события с указанным идентификатором');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать стоимости события с указанным идентификатором');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате проведения события с указанным идентификатором');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать названию события с указанным идентификатором');\r", "});" ], "type": "text/javascript" @@ -9026,31 +12602,67 @@ } ], "url": { - "raw": "{{baseUrl}}/categories?from=0&size=1000", + "raw": "{{baseUrl}}/events?text=0&categories=0&paid=true&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&onlyAvailable=false&sort=EVENT_DATE&from=0&size=1000", "host": [ "{{baseUrl}}" ], "path": [ - "categories" + "events" ], "query": [ + { + "key": "text", + "value": "0", + "description": "текст для поиска в содержимом аннотации и подробном описании события" + }, + { + "key": "categories", + "value": "0", + "description": "список идентификаторов категорий в которых будет вестись поиск" + }, + { + "key": "paid", + "value": "true", + "description": "поиск только платных/бесплатных событий" + }, + { + "key": "rangeStart", + "value": "2022-01-06%2013%3A30%3A38", + "description": "дата и время не раньше которых должно произойти событие" + }, + { + "key": "rangeEnd", + "value": "2097-09-06%2013%3A30%3A38", + "description": "дата и время не позже которых должно произойти событие" + }, + { + "key": "onlyAvailable", + "value": "false", + "description": "только события у которых не исчерпан лимит запросов на участие" + }, + { + "key": "sort", + "value": "EVENT_DATE", + "description": "Вариант сортировки: по дате события или по количеству просмотров" + }, { "key": "from", "value": "0", - "description": "количество категорий, которые нужно пропустить для формирования текущего набора" + "description": "количество событий, которые нужно пропустить для формирования текущего набора" }, { "key": "size", "value": "1000", - "description": "количество категорий в наборе" + "description": "количество событий в наборе" } ] - } + }, + "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" }, "response": [] }, { - "name": "Получение информации о категории по её идентификатору", + "name": "Получение подробной информации об опубликованном событии по его идентификатору", "event": [ { "listen": "prerequest", @@ -9061,9 +12673,12 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " pm.collectionVariables.set(\"response\", category)\r", - " pm.collectionVariables.set(\"catid\", category.id)\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.collectionVariables.set('response', event);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -9074,6 +12689,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -9100,14 +12716,33 @@ "const source = pm.collectionVariables.get('response');\r", "const target = pm.response.json();\r", "\r", - "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('name');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('views');\r", + "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.id).equal(target.id, 'Идентификатор категории должен соответствовать идентификатору в запросе');\r", - " pm.expect(source.name).equal(target.name, 'Название категории должно соответствовать названию категории с указанным идентификатором');\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать аннотации события с указанным идентификатором');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория события должна соответствовать категории события с указанным идентификатором');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать стоимости события с указанным идентификатором');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате проведения события с указанным идентификатором');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать описанию события с указанным идентификатором');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать названию события с указанным идентификатором');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников события должен соответствовать лимиту участников события с указанным идентификатором');\r", "});" ], "type": "text/javascript" @@ -9123,27 +12758,28 @@ } ], "url": { - "raw": "{{baseUrl}}/categories/:catId", + "raw": "{{baseUrl}}/events/:id", "host": [ "{{baseUrl}}" ], "path": [ - "categories", - ":catId" + "events", + ":id" ], "variable": [ { - "key": "catId", - "value": "{{catid}}", - "description": "(Required) id категории" + "key": "id", + "value": "{{eid}}", + "description": "(Required) id события" } ] - } + }, + "description": "Обратите внимание:\n- событие должно быть опубликовано\n- информация о событии должна включать в себя количество просмотров и количество подтвержденных запросов\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" }, "response": [] }, { - "name": "Удаление категории", + "name": "Получение полной информации о событии добавленном текущим пользователем", "event": [ { "listen": "prerequest", @@ -9154,10 +12790,11 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " const user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"uid\", user.id)\r", " const category = await api.addCategory(rnd.getCategory());\r", - " const findedCategory = await api.findCategory(category.id);\r", - " pm.collectionVariables.set(\"catid\", category.id)\r", - " pm.collectionVariables.set(\"response\", findedCategory)\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -9185,32 +12822,40 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", - " pm.response.to.have.status(204);\r", + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", "});\r", "\r", - "source = pm.collectionVariables.get('response');\r", - "catId = pm.collectionVariables.get('catid');\r", + "const target = pm.response.json();\r", "\r", - "pm.test(\"Категория должна быть найдена до удаления\", function () {\r", - " pm.expect(source.id).equal(catId, 'Идентификтор категории должен совпадать с удаляемым');\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", "});\r", "\r", - "pm.sendRequest({\r", - " url: pm.collectionVariables.get(\"baseUrl\") + \"/categories/\" + catId,\r", - " method: 'GET',\r", - " }, (error, response) => {\r", - " pm.test(\"Категория не должна быть найдена после удаления\", function () {\r", - " pm.expect(response.code).to.eql(404);\r", - " });\r", - " });" + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(target.id).to.not.be.null;\r", + "});" ], "type": "text/javascript" } } ], "request": { - "method": "DELETE", + "method": "GET", "header": [ { "key": "Accept", @@ -9218,28 +12863,34 @@ } ], "url": { - "raw": "{{baseUrl}}/admin/categories/:catId", + "raw": "{{baseUrl}}/users/:userId/events/:eventId", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "categories", - ":catId" + "users", + ":userId", + "events", + ":eventId" ], "variable": [ { - "key": "catId", - "value": "{{catid}}" + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" } ] - }, - "description": "Обратите внимание: с категорий не должно быть связано ни одного события." + } }, "response": [] }, { - "name": "Изменение категории", + "name": "Получение информации о заявках текущего пользователя на участие в чужих событиях", "event": [ { "listen": "prerequest", @@ -9248,20 +12899,22 @@ "const main = async () => {\r", " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", - " let category\r", + "\r", " try {\r", - " category = await api.addCategory(rnd.getCategory());\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", - " pm.collectionVariables.set(\"catid\", Number(category.id))\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify({\r", - " name : rnd.getCategory().name\r", - " }),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", "};\r", "\r", "const interval = setInterval(() => {}, 1000);\r", @@ -9269,6 +12922,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -9292,17 +12946,22 @@ " pm.response.to.be.json;\r", "});\r", "\r", - "const source = JSON.parse(pm.request.body.raw);\r", - "const target = pm.response.json();\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", "\r", - "pm.test(\"Категория должна содержать поля: id, name\", function () {\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('name');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(target.id).to.not.be.null;\r", - " pm.expect(source.name).equal(target.name, 'Название категории должно совпадать с отправленным');\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса, созданного ранее');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователя, запрашивающий доступ на участие в событии, должен соответствовать указанному пользователю');\r", + " pm.expect(source.event).equal(target.event, 'Событие, доступ к которому запрашивает пользователь, должно соответствовать событию, доступ к которому пользователь запрашивал доступ ранее');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии должно соответствовать времени создания запроса, созданного ранее указанным пользователем');\r", "});" ], "type": "text/javascript" @@ -9310,39 +12969,36 @@ } ], "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{{request_body}}" - }, + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + } + ], "url": { - "raw": "{{baseUrl}}/admin/categories/:catId", + "raw": "{{baseUrl}}/users/:userId/requests", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "categories", - ":catId" + "users", + ":userId", + "requests" ], "variable": [ { - "key": "catId", - "value": "{{catid}}" + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" } ] } }, "response": [] - } - ] - }, - { - "name": "Users", - "item": [ + }, { - "name": "Поиск пользователей", + "name": "Получение информации о запросах на участие в событии текущего пользователя", "event": [ { "listen": "prerequest", @@ -9352,10 +13008,19 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", - " let compilation;\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"uid\", user.id)\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -9366,6 +13031,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -9389,20 +13055,22 @@ " pm.response.to.be.json;\r", "});\r", "\r", - "const target = pm.response.json();\r", - "\r", - "pm.test(\"Пользователи должны содержать поля: id, name, email\", function () {\r", - " pm.expect(target[0]).to.have.property('id');\r", - " pm.expect(target[0]).to.have.property('name');\r", - " pm.expect(target[0]).to.have.property('email');\r", - "});\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[0];\r", "\r", - "pm.test(\"Должен быть найден только один пользователь по заданному фильтру\", function () {\r", - " pm.expect(target.length).to.eql(1);\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(target[0].id).equal(pm.collectionVariables.get(\"uid\"));\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса на участие в событии указанного пользователя');\r", + " pm.expect(source.requester).equal(target.requester, 'Автор запроса на участие в событии должен соответствовать указанному пользователю');\r", + " pm.expect(source.event).equal(target.event, 'Событие в ответе должно соответствовать событию с запросом на участие от указанного пользователя');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии должно соответствовать времени создания запроса на участие в событии указанного пользователя');\r", "});" ], "type": "text/javascript" @@ -9418,37 +13086,27 @@ } ], "url": { - "raw": "{{baseUrl}}/admin/users?ids={{uid}}", + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "users" + "users", + ":userId", + "events", + ":eventId", + "requests" ], - "query": [ + "variable": [ { - "key": "ids", + "key": "userId", "value": "{{uid}}", - "description": "id пользователей" - }, - { - "key": "ids", - "value": "-10833646", - "description": "id пользователей", - "disabled": true - }, - { - "key": "from", - "value": "0", - "description": "количество элементов, которые нужно пропустить для формирования текущего набора", - "disabled": true + "description": "(Required) id текущего пользователя" }, { - "key": "size", - "value": "10", - "description": "количество элементов в наборе", - "disabled": true + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" } ] } @@ -9456,7 +13114,7 @@ "response": [] }, { - "name": "Добавление нового пользователя", + "name": "Редактирование данных события и его статуса (отклонение/публикация).", "event": [ { "listen": "prerequest", @@ -9466,18 +13124,22 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", - " let user;\r", " try {\r", - " user = rnd.getUser();\r", + " const user = await api.addUser(rnd.getUser());\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let event2 = rnd.getEvent(category.id)\r", + " event2.stateAction = \"PUBLISH_EVENT\"\r", + " pm.collectionVariables.set('response', event2);\r", + " pm.collectionVariables.set(\"eid\", event.id)\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: event2,\r", + " options: { raw: { language: 'json' } }\r", + " });\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", - "\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify(user),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", "};\r", "\r", "const interval = setInterval(() => {}, 1000);\r", @@ -9485,6 +13147,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -9502,25 +13165,39 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", - " pm.response.to.have.status(201);\r", + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", "});\r", "\r", - "const source = JSON.parse(pm.request.body.raw);\r", + "const source = pm.collectionVariables.get('response');\r", "const target = pm.response.json();\r", "\r", - "pm.test(\"Пользователь должен содержать поля: id, name, email\", function () {\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('name');\r", - "pm.expect(target).to.have.property('email');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('publishedOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(target.id).to.not.be.null;\r", - " pm.expect(source.name).equal(target.name, 'Имя пользователя должно соответствовать отправленному в запросе');\r", - " pm.expect(source.email).equal(target.email, 'Почта пользователя должна соответствовать отправленной в запросе');\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать искомому событию');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать искомому событию');\r", + " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", + " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников события должен соответствовать искомому событию');\r", "});" ], "type": "text/javascript" @@ -9528,12 +13205,8 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, { "key": "Accept", "value": "application/json" @@ -9541,59 +13214,33 @@ ], "body": { "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{{request_body}}" }, "url": { - "raw": "{{baseUrl}}/admin/users", + "raw": "{{baseUrl}}/admin/events/:eventId", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "users" + "events", + ":eventId" + ], + "variable": [ + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события" + } ] - } + }, + "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" }, "response": [] }, { - "name": "Удаление пользователя", + "name": "Изменение события добавленного текущим пользователем", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", - " pm.response.to.have.status(204);\r", - "});\r", - "const source = pm.collectionVariables.get('response');\r", - "const userId = pm.collectionVariables.get('uid');\r", - "\r", - "pm.test(\"Пользователь должен быть найден до выполнения запроса\", function(){\r", - " pm.expect(source.length).to.eql(1);\r", - " pm.expect(source[0].id).to.eql(userId);\r", - "});\r", - "let body\r", - "const req = {\r", - " url: \"http://localhost:8080/admin/users?ids=\" + pm.collectionVariables.get(\"uid\"),\r", - " method: \"GET\",\r", - " body: body == null ? \"\" : JSON.stringify(body),\r", - " header: { \"Content-Type\": \"application/json\" },\r", - " };\r", - "pm.sendRequest(req, (error, response) => {\r", - " pm.test(\"Пользователь должен быть удалён после выполнения запроса\", function(){\r", - " pm.expect(response.json().length).to.eql(0);\r", - " });\r", - "})" - ], - "type": "text/javascript" - } - }, { "listen": "prerequest", "script": { @@ -9602,12 +13249,20 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", - " let compilation;\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", - " const foundedUser = await api.findUser(user.id);\r", + " const category = await api.addCategory(rnd.getCategory());\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", " pm.collectionVariables.set(\"uid\", user.id);\r", - " pm.collectionVariables.set(\"response\", foundedUser)\r", + " pm.collectionVariables.set(\"eid\", event.id);\r", + " pm.collectionVariables.set(\"response\", event);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({\r", + " stateAction: \"CANCEL_REVIEW\"\r", + " }),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -9630,44 +13285,108 @@ ], "type": "text/javascript" } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "const source = pm.collectionVariables.get(\"response\");\r", + "const target = pm.response.json();\r", + "\r", + "\r", + "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('title');\r", + "pm.expect(target).to.have.property('annotation');\r", + "pm.expect(target).to.have.property('category');\r", + "pm.expect(target).to.have.property('paid');\r", + "pm.expect(target).to.have.property('eventDate');\r", + "pm.expect(target).to.have.property('initiator');\r", + "pm.expect(target).to.have.property('description');\r", + "pm.expect(target).to.have.property('participantLimit');\r", + "pm.expect(target).to.have.property('state');\r", + "pm.expect(target).to.have.property('createdOn');\r", + "pm.expect(target).to.have.property('location');\r", + "pm.expect(target).to.have.property('requestModeration');\r", + "});\r", + "\r", + "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", + " pm.expect(source.annotation).equal(target.annotation, 'Аннотация отменённого события должна соответствовать аннотации события до отмены');\r", + " pm.expect(source.category.id).equal(target.category.id, 'Категория отменённого события должна соответствовать категории события до отмены');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения отменённого события должна соответствовать дате проведения события до отмены');\r", + " pm.expect(source.description).equal(target.description, 'Описание отменённого события должно соответствовать описанию события до отмены');\r", + " pm.expect(source.title).equal(target.title, 'Название отменённого события должно соответствовать названию события до отмены');\r", + " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников отменённого события должен соответствовать лимиту участников события до отмены');\r", + " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + "});\r", + "\r", + "pm.test(\"Событие должно иметь статус PENDING при создании и статус CANCELED после выполнения запроса\", function () {\r", + " pm.expect(source.state).equal(\"PENDING\");\r", + " pm.expect(target.state).equal(\"CANCELED\");\r", + "});" + ], + "type": "text/javascript" + } } ], "request": { - "method": "DELETE", + "method": "PATCH", "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, { "key": "Accept", "value": "application/json" } ], + "body": { + "mode": "raw", + "raw": "{{request_body}}", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{baseUrl}}/admin/users/:userId", + "raw": "{{baseUrl}}/users/:userId/events/:eventId", "host": [ "{{baseUrl}}" ], "path": [ - "admin", "users", - ":userId" + ":userId", + "events", + ":eventId" ], "variable": [ { "key": "userId", "value": "{{uid}}", - "description": "(Required) id пользователя" + "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id отменяемого события" } ] - } + }, + "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." }, "response": [] - } - ] - }, - { - "name": "Event", - "item": [ + }, { - "name": "Добавление нового события", + "name": "Отмена своего запроса на участие в событии", "event": [ { "listen": "prerequest", @@ -9677,21 +13396,22 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", - " let event;\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"uid\", user.id)\r", " const category = await api.addCategory(rnd.getCategory());\r", - " event = rnd.getEvent(category.id);\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', submittedUser.id);\r", + " pm.collectionVariables.set('reqid', requestToJoin.id);\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", - "\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: JSON.stringify(event),\r", - " options: { raw: { language: 'json' } }\r", - " });\r", "};\r", "\r", "const interval = setInterval(() => {}, 1000);\r", @@ -9699,6 +13419,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -9716,42 +13437,33 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", - " pm.response.to.have.status(201); \r", + "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + " pm.response.to.be.ok; \r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", "});\r", "\r", - "const source = JSON.parse(pm.request.body.raw);\r", "const target = pm.response.json();\r", + "const source = pm.collectionVariables.get('response');\r", "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('description');\r", - "pm.expect(target).to.have.property('participantLimit');\r", - "pm.expect(target).to.have.property('state');\r", - "pm.expect(target).to.have.property('createdOn');\r", - "pm.expect(target).to.have.property('location');\r", - "pm.expect(target).to.have.property('requestModeration');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", + "});\r", + "\r", + "pm.test(\"При создании у запроса на участие должен быть статус PENDING, а при удалении - CANCELED\", function () {\r", + " pm.expect(source.status).equal(\"PENDING\");\r", + " pm.expect(target.status).equal(\"CANCELED\");\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(target.id).to.not.be.null;\r", - " pm.expect(target.title).equal(source.title, 'Название события должно соответствовать названию события в запросе');\r", - " pm.expect(target.annotation).equal(source.annotation, 'Аннотация события должна соответствовать аннотации события в запросе');\r", - " pm.expect(target.paid.toString()).equal(source.paid.toString(), 'Стоимость события должна соответствовать стоимости события в запросе');\r", - " pm.expect(target.eventDate).equal(source.eventDate, 'Дата проведения события должна соответствовать дате проведения события в запросе');\r", - " pm.expect(target.description).equal(source.description, 'Описание события должно соответствовать описание события в запросе');\r", - " pm.expect(target.participantLimit.toString()).equal(source.participantLimit.toString(), 'Лимит участников события должно соответствовать лимиту участников события в запросе');\r", - " pm.expect(target.location.lat.toString()).equal(source.location.lat.toString(), 'Широта локации проведения события должна соответствовать широте локации проведения события в запросе');\r", - " pm.expect(target.location.lon.toString()).equal(source.location.lon.toString(), 'Долгота локации проведения события должна соответствовать долготе локации проведения события в запросе');\r", - " pm.expect(target.requestModeration.toString()).equal(source.requestModeration.toString(), 'Необходимость модерации события должна соответствовать необходимости модерации события в запросе');\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор отменённого запроса на участие в событии должен соответствовать идентификатору запроса до отмены');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователь, отменяющий запрос на участие в событии, должен соответствовать текущему пользователю');\r", + " pm.expect(source.event).equal(target.event, 'Событие отменённого запроса на участие должно соответствовать запросу на участие в событии до отмены');\r", + " pm.expect(source.created).equal(target.created, 'Дата создания отменённого запроса на участие в событии должна соответствовать дате создания запроса до отмены');\r", "});" ], "type": "text/javascript" @@ -9759,50 +13471,43 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } - }, "url": { - "raw": "{{baseUrl}}/users/:userId/events", + "raw": "{{baseUrl}}/users/:userId/requests/:requestId/cancel", "host": [ "{{baseUrl}}" ], "path": [ "users", ":userId", - "events" + "requests", + ":requestId", + "cancel" ], "variable": [ { "key": "userId", "value": "{{uid}}", "description": "(Required) id текущего пользователя" + }, + { + "key": "requestId", + "value": "{{reqid}}", + "description": "(Required) id запроса на участие" } ] - }, - "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента" + } }, "response": [] }, { - "name": "Поиск событий", + "name": "Отклонение запроса на участие в событии", "event": [ { "listen": "prerequest", @@ -9815,11 +13520,21 @@ " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", " event = await api.publishEvent(event.id);\r", - " pm.request.removeQueryParams(['users', 'categories']);\r", - " pm.request.addQueryParams([`users=` + user.id, 'categories=' + category.id]);\r", - " pm.collectionVariables.set('response', event);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToJoin.id],\r", + " status:\"REJECTED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -9855,35 +13570,26 @@ "});\r", "\r", "const source = pm.collectionVariables.get('response');\r", - "const target = pm.response.json()[0];\r", + "const target = pm.response.json()[\"rejectedRequests\"][0];\r", "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('views');\r", - "pm.expect(target).to.have.property('confirmedRequests');\r", - "pm.expect(target).to.have.property('description');\r", - "pm.expect(target).to.have.property('participantLimit');\r", - "pm.expect(target).to.have.property('state');\r", - "pm.expect(target).to.have.property('createdOn');\r", - "pm.expect(target).to.have.property('publishedOn');\r", - "pm.expect(target).to.have.property('location');\r", - "pm.expect(target).to.have.property('requestModeration');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", - " pm.expect(source.category.id).equal(target.category.id, 'Идентификатор категории должен соответствовать искомой категории');\r", - " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость посещения события должна соответствовать искомому событию');\r", - " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате искомого события');\r", - " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", - " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", - " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Число участников события должно соответствовать искомому событию');\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса на участие в событии указанного пользователя');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователь, запрашивающий доступ на участие в событии должен пользователю, отправившему запрос на участие в событии указанного пользователя ранее');\r", + " pm.expect(source.event).equal(target.event, 'Событие, запрос на участие в котором надо подтвердить, должно соответствовать событию указанного пользователя');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии после подтверждения должно соответствовать времени создания запроса на участие в событии указанного пользователя до подтверждения');\r", + "});\r", + "\r", + "pm.test(\"Запрос на участие должен иметь статус PENDING при создании и статус REJECTED после выполнения запроса\", function () {\r", + " pm.expect(source.status).equal(\"PENDING\");\r", + " pm.expect(target.status).equal(\"REJECTED\");\r", "});" ], "type": "text/javascript" @@ -9891,66 +13597,57 @@ } ], "request": { - "method": "GET", + "method": "PATCH", "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, { "key": "Accept", "value": "application/json" } ], + "body": { + "mode": "raw", + "raw": "{\n \"requestIds\": [\n 1,\n 2,\n 3\n ],\n \"status\": \"CONFIRMED\"\n}", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{baseUrl}}/admin/events?users=0&states=PUBLISHED&categories=0&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&from=0&size=1000", + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", "host": [ "{{baseUrl}}" ], "path": [ - "admin", - "events" + "users", + ":userId", + "events", + ":eventId", + "requests" ], - "query": [ - { - "key": "users", - "value": "0", - "description": "список id пользователей, чьи события нужно найти" - }, - { - "key": "states", - "value": "PUBLISHED", - "description": "список состояний в которых находятся искомые события" - }, - { - "key": "categories", - "value": "0", - "description": "список id категорий в которых будет вестись поиск" - }, - { - "key": "rangeStart", - "value": "2022-01-06%2013%3A30%3A38", - "description": "дата и время не раньше которых должно произойти событие" - }, - { - "key": "rangeEnd", - "value": "2097-09-06%2013%3A30%3A38", - "description": "дата и время не позже которых должно произойти событие" - }, + "variable": [ { - "key": "from", - "value": "0", - "description": "количество событий, которые нужно пропустить для формирования текущего набора" + "key": "userId", + "value": "{{uid}}", + "description": "(Required) id текущего пользователя" }, { - "key": "size", - "value": "1000", - "description": "количество событий в наборе" + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" } ] }, - "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия" + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" }, "response": [] }, { - "name": "Получение событий, добавленных текущим пользователем", + "name": "Изменение статуса (подтверждена, отменена) заявок на участие в событии текущего пользователя", "event": [ { "listen": "prerequest", @@ -9962,9 +13659,22 @@ "\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"uid\", user.id)\r", " const category = await api.addCategory(rnd.getCategory());\r", - " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " let eventBody = rnd.getEvent(category.id);\r", + " eventBody['requestModeration'] = true\r", + " let event = await api.addEvent(user.id, eventBody);\r", + " event = await api.publishEvent(event.id);\r", + " const submittedUser = await api.addUser(rnd.getUser());\r", + " const requestToJoin = await api.publishParticipationRequest(event.id, submittedUser.id);\r", + " pm.collectionVariables.set('response', requestToJoin);\r", + " pm.collectionVariables.set('uid', user.id);\r", + " pm.collectionVariables.set('eid', event.id);\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify({requestIds: [requestToJoin.id],\r", + " status:\"CONFIRMED\"}),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -9975,6 +13685,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -9998,14 +13709,27 @@ " pm.response.to.be.json;\r", "});\r", "\r", - "const target = pm.response.json()[0];\r", + "const source = pm.collectionVariables.get('response');\r", + "const target = pm.response.json()[\"confirmedRequests\"][0];\r", "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate\", function () {\r", - " pm.expect(target).to.contain.keys('id', 'title', 'annotation', 'category', 'paid', 'eventDate');\r", + "pm.test(\"Запрос на участие должен содержать поля: id, requester, event, status, created\", function () {\r", + "pm.expect(target).to.have.property('id');\r", + "pm.expect(target).to.have.property('requester');\r", + "pm.expect(target).to.have.property('event');\r", + "pm.expect(target).to.have.property('status');\r", + "pm.expect(target).to.have.property('created');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор запроса на участие в событии должен соответствовать идентификатору запроса на участие в событии указанного пользователя');\r", + " pm.expect(source.requester).equal(target.requester, 'Пользователь, запрашивающий доступ на участие в событии должен пользователю, отправившему запрос на участие в событии указанного пользователя ранее');\r", + " pm.expect(source.event).equal(target.event, 'Событие, запрос на участие в котором надо подтвердить, должно соответствовать событию указанного пользователя');\r", + " pm.expect(source.created).equal(target.created, 'Время создания запроса на участие в событии после подтверждения должно соответствовать времени создания запроса на участие в событии указанного пользователя до подтверждения');\r", + "});\r", + "\r", + "pm.test(\"Запрос на участие должен иметь статус PENDING при создании и статус CONFIRMED после выполнения запроса\", function () {\r", + " pm.expect(source.status).equal(\"PENDING\");\r", + " pm.expect(target.status).equal(\"CONFIRMED\");\r", "});" ], "type": "text/javascript" @@ -10013,48 +13737,62 @@ } ], "request": { - "method": "GET", + "method": "PATCH", "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, { "key": "Accept", "value": "application/json" } ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{baseUrl}}/users/:userId/events?from=0&size=1000", + "raw": "{{baseUrl}}/users/:userId/events/:eventId/requests", "host": [ "{{baseUrl}}" ], "path": [ "users", ":userId", - "events" - ], - "query": [ - { - "key": "from", - "value": "0", - "description": "количество элементов, которые нужно пропустить для формирования текущего набора" - }, - { - "key": "size", - "value": "1000", - "description": "количество элементов в наборе" - } + "events", + ":eventId", + "requests" ], "variable": [ { "key": "userId", "value": "{{uid}}", "description": "(Required) id текущего пользователя" + }, + { + "key": "eventId", + "value": "{{eid}}", + "description": "(Required) id события текущего пользователя" } ] - } + }, + "description": "Обратите внимание:\n- если для события лимит заявок равен 0 или отключена пре-модерация заявок, то подтверждение заявок не требуется\n- нельзя подтвердить заявку, если уже достигнут лимит по заявкам на данное событие\n- статус можно изменить только у заявок, находящихся в состоянии ожидания\n- если при подтверждении данной заявки, лимит заявок для события исчерпан, то все неподтверждённые заявки необходимо отклонить" }, "response": [] - }, + } + ] + }, + { + "name": "Compilation", + "item": [ { - "name": "Получение событий с возможностью фильтрации", + "name": "Добавление новой подборки", "event": [ { "listen": "prerequest", @@ -10064,17 +13802,21 @@ " const api = new API(pm);\r", " const rnd = new RandomUtils();\r", "\r", + " let compilation;\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " event = await api.publishEvent(event.id);\r", - " pm.request.removeQueryParams(['text', 'categories', 'paid']);\r", - " pm.request.addQueryParams([`text=` + event.annotation, 'categories=' + category.id, 'paid=' + event.paid]);\r", - " pm.collectionVariables.set('response', event);\r", + " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " compilation = rnd.getCompilation(event.id);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", + "\r", + " pm.request.body.update({\r", + " mode: 'raw',\r", + " raw: JSON.stringify(compilation),\r", + " options: { raw: { language: 'json' } }\r", + " });\r", "};\r", "\r", "const interval = setInterval(() => {}, 1000);\r", @@ -10082,7 +13824,6 @@ "setTimeout(async () => \r", " {\r", " try {\r", - " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -10100,33 +13841,29 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", - " pm.response.to.be.ok; \r", + "pm.test(\"Ответ должен содержать код статуса 201 и данные в формате json\", function () {\r", + " pm.response.to.have.status(201);\r", " pm.response.to.be.withBody;\r", " pm.response.to.be.json;\r", "});\r", "\r", - "const source = pm.collectionVariables.get('response');\r", - "const target = pm.response.json()[0];\r", + "const source = JSON.parse(pm.request.body.raw);\r", + "const target = pm.response.json();\r", "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests\", function () {\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", "pm.expect(target).to.have.property('id');\r", "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('views');\r", - "pm.expect(target).to.have.property('confirmedRequests');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать аннотации события с указанным идентификатором');\r", - " pm.expect(source.category.id).equal(target.category.id, 'Категория события должна соответствовать категории события с указанным идентификатором');\r", - " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать стоимости события с указанным идентификатором');\r", - " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате проведения события с указанным идентификатором');\r", - " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать названию события с указанным идентификатором');\r", + " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(target.title).to.be.a(\"string\");\r", + " pm.expect(target.events).to.be.an(\"array\");\r", + "\r", + " pm.expect(source.events[0]).equal(target.events[0].id, 'Идентификаторы событий в подборке должен быть идентичен идентификаторам, указанным при создании подборки ');\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать указанному при создании');\r", "});" ], "type": "text/javascript" @@ -10134,75 +13871,41 @@ } ], "request": { - "method": "GET", + "method": "POST", "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, { "key": "Accept", "value": "application/json" } ], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, "url": { - "raw": "{{baseUrl}}/events?text=0&categories=0&paid=true&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2097-09-06%2013%3A30%3A38&onlyAvailable=false&sort=EVENT_DATE&from=0&size=1000", + "raw": "{{baseUrl}}/admin/compilations", "host": [ "{{baseUrl}}" ], "path": [ - "events" - ], - "query": [ - { - "key": "text", - "value": "0", - "description": "текст для поиска в содержимом аннотации и подробном описании события" - }, - { - "key": "categories", - "value": "0", - "description": "список идентификаторов категорий в которых будет вестись поиск" - }, - { - "key": "paid", - "value": "true", - "description": "поиск только платных/бесплатных событий" - }, - { - "key": "rangeStart", - "value": "2022-01-06%2013%3A30%3A38", - "description": "дата и время не раньше которых должно произойти событие" - }, - { - "key": "rangeEnd", - "value": "2097-09-06%2013%3A30%3A38", - "description": "дата и время не позже которых должно произойти событие" - }, - { - "key": "onlyAvailable", - "value": "false", - "description": "только события у которых не исчерпан лимит запросов на участие" - }, - { - "key": "sort", - "value": "EVENT_DATE", - "description": "Вариант сортировки: по дате события или по количеству просмотров" - }, - { - "key": "from", - "value": "0", - "description": "количество событий, которые нужно пропустить для формирования текущего набора" - }, - { - "key": "size", - "value": "1000", - "description": "количество событий в наборе" - } - ] - }, - "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + "admin", + "compilations" + ] + } }, "response": [] }, { - "name": "Получение подробной информации об опубликованном событии по его идентификатору", + "name": "Получение подборки событий по её id", "event": [ { "listen": "prerequest", @@ -10215,10 +13918,9 @@ " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " event = await api.publishEvent(event.id);\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", - " pm.collectionVariables.set('response', event);\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " pm.collectionVariables.set('response', compilation);\r", + " pm.collectionVariables.set('compid', compilation.id);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -10256,33 +13958,18 @@ "const source = pm.collectionVariables.get('response');\r", "const target = pm.response.json();\r", "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, views, confirmedRequests, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", "pm.expect(target).to.have.property('id');\r", "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('views');\r", - "pm.expect(target).to.have.property('confirmedRequests');\r", - "pm.expect(target).to.have.property('description');\r", - "pm.expect(target).to.have.property('participantLimit');\r", - "pm.expect(target).to.have.property('state');\r", - "pm.expect(target).to.have.property('createdOn');\r", - "pm.expect(target).to.have.property('publishedOn');\r", - "pm.expect(target).to.have.property('location');\r", - "pm.expect(target).to.have.property('requestModeration');\r", + "pm.expect(target).to.have.property('pinned');\r", + "pm.expect(target).to.have.property('events');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать аннотации события с указанным идентификатором');\r", - " pm.expect(source.category.id).equal(target.category.id, 'Категория события должна соответствовать категории события с указанным идентификатором');\r", - " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать стоимости события с указанным идентификатором');\r", - " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать дате проведения события с указанным идентификатором');\r", - " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать описанию события с указанным идентификатором');\r", - " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать названию события с указанным идентификатором');\r", - " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников события должен соответствовать лимиту участников события с указанным идентификатором');\r", + " pm.expect(source.id).equal(target.id, 'Идентификатор подборки должен соответствовать идентификатору подборки добавленной ранее');\r", + " pm.expect(source.title).equal(target.title, 'Название подборки должно соответствовать названию подборки добавленной ранее');\r", + " pm.expect(source.pinned).equal(target.pinned, 'Закреплённость подборки должна соответствовать закреплённости подборки добавленной ранее');\r", + " pm.expect(source.events.join()).equal(target.events.join(), 'События подборки должны соответствовать событиям подборки добавленной ранее');\r", "});" ], "type": "text/javascript" @@ -10298,28 +13985,27 @@ } ], "url": { - "raw": "{{baseUrl}}/events/:id", + "raw": "{{baseUrl}}/compilations/:compId", "host": [ "{{baseUrl}}" ], "path": [ - "events", - ":id" + "compilations", + ":compId" ], "variable": [ { - "key": "id", - "value": "{{eid}}", - "description": "(Required) id события" + "key": "compId", + "value": "{{compid}}", + "description": "(Required) id подборки" } ] - }, - "description": "Обратите внимание:\n- событие должно быть опубликовано\n- информация о событии должна включать в себя количество просмотров и количество подтвержденных запросов\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики" + } }, "response": [] }, { - "name": "Получение полной информации о событии добавленном текущим пользователем", + "name": "Получение подборок событий", "event": [ { "listen": "prerequest", @@ -10331,10 +14017,11 @@ "\r", " try {\r", " const user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"uid\", user.id)\r", " const category = await api.addCategory(rnd.getCategory());\r", - " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", + " let newComp = rnd.getCompilation();\r", + " newComp['pinned'] = true;\r", + " const compilation = await api.addCompilation(newComp);\r", + " pm.collectionVariables.set('response', compilation);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -10345,6 +14032,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -10368,26 +14056,23 @@ " pm.response.to.be.json;\r", "});\r", "\r", + "const source = pm.collectionVariables.get('response');\r", "const target = pm.response.json();\r", + "let founded;\r", + "target.forEach(function(element){if (element.id == source.id) founded = element});\r", "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", - "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('description');\r", - "pm.expect(target).to.have.property('participantLimit');\r", - "pm.expect(target).to.have.property('state');\r", - "pm.expect(target).to.have.property('createdOn');\r", - "pm.expect(target).to.have.property('location');\r", - "pm.expect(target).to.have.property('requestModeration');\r", + "pm.test(\"Подборка должны содержать поля: id, title, pinned, events\", function () {\r", + "pm.expect(target[0]).to.have.property('id');\r", + "pm.expect(target[0]).to.have.property('title');\r", + "pm.expect(target[0]).to.have.property('pinned');\r", + "pm.expect(target[0]).to.have.property('events');\r", "});\r", "\r", "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(target.id).to.not.be.null;\r", + " pm.expect(source.id).equal(founded.id, 'Идентификатор подборки должен соответствовать идентификатору подборки добавленной ранее');\r", + " pm.expect(source.title).equal(founded.title, 'Название подборки должно соответствовать названию подборки добавленной ранее');\r", + " pm.expect(source.pinned).equal(founded.pinned, 'Закрепленность подборки должна соответствовать закрепленности подборки добавленной ранее');\r", + " pm.expect(source.events.join()).equal(founded.events.join(), 'События подборки должны соответствовать событиям подборки добавленной ранее');\r", "});" ], "type": "text/javascript" @@ -10403,26 +14088,28 @@ } ], "url": { - "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "raw": "{{baseUrl}}/compilations?pinned=true&from=0&size=1000", "host": [ "{{baseUrl}}" ], "path": [ - "users", - ":userId", - "events", - ":eventId" + "compilations" ], - "variable": [ + "query": [ { - "key": "userId", - "value": "{{uid}}", - "description": "(Required) id текущего пользователя" + "key": "pinned", + "value": "true", + "description": "искать только закрепленные/не закрепленные подборки" }, { - "key": "eventId", - "value": "{{eid}}", - "description": "(Required) id события" + "key": "from", + "value": "0", + "description": "количество элементов, которые нужно пропустить для формирования текущего набора" + }, + { + "key": "size", + "value": "1000", + "description": "количество элементов в наборе" } ] } @@ -10430,8 +14117,43 @@ "response": [] }, { - "name": "Редактирование данных события и его статуса (отклонение/публикация).", + "name": "Удаление подборки", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Ответ должен содержать код статуса 204\", function () {\r", + " pm.response.to.have.status(204);\r", + "});\r", + "\r", + "let source = pm.collectionVariables.get('response');\r", + "\r", + "pm.test(\"Подборка должна быть найдена до удаления\", function () {\r", + " pm.expect(source).not.to.be.null;\r", + "});\r", + "\r", + "let body\r", + "const req = {\r", + " url: \"http://localhost:8080/compilations?from=0&size=1000\" + pm.collectionVariables.get(\"uid\"),\r", + " method: \"GET\",\r", + " body: body == null ? \"\" : JSON.stringify(body),\r", + " header: { \"Content-Type\": \"application/json\" },\r", + " };\r", + "pm.sendRequest(req, (error, response) => {\r", + " pm.test(\"Подборка должна быть удалена после выполнения запроса\", function(){\r", + " response.json().forEach(element => {\r", + " if(element.id == pm.collectionVariables.get('compid')){\r", + " throw new Error(\"Подборка все еще находится в списке существующих\");\r", + " }\r", + " })\r", + " });\r", + "})\r", + "" + ], + "type": "text/javascript" + } + }, { "listen": "prerequest", "script": { @@ -10443,16 +14165,10 @@ " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " let event2 = rnd.getEvent(category.id)\r", - " event2.stateAction = \"PUBLISH_EVENT\"\r", - " pm.collectionVariables.set('response', event2);\r", - " pm.collectionVariables.set(\"eid\", event.id)\r", - " pm.request.body.update({\r", - " mode: 'raw',\r", - " raw: event2,\r", - " options: { raw: { language: 'json' } }\r", - " });\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " const foundedCompilation = await api.findCompilation(compilation.id);\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.collectionVariables.set('response', foundedCompilation);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -10476,86 +14192,39 @@ ], "type": "text/javascript" } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", - " pm.response.to.be.ok; \r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "\r", - "const source = pm.collectionVariables.get('response');\r", - "const target = pm.response.json();\r", - "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, publishedOn, location, requestModeration\", function () {\r", - "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('description');\r", - "pm.expect(target).to.have.property('participantLimit');\r", - "pm.expect(target).to.have.property('state');\r", - "pm.expect(target).to.have.property('createdOn');\r", - "pm.expect(target).to.have.property('publishedOn');\r", - "pm.expect(target).to.have.property('location');\r", - "pm.expect(target).to.have.property('requestModeration');\r", - "});\r", - "\r", - "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.annotation).equal(target.annotation, 'Аннотация события должна соответствовать искомому событию');\r", - " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость события должна соответствовать искомому событию');\r", - " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения события должна соответствовать искомому событию');\r", - " pm.expect(source.description).equal(target.description, 'Описание события должно соответствовать искомому событию');\r", - " pm.expect(source.title).equal(target.title, 'Название события должно соответствовать искомому событию');\r", - " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников события должен соответствовать искомому событию');\r", - "});" - ], - "type": "text/javascript" - } } ], "request": { - "method": "PATCH", + "method": "DELETE", "header": [ { "key": "Accept", "value": "application/json" } ], - "body": { - "mode": "raw", - "raw": "{{request_body}}" - }, "url": { - "raw": "{{baseUrl}}/admin/events/:eventId", + "raw": "{{baseUrl}}/admin/compilations/:compId", "host": [ "{{baseUrl}}" ], "path": [ "admin", - "events", - ":eventId" + "compilations", + ":compId" ], "variable": [ { - "key": "eventId", - "value": "{{eid}}", - "description": "(Required) id события" + "key": "compId", + "value": "{{compid}}", + "description": "(Required) id подборки" } ] - }, - "description": "Обратите внимание:\n - дата начала события должна быть не ранее чем за час от даты публикации.\n- событие должно быть в состоянии ожидания публикации" + } }, "response": [] }, { - "name": "Изменение события добавленного текущим пользователем", + "name": "Обновить информацию о подборке", "event": [ { "listen": "prerequest", @@ -10568,14 +14237,19 @@ " try {\r", " const user = await api.addUser(rnd.getUser());\r", " const category = await api.addCategory(rnd.getCategory());\r", - " const event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", - " pm.collectionVariables.set(\"uid\", user.id);\r", - " pm.collectionVariables.set(\"eid\", event.id);\r", - " pm.collectionVariables.set(\"response\", event);\r", + " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r", + " event = await api.publishEvent(event.id);\r", + " const compilation = await api.addCompilation(rnd.getCompilation());\r", + " const foundedCompilation = await api.findCompilation(compilation.id);\r", + " pm.collectionVariables.set('compid', compilation.id);\r", + " pm.collectionVariables.set('response', foundedCompilation);\r", + " pm.collectionVariables.set('eid', event.id);\r", " pm.request.body.update({\r", " mode: 'raw',\r", " raw: JSON.stringify({\r", - " stateAction: \"CANCEL_REVIEW\"\r", + " events : [event.id],\r", + " pinned: true,\r", + " title: rnd.getCompilation().name\r", " }),\r", " options: { raw: { language: 'json' } }\r", " });\r", @@ -10589,6 +14263,7 @@ "setTimeout(async () => \r", " {\r", " try {\r", + " // выполняем наш скрипт\r", " await main();\r", " } catch (e) {\r", " console.error(e);\r", @@ -10606,47 +14281,27 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Ответ должен содержать код статуса 200 и данные в формате json\", function () {\r", + "pm.test(\"Ответ должен содержать код статуса 200\", function () {\r", " pm.response.to.be.ok; \r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", "});\r", "\r", - "const source = pm.collectionVariables.get(\"response\");\r", - "const target = pm.response.json();\r", - "\r", - "\r", - "pm.test(\"Событие должно содержать поля: id, title, annotation, category, paid, eventDate, initiator, description, participantLimit, state, createdOn, location, requestModeration\", function () {\r", - "pm.expect(target).to.have.property('id');\r", - "pm.expect(target).to.have.property('title');\r", - "pm.expect(target).to.have.property('annotation');\r", - "pm.expect(target).to.have.property('category');\r", - "pm.expect(target).to.have.property('paid');\r", - "pm.expect(target).to.have.property('eventDate');\r", - "pm.expect(target).to.have.property('initiator');\r", - "pm.expect(target).to.have.property('description');\r", - "pm.expect(target).to.have.property('participantLimit');\r", - "pm.expect(target).to.have.property('state');\r", - "pm.expect(target).to.have.property('createdOn');\r", - "pm.expect(target).to.have.property('location');\r", - "pm.expect(target).to.have.property('requestModeration');\r", - "});\r", + "source = pm.collectionVariables.get('response');\r", + "compId = pm.collectionVariables.get('compid');\r", + "eventId = pm.collectionVariables.get('eid');\r", "\r", - "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r", - " pm.expect(source.annotation).equal(target.annotation, 'Аннотация отменённого события должна соответствовать аннотации события до отмены');\r", - " pm.expect(source.category.id).equal(target.category.id, 'Категория отменённого события должна соответствовать категории события до отмены');\r", - " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", - " pm.expect(source.eventDate).equal(target.eventDate, 'Дата проведения отменённого события должна соответствовать дате проведения события до отмены');\r", - " pm.expect(source.description).equal(target.description, 'Описание отменённого события должно соответствовать описанию события до отмены');\r", - " pm.expect(source.title).equal(target.title, 'Название отменённого события должно соответствовать названию события до отмены');\r", - " pm.expect(source.participantLimit.toString()).equal(target.participantLimit.toString(), 'Лимит участников отменённого события должен соответствовать лимиту участников события до отмены');\r", - " pm.expect(source.paid.toString()).equal(target.paid.toString(), 'Стоимость отменённого события должна соответствовать стоимости события до отмены');\r", + "pm.test(\"Событие не должно быть найдено в подборке до добавления\", function () {\r", + " pm.expect(source.events.length).equal(0);\r", "});\r", "\r", - "pm.test(\"Событие должно иметь статус PENDING при создании и статус CANCELED после выполнения запроса\", function () {\r", - " pm.expect(source.state).equal(\"PENDING\");\r", - " pm.expect(target.state).equal(\"CANCELED\");\r", - "});" + "pm.sendRequest({\r", + " url: pm.collectionVariables.get(\"baseUrl\") + \"/compilations/\" + compId,\r", + " method: 'GET',\r", + " }, (error, response) => {\r", + " \r", + " pm.test(\"Событие должно быть найдено в подборке после добавления\", function () {\r", + " pm.expect(response.json().events[0].id).equal(eventId);\r", + " });\r", + " });" ], "type": "text/javascript" } @@ -10654,50 +14309,28 @@ ], "request": { "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "Accept", - "value": "application/json" - } - ], + "header": [], "body": { "mode": "raw", - "raw": "{{request_body}}", - "options": { - "raw": { - "language": "json" - } - } + "raw": "{{request_body}}" }, "url": { - "raw": "{{baseUrl}}/users/:userId/events/:eventId", + "raw": "{{baseUrl}}/admin/compilations/:compId", "host": [ "{{baseUrl}}" ], "path": [ - "users", - ":userId", - "events", - ":eventId" + "admin", + "compilations", + ":compId" ], "variable": [ { - "key": "userId", - "value": "{{uid}}", - "description": "(Required) id текущего пользователя" - }, - { - "key": "eventId", - "value": "{{eid}}", - "description": "(Required) id отменяемого события" + "key": "compId", + "value": "{{compid}}" } ] - }, - "description": "Обратите внимание: Отменить можно только событие в состоянии ожидания модерации." + } }, "response": [] } @@ -10994,4 +14627,4 @@ "value": "" } ] -} \ No newline at end of file +} From cfc2bdd66031cbfe7134299e32ca89775b1c99f8 Mon Sep 17 00:00:00 2001 From: Ilia Egorov Date: Sat, 17 Jan 2026 10:42:36 +0300 Subject: [PATCH 3/3] feat: add compilations --- .../CompilationsAdminController.java | 18 +-- .../CompilationsPublicController.java | 20 +-- .../mapper/CompilationsMapper.java | 22 ++- .../repository/CompilationsRepository.java | 7 +- .../service/CompilationsPublicGetRequest.java | 6 +- .../service/CompilationsService.java | 5 +- .../service/CompilationsServiceImpl.java | 127 +++++++++--------- .../event/service/EventServiceImpl.java | 82 ++++++----- .../handler/GlobalExceptionHandler.java | 2 +- ...ParticipationRequestPrivateController.java | 10 +- .../repository/ConfirmedRequestsCount.java | 8 ++ .../ParticipationRequestRepository.java | 28 ++++ 12 files changed, 196 insertions(+), 139 deletions(-) create mode 100644 main-service/src/main/java/ru/practicum/request/repository/ConfirmedRequestsCount.java diff --git a/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java index 85872e1..3ffd8fa 100644 --- a/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java +++ b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java @@ -1,15 +1,18 @@ package ru.practicum.compilation.controller; import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; + import ru.practicum.compilation.dto.CompilationDto; import ru.practicum.compilation.dto.NewCompilationDto; import ru.practicum.compilation.dto.UpdateCompilationRequest; import ru.practicum.compilation.service.CompilationsService; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + @Slf4j @RestController @RequiredArgsConstructor @@ -19,8 +22,7 @@ public class CompilationsAdminController { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public CompilationDto saveCompilation( - @RequestBody @Valid NewCompilationDto newCompilationDto) { + public CompilationDto saveCompilation(@RequestBody @Valid NewCompilationDto newCompilationDto) { log.info("Admin save compilation requested with body={}", newCompilationDto); return compService.save(newCompilationDto); } @@ -33,8 +35,8 @@ public void deleteCompilation(@PathVariable long compId) { } @PatchMapping("/{compId}") - public CompilationDto updateCompilation(@PathVariable long compId, - @RequestBody @Valid UpdateCompilationRequest updateRequest) { + public CompilationDto updateCompilation( + @PathVariable long compId, @RequestBody @Valid UpdateCompilationRequest updateRequest) { log.info("Admin update compilation requested with id={}, body={}", compId, updateRequest); return compService.update(compId, updateRequest); } diff --git a/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java index fc70542..0f6be83 100644 --- a/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java +++ b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java @@ -1,14 +1,15 @@ package ru.practicum.compilation.controller; +import java.util.Collection; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; import ru.practicum.compilation.dto.CompilationDto; import ru.practicum.compilation.service.CompilationsPublicGetRequest; import ru.practicum.compilation.service.CompilationsService; -import java.util.Collection; +import org.springframework.web.bind.annotation.*; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Slf4j @RestController @@ -18,11 +19,12 @@ public class CompilationsPublicController { private final CompilationsService compService; @GetMapping - public Collection getCompilations(@RequestParam boolean pinned, - @RequestParam(defaultValue = "0") int from, - @RequestParam(defaultValue = "10") int size - ) { - CompilationsPublicGetRequest getRequest = new CompilationsPublicGetRequest(pinned, from, size); + public Collection getCompilations( + @RequestParam(required = false) Boolean pinned, + @RequestParam(defaultValue = "0") int from, + @RequestParam(defaultValue = "10") int size) { + CompilationsPublicGetRequest getRequest = + new CompilationsPublicGetRequest(pinned, from, size); log.info("Public get compilations requested with params= {}", getRequest); return compService.findAll(getRequest); } diff --git a/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java index e395cbd..4424c0d 100644 --- a/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java +++ b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java @@ -1,6 +1,8 @@ package ru.practicum.compilation.mapper; -import lombok.experimental.UtilityClass; +import java.util.List; +import java.util.Set; + import ru.practicum.compilation.dto.CompilationDto; import ru.practicum.compilation.dto.NewCompilationDto; import ru.practicum.compilation.dto.UpdateCompilationRequest; @@ -8,19 +10,13 @@ import ru.practicum.event.dto.EventShortDto; import ru.practicum.event.model.Event; -import java.util.List; -import java.util.Set; +import lombok.experimental.UtilityClass; @UtilityClass public class CompilationsMapper { public Compilation mapToEntity(NewCompilationDto newCompilationDto, Set events) { - return new Compilation( - null, - newCompilationDto.title(), - newCompilationDto.pinned(), - events - ); + return new Compilation(null, newCompilationDto.title(), newCompilationDto.pinned(), events); } public CompilationDto mapToDto(Compilation compilation, List events) { @@ -28,13 +24,11 @@ public CompilationDto mapToDto(Compilation compilation, List even events, compilation.getId(), Boolean.TRUE.equals(compilation.getPinned()), - compilation.getTitle() - ); + compilation.getTitle()); } - public void updateEntity(Compilation compilation, - UpdateCompilationRequest updateRequest, - Set events) { + public void updateEntity( + Compilation compilation, UpdateCompilationRequest updateRequest, Set events) { if (updateRequest.hasTitle()) { compilation.setTitle(updateRequest.title()); diff --git a/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java index 31804fd..3f6d509 100644 --- a/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java +++ b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java @@ -1,12 +1,13 @@ package ru.practicum.compilation.repository; +import java.util.Optional; + +import ru.practicum.compilation.model.Compilation; + import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.compilation.model.Compilation; - -import java.util.Optional; public interface CompilationsRepository extends JpaRepository { diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java index 09a0827..476e56a 100644 --- a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java @@ -3,7 +3,11 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; -public record CompilationsPublicGetRequest(boolean pinned, int from, int size) { +public record CompilationsPublicGetRequest(Boolean pinned, int from, int size) { + + public CompilationsPublicGetRequest { + if (pinned == null) pinned = false; + } public Pageable getPageable() { int page = from / size; diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java index 98a36ad..1427a38 100644 --- a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java @@ -1,12 +1,13 @@ package ru.practicum.compilation.service; +import java.util.Collection; + import jakarta.validation.Valid; + import ru.practicum.compilation.dto.CompilationDto; import ru.practicum.compilation.dto.NewCompilationDto; import ru.practicum.compilation.dto.UpdateCompilationRequest; -import java.util.Collection; - public interface CompilationsService { Collection findAll(CompilationsPublicGetRequest getRequest); diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java index e4ef380..32ffbe8 100644 --- a/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java @@ -1,11 +1,12 @@ package ru.practicum.compilation.service; +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Page; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import ru.practicum.client.StatsClient; import ru.practicum.compilation.dto.CompilationDto; import ru.practicum.compilation.dto.NewCompilationDto; @@ -13,58 +14,58 @@ import ru.practicum.compilation.mapper.CompilationsMapper; import ru.practicum.compilation.model.Compilation; import ru.practicum.compilation.repository.CompilationsRepository; +import ru.practicum.dto.ViewStatsDto; import ru.practicum.event.dto.EventShortDto; import ru.practicum.event.mapper.EventMapper; import ru.practicum.event.model.Event; import ru.practicum.event.repository.EventRepository; import ru.practicum.exception.ConflictException; import ru.practicum.exception.NotFoundException; +import ru.practicum.request.repository.ParticipationRequestRepository; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; @Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) public class CompilationsServiceImpl implements CompilationsService { - + private static final LocalDateTime MINIMAL_LOCAL_DATE_TIME = + LocalDateTime.of(1000, 1, 1, 0, 0, 0); private final CompilationsRepository compRepository; private final EventRepository eventRepository; - private final RequestRepository requestRepository; + private final ParticipationRequestRepository requestRepository; private final StatsClient statsClient; @Override public Collection findAll(CompilationsPublicGetRequest getRequest) { - Page page = compRepository.findAllByPinned( - getRequest.pinned(), - getRequest.getPageable() - ); + Page page = + compRepository.findAllByPinned(getRequest.pinned(), getRequest.getPageable()); - Set events = page.stream() - .flatMap(c -> c.getEvents().stream()) - .collect(Collectors.toSet()); + Set events = + page.stream().flatMap(c -> c.getEvents().stream()).collect(Collectors.toSet()); Map confirmedRequests = getConfirmedRequests(events); Map views = getViews(events); - return page.stream() - .map(c -> toDto(c, confirmedRequests, views)) - .toList(); + return page.stream().map(c -> toDto(c, confirmedRequests, views)).toList(); } @Override public CompilationDto findById(long compId) { - Compilation compilation = compRepository.findWithEventsById(compId) - .orElseThrow( - NotFoundException.supplier( - "Compilation with id=%d was not found", compId - ) - ); + Compilation compilation = + compRepository + .findWithEventsById(compId) + .orElseThrow( + NotFoundException.supplier( + "Compilation with id=%d was not found", compId)); Set events = compilation.getEvents(); @@ -80,16 +81,12 @@ public CompilationDto save(NewCompilationDto newCompilationDto) { if (compRepository.existsByTitle(newCompilationDto.title())) { throw new ConflictException( - "Compilation with title=" + newCompilationDto.title() + " already exists" - ); + "Compilation with title=" + newCompilationDto.title() + " already exists"); } Set events = getEvents(newCompilationDto.events()); - Compilation compilation = CompilationsMapper.mapToEntity( - newCompilationDto, - events - ); + Compilation compilation = CompilationsMapper.mapToEntity(newCompilationDto, events); Compilation saved = compRepository.save(compilation); @@ -103,9 +100,7 @@ public CompilationDto save(NewCompilationDto newCompilationDto) { @Transactional public void deleteById(long compId) { if (!compRepository.existsById(compId)) { - throw new NotFoundException( - "Compilation with id=" + compId + " was not found" - ); + throw new NotFoundException("Compilation with id=" + compId + " was not found"); } compRepository.deleteById(compId); @@ -115,12 +110,12 @@ public void deleteById(long compId) { @Transactional public CompilationDto update(long compId, UpdateCompilationRequest updateRequest) { - Compilation compilation = compRepository.findWithEventsById(compId) - .orElseThrow( - NotFoundException.supplier( - "Compilation with id=%d was not found", compId - ) - ); + Compilation compilation = + compRepository + .findWithEventsById(compId) + .orElseThrow( + NotFoundException.supplier( + "Compilation with id=%d was not found", compId)); Set events = null; if (updateRequest.hasEvents()) { @@ -140,17 +135,16 @@ public CompilationDto update(long compId, UpdateCompilationRequest updateRequest } private CompilationDto toDto( - Compilation compilation, - Map confirmedRequests, - Map views - ) { - List events = compilation.getEvents().stream() - .map(event -> EventMapper.mapToShortDto( - event, - confirmedRequests.getOrDefault(event.getId(), 0L), - views.get(event.getId()) - )) - .toList(); + Compilation compilation, Map confirmedRequests, Map views) { + List events = + compilation.getEvents().stream() + .map( + event -> + EventMapper.mapToShortDto( + event, + confirmedRequests.getOrDefault(event.getId(), 0L), + views.get(event.getId()))) + .toList(); return CompilationsMapper.mapToDto(compilation, events); } @@ -174,9 +168,7 @@ private Map getConfirmedRequests(Set events) { return Map.of(); } - List eventIds = events.stream() - .map(Event::getId) - .toList(); + List eventIds = events.stream().map(Event::getId).toList(); return requestRepository.countConfirmedByEventIds(eventIds); } @@ -186,10 +178,25 @@ private Map getViews(Set events) { return Map.of(); } - List eventIds = events.stream() - .map(Event::getId) - .toList(); + List uris = events.stream().map(e -> "/events/%s".formatted(e.getId())).toList(); + + try { + List stats = + statsClient.getStats(MINIMAL_LOCAL_DATE_TIME, LocalDateTime.now(), uris, true); - return statsClient.getViews(eventIds); + return stats.stream() + .collect( + Collectors.toMap( + s -> { + String uri = s.uri(); + return Long.parseLong( + uri.substring(uri.lastIndexOf("/") + 1)); + }, + ViewStatsDto::hits)); + + } catch (Exception e) { + log.error("Error during getting stats for events", e); + return Map.of(); + } } } diff --git a/main-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java b/main-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java index f5a456e..b19d9db 100644 --- a/main-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java +++ b/main-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java @@ -23,6 +23,7 @@ import ru.practicum.exception.IllegalEventUpdateException; import ru.practicum.exception.NotFoundException; import ru.practicum.exception.ValidationException; +import ru.practicum.request.repository.ParticipationRequestRepository; import ru.practicum.user.model.User; import ru.practicum.user.repository.UserRepository; @@ -41,13 +42,12 @@ public class EventServiceImpl implements EventService { private static final Duration MIN_TIME_BEFORE_EVENT = Duration.ofHours(2); private static final LocalDateTime MINIMAL_LOCAL_DATE_TIME = LocalDateTime.of(1000, 1, 1, 0, 0, 0); - private static final LocalDateTime MAXIMUM_LOCAL_DATE_TIME = - LocalDateTime.of(9999, 1, 1, 0, 0, 0); private static final String EVENTS_URI = "/events/%d"; private final EventRepository eventRepository; private final UserRepository userRepository; private final CategoryRepository categoryRepository; private final StatsClient statsClient; + private final ParticipationRequestRepository requestRepository; @Override public EventFullDto getById(Long eventId, HttpServletRequest request) { @@ -62,8 +62,10 @@ public EventFullDto getById(Long eventId, HttpServletRequest request) { ViewStatsDto statsDto = getStatsForEvent(event, uri); + Map confirmedRequests = getConfirmedRequests(Set.of(event)); + return EventMapper.mapToFullDto( - event, 0, statsDto.hits()); // change 0 to actual number of requests + event, confirmedRequests.getOrDefault(event.getId(), 0L), statsDto.hits()); } @Override @@ -74,12 +76,10 @@ public Collection getEvents(EventsPublicGetRequest getRequest) { statsClient.hit(getRequest.httpRequest()); - LocalDateTime statsFrom = - getRequest.hasRangeStart() ? getRequest.rangeStart() : LocalDateTime.now(); - LocalDateTime statsTo = - getRequest.hasRangeEnd() ? getRequest.rangeEnd() : MAXIMUM_LOCAL_DATE_TIME; + Map statsForEvents = getStatsMapForEvents(events); - Map statsForEvents = getStatsMapForEvents(events, statsFrom, statsTo); + Map confirmedRequests = + getConfirmedRequests(events.stream().collect(Collectors.toSet())); List eventsList = events.stream() @@ -87,10 +87,8 @@ public Collection getEvents(EventsPublicGetRequest getRequest) { event -> EventMapper.mapToShortDto( event, - 0, - statsForEvents.get( - event.getId()))) // change 0 to actual - // number of requests + confirmedRequests.getOrDefault(event.getId(), 0L), + statsForEvents.get(event.getId()))) .toList(); if (EventSortBy.VIEWS.equals(getRequest.sort())) { @@ -106,18 +104,18 @@ public Collection getEvents(EventsAdminGetRequest getRequest) { eventRepository.findAll( EventRepository.createPredicate(getRequest), getRequest.getPageable()); - Map statsForEvents = - getStatsMapForEvents(events, MINIMAL_LOCAL_DATE_TIME, MAXIMUM_LOCAL_DATE_TIME); + Map statsForEvents = getStatsMapForEvents(events); + + Map confirmedRequests = + getConfirmedRequests(events.stream().collect(Collectors.toSet())); return events.stream() .map( event -> EventMapper.mapToFullDto( event, - 0, - statsForEvents.get( - event.getId()))) // change 0 to actual number of - // requests + confirmedRequests.getOrDefault(event.getId(), 0L), + statsForEvents.get(event.getId()))) .toList(); } @@ -127,18 +125,18 @@ public Collection getEvents(EventsPrivateGetRequest getRequest) { Page events = eventRepository.findByInitiator_Id(getRequest.userId(), getRequest.getPageable()); - Map statsForEvents = - getStatsMapForEvents(events, MINIMAL_LOCAL_DATE_TIME, MAXIMUM_LOCAL_DATE_TIME); + Map statsForEvents = getStatsMapForEvents(events); + + Map confirmedRequests = + getConfirmedRequests(events.stream().collect(Collectors.toSet())); return events.stream() .map( event -> EventMapper.mapToShortDto( event, - 0, - statsForEvents.get( - event.getId()))) // change 0 to actual number of - // requests + confirmedRequests.getOrDefault(event.getId(), 0L), + statsForEvents.get(event.getId()))) .toList(); } @@ -173,8 +171,10 @@ public EventFullDto getByUserById(Long userId, Long eventId) { ViewStatsDto statsDto = getStatsForEvent(event, EVENTS_URI.formatted(eventId)); + Map confirmedRequests = getConfirmedRequests(Set.of(event)); + return EventMapper.mapToFullDto( - event, 0, statsDto.hits()); // change 0 to actual number of requests + event, confirmedRequests.getOrDefault(event.getId(), 0L), statsDto.hits()); } @Override @@ -198,7 +198,10 @@ public EventFullDto updateEvent(Long eventId, UpdateEventAdminRequest updateRequ Event saved = eventRepository.save(event); - return EventMapper.mapToFullDto(saved, 0, null); // change 0 to actual number of requests + Map confirmedRequests = getConfirmedRequests(Set.of(event)); + + return EventMapper.mapToFullDto( + saved, confirmedRequests.getOrDefault(event.getId(), 0L), null); } @Override @@ -228,14 +231,16 @@ public EventFullDto updateEventByUser( Event saved = eventRepository.save(event); - return EventMapper.mapToFullDto(saved, 0, null); // change 0 to actual number of requests + Map confirmedRequests = getConfirmedRequests(Set.of(event)); + + return EventMapper.mapToFullDto( + saved, confirmedRequests.getOrDefault(event.getId(), 0L), null); } - private Map getStatsMapForEvents( - Page events, LocalDateTime from, LocalDateTime to) { + private Map getStatsMapForEvents(Page events) { List listOfUris = events.stream().map(event -> EVENTS_URI.formatted(event.getId())).toList(); - return getStatsForEvents(listOfUris, from, to).stream() + return getStatsForEvents(listOfUris).stream() .collect( Collectors.toMap( statsDto -> @@ -247,10 +252,19 @@ private Map getStatsMapForEvents( ViewStatsDto::hits)); } - private List getStatsForEvents( - List uris, LocalDateTime from, LocalDateTime to) { + private Map getConfirmedRequests(Set events) { + if (events.isEmpty()) { + return Map.of(); + } + + List eventIds = events.stream().map(Event::getId).toList(); + + return requestRepository.countConfirmedByEventIds(eventIds); + } + + private List getStatsForEvents(List uris) { try { - return statsClient.getStats(from, to, uris, true); + return statsClient.getStats(MINIMAL_LOCAL_DATE_TIME, LocalDateTime.now(), uris, true); } catch (Exception e) { log.error("Error during getting stats for events", e); } @@ -264,7 +278,7 @@ private ViewStatsDto getStatsForEvent(Event event, String uri) { statsClient .getStats( MINIMAL_LOCAL_DATE_TIME, - MAXIMUM_LOCAL_DATE_TIME, + LocalDateTime.now(), List.of(uri), true) .getFirst(); diff --git a/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java b/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java index 1641496..7a51bc4 100644 --- a/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java +++ b/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java @@ -8,9 +8,9 @@ import jakarta.validation.ConstraintViolationException; -import ru.practicum.exception.*; import ru.practicum.exception.dto.ApiError; import ru.practicum.exception.dto.Violation; +import ru.practicum.exception.*; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; diff --git a/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestPrivateController.java b/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestPrivateController.java index 37f49f3..da28856 100644 --- a/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestPrivateController.java +++ b/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestPrivateController.java @@ -5,13 +5,8 @@ import ru.practicum.request.dto.ParticipationRequestDto; import ru.practicum.request.service.ParticipationRequestService; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PatchMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -25,6 +20,7 @@ public class ParticipationRequestPrivateController { private final ParticipationRequestService requestService; @PostMapping + @ResponseStatus(HttpStatus.CREATED) public ParticipationRequestDto createRequest( @PathVariable Long userId, @RequestParam Long eventId) { log.info("Create participation request userId={}, eventId={}", userId, eventId); diff --git a/main-service/src/main/java/ru/practicum/request/repository/ConfirmedRequestsCount.java b/main-service/src/main/java/ru/practicum/request/repository/ConfirmedRequestsCount.java new file mode 100644 index 0000000..64b26ad --- /dev/null +++ b/main-service/src/main/java/ru/practicum/request/repository/ConfirmedRequestsCount.java @@ -0,0 +1,8 @@ +package ru.practicum.request.repository; + +public interface ConfirmedRequestsCount { + + Long getEventId(); + + Long getCnt(); +} diff --git a/main-service/src/main/java/ru/practicum/request/repository/ParticipationRequestRepository.java b/main-service/src/main/java/ru/practicum/request/repository/ParticipationRequestRepository.java index 4678b92..ad85ac6 100644 --- a/main-service/src/main/java/ru/practicum/request/repository/ParticipationRequestRepository.java +++ b/main-service/src/main/java/ru/practicum/request/repository/ParticipationRequestRepository.java @@ -2,11 +2,15 @@ import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import ru.practicum.request.model.EventRequestStatus; import ru.practicum.request.model.ParticipationRequest; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; public interface ParticipationRequestRepository extends JpaRepository { boolean existsByEvent_IdAndRequester_Id(Long eventId, Long requesterId); @@ -20,4 +24,28 @@ public interface ParticipationRequestRepository extends JpaRepository findAllByIdInAndEvent_Id(Collection ids, Long eventId); List findAllByEvent_IdAndStatus(Long eventId, EventRequestStatus status); + + @Query( + """ + select pr.event.id as eventId, count(pr.id) as cnt + from ParticipationRequest pr + where pr.status = :status + and pr.event.id in :eventIds + group by pr.event.id + """) + List countByEventIdsAndStatusRaw( + @Param("eventIds") Collection eventIds, + @Param("status") EventRequestStatus status); + + default Map countConfirmedByEventIds(Collection eventIds) { + if (eventIds == null || eventIds.isEmpty()) { + return Map.of(); + } + + return countByEventIdsAndStatusRaw(eventIds, EventRequestStatus.CONFIRMED).stream() + .collect( + Collectors.toMap( + ConfirmedRequestsCount::getEventId, + ConfirmedRequestsCount::getCnt)); + } }