diff --git a/README.MD b/README.MD
new file mode 100644
index 0000000..58ec58d
--- /dev/null
+++ b/README.MD
@@ -0,0 +1,22 @@
+# Explore with me
+
+Repository for explore with me app - social network for sharing interesting events with users and find
+companies to participate in them. Written in java.
+
+# Contributors
+
+[LightInTheFire](https://github.com/LightInTheFire)
+[Basarus](https://github.com/Basarus)
+[Kreidl](https://github.com/Kreidl)
+
+# Main service db schema
+
+
+
+
+
+# Stats service db schema
+
+
+
+
diff --git a/assets/readme/main-service-schema.svg b/assets/readme/main-service-schema.svg
new file mode 100644
index 0000000..96f2dbc
--- /dev/null
+++ b/assets/readme/main-service-schema.svg
@@ -0,0 +1,20 @@
+1 * 1 * 1 * 1 * 1 * 1 * category id BIGINT name VARCHAR(50) users id BIGINT name VARCHAR(250) email VARCHAR(254) event id BIGINT title VARCHAR(120) annotation VARCHAR(2000) description VARCHAR(7000) event_date TIMESTAMP created_on TIMESTAMP published_on TIMESTAMP paid BOOLEAN participant_limit INT request_moderation BOOLEAN state VARCHAR(20) location_lat NUMERIC(9,6) location_lon NUMERIC(9,6) category_id BIGINT initiator_id BIGINT compilation id BIGINT title VARCHAR(50) pinned BOOLEAN compilation_events compilation_id BIGINT event_id BIGINT participation_request id BIGINT created TIMESTAMP status VARCHAR(20) event_id BIGINT requester_id BIGINT
\ No newline at end of file
diff --git a/assets/readme/stats-service-schema.svg b/assets/readme/stats-service-schema.svg
new file mode 100644
index 0000000..1bc90b0
--- /dev/null
+++ b/assets/readme/stats-service-schema.svg
@@ -0,0 +1,20 @@
+stat id BIGINT app VARCHAR(255) uri VARCHAR(512) ip VARCHAR(45) created TIMESTAMP
\ No newline at end of file
diff --git a/main-service/pom.xml b/main-service/pom.xml
index a03c392..75b3a9b 100644
--- a/main-service/pom.xml
+++ b/main-service/pom.xml
@@ -51,8 +51,55 @@
org.postgresql
postgresql
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+
+ com.querydsl
+ querydsl-jpa
+ jakarta
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ com.mysema.maven
+ apt-maven-plugin
+ 1.1.3
+
+
+
+ process
+
+
+ target/generated-sources/java
+ com.querydsl.apt.jpa.JPAAnnotationProcessor
+
+
+
+
+
+ com.querydsl
+ querydsl-apt
+ jakarta
+ ${querydsl.version}
+
+
+
+
+
+
dev
diff --git a/main-service/src/main/java/ru/practicum/category/controller/CategoryAdminController.java b/main-service/src/main/java/ru/practicum/category/controller/CategoryAdminController.java
new file mode 100644
index 0000000..5495dcd
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/controller/CategoryAdminController.java
@@ -0,0 +1,45 @@
+package ru.practicum.category.controller;
+
+import jakarta.validation.Valid;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.category.dto.NewCategoryDto;
+import ru.practicum.category.service.CategoryService;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/admin/categories")
+public class CategoryAdminController {
+ private final CategoryService categoryService;
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public CategoryDto createCategory(@Valid @RequestBody NewCategoryDto newCategoryDto) {
+ log.info("Create category requested {}", newCategoryDto);
+ return categoryService.createCategory(newCategoryDto);
+ }
+
+ @PatchMapping("/{catId}")
+ public CategoryDto updateCategory(
+ @PathVariable Long catId, @Valid @RequestBody NewCategoryDto updateCategoryDto) {
+ log.info(
+ "Update category with id={} requested, updated category {}",
+ catId,
+ updateCategoryDto);
+ return categoryService.updateCategory(catId, updateCategoryDto);
+ }
+
+ @DeleteMapping("/{catId}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteCategory(@PathVariable Long catId) {
+ log.info("Delete category with id={} requested", catId);
+ categoryService.deleteCategoryById(catId);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/category/controller/CategoryPublicController.java b/main-service/src/main/java/ru/practicum/category/controller/CategoryPublicController.java
new file mode 100644
index 0000000..1ffa2f0
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/controller/CategoryPublicController.java
@@ -0,0 +1,38 @@
+package ru.practicum.category.controller;
+
+import java.util.Collection;
+
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.category.service.CategoryService;
+
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/categories")
+public class CategoryPublicController {
+ private final CategoryService categoryService;
+
+ @GetMapping
+ public Collection getAllCategoriesPaged(
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size) {
+ log.info("View categories page by page requested from={}, size={}", from, size);
+ return categoryService.getAllCategoriesPaged(from, size);
+ }
+
+ @GetMapping("/{id}")
+ public CategoryDto getCategoryById(@PathVariable Long id) {
+ log.info("View category with id={} requested", id);
+ return categoryService.getCategoryById(id);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/category/dto/CategoryDto.java b/main-service/src/main/java/ru/practicum/category/dto/CategoryDto.java
new file mode 100644
index 0000000..d4bf840
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/dto/CategoryDto.java
@@ -0,0 +1,3 @@
+package ru.practicum.category.dto;
+
+public record CategoryDto(Long id, String name) {}
diff --git a/main-service/src/main/java/ru/practicum/category/dto/NewCategoryDto.java b/main-service/src/main/java/ru/practicum/category/dto/NewCategoryDto.java
new file mode 100644
index 0000000..3d04b86
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/dto/NewCategoryDto.java
@@ -0,0 +1,6 @@
+package ru.practicum.category.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+public record NewCategoryDto(@Size(min = 1, max = 50) @NotBlank String name) {}
diff --git a/main-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java b/main-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java
new file mode 100644
index 0000000..68035b6
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/mapper/CategoryMapper.java
@@ -0,0 +1,18 @@
+package ru.practicum.category.mapper;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.category.dto.NewCategoryDto;
+import ru.practicum.category.model.Category;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class CategoryMapper {
+ public Category mapToEntity(NewCategoryDto newCategoryDto) {
+ return new Category(null, newCategoryDto.name());
+ }
+
+ public CategoryDto mapToDto(Category category) {
+ return new CategoryDto(category.getId(), category.getName());
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/category/model/Category.java b/main-service/src/main/java/ru/practicum/category/model/Category.java
new file mode 100644
index 0000000..203610c
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/model/Category.java
@@ -0,0 +1,24 @@
+package ru.practicum.category.model;
+
+import jakarta.persistence.*;
+
+import lombok.experimental.FieldDefaults;
+import lombok.*;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "category")
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Category {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+
+ @Column(nullable = false, unique = true)
+ String name;
+}
diff --git a/main-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java b/main-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java
new file mode 100644
index 0000000..27a0d50
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/repository/CategoryRepository.java
@@ -0,0 +1,7 @@
+package ru.practicum.category.repository;
+
+import ru.practicum.category.model.Category;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CategoryRepository extends JpaRepository {}
diff --git a/main-service/src/main/java/ru/practicum/category/service/CategoryService.java b/main-service/src/main/java/ru/practicum/category/service/CategoryService.java
new file mode 100644
index 0000000..69d5377
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/service/CategoryService.java
@@ -0,0 +1,18 @@
+package ru.practicum.category.service;
+
+import java.util.List;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.category.dto.NewCategoryDto;
+
+public interface CategoryService {
+ CategoryDto createCategory(NewCategoryDto newCategoryDto);
+
+ List getAllCategoriesPaged(int from, int size);
+
+ CategoryDto getCategoryById(Long id);
+
+ void deleteCategoryById(Long id);
+
+ CategoryDto updateCategory(Long catId, NewCategoryDto updateCategoryDto);
+}
diff --git a/main-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java b/main-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java
new file mode 100644
index 0000000..2697af3
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/category/service/CategoryServiceImpl.java
@@ -0,0 +1,69 @@
+package ru.practicum.category.service;
+
+import java.util.List;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.category.dto.NewCategoryDto;
+import ru.practicum.category.mapper.CategoryMapper;
+import ru.practicum.category.model.Category;
+import ru.practicum.category.repository.CategoryRepository;
+import ru.practicum.exception.NotFoundException;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import lombok.RequiredArgsConstructor;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CategoryServiceImpl implements CategoryService {
+ private final CategoryRepository categoryRepository;
+
+ @Override
+ public CategoryDto createCategory(NewCategoryDto newCategoryDto) {
+ Category category = CategoryMapper.mapToEntity(newCategoryDto);
+ return CategoryMapper.mapToDto(categoryRepository.save(category));
+ }
+
+ @Override
+ public CategoryDto updateCategory(Long catId, NewCategoryDto updateCategoryDto) {
+ Category category =
+ categoryRepository
+ .findById(catId)
+ .orElseThrow(
+ NotFoundException.supplier(
+ "Category with id=%d was not found", catId));
+ category.setName(updateCategoryDto.name());
+ return CategoryMapper.mapToDto(categoryRepository.save(category));
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getAllCategoriesPaged(int from, int size) {
+ Pageable pageable = PageRequest.of(from, size);
+ return categoryRepository.findAll(pageable).stream().map(CategoryMapper::mapToDto).toList();
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public CategoryDto getCategoryById(Long id) {
+ Category category =
+ categoryRepository
+ .findById(id)
+ .orElseThrow(
+ NotFoundException.supplier(
+ "Category with id=%d was not found", id));
+ return CategoryMapper.mapToDto(category);
+ }
+
+ @Override
+ public void deleteCategoryById(Long id) {
+ categoryRepository
+ .findById(id)
+ .orElseThrow(NotFoundException.supplier("Category with id=%d was not found", id));
+ categoryRepository.deleteById(id);
+ }
+}
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..3ffd8fa
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsAdminController.java
@@ -0,0 +1,43 @@
+package ru.practicum.compilation.controller;
+
+import jakarta.validation.Valid;
+
+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
+@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..fbf0812
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/controller/CompilationsPublicController.java
@@ -0,0 +1,42 @@
+package ru.practicum.compilation.controller;
+
+import java.util.Collection;
+
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+
+import ru.practicum.compilation.dto.CompilationDto;
+import ru.practicum.compilation.service.CompilationsPublicGetRequest;
+import ru.practicum.compilation.service.CompilationsService;
+
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/compilations")
+public class CompilationsPublicController {
+ private final CompilationsService compService;
+
+ @GetMapping
+ public Collection getCompilations(
+ @RequestParam(required = false) Boolean pinned,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive 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/dto/CompilationDto.java b/main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java
new file mode 100644
index 0000000..3cc0c11
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java
@@ -0,0 +1,7 @@
+package ru.practicum.compilation.dto;
+
+import java.util.List;
+
+import ru.practicum.event.dto.EventShortDto;
+
+public record CompilationDto(List events, Long id, boolean pinned, String title) {}
diff --git a/main-service/src/main/java/ru/practicum/compilation/dto/NewCompilationDto.java b/main-service/src/main/java/ru/practicum/compilation/dto/NewCompilationDto.java
new file mode 100644
index 0000000..63885a3
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/dto/NewCompilationDto.java
@@ -0,0 +1,14 @@
+package ru.practicum.compilation.dto;
+
+import java.util.List;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+public record NewCompilationDto(
+ List events, Boolean pinned, @Size(min = 1, max = 50) @NotBlank String title) {
+ public NewCompilationDto {
+ if (pinned == null) pinned = false;
+ if (events == null) events = List.of();
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java b/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java
new file mode 100644
index 0000000..9b18070
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java
@@ -0,0 +1,21 @@
+package ru.practicum.compilation.dto;
+
+import java.util.List;
+
+import jakarta.validation.constraints.Size;
+
+public record UpdateCompilationRequest(
+ List events, Boolean pinned, @Size(min = 1, max = 50) String title) {
+
+ public boolean hasEvents() {
+ return events != null;
+ }
+
+ public boolean hasPinned() {
+ return pinned != null;
+ }
+
+ public boolean hasTitle() {
+ return title != null && !title.isBlank();
+ }
+}
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..4424c0d
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationsMapper.java
@@ -0,0 +1,45 @@
+package ru.practicum.compilation.mapper;
+
+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;
+import ru.practicum.compilation.model.Compilation;
+import ru.practicum.event.dto.EventShortDto;
+import ru.practicum.event.model.Event;
+
+import lombok.experimental.UtilityClass;
+
+@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/model/Compilation.java b/main-service/src/main/java/ru/practicum/compilation/model/Compilation.java
new file mode 100644
index 0000000..7fc3523
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/model/Compilation.java
@@ -0,0 +1,39 @@
+package ru.practicum.compilation.model;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import jakarta.persistence.*;
+
+import ru.practicum.event.model.Event;
+
+import lombok.experimental.FieldDefaults;
+import lombok.*;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "compilation")
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Compilation {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id", nullable = false)
+ Long id;
+
+ @Column(name = "title", nullable = false)
+ String title;
+
+ @Column(name = "pinned")
+ Boolean pinned;
+
+ @ManyToMany
+ @JoinTable(
+ name = "compilation_events",
+ joinColumns = @JoinColumn(name = "compilation_id"),
+ inverseJoinColumns = @JoinColumn(name = "event_id"))
+ Set events = new LinkedHashSet<>();
+}
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..3f6d509
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationsRepository.java
@@ -0,0 +1,20 @@
+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;
+
+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..476e56a
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsPublicGetRequest.java
@@ -0,0 +1,16 @@
+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 CompilationsPublicGetRequest {
+ if (pinned == null) pinned = false;
+ }
+
+ 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..1427a38
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsService.java
@@ -0,0 +1,21 @@
+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;
+
+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..e4e3d1c
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationsServiceImpl.java
@@ -0,0 +1,164 @@
+package ru.practicum.compilation.service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+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 ru.practicum.request.repository.ParticipationRequestRepository;
+
+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 final CompilationsRepository compRepository;
+ private final EventRepository eventRepository;
+ private final ParticipationRequestRepository requestRepository;
+
+ @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);
+
+ return page.stream().map(c -> toDto(c, confirmedRequests)).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);
+
+ return toDto(compilation, confirmedRequests);
+ }
+
+ @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);
+
+ return toDto(saved, confirmedRequests);
+ }
+
+ @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);
+
+ return toDto(updated, confirmedRequests);
+ }
+
+ private CompilationDto toDto(Compilation compilation, Map confirmedRequests) {
+ List events =
+ compilation.getEvents().stream()
+ .map(
+ event ->
+ EventMapper.mapToShortDto(
+ event,
+ confirmedRequests.getOrDefault(event.getId(), 0L),
+ null))
+ .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);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/config/DateTimeProperties.java b/main-service/src/main/java/ru/practicum/config/DateTimeProperties.java
new file mode 100644
index 0000000..79f229f
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/config/DateTimeProperties.java
@@ -0,0 +1,13 @@
+package ru.practicum.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Component
+@ConfigurationProperties(prefix = "app")
+public class DateTimeProperties {
+ @Getter @Setter private String dateTimeFormat = "yyyy-MM-dd HH:mm:ss";
+}
diff --git a/main-service/src/main/java/ru/practicum/config/JsonConfig.java b/main-service/src/main/java/ru/practicum/config/JsonConfig.java
new file mode 100644
index 0000000..82059fa
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/config/JsonConfig.java
@@ -0,0 +1,31 @@
+package ru.practicum.config;
+
+import java.time.format.DateTimeFormatter;
+
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+
+import lombok.RequiredArgsConstructor;
+
+@Configuration
+@RequiredArgsConstructor
+public class JsonConfig {
+ private final DateTimeProperties properties;
+
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
+ return builder -> {
+ builder.simpleDateFormat(properties.getDateTimeFormat());
+ builder.serializers(
+ new LocalDateTimeSerializer(
+ DateTimeFormatter.ofPattern(properties.getDateTimeFormat())));
+ builder.deserializers(
+ new LocalDateTimeDeserializer(
+ DateTimeFormatter.ofPattern(properties.getDateTimeFormat())));
+ };
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/config/WebConfig.java b/main-service/src/main/java/ru/practicum/config/WebConfig.java
new file mode 100644
index 0000000..6fcbd80
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/config/WebConfig.java
@@ -0,0 +1,38 @@
+package ru.practicum.config;
+
+import java.text.ParseException;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Locale;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.format.Formatter;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import lombok.RequiredArgsConstructor;
+
+@Configuration
+@RequiredArgsConstructor
+public class WebConfig implements WebMvcConfigurer {
+ private final DateTimeProperties properties;
+
+ @Override
+ public void addFormatters(FormatterRegistry registry) {
+ registry.addFormatter(
+ new Formatter() {
+
+ @Override
+ public String print(LocalDateTime object, Locale locale) {
+ return object.format(
+ DateTimeFormatter.ofPattern(properties.getDateTimeFormat()));
+ }
+
+ @Override
+ public LocalDateTime parse(String text, Locale locale) throws ParseException {
+ return LocalDateTime.parse(
+ text, DateTimeFormatter.ofPattern(properties.getDateTimeFormat()));
+ }
+ });
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/controller/EventAdminController.java b/main-service/src/main/java/ru/practicum/event/controller/EventAdminController.java
new file mode 100644
index 0000000..c0760d6
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/controller/EventAdminController.java
@@ -0,0 +1,53 @@
+package ru.practicum.event.controller;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+
+import ru.practicum.event.dto.EventFullDto;
+import ru.practicum.event.dto.UpdateEventAdminRequest;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.service.EventService;
+import ru.practicum.event.service.EventsAdminGetRequest;
+
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/admin/events")
+public class EventAdminController {
+ private final EventService eventService;
+
+ @GetMapping
+ public Collection getEventsFiltered(
+ @RequestParam(required = false) List users,
+ @RequestParam(required = false) List states,
+ @RequestParam(required = false) List categories,
+ @RequestParam(required = false) LocalDateTime rangeStart,
+ @RequestParam(required = false) LocalDateTime rangeEnd,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size) {
+ EventsAdminGetRequest getRequest =
+ new EventsAdminGetRequest(
+ users, states, categories, rangeStart, rangeEnd, from, size);
+ log.info("Admin get events requested with params= {}", getRequest);
+ return eventService.getEvents(getRequest);
+ }
+
+ @PatchMapping("/{eventId}")
+ public EventFullDto updateEvent(
+ @PathVariable Long eventId, @RequestBody @Valid UpdateEventAdminRequest updateRequest) {
+ log.info("Admin update event requested with body= {}", updateRequest);
+ return eventService.updateEvent(eventId, updateRequest);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/controller/EventPrivateController.java b/main-service/src/main/java/ru/practicum/event/controller/EventPrivateController.java
new file mode 100644
index 0000000..aee1200
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/controller/EventPrivateController.java
@@ -0,0 +1,67 @@
+package ru.practicum.event.controller;
+
+import java.util.Collection;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+
+import ru.practicum.event.dto.EventFullDto;
+import ru.practicum.event.dto.EventShortDto;
+import ru.practicum.event.dto.NewEventDto;
+import ru.practicum.event.dto.UpdateEventUserRequest;
+import ru.practicum.event.service.EventService;
+import ru.practicum.event.service.EventsPrivateGetRequest;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/users/{userId}/events")
+public class EventPrivateController {
+ private final EventService eventService;
+
+ @GetMapping()
+ public Collection getEventsOfUserPaged(
+ @PathVariable Long userId,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size) {
+ EventsPrivateGetRequest getRequest = new EventsPrivateGetRequest(userId, from, size);
+ log.info("Private get events requested with params= {}", getRequest);
+ return eventService.getEvents(getRequest);
+ }
+
+ @PostMapping()
+ @ResponseStatus(HttpStatus.CREATED)
+ public EventFullDto createEvent(
+ @PathVariable Long userId, @RequestBody @Valid NewEventDto newEventDto) {
+ log.info("Create event requested with body= {}", newEventDto);
+ return eventService.createEvent(userId, newEventDto);
+ }
+
+ @GetMapping("/{eventId}")
+ public EventFullDto getEvent(@PathVariable Long userId, @PathVariable Long eventId) {
+ log.info("Private get event requested with userId= {}, eventId= {}", userId, eventId);
+ return eventService.getByUserById(userId, eventId);
+ }
+
+ @PatchMapping("/{eventId}")
+ public EventFullDto updateEvent(
+ @PathVariable Long userId,
+ @PathVariable Long eventId,
+ @RequestBody @Valid UpdateEventUserRequest updateRequest) {
+ log.info(
+ "Private update event requested with userId= {}, eventId= {}, body = {}",
+ userId,
+ eventId,
+ updateRequest);
+ return eventService.updateEventByUser(userId, eventId, updateRequest);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/controller/EventPublicController.java b/main-service/src/main/java/ru/practicum/event/controller/EventPublicController.java
new file mode 100644
index 0000000..2f4d5ee
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/controller/EventPublicController.java
@@ -0,0 +1,69 @@
+package ru.practicum.event.controller;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+
+import ru.practicum.event.dto.EventFullDto;
+import ru.practicum.event.dto.EventShortDto;
+import ru.practicum.event.service.EventService;
+import ru.practicum.event.service.EventsPublicGetRequest;
+import ru.practicum.exception.ValidationException;
+
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/events")
+public class EventPublicController {
+ private final EventService eventService;
+
+ @GetMapping()
+ public Collection getEventsFiltered(
+ @RequestParam(required = false) String text,
+ @RequestParam(required = false) List categories,
+ @RequestParam(required = false) Boolean paid,
+ @RequestParam(required = false) LocalDateTime rangeStart,
+ @RequestParam(required = false) LocalDateTime rangeEnd,
+ @RequestParam(defaultValue = "false") boolean onlyAvailable,
+ @RequestParam(required = false) EventSortBy sort,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size,
+ HttpServletRequest request) {
+ EventsPublicGetRequest getRequest =
+ new EventsPublicGetRequest(
+ text,
+ categories,
+ paid,
+ rangeStart,
+ rangeEnd,
+ onlyAvailable,
+ sort,
+ from,
+ size,
+ request);
+ if (getRequest.hasRangeStart() && getRequest.hasRangeEnd()) {
+ if (getRequest.rangeEnd().isBefore(getRequest.rangeStart())) {
+ throw new ValidationException("End date must be before start date");
+ }
+ }
+ log.info("Public get events requested with params= {}", getRequest);
+ return eventService.getEvents(getRequest);
+ }
+
+ @GetMapping("/{eventId}")
+ public EventFullDto getEventById(@PathVariable Long eventId, HttpServletRequest request) {
+ log.info("Public get event with eventId={} requested", eventId);
+ return eventService.getById(eventId, request);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/controller/EventSortBy.java b/main-service/src/main/java/ru/practicum/event/controller/EventSortBy.java
new file mode 100644
index 0000000..0ff03c7
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/controller/EventSortBy.java
@@ -0,0 +1,6 @@
+package ru.practicum.event.controller;
+
+public enum EventSortBy {
+ EVENT_DATE,
+ VIEWS
+}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/EventFullDto.java b/main-service/src/main/java/ru/practicum/event/dto/EventFullDto.java
new file mode 100644
index 0000000..6c5788b
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/EventFullDto.java
@@ -0,0 +1,25 @@
+package ru.practicum.event.dto;
+
+import java.time.LocalDateTime;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.event.model.EventState;
+import ru.practicum.user.dto.UserShortDto;
+
+public record EventFullDto(
+ String annotation,
+ CategoryDto category,
+ Long confirmedRequests,
+ LocalDateTime createdOn,
+ String description,
+ LocalDateTime eventDate,
+ Long id,
+ UserShortDto initiator,
+ LocationDto location,
+ boolean paid,
+ Integer participantLimit,
+ LocalDateTime publishedOn,
+ boolean requestModeration,
+ EventState state,
+ String title,
+ Long views) {}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/EventShortDto.java b/main-service/src/main/java/ru/practicum/event/dto/EventShortDto.java
new file mode 100644
index 0000000..5f102c4
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/EventShortDto.java
@@ -0,0 +1,17 @@
+package ru.practicum.event.dto;
+
+import java.time.LocalDateTime;
+
+import ru.practicum.category.dto.CategoryDto;
+import ru.practicum.user.dto.UserShortDto;
+
+public record EventShortDto(
+ String annotation,
+ CategoryDto category,
+ Long confirmedRequests,
+ LocalDateTime eventDate,
+ Long id,
+ UserShortDto initiator,
+ boolean paid,
+ String title,
+ Long views) {}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/LocationDto.java b/main-service/src/main/java/ru/practicum/event/dto/LocationDto.java
new file mode 100644
index 0000000..cfbb0de
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/LocationDto.java
@@ -0,0 +1,11 @@
+package ru.practicum.event.dto;
+
+import java.math.BigDecimal;
+
+import jakarta.validation.constraints.DecimalMax;
+import jakarta.validation.constraints.DecimalMin;
+import jakarta.validation.constraints.NotNull;
+
+public record LocationDto(
+ @NotNull @DecimalMin("-90.0") @DecimalMax("90.0") BigDecimal lat,
+ @NotNull @DecimalMin("-180.0") @DecimalMax("180.0") BigDecimal lon) {}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/NewEventDto.java b/main-service/src/main/java/ru/practicum/event/dto/NewEventDto.java
new file mode 100644
index 0000000..1d9de92
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/NewEventDto.java
@@ -0,0 +1,22 @@
+package ru.practicum.event.dto;
+
+import java.time.LocalDateTime;
+
+import jakarta.validation.constraints.*;
+
+public record NewEventDto(
+ @NotBlank @Size(min = 20, max = 2000) String annotation,
+ @NotNull Long category,
+ @NotBlank @Size(min = 20, max = 7000) String description,
+ @NotNull @Future LocalDateTime eventDate,
+ @NotNull LocationDto location,
+ Boolean paid,
+ @PositiveOrZero Integer participantLimit,
+ Boolean requestModeration,
+ @NotBlank @Size(min = 3, max = 120) String title) {
+ public NewEventDto {
+ if (paid == null) paid = false;
+ if (requestModeration == null) requestModeration = true;
+ if (participantLimit == null) participantLimit = 0;
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/UpdatableEvent.java b/main-service/src/main/java/ru/practicum/event/dto/UpdatableEvent.java
new file mode 100644
index 0000000..6c1b17c
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/UpdatableEvent.java
@@ -0,0 +1,39 @@
+package ru.practicum.event.dto;
+
+import java.time.LocalDateTime;
+
+public interface UpdatableEvent {
+ boolean hasAnnotation();
+
+ String annotation();
+
+ boolean hasEventDate();
+
+ LocalDateTime eventDate();
+
+ boolean hasCategory();
+
+ boolean hasLocation();
+
+ LocationDto location();
+
+ boolean hasParticipantLimit();
+
+ Integer participantLimit();
+
+ boolean hasPaid();
+
+ Boolean paid();
+
+ boolean hasRequestModeration();
+
+ Boolean requestModeration();
+
+ boolean hasTitle();
+
+ String title();
+
+ boolean hasDescription();
+
+ String description();
+}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/UpdateEventAdminRequest.java b/main-service/src/main/java/ru/practicum/event/dto/UpdateEventAdminRequest.java
new file mode 100644
index 0000000..c39faff
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/UpdateEventAdminRequest.java
@@ -0,0 +1,63 @@
+package ru.practicum.event.dto;
+
+import java.time.LocalDateTime;
+
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.PositiveOrZero;
+import jakarta.validation.constraints.Size;
+
+import ru.practicum.event.model.AdminStateAction;
+
+public record UpdateEventAdminRequest(
+ @Size(min = 20, max = 2000) String annotation,
+ Long category,
+ @Size(min = 20, max = 7000) String description,
+ @Future LocalDateTime eventDate,
+ LocationDto location,
+ Boolean paid,
+ @PositiveOrZero Integer participantLimit,
+ Boolean requestModeration,
+ AdminStateAction stateAction,
+ @Size(min = 3, max = 120) String title)
+ implements UpdatableEvent {
+
+ public boolean hasAnnotation() {
+ return annotation != null && !annotation.isBlank();
+ }
+
+ public boolean hasCategory() {
+ return category != null;
+ }
+
+ public boolean hasDescription() {
+ return description != null && !description.isBlank();
+ }
+
+ public boolean hasEventDate() {
+ return eventDate != null;
+ }
+
+ public boolean hasLocation() {
+ return location != null;
+ }
+
+ public boolean hasPaid() {
+ return paid != null;
+ }
+
+ public boolean hasParticipantLimit() {
+ return participantLimit != null;
+ }
+
+ public boolean hasRequestModeration() {
+ return requestModeration != null;
+ }
+
+ public boolean hasStateAction() {
+ return stateAction != null;
+ }
+
+ public boolean hasTitle() {
+ return title != null && !title.isBlank();
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/dto/UpdateEventUserRequest.java b/main-service/src/main/java/ru/practicum/event/dto/UpdateEventUserRequest.java
new file mode 100644
index 0000000..82aee57
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/dto/UpdateEventUserRequest.java
@@ -0,0 +1,63 @@
+package ru.practicum.event.dto;
+
+import java.time.LocalDateTime;
+
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.PositiveOrZero;
+import jakarta.validation.constraints.Size;
+
+import ru.practicum.event.model.UserStateAction;
+
+public record UpdateEventUserRequest(
+ @Size(min = 20, max = 2000) String annotation,
+ Long category,
+ @Size(min = 20, max = 7000) String description,
+ @Future LocalDateTime eventDate,
+ LocationDto location,
+ Boolean paid,
+ @PositiveOrZero Integer participantLimit,
+ Boolean requestModeration,
+ UserStateAction stateAction,
+ @Size(min = 3, max = 120) String title)
+ implements UpdatableEvent {
+
+ public boolean hasAnnotation() {
+ return annotation != null && !annotation.isBlank();
+ }
+
+ public boolean hasCategory() {
+ return category != null;
+ }
+
+ public boolean hasDescription() {
+ return description != null && !description.isBlank();
+ }
+
+ public boolean hasEventDate() {
+ return eventDate != null;
+ }
+
+ public boolean hasLocation() {
+ return location != null;
+ }
+
+ public boolean hasPaid() {
+ return paid != null;
+ }
+
+ public boolean hasParticipantLimit() {
+ return participantLimit != null;
+ }
+
+ public boolean hasRequestModeration() {
+ return requestModeration != null;
+ }
+
+ public boolean hasStateAction() {
+ return stateAction != null;
+ }
+
+ public boolean hasTitle() {
+ return title != null && !title.isBlank();
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java b/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java
new file mode 100644
index 0000000..4df4931
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java
@@ -0,0 +1,136 @@
+package ru.practicum.event.mapper;
+
+import java.time.LocalDateTime;
+
+import ru.practicum.category.mapper.CategoryMapper;
+import ru.practicum.category.model.Category;
+import ru.practicum.event.dto.*;
+import ru.practicum.event.model.Event;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.model.Location;
+import ru.practicum.user.mapper.UserMapper;
+import ru.practicum.user.model.User;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class EventMapper {
+
+ public Event mapToEntity(
+ NewEventDto newEventDto, Category category, User initiator, Location location) {
+ return new Event(
+ null,
+ newEventDto.annotation(),
+ category,
+ LocalDateTime.now(),
+ newEventDto.description(),
+ newEventDto.eventDate(),
+ initiator,
+ location,
+ newEventDto.paid(),
+ newEventDto.participantLimit(),
+ null,
+ newEventDto.requestModeration(),
+ EventState.PENDING,
+ newEventDto.title());
+ }
+
+ public EventFullDto mapToFullDto(Event event, long confirmedRequests, Long views) {
+ return new EventFullDto(
+ event.getAnnotation(),
+ CategoryMapper.mapToDto(event.getCategory()),
+ confirmedRequests,
+ event.getCreatedOn(),
+ event.getDescription(),
+ event.getEventDate(),
+ event.getId(),
+ UserMapper.mapToUserShortDto(event.getInitiator()),
+ LocationMapper.mapToDto(event.getLocation()),
+ event.getPaid(),
+ event.getParticipantLimit(),
+ event.getPublishedOn(),
+ event.getRequestModeration(),
+ event.getState(),
+ event.getTitle(),
+ views);
+ }
+
+ public EventShortDto mapToShortDto(Event event, long confirmedRequests, Long views) {
+ return new EventShortDto(
+ event.getAnnotation(),
+ CategoryMapper.mapToDto(event.getCategory()),
+ confirmedRequests,
+ event.getEventDate(),
+ event.getId(),
+ UserMapper.mapToUserShortDto(event.getInitiator()),
+ event.getPaid(),
+ event.getTitle(),
+ views);
+ }
+
+ public void updateEventFromDto(
+ Event event, UpdateEventAdminRequest updateDto, Category newCategory) {
+ if (updateDto.hasStateAction()) {
+ switch (updateDto.stateAction()) {
+ case PUBLISH_EVENT -> {
+ event.setState(EventState.PUBLISHED);
+ event.setPublishedOn(LocalDateTime.now());
+ }
+ case REJECT_EVENT -> event.setState(EventState.CANCELED);
+ }
+ }
+
+ updateCommonFields(event, updateDto, newCategory);
+ }
+
+ public void updateEventFromDto(
+ Event event, UpdateEventUserRequest updateDto, Category newCategory) {
+ if (updateDto.hasStateAction()) {
+ switch (updateDto.stateAction()) {
+ case SEND_TO_REVIEW -> event.setState(EventState.PENDING);
+ case CANCEL_REVIEW -> event.setState(EventState.CANCELED);
+ }
+ }
+
+ updateCommonFields(event, updateDto, newCategory);
+ }
+
+ private void updateCommonFields(Event event, UpdatableEvent updateDto, Category newCategory) {
+ if (updateDto.hasAnnotation()) {
+ event.setAnnotation(updateDto.annotation());
+ }
+
+ if (updateDto.hasEventDate()) {
+ event.setEventDate(updateDto.eventDate());
+ }
+
+ if (updateDto.hasCategory()) {
+ event.setCategory(newCategory);
+ }
+
+ if (updateDto.hasLocation()) {
+ Location location = LocationMapper.mapToEntity(updateDto.location());
+ event.setLocation(location);
+ }
+
+ if (updateDto.hasParticipantLimit()) {
+ event.setParticipantLimit(updateDto.participantLimit());
+ }
+
+ if (updateDto.hasPaid()) {
+ event.setPaid(updateDto.paid());
+ }
+
+ if (updateDto.hasRequestModeration()) {
+ event.setRequestModeration(updateDto.requestModeration());
+ }
+
+ if (updateDto.hasTitle()) {
+ event.setTitle(updateDto.title());
+ }
+
+ if (updateDto.hasDescription()) {
+ event.setDescription(updateDto.description());
+ }
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/mapper/LocationMapper.java b/main-service/src/main/java/ru/practicum/event/mapper/LocationMapper.java
new file mode 100644
index 0000000..15472d9
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/mapper/LocationMapper.java
@@ -0,0 +1,18 @@
+package ru.practicum.event.mapper;
+
+import ru.practicum.event.dto.LocationDto;
+import ru.practicum.event.model.Location;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class LocationMapper {
+
+ public Location mapToEntity(LocationDto locationDto) {
+ return new Location(locationDto.lat(), locationDto.lon());
+ }
+
+ public LocationDto mapToDto(Location location) {
+ return new LocationDto(location.getLat(), location.getLon());
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/model/AdminStateAction.java b/main-service/src/main/java/ru/practicum/event/model/AdminStateAction.java
new file mode 100644
index 0000000..b6574b1
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/model/AdminStateAction.java
@@ -0,0 +1,6 @@
+package ru.practicum.event.model;
+
+public enum AdminStateAction {
+ PUBLISH_EVENT,
+ REJECT_EVENT
+}
diff --git a/main-service/src/main/java/ru/practicum/event/model/Event.java b/main-service/src/main/java/ru/practicum/event/model/Event.java
new file mode 100644
index 0000000..923ba49
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/model/Event.java
@@ -0,0 +1,66 @@
+package ru.practicum.event.model;
+
+import java.time.LocalDateTime;
+
+import jakarta.persistence.*;
+
+import ru.practicum.category.model.Category;
+import ru.practicum.user.model.User;
+
+import lombok.experimental.FieldDefaults;
+import lombok.*;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "event")
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Event {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+
+ @Column(nullable = false, length = 2000)
+ String annotation;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id", nullable = false)
+ Category category;
+
+ @Column(nullable = false)
+ LocalDateTime createdOn;
+
+ @Column(nullable = false, length = 7000)
+ String description;
+
+ @Column(nullable = false)
+ LocalDateTime eventDate;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "initiator_id", nullable = false)
+ User initiator;
+
+ @Embedded Location location;
+
+ @Column(nullable = false)
+ Boolean paid;
+
+ @Column(nullable = false)
+ Integer participantLimit;
+
+ @Column LocalDateTime publishedOn;
+
+ @Column(nullable = false)
+ Boolean requestModeration;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ EventState state;
+
+ @Column(nullable = false, length = 120)
+ String title;
+}
diff --git a/main-service/src/main/java/ru/practicum/event/model/EventState.java b/main-service/src/main/java/ru/practicum/event/model/EventState.java
new file mode 100644
index 0000000..9cfbb30
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/model/EventState.java
@@ -0,0 +1,7 @@
+package ru.practicum.event.model;
+
+public enum EventState {
+ PENDING,
+ PUBLISHED,
+ CANCELED
+}
diff --git a/main-service/src/main/java/ru/practicum/event/model/Location.java b/main-service/src/main/java/ru/practicum/event/model/Location.java
new file mode 100644
index 0000000..9a53749
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/model/Location.java
@@ -0,0 +1,24 @@
+package ru.practicum.event.model;
+
+import java.math.BigDecimal;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Embeddable;
+
+import lombok.experimental.FieldDefaults;
+import lombok.*;
+
+@Getter
+@Setter
+@Builder
+@Embeddable
+@NoArgsConstructor
+@AllArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class Location {
+ @Column(name = "location_lat", nullable = false, precision = 9, scale = 6)
+ BigDecimal lat;
+
+ @Column(name = "location_lon", nullable = false, precision = 9, scale = 6)
+ BigDecimal lon;
+}
diff --git a/main-service/src/main/java/ru/practicum/event/model/UserStateAction.java b/main-service/src/main/java/ru/practicum/event/model/UserStateAction.java
new file mode 100644
index 0000000..114d5a7
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/model/UserStateAction.java
@@ -0,0 +1,6 @@
+package ru.practicum.event.model;
+
+public enum UserStateAction {
+ SEND_TO_REVIEW,
+ CANCEL_REVIEW
+}
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
new file mode 100644
index 0000000..9c07c7a
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/repository/EventRepository.java
@@ -0,0 +1,93 @@
+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;
+import ru.practicum.event.model.QEvent;
+import ru.practicum.event.service.EventsAdminGetRequest;
+import ru.practicum.event.service.EventsPublicGetRequest;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+
+import com.querydsl.core.BooleanBuilder;
+import com.querydsl.core.types.Predicate;
+
+public interface EventRepository
+ extends JpaRepository, QuerydslPredicateExecutor {
+ static Predicate createPredicate(EventsAdminGetRequest request) {
+ QEvent event = QEvent.event;
+ BooleanBuilder builder = new BooleanBuilder();
+
+ if (request.hasUsers()) {
+ builder.and(event.initiator.id.in(request.users()));
+ }
+
+ if (request.hasStates()) {
+ builder.and(event.state.in(request.states()));
+ }
+
+ if (request.hasCategories()) {
+ builder.and(event.category.id.in(request.categories()));
+ }
+
+ if (request.hasRangeStart()) {
+ builder.and(event.eventDate.goe(request.rangeStart()));
+ }
+
+ if (request.hasRangeEnd()) {
+ builder.and(event.eventDate.loe(request.rangeEnd()));
+ }
+
+ return builder;
+ }
+
+ static Predicate createPredicate(EventsPublicGetRequest request) {
+ QEvent event = QEvent.event;
+ BooleanBuilder builder = new BooleanBuilder();
+
+ builder.and(event.state.eq(EventState.PUBLISHED));
+
+ if (request.hasPaid()) {
+ builder.and(event.paid.eq(request.paid()));
+ }
+
+ if (request.hasText()) {
+ String text = request.text();
+ builder.and(
+ event.annotation
+ .containsIgnoreCase(text)
+ .or(event.description.containsIgnoreCase(text)));
+ }
+
+ if (request.hasCategories()) {
+ builder.and(event.category.id.in(request.categories()));
+ }
+
+ if (request.hasRangeStart()) {
+ builder.and(event.eventDate.goe(request.rangeStart()));
+ }
+
+ if (request.hasRangeEnd()) {
+ builder.and(event.eventDate.loe(request.rangeEnd()));
+ }
+
+ if (!(request.hasRangeStart() && request.hasRangeEnd())) {
+ builder.and(event.eventDate.goe(LocalDateTime.now()));
+ }
+
+ return builder;
+ }
+
+ 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/event/service/EventService.java b/main-service/src/main/java/ru/practicum/event/service/EventService.java
new file mode 100644
index 0000000..be8a801
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/service/EventService.java
@@ -0,0 +1,25 @@
+package ru.practicum.event.service;
+
+import java.util.Collection;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import ru.practicum.event.dto.*;
+
+public interface EventService {
+ EventFullDto getById(Long eventId, HttpServletRequest request);
+
+ Collection getEvents(EventsPublicGetRequest getRequest);
+
+ Collection getEvents(EventsAdminGetRequest getRequest);
+
+ Collection getEvents(EventsPrivateGetRequest getRequest);
+
+ EventFullDto createEvent(Long userId, NewEventDto newEventDto);
+
+ EventFullDto getByUserById(Long userId, Long eventId);
+
+ EventFullDto updateEvent(Long eventId, UpdateEventAdminRequest updateRequest);
+
+ EventFullDto updateEventByUser(Long userId, Long eventId, UpdateEventUserRequest updateRequest);
+}
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
new file mode 100644
index 0000000..246a538
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/service/EventServiceImpl.java
@@ -0,0 +1,321 @@
+package ru.practicum.event.service;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.stream.Collectors;
+import java.util.*;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import ru.practicum.category.model.Category;
+import ru.practicum.category.repository.CategoryRepository;
+import ru.practicum.client.StatsClient;
+import ru.practicum.dto.ViewStatsDto;
+import ru.practicum.event.controller.EventSortBy;
+import ru.practicum.event.dto.*;
+import ru.practicum.event.mapper.EventMapper;
+import ru.practicum.event.mapper.LocationMapper;
+import ru.practicum.event.model.Event;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.model.Location;
+import ru.practicum.event.repository.EventRepository;
+import ru.practicum.exception.ForbiddenAccessException;
+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;
+
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.client.RestClientException;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class EventServiceImpl implements EventService {
+ private static final Duration MIN_TIME_BEFORE_EVENT = Duration.ofHours(2);
+ 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) {
+ Optional eventOptional =
+ eventRepository.findByIdAndState(eventId, EventState.PUBLISHED);
+ Event event =
+ eventOptional.orElseThrow(
+ NotFoundException.supplier("Event with id=%d not found", eventId));
+
+ statsClient.hit(request);
+ String uri = request.getRequestURI();
+
+ ViewStatsDto statsDto = getStatsForEvent(event, uri);
+
+ Map confirmedRequests = getConfirmedRequests(Set.of(event));
+
+ return EventMapper.mapToFullDto(
+ event, confirmedRequests.getOrDefault(event.getId(), 0L), statsDto.hits());
+ }
+
+ @Override
+ public Collection getEvents(EventsPublicGetRequest getRequest) {
+ Page events =
+ eventRepository.findAll(
+ EventRepository.createPredicate(getRequest), getRequest.getPageable());
+
+ statsClient.hit(getRequest.httpRequest());
+
+ Map statsForEvents = getStatsMapForEvents(events);
+
+ Map confirmedRequests =
+ getConfirmedRequests(events.stream().collect(Collectors.toSet()));
+
+ List eventsList =
+ events.stream()
+ .map(
+ event ->
+ EventMapper.mapToShortDto(
+ event,
+ confirmedRequests.getOrDefault(event.getId(), 0L),
+ statsForEvents.get(event.getId())))
+ .toList();
+
+ if (EventSortBy.VIEWS.equals(getRequest.sort())) {
+ return eventsList.stream().sorted(Comparator.comparing(EventShortDto::views)).toList();
+ }
+
+ return eventsList;
+ }
+
+ @Override
+ public Collection getEvents(EventsAdminGetRequest getRequest) {
+ Page events =
+ eventRepository.findAll(
+ EventRepository.createPredicate(getRequest), getRequest.getPageable());
+
+ Map statsForEvents = getStatsMapForEvents(events);
+
+ Map confirmedRequests =
+ getConfirmedRequests(events.stream().collect(Collectors.toSet()));
+
+ return events.stream()
+ .map(
+ event ->
+ EventMapper.mapToFullDto(
+ event,
+ confirmedRequests.getOrDefault(event.getId(), 0L),
+ statsForEvents.get(event.getId())))
+ .toList();
+ }
+
+ @Override
+ public Collection getEvents(EventsPrivateGetRequest getRequest) {
+ getUserByIdOrThrow(getRequest.userId());
+ Page events =
+ eventRepository.findByInitiator_Id(getRequest.userId(), getRequest.getPageable());
+
+ Map statsForEvents = getStatsMapForEvents(events);
+
+ Map confirmedRequests =
+ getConfirmedRequests(events.stream().collect(Collectors.toSet()));
+
+ return events.stream()
+ .map(
+ event ->
+ EventMapper.mapToShortDto(
+ event,
+ confirmedRequests.getOrDefault(event.getId(), 0L),
+ statsForEvents.get(event.getId())))
+ .toList();
+ }
+
+ @Override
+ @Transactional
+ public EventFullDto createEvent(Long userId, NewEventDto newEventDto) {
+ Location location = LocationMapper.mapToEntity(newEventDto.location());
+ Category category = getCategoryByIdOrThrow(newEventDto.category());
+ User initiator = getUserByIdOrThrow(userId);
+ Event event = EventMapper.mapToEntity(newEventDto, category, initiator, location);
+
+ LocalDateTime now = LocalDateTime.now();
+ if (event.getEventDate().isBefore(now.plus(MIN_TIME_BEFORE_EVENT))) {
+ throw new ValidationException(
+ "The event must be scheduled at least %d hours from now."
+ .formatted(MIN_TIME_BEFORE_EVENT.toHours()));
+ }
+ Event saved = eventRepository.save(event);
+
+ return EventMapper.mapToFullDto(saved, 0, 0L);
+ }
+
+ @Override
+ public EventFullDto getByUserById(Long userId, Long eventId) {
+ User user = getUserByIdOrThrow(userId);
+
+ Event event = getEventByIdOrThrow(eventId);
+
+ if (!event.getInitiator().getId().equals(user.getId())) {
+ throw new ForbiddenAccessException("You can't view event that's not yours");
+ }
+
+ ViewStatsDto statsDto = getStatsForEvent(event, EVENTS_URI.formatted(eventId));
+
+ Map confirmedRequests = getConfirmedRequests(Set.of(event));
+
+ return EventMapper.mapToFullDto(
+ event, confirmedRequests.getOrDefault(event.getId(), 0L), statsDto.hits());
+ }
+
+ @Override
+ @Transactional
+ public EventFullDto updateEvent(Long eventId, UpdateEventAdminRequest updateRequest) {
+ Event event = getEventByIdOrThrow(eventId);
+
+ if ((event.getState().equals(EventState.PUBLISHED)
+ || event.getState().equals(EventState.CANCELED))
+ && updateRequest.hasStateAction()) {
+ throw new IllegalEventUpdateException(
+ "Forbidden to update event that already %s"
+ .formatted(event.getState().toString()));
+ }
+
+ Category newCategory = null;
+ if (updateRequest.hasCategory()) {
+ newCategory = getCategoryByIdOrThrow(updateRequest.category());
+ }
+ EventMapper.updateEventFromDto(event, updateRequest, newCategory);
+
+ Event saved = eventRepository.save(event);
+
+ Map confirmedRequests = getConfirmedRequests(Set.of(event));
+
+ return EventMapper.mapToFullDto(
+ saved, confirmedRequests.getOrDefault(event.getId(), 0L), null);
+ }
+
+ @Override
+ @Transactional
+ public EventFullDto updateEventByUser(
+ Long userId, Long eventId, UpdateEventUserRequest updateRequest) {
+ Event event = getEventByIdOrThrow(eventId);
+ User user = getUserByIdOrThrow(userId);
+
+ if (!event.getInitiator().getId().equals(user.getId())) {
+ throw new ForbiddenAccessException("You can't update event that's not yours");
+ }
+
+ if ((event.getState().equals(EventState.PUBLISHED)
+ || event.getState().equals(EventState.CANCELED))
+ && !updateRequest.hasStateAction()) {
+ throw new IllegalEventUpdateException(
+ "Forbidden to update event that already %s"
+ .formatted(event.getState().toString()));
+ }
+
+ Category newCategory = null;
+ if (updateRequest.hasCategory()) {
+ newCategory = getCategoryByIdOrThrow(updateRequest.category());
+ }
+ EventMapper.updateEventFromDto(event, updateRequest, newCategory);
+
+ Event saved = eventRepository.save(event);
+
+ Map confirmedRequests = getConfirmedRequests(Set.of(event));
+
+ return EventMapper.mapToFullDto(
+ saved, confirmedRequests.getOrDefault(event.getId(), 0L), null);
+ }
+
+ private Map getStatsMapForEvents(Page events) {
+ if (events.isEmpty()) {
+ return Collections.emptyMap();
+ }
+ List listOfUris =
+ events.stream().map(event -> EVENTS_URI.formatted(event.getId())).toList();
+ LocalDateTime minimalPublishDate =
+ events.stream()
+ .map(Event::getPublishedOn)
+ .filter(Objects::nonNull)
+ .min(LocalDateTime::compareTo)
+ .orElse(LocalDateTime.of(1000, 1, 1, 1, 1));
+
+ return getStatsForEvents(listOfUris, minimalPublishDate).stream()
+ .collect(
+ Collectors.toMap(
+ statsDto ->
+ Long.valueOf(
+ statsDto.uri()
+ .substring(
+ statsDto.uri().lastIndexOf('/')
+ + 1)),
+ ViewStatsDto::hits));
+ }
+
+ 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, LocalDateTime startDate) {
+ try {
+ return statsClient.getStats(startDate, LocalDateTime.now(), uris, true);
+ } catch (RestClientException e) {
+ log.error("Error during getting stats for events", e);
+ }
+ return List.of();
+ }
+
+ private ViewStatsDto getStatsForEvent(Event event, String uri) {
+ if (event.getPublishedOn() == null) {
+ return new ViewStatsDto(null, null, null);
+ }
+
+ LocalDateTime startDate = event.getPublishedOn().minusSeconds(10);
+ LocalDateTime endDate = LocalDateTime.now().plusSeconds(10);
+ ViewStatsDto statsDto;
+
+ try {
+ statsDto = statsClient.getStats(startDate, endDate, List.of(uri), true).getFirst();
+ } catch (NoSuchElementException e) {
+ log.trace("No stats for event with id={} found", event.getId());
+ statsDto = new ViewStatsDto(null, null, 0L);
+ } catch (RestClientException e) {
+ log.error("Error during getting stats for event with id={}", event.getId(), e);
+ statsDto = new ViewStatsDto(null, null, null);
+ }
+ return statsDto;
+ }
+
+ private Event getEventByIdOrThrow(Long eventId) {
+ Optional eventOptional = eventRepository.findById(eventId);
+ return eventOptional.orElseThrow(
+ NotFoundException.supplier("Event with id=%d not found", eventId));
+ }
+
+ private User getUserByIdOrThrow(Long userId) {
+ Optional optionalUser = userRepository.findById(userId);
+ return optionalUser.orElseThrow(
+ NotFoundException.supplier("User with id=%d not found", userId));
+ }
+
+ private Category getCategoryByIdOrThrow(Long categoryId) {
+ Optional optionalCategory = categoryRepository.findById(categoryId);
+ return optionalCategory.orElseThrow(
+ NotFoundException.supplier("Category with id=%d not found", categoryId));
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/service/EventsAdminGetRequest.java b/main-service/src/main/java/ru/practicum/event/service/EventsAdminGetRequest.java
new file mode 100644
index 0000000..18bd132
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/service/EventsAdminGetRequest.java
@@ -0,0 +1,44 @@
+package ru.practicum.event.service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import ru.practicum.event.model.EventState;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+public record EventsAdminGetRequest(
+ List users,
+ List states,
+ List categories,
+ LocalDateTime rangeStart,
+ LocalDateTime rangeEnd,
+ int from,
+ int size) {
+
+ public boolean hasUsers() {
+ return users != null && !users.isEmpty();
+ }
+
+ public boolean hasStates() {
+ return states != null && !states.isEmpty();
+ }
+
+ public boolean hasCategories() {
+ return categories != null && !categories.isEmpty();
+ }
+
+ public boolean hasRangeStart() {
+ return rangeStart != null;
+ }
+
+ public boolean hasRangeEnd() {
+ return rangeEnd != null;
+ }
+
+ public Pageable getPageable() {
+ int page = from / size;
+ return PageRequest.of(page, size);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/event/service/EventsPrivateGetRequest.java b/main-service/src/main/java/ru/practicum/event/service/EventsPrivateGetRequest.java
new file mode 100644
index 0000000..3df3186
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/service/EventsPrivateGetRequest.java
@@ -0,0 +1,12 @@
+package ru.practicum.event.service;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+
+public record EventsPrivateGetRequest(Long userId, 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/event/service/EventsPublicGetRequest.java b/main-service/src/main/java/ru/practicum/event/service/EventsPublicGetRequest.java
new file mode 100644
index 0000000..b4a8cfa
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/event/service/EventsPublicGetRequest.java
@@ -0,0 +1,57 @@
+package ru.practicum.event.service;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+import ru.practicum.event.controller.EventSortBy;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+
+public record EventsPublicGetRequest(
+ String text,
+ List categories,
+ Boolean paid,
+ LocalDateTime rangeStart,
+ LocalDateTime rangeEnd,
+ boolean onlyAvailable,
+ EventSortBy sort,
+ int from,
+ int size,
+ HttpServletRequest httpRequest) {
+
+ public boolean hasText() {
+ return text != null && !text.isEmpty();
+ }
+
+ public boolean hasCategories() {
+ return categories != null && !categories.isEmpty();
+ }
+
+ public boolean hasPaid() {
+ return paid != null;
+ }
+
+ public boolean hasRangeStart() {
+ return rangeStart != null;
+ }
+
+ public boolean hasRangeEnd() {
+ return rangeEnd != null;
+ }
+
+ public boolean hasSortBy() {
+ return sort != null;
+ }
+
+ public Pageable getPageable() {
+ int page = from / size;
+ if (EventSortBy.EVENT_DATE.equals(sort)) {
+ return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "eventDate"));
+ }
+ return PageRequest.of(page, size);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/exception/ConflictException.java b/main-service/src/main/java/ru/practicum/exception/ConflictException.java
new file mode 100644
index 0000000..e2fddf1
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/ConflictException.java
@@ -0,0 +1,7 @@
+package ru.practicum.exception;
+
+public class ConflictException extends RuntimeException {
+ public ConflictException(String message) {
+ super(message);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/exception/ForbiddenAccessException.java b/main-service/src/main/java/ru/practicum/exception/ForbiddenAccessException.java
new file mode 100644
index 0000000..1e7f8b5
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/ForbiddenAccessException.java
@@ -0,0 +1,7 @@
+package ru.practicum.exception;
+
+public class ForbiddenAccessException extends RuntimeException {
+ public ForbiddenAccessException(String message) {
+ super(message);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/exception/IllegalEventUpdateException.java b/main-service/src/main/java/ru/practicum/exception/IllegalEventUpdateException.java
new file mode 100644
index 0000000..7e961c7
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/IllegalEventUpdateException.java
@@ -0,0 +1,7 @@
+package ru.practicum.exception;
+
+public class IllegalEventUpdateException extends RuntimeException {
+ public IllegalEventUpdateException(String message) {
+ super(message);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/exception/NotFoundException.java b/main-service/src/main/java/ru/practicum/exception/NotFoundException.java
new file mode 100644
index 0000000..5221e05
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/NotFoundException.java
@@ -0,0 +1,13 @@
+package ru.practicum.exception;
+
+import java.util.function.Supplier;
+
+public class NotFoundException extends RuntimeException {
+ public NotFoundException(String message) {
+ super(message);
+ }
+
+ public static Supplier supplier(String message, Object... args) {
+ return () -> new NotFoundException(message.formatted(args));
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/exception/ValidationException.java b/main-service/src/main/java/ru/practicum/exception/ValidationException.java
new file mode 100644
index 0000000..d751d21
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/ValidationException.java
@@ -0,0 +1,7 @@
+package ru.practicum.exception;
+
+public class ValidationException extends RuntimeException {
+ public ValidationException(String message) {
+ super(message);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/exception/dto/ApiError.java b/main-service/src/main/java/ru/practicum/exception/dto/ApiError.java
new file mode 100644
index 0000000..65b2421
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/dto/ApiError.java
@@ -0,0 +1,11 @@
+package ru.practicum.exception.dto;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+public record ApiError(
+ List errors,
+ String message,
+ String reason,
+ String status,
+ LocalDateTime timestamp) {}
diff --git a/main-service/src/main/java/ru/practicum/exception/dto/Violation.java b/main-service/src/main/java/ru/practicum/exception/dto/Violation.java
new file mode 100644
index 0000000..39174d4
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/dto/Violation.java
@@ -0,0 +1,3 @@
+package ru.practicum.exception.dto;
+
+public record Violation(String fieldName, String message) {}
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
new file mode 100644
index 0000000..7a51bc4
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/exception/handler/GlobalExceptionHandler.java
@@ -0,0 +1,171 @@
+package ru.practicum.exception.handler;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import jakarta.validation.ConstraintViolationException;
+
+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;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(Exception.class)
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public ApiError handleException(Exception e) {
+ log.error(e.getMessage(), e);
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ e.printStackTrace(pw);
+ String stackTrace = sw.toString();
+
+ return new ApiError(
+ List.of(stackTrace),
+ "An error occured while processing request",
+ "Exception",
+ HttpStatus.INTERNAL_SERVER_ERROR.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler({
+ MissingServletRequestParameterException.class,
+ MethodArgumentTypeMismatchException.class
+ })
+ public ApiError handleRequestParamException(Exception e) {
+ log.warn(e.getMessage(), e);
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "Incorrect request",
+ HttpStatus.BAD_REQUEST.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ExceptionHandler(ConflictException.class)
+ public ApiError handleConflictException(ConflictException e) {
+ log.warn(e.getMessage(), e);
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "Conflict",
+ HttpStatus.CONFLICT.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(ConstraintViolationException.class)
+ public ApiError handleConstraintValidationException(ConstraintViolationException e) {
+ final List violations =
+ e.getConstraintViolations().stream()
+ .map(
+ violation ->
+ new Violation(
+ violation.getPropertyPath().toString(),
+ violation.getMessage()))
+ .collect(Collectors.toList());
+ log.warn(violations.toString());
+ List errors = violations.stream().map(Violation::toString).toList();
+ return new ApiError(
+ errors,
+ e.getMessage(),
+ "Some fields of RequestBody for request are invalid",
+ HttpStatus.BAD_REQUEST.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ApiError handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ final List violations =
+ e.getBindingResult().getFieldErrors().stream()
+ .map(error -> new Violation(error.getField(), error.getDefaultMessage()))
+ .collect(Collectors.toList());
+ log.warn(violations.toString());
+ List errors = violations.stream().map(Violation::toString).toList();
+ return new ApiError(
+ errors,
+ e.getMessage(),
+ "Some fields of RequestBody for request are invalid",
+ HttpStatus.BAD_REQUEST.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ @ExceptionHandler(NotFoundException.class)
+ public ApiError handleNotFoundException(NotFoundException e) {
+ log.warn("Not found: {}", e.getMessage());
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "The required object was not found",
+ HttpStatus.NOT_FOUND.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ExceptionHandler(DataIntegrityViolationException.class)
+ public ApiError handleDataIntegrityViolationException(DataIntegrityViolationException e) {
+ log.warn(e.getMessage(), e);
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "Some fields of RequestBody for request are invalid",
+ HttpStatus.NOT_FOUND.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.CONFLICT)
+ @ExceptionHandler(IllegalEventUpdateException.class)
+ public ApiError handleIllegalEventUpdateException(IllegalEventUpdateException e) {
+ log.warn(e.getMessage(), e);
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "Trying to update event that already Published or Canceled",
+ HttpStatus.CONFLICT.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.FORBIDDEN)
+ @ExceptionHandler(ForbiddenAccessException.class)
+ public ApiError handleForbiddenAccessException(ForbiddenAccessException e) {
+ log.warn(e.getMessage(), e);
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "Forbidden",
+ HttpStatus.FORBIDDEN.toString(),
+ LocalDateTime.now());
+ }
+
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ @ExceptionHandler(ValidationException.class)
+ public ApiError handleValidationException(ValidationException e) {
+ log.warn(e.getMessage(), e);
+ return new ApiError(
+ null,
+ e.getMessage(),
+ "Incorrect request",
+ HttpStatus.BAD_REQUEST.toString(),
+ LocalDateTime.now());
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestEventPrivateController.java b/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestEventPrivateController.java
new file mode 100644
index 0000000..c20a510
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestEventPrivateController.java
@@ -0,0 +1,50 @@
+package ru.practicum.request.controller;
+
+import java.util.List;
+
+import jakarta.validation.Valid;
+
+import ru.practicum.request.dto.EventRequestStatusUpdateRequest;
+import ru.practicum.request.dto.EventRequestStatusUpdateResult;
+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.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/users/{userId}/events/{eventId}/requests")
+public class ParticipationRequestEventPrivateController {
+
+ private final ParticipationRequestService requestService;
+
+ @GetMapping
+ public List getEventRequests(
+ @PathVariable Long userId, @PathVariable Long eventId) {
+ log.info("Get requests for eventId={} by initiator userId={}", eventId, userId);
+ return requestService.getEventRequestsByInitiator(userId, eventId);
+ }
+
+ @PatchMapping
+ public EventRequestStatusUpdateResult updateRequestsStatus(
+ @PathVariable Long userId,
+ @PathVariable Long eventId,
+ @Valid @RequestBody EventRequestStatusUpdateRequest updateRequest) {
+ log.info(
+ "Update requests status for eventId={} by userId={}, requestIds={}, status={}",
+ eventId,
+ userId,
+ updateRequest.requestIds(),
+ updateRequest.status());
+ return requestService.updateEventRequestsStatus(userId, eventId, updateRequest);
+ }
+}
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
new file mode 100644
index 0000000..da28856
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/controller/ParticipationRequestPrivateController.java
@@ -0,0 +1,42 @@
+package ru.practicum.request.controller;
+
+import java.util.List;
+
+import ru.practicum.request.dto.ParticipationRequestDto;
+import ru.practicum.request.service.ParticipationRequestService;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/users/{userId}/requests")
+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);
+ return requestService.createRequest(userId, eventId);
+ }
+
+ @GetMapping
+ public List getUserRequests(@PathVariable Long userId) {
+ log.info("Get participation requests by userId={}", userId);
+ return requestService.getUserRequests(userId);
+ }
+
+ @PatchMapping("/{requestId}/cancel")
+ public ParticipationRequestDto cancelRequest(
+ @PathVariable Long userId, @PathVariable Long requestId) {
+ log.info("Cancel participation request userId={}, requestId={}", userId, requestId);
+ return requestService.cancelRequest(userId, requestId);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/request/dto/EventRequestStatusUpdateRequest.java b/main-service/src/main/java/ru/practicum/request/dto/EventRequestStatusUpdateRequest.java
new file mode 100644
index 0000000..b9986cd
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/dto/EventRequestStatusUpdateRequest.java
@@ -0,0 +1,11 @@
+package ru.practicum.request.dto;
+
+import java.util.List;
+
+import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
+
+import ru.practicum.request.model.EventRequestStatus;
+
+public record EventRequestStatusUpdateRequest(
+ @NotEmpty List requestIds, @NotNull EventRequestStatus status) {}
diff --git a/main-service/src/main/java/ru/practicum/request/dto/EventRequestStatusUpdateResult.java b/main-service/src/main/java/ru/practicum/request/dto/EventRequestStatusUpdateResult.java
new file mode 100644
index 0000000..bae32df
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/dto/EventRequestStatusUpdateResult.java
@@ -0,0 +1,7 @@
+package ru.practicum.request.dto;
+
+import java.util.List;
+
+public record EventRequestStatusUpdateResult(
+ List confirmedRequests,
+ List rejectedRequests) {}
diff --git a/main-service/src/main/java/ru/practicum/request/dto/ParticipationRequestDto.java b/main-service/src/main/java/ru/practicum/request/dto/ParticipationRequestDto.java
new file mode 100644
index 0000000..b93cff3
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/dto/ParticipationRequestDto.java
@@ -0,0 +1,6 @@
+package ru.practicum.request.dto;
+
+import java.time.LocalDateTime;
+
+public record ParticipationRequestDto(
+ LocalDateTime created, Long event, Long id, Long requester, String status) {}
diff --git a/main-service/src/main/java/ru/practicum/request/mapper/ParticipationRequestMapper.java b/main-service/src/main/java/ru/practicum/request/mapper/ParticipationRequestMapper.java
new file mode 100644
index 0000000..2060323
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/mapper/ParticipationRequestMapper.java
@@ -0,0 +1,31 @@
+package ru.practicum.request.mapper;
+
+import java.util.List;
+
+import ru.practicum.request.dto.ParticipationRequestDto;
+import ru.practicum.request.model.ParticipationRequest;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class ParticipationRequestMapper {
+
+ public ParticipationRequestDto toDto(ParticipationRequest request) {
+ if (request == null) {
+ return null;
+ }
+ return new ParticipationRequestDto(
+ request.getCreated(),
+ request.getEvent().getId(),
+ request.getId(),
+ request.getRequester().getId(),
+ request.getStatus().name());
+ }
+
+ public List toDtoList(List requests) {
+ if (requests == null) {
+ return List.of();
+ }
+ return requests.stream().map(ParticipationRequestMapper::toDto).toList();
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/request/model/EventRequestStatus.java b/main-service/src/main/java/ru/practicum/request/model/EventRequestStatus.java
new file mode 100644
index 0000000..fb21ff3
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/model/EventRequestStatus.java
@@ -0,0 +1,8 @@
+package ru.practicum.request.model;
+
+public enum EventRequestStatus {
+ PENDING,
+ CONFIRMED,
+ REJECTED,
+ CANCELED
+}
diff --git a/main-service/src/main/java/ru/practicum/request/model/ParticipationRequest.java b/main-service/src/main/java/ru/practicum/request/model/ParticipationRequest.java
new file mode 100644
index 0000000..9e99701
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/model/ParticipationRequest.java
@@ -0,0 +1,56 @@
+package ru.practicum.request.model;
+
+import java.time.LocalDateTime;
+
+import jakarta.persistence.Column;
+import jakarta.persistence.Entity;
+import jakarta.persistence.EnumType;
+import jakarta.persistence.Enumerated;
+import jakarta.persistence.FetchType;
+import jakarta.persistence.GeneratedValue;
+import jakarta.persistence.GenerationType;
+import jakarta.persistence.Id;
+import jakarta.persistence.JoinColumn;
+import jakarta.persistence.ManyToOne;
+import jakarta.persistence.Table;
+
+import ru.practicum.event.model.Event;
+import ru.practicum.user.model.User;
+
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.FieldDefaults;
+
+@Entity
+@Table(name = "participation_request")
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class ParticipationRequest {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "event_id", nullable = false)
+ Event event;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "requester_id", nullable = false)
+ User requester;
+
+ @Column(nullable = false)
+ LocalDateTime created;
+
+ @Enumerated(EnumType.STRING)
+ @Column(nullable = false)
+ EventRequestStatus status;
+}
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
new file mode 100644
index 0000000..ad85ac6
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/repository/ParticipationRequestRepository.java
@@ -0,0 +1,51 @@
+package ru.practicum.request.repository;
+
+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);
+
+ List findAllByRequester_IdOrderByCreatedDesc(Long requesterId);
+
+ List findAllByEvent_IdOrderByCreatedAsc(Long eventId);
+
+ long countByEvent_IdAndStatus(Long eventId, EventRequestStatus status);
+
+ List 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));
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/request/service/ParticipationRequestService.java b/main-service/src/main/java/ru/practicum/request/service/ParticipationRequestService.java
new file mode 100644
index 0000000..543a5b0
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/service/ParticipationRequestService.java
@@ -0,0 +1,20 @@
+package ru.practicum.request.service;
+
+import java.util.List;
+
+import ru.practicum.request.dto.EventRequestStatusUpdateRequest;
+import ru.practicum.request.dto.EventRequestStatusUpdateResult;
+import ru.practicum.request.dto.ParticipationRequestDto;
+
+public interface ParticipationRequestService {
+ ParticipationRequestDto createRequest(Long userId, Long eventId);
+
+ List getUserRequests(Long userId);
+
+ ParticipationRequestDto cancelRequest(Long userId, Long requestId);
+
+ List getEventRequestsByInitiator(Long userId, Long eventId);
+
+ EventRequestStatusUpdateResult updateEventRequestsStatus(
+ Long userId, Long eventId, EventRequestStatusUpdateRequest request);
+}
diff --git a/main-service/src/main/java/ru/practicum/request/service/ParticipationRequestServiceImpl.java b/main-service/src/main/java/ru/practicum/request/service/ParticipationRequestServiceImpl.java
new file mode 100644
index 0000000..a4bd298
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/request/service/ParticipationRequestServiceImpl.java
@@ -0,0 +1,246 @@
+package ru.practicum.request.service;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import ru.practicum.event.model.Event;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.repository.EventRepository;
+import ru.practicum.exception.ConflictException;
+import ru.practicum.exception.ForbiddenAccessException;
+import ru.practicum.exception.NotFoundException;
+import ru.practicum.request.dto.EventRequestStatusUpdateRequest;
+import ru.practicum.request.dto.EventRequestStatusUpdateResult;
+import ru.practicum.request.dto.ParticipationRequestDto;
+import ru.practicum.request.mapper.ParticipationRequestMapper;
+import ru.practicum.request.model.EventRequestStatus;
+import ru.practicum.request.model.ParticipationRequest;
+import ru.practicum.request.repository.ParticipationRequestRepository;
+import ru.practicum.user.model.User;
+import ru.practicum.user.repository.UserRepository;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import lombok.RequiredArgsConstructor;
+
+@Service
+@RequiredArgsConstructor
+@Transactional(readOnly = true)
+public class ParticipationRequestServiceImpl implements ParticipationRequestService {
+
+ private final ParticipationRequestRepository requestRepository;
+ private final EventRepository eventRepository;
+ private final UserRepository userRepository;
+
+ @Override
+ @Transactional
+ public ParticipationRequestDto createRequest(Long userId, Long eventId) {
+ User user = getUserByIdOrThrow(userId);
+ Event event = getEventByIdOrThrow(eventId);
+
+ if (requestRepository.existsByEvent_IdAndRequester_Id(eventId, userId)) {
+ throw new ConflictException("Request already exists");
+ }
+
+ if (event.getInitiator().getId().equals(userId)) {
+ throw new ConflictException("Initiator can't participate in own event");
+ }
+
+ if (!EventState.PUBLISHED.equals(event.getState())) {
+ throw new ConflictException("Event must be published");
+ }
+
+ long confirmed =
+ requestRepository.countByEvent_IdAndStatus(eventId, EventRequestStatus.CONFIRMED);
+ if (event.getParticipantLimit() != null
+ && event.getParticipantLimit() > 0
+ && confirmed >= event.getParticipantLimit()) {
+ throw new ConflictException("Participant limit has been reached");
+ }
+
+ EventRequestStatus status = EventRequestStatus.PENDING;
+ if (Boolean.FALSE.equals(event.getRequestModeration())
+ || event.getParticipantLimit() == null
+ || event.getParticipantLimit() == 0) {
+ status = EventRequestStatus.CONFIRMED;
+ }
+
+ ParticipationRequest request =
+ ParticipationRequest.builder()
+ .event(event)
+ .requester(user)
+ .created(LocalDateTime.now())
+ .status(status)
+ .build();
+
+ ParticipationRequest saved = requestRepository.save(request);
+ return ParticipationRequestMapper.toDto(saved);
+ }
+
+ @Override
+ public List getUserRequests(Long userId) {
+ getUserByIdOrThrow(userId);
+ return ParticipationRequestMapper.toDtoList(
+ requestRepository.findAllByRequester_IdOrderByCreatedDesc(userId));
+ }
+
+ @Override
+ @Transactional
+ public ParticipationRequestDto cancelRequest(Long userId, Long requestId) {
+ getUserByIdOrThrow(userId);
+ ParticipationRequest request = getRequestByIdOrThrow(requestId);
+
+ if (!request.getRequester().getId().equals(userId)) {
+ throw new ForbiddenAccessException("You can't cancel request that is not yours");
+ }
+
+ request.setStatus(EventRequestStatus.CANCELED);
+ ParticipationRequest saved = requestRepository.save(request);
+ return ParticipationRequestMapper.toDto(saved);
+ }
+
+ @Override
+ public List getEventRequestsByInitiator(Long userId, Long eventId) {
+ getUserByIdOrThrow(userId);
+ Event event = getEventByIdOrThrow(eventId);
+
+ if (!event.getInitiator().getId().equals(userId)) {
+ throw new ForbiddenAccessException(
+ "You can't view requests for event that is not yours");
+ }
+
+ return ParticipationRequestMapper.toDtoList(
+ requestRepository.findAllByEvent_IdOrderByCreatedAsc(eventId));
+ }
+
+ @Override
+ @Transactional
+ public EventRequestStatusUpdateResult updateEventRequestsStatus(
+ Long userId, Long eventId, EventRequestStatusUpdateRequest updateRequest) {
+ getUserByIdOrThrow(userId);
+ Event event = getEventByIdOrThrow(eventId);
+
+ if (!event.getInitiator().getId().equals(userId)) {
+ throw new ForbiddenAccessException(
+ "You can't update requests for event that is not yours");
+ }
+
+ Set ids = new HashSet<>(updateRequest.requestIds());
+ List requests =
+ requestRepository.findAllByIdInAndEvent_Id(ids, eventId);
+ if (requests.size() != ids.size()) {
+ throw new NotFoundException("Some requests were not found");
+ }
+
+ for (ParticipationRequest r : requests) {
+ if (!EventRequestStatus.PENDING.equals(r.getStatus())) {
+ throw new ConflictException("Only PENDING requests can be updated");
+ }
+ }
+
+ EventRequestStatus targetStatus = updateRequest.status();
+ if (EventRequestStatus.CONFIRMED.equals(targetStatus)) {
+ return confirmRequests(event, requests);
+ }
+ if (EventRequestStatus.REJECTED.equals(targetStatus)) {
+ requests.forEach(r -> r.setStatus(EventRequestStatus.REJECTED));
+ requestRepository.saveAll(requests);
+ return new EventRequestStatusUpdateResult(
+ List.of(), ParticipationRequestMapper.toDtoList(requests));
+ }
+
+ throw new ConflictException("Unsupported status update: " + targetStatus);
+ }
+
+ private EventRequestStatusUpdateResult confirmRequests(
+ Event event, List requests) {
+ int limit = event.getParticipantLimit() == null ? 0 : event.getParticipantLimit();
+ boolean moderation = Boolean.TRUE.equals(event.getRequestModeration());
+
+ if (!moderation || limit == 0) {
+ requests.forEach(r -> r.setStatus(EventRequestStatus.CONFIRMED));
+ requestRepository.saveAll(requests);
+ return new EventRequestStatusUpdateResult(
+ ParticipationRequestMapper.toDtoList(requests), List.of());
+ }
+
+ long confirmed =
+ requestRepository.countByEvent_IdAndStatus(
+ event.getId(), EventRequestStatus.CONFIRMED);
+ long available = limit - confirmed;
+ if (available <= 0) {
+ throw new ConflictException("Participant limit has been reached");
+ }
+
+ List confirmedRequests;
+ List rejectedRequests;
+
+ if (requests.size() <= available) {
+ requests.forEach(r -> r.setStatus(EventRequestStatus.CONFIRMED));
+ requestRepository.saveAll(requests);
+ confirmedRequests = requests;
+ rejectedRequests = List.of();
+ } else {
+ confirmedRequests = requests.subList(0, (int) available);
+ rejectedRequests = requests.subList((int) available, requests.size());
+ confirmedRequests.forEach(r -> r.setStatus(EventRequestStatus.CONFIRMED));
+ rejectedRequests.forEach(r -> r.setStatus(EventRequestStatus.REJECTED));
+ requestRepository.saveAll(requests);
+ }
+
+ long nowConfirmed = confirmed + confirmedRequests.size();
+ if (nowConfirmed >= limit) {
+ List pendingToReject =
+ requestRepository.findAllByEvent_IdAndStatus(
+ event.getId(), EventRequestStatus.PENDING);
+
+ Set touched = idsOf(requests);
+ List toReject =
+ pendingToReject.stream()
+ .filter(r -> !touched.contains(r.getId()))
+ .collect(Collectors.toList());
+ if (!toReject.isEmpty()) {
+ toReject.forEach(r -> r.setStatus(EventRequestStatus.REJECTED));
+ requestRepository.saveAll(toReject);
+ }
+ }
+
+ return new EventRequestStatusUpdateResult(
+ ParticipationRequestMapper.toDtoList(confirmedRequests),
+ ParticipationRequestMapper.toDtoList(rejectedRequests));
+ }
+
+ private static Set idsOf(Collection requests) {
+ return requests.stream().map(ParticipationRequest::getId).collect(Collectors.toSet());
+ }
+
+ private User getUserByIdOrThrow(Long userId) {
+ return userRepository
+ .findById(userId)
+ .orElseThrow(
+ () -> new NotFoundException("User with id=%d not found".formatted(userId)));
+ }
+
+ private Event getEventByIdOrThrow(Long eventId) {
+ return eventRepository
+ .findById(eventId)
+ .orElseThrow(
+ () ->
+ new NotFoundException(
+ "Event with id=%d not found".formatted(eventId)));
+ }
+
+ private ParticipationRequest getRequestByIdOrThrow(Long requestId) {
+ return requestRepository
+ .findById(requestId)
+ .orElseThrow(
+ () ->
+ new NotFoundException(
+ "Request with id=%d not found".formatted(requestId)));
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/user/controller/UserAdminController.java b/main-service/src/main/java/ru/practicum/user/controller/UserAdminController.java
new file mode 100644
index 0000000..b8c4699
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/controller/UserAdminController.java
@@ -0,0 +1,53 @@
+package ru.practicum.user.controller;
+
+import java.util.Collection;
+import java.util.List;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+
+import ru.practicum.user.dto.NewUserRequest;
+import ru.practicum.user.dto.UserDto;
+import ru.practicum.user.service.UserService;
+import ru.practicum.user.service.UsersGetRequest;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/admin/users")
+public class UserAdminController {
+ private final UserService userService;
+
+ @GetMapping()
+ public Collection getUsersPaged(
+ @RequestParam(required = false) List ids,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size) {
+ log.info("Get users requested with ids={} from={} size={}", ids, from, size);
+ UsersGetRequest request = new UsersGetRequest(from, size, ids);
+ return userService.getUsersPaged(request);
+ }
+
+ @PostMapping()
+ @ResponseStatus(HttpStatus.CREATED)
+ public UserDto createUser(@RequestBody @Valid NewUserRequest newUserRequest) {
+ log.info("Create user requested: {}", newUserRequest);
+ return userService.createUser(newUserRequest);
+ }
+
+ @DeleteMapping("/{userId}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteUser(@PathVariable Long userId) {
+ log.info("Delete user with id={} requested", userId);
+ userService.deleteUserById(userId);
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/user/dto/NewUserRequest.java b/main-service/src/main/java/ru/practicum/user/dto/NewUserRequest.java
new file mode 100644
index 0000000..d52d433
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/dto/NewUserRequest.java
@@ -0,0 +1,9 @@
+package ru.practicum.user.dto;
+
+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+
+public record NewUserRequest(
+ @Size(min = 6, max = 254) @Email @NotBlank String email,
+ @Size(min = 2, max = 250) @NotBlank String name) {}
diff --git a/main-service/src/main/java/ru/practicum/user/dto/UserDto.java b/main-service/src/main/java/ru/practicum/user/dto/UserDto.java
new file mode 100644
index 0000000..15c6cb7
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/dto/UserDto.java
@@ -0,0 +1,3 @@
+package ru.practicum.user.dto;
+
+public record UserDto(String email, Long id, String name) {}
diff --git a/main-service/src/main/java/ru/practicum/user/dto/UserShortDto.java b/main-service/src/main/java/ru/practicum/user/dto/UserShortDto.java
new file mode 100644
index 0000000..6eb0c9e
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/dto/UserShortDto.java
@@ -0,0 +1,3 @@
+package ru.practicum.user.dto;
+
+public record UserShortDto(Long id, String name) {}
diff --git a/main-service/src/main/java/ru/practicum/user/mapper/UserMapper.java b/main-service/src/main/java/ru/practicum/user/mapper/UserMapper.java
new file mode 100644
index 0000000..79dd42f
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/mapper/UserMapper.java
@@ -0,0 +1,23 @@
+package ru.practicum.user.mapper;
+
+import ru.practicum.user.dto.NewUserRequest;
+import ru.practicum.user.dto.UserDto;
+import ru.practicum.user.dto.UserShortDto;
+import ru.practicum.user.model.User;
+
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class UserMapper {
+ public User mapToEntity(NewUserRequest dto) {
+ return new User(null, dto.name(), dto.email());
+ }
+
+ public UserDto mapToUserDto(User user) {
+ return new UserDto(user.getEmail(), user.getId(), user.getName());
+ }
+
+ public UserShortDto mapToUserShortDto(User user) {
+ return new UserShortDto(user.getId(), user.getName());
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/user/model/User.java b/main-service/src/main/java/ru/practicum/user/model/User.java
new file mode 100644
index 0000000..d58a759
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/model/User.java
@@ -0,0 +1,27 @@
+package ru.practicum.user.model;
+
+import jakarta.persistence.*;
+
+import lombok.experimental.FieldDefaults;
+import lombok.*;
+
+@Entity
+@Getter
+@Setter
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "users")
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class User {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ Long id;
+
+ @Column(nullable = false)
+ String name;
+
+ @Column(nullable = false, unique = true)
+ String email;
+}
diff --git a/main-service/src/main/java/ru/practicum/user/repository/UserRepository.java b/main-service/src/main/java/ru/practicum/user/repository/UserRepository.java
new file mode 100644
index 0000000..030e790
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/repository/UserRepository.java
@@ -0,0 +1,13 @@
+package ru.practicum.user.repository;
+
+import java.util.Collection;
+import java.util.List;
+
+import ru.practicum.user.model.User;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface UserRepository extends JpaRepository {
+ List findAllByIdIn(Collection ids, Pageable pageable);
+}
diff --git a/main-service/src/main/java/ru/practicum/user/service/UserService.java b/main-service/src/main/java/ru/practicum/user/service/UserService.java
new file mode 100644
index 0000000..88aa2ca
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/service/UserService.java
@@ -0,0 +1,14 @@
+package ru.practicum.user.service;
+
+import java.util.Collection;
+
+import ru.practicum.user.dto.NewUserRequest;
+import ru.practicum.user.dto.UserDto;
+
+public interface UserService {
+ Collection getUsersPaged(UsersGetRequest request);
+
+ UserDto createUser(NewUserRequest newUserRequest);
+
+ void deleteUserById(Long userId);
+}
diff --git a/main-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java b/main-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java
new file mode 100644
index 0000000..3e0879c
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/service/UserServiceImpl.java
@@ -0,0 +1,57 @@
+package ru.practicum.user.service;
+
+import java.util.Collection;
+
+import ru.practicum.exception.NotFoundException;
+import ru.practicum.user.dto.NewUserRequest;
+import ru.practicum.user.dto.UserDto;
+import ru.practicum.user.mapper.UserMapper;
+import ru.practicum.user.model.User;
+import ru.practicum.user.repository.UserRepository;
+
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import lombok.RequiredArgsConstructor;
+
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class UserServiceImpl implements UserService {
+ private final UserRepository userRepository;
+
+ @Override
+ @Transactional(readOnly = true)
+ public Collection getUsersPaged(UsersGetRequest request) {
+ Pageable pageable = PageRequest.of(request.from(), request.size());
+
+ if (request.hasIds()) {
+ return userRepository.findAllByIdIn(request.ids(), pageable).stream()
+ .map(UserMapper::mapToUserDto)
+ .toList();
+ }
+
+ return userRepository.findAll(pageable).stream().map(UserMapper::mapToUserDto).toList();
+ }
+
+ @Override
+ public UserDto createUser(NewUserRequest newUserRequest) {
+ User user = UserMapper.mapToEntity(newUserRequest);
+ User savedUser = userRepository.save(user);
+ return UserMapper.mapToUserDto(savedUser);
+ }
+
+ @Override
+ public void deleteUserById(Long userId) {
+ getUserByIdOrThrow(userId);
+ userRepository.deleteById(userId);
+ }
+
+ private User getUserByIdOrThrow(Long userId) {
+ return userRepository
+ .findById(userId)
+ .orElseThrow(NotFoundException.supplier("User with id=%d not found", userId));
+ }
+}
diff --git a/main-service/src/main/java/ru/practicum/user/service/UsersGetRequest.java b/main-service/src/main/java/ru/practicum/user/service/UsersGetRequest.java
new file mode 100644
index 0000000..3fec225
--- /dev/null
+++ b/main-service/src/main/java/ru/practicum/user/service/UsersGetRequest.java
@@ -0,0 +1,9 @@
+package ru.practicum.user.service;
+
+import java.util.List;
+
+public record UsersGetRequest(int from, int size, List ids) {
+ public boolean hasIds() {
+ return ids != null && !ids.isEmpty();
+ }
+}
diff --git a/main-service/src/main/resources/application-dev.yaml b/main-service/src/main/resources/application-dev.yaml
index 2745e97..0c0b25f 100644
--- a/main-service/src/main/resources/application-dev.yaml
+++ b/main-service/src/main/resources/application-dev.yaml
@@ -4,12 +4,13 @@ server:
stats-server:
url: http://localhost:9090
app: ${STATS_SERVER_APP:ewm-main-service}
+
spring:
application:
- name: stat-server
+ name: ewm-service
datasource:
- url: jdbc:h2:mem:stat-db
+ url: jdbc:h2:mem:ewm-db
driver-class-name: org.h2.Driver
username: sa
password:
@@ -25,3 +26,10 @@ spring:
sql:
init:
mode: embedded
+logging:
+ level:
+ org:
+ hibernate:
+ orm:
+ jdbc:
+ bind: trace
diff --git a/main-service/src/main/resources/application.yaml b/main-service/src/main/resources/application.yaml
index f44e14a..9e5b089 100644
--- a/main-service/src/main/resources/application.yaml
+++ b/main-service/src/main/resources/application.yaml
@@ -7,3 +7,9 @@ spring:
url: ${SPRING_DATASOURCE_URL}
username: ${SPRING_DATASOURCE_USERNAME}
password: ${SPRING_DATASOURCE_PASSWORD}
+ sql:
+ init:
+ mode: always
+ jpa:
+ hibernate:
+ ddl-auto: validate
diff --git a/main-service/src/main/resources/schema.sql b/main-service/src/main/resources/schema.sql
new file mode 100644
index 0000000..33d7a92
--- /dev/null
+++ b/main-service/src/main/resources/schema.sql
@@ -0,0 +1,63 @@
+CREATE TABLE category
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ name VARCHAR(50) NOT NULL UNIQUE
+);
+
+CREATE TABLE users
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ name VARCHAR(250) NOT NULL,
+ email VARCHAR(254) NOT NULL UNIQUE
+);
+
+CREATE TABLE event
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ title VARCHAR(120) NOT NULL,
+ annotation VARCHAR(2000) NOT NULL,
+ description VARCHAR(7000) NOT NULL,
+ event_date TIMESTAMP NOT NULL,
+ created_on TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ published_on TIMESTAMP,
+ paid BOOLEAN DEFAULT FALSE,
+ participant_limit INT DEFAULT 0,
+ request_moderation BOOLEAN DEFAULT TRUE,
+ state VARCHAR(20) DEFAULT 'PENDING' CHECK (state IN ('PENDING', 'PUBLISHED', 'CANCELED')),
+
+ location_lat NUMERIC(9, 6) NOT NULL,
+ location_lon NUMERIC(9, 6) NOT NULL,
+
+ category_id BIGINT NOT NULL,
+ initiator_id BIGINT NOT NULL,
+
+ CONSTRAINT fk_event_category FOREIGN KEY (category_id) REFERENCES category (id),
+ CONSTRAINT fk_event_user FOREIGN KEY (initiator_id) REFERENCES users (id)
+);
+
+CREATE TABLE compilation
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ title VARCHAR(50) NOT NULL,
+ pinned BOOLEAN DEFAULT FALSE
+);
+
+CREATE TABLE compilation_events
+(
+ compilation_id BIGINT NOT NULL,
+ event_id BIGINT NOT NULL,
+ PRIMARY KEY (compilation_id, event_id),
+ CONSTRAINT fk_compilation FOREIGN KEY (compilation_id) REFERENCES compilation (id) ON DELETE CASCADE,
+ CONSTRAINT fk_event FOREIGN KEY (event_id) REFERENCES event (id) ON DELETE CASCADE
+);
+
+CREATE TABLE participation_request
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ created TIMESTAMP NOT NULL,
+ status VARCHAR(20) NOT NULL,
+ event_id BIGINT NOT NULL,
+ requester_id BIGINT NOT NULL,
+ CONSTRAINT fk_request_event FOREIGN KEY (event_id) REFERENCES event (id),
+ CONSTRAINT fk_request_user FOREIGN KEY (requester_id) REFERENCES users (id)
+);
diff --git a/main-service/src/test/java/ru/practicum/request/ParticipationRequestBatchUpdateIT.java b/main-service/src/test/java/ru/practicum/request/ParticipationRequestBatchUpdateIT.java
new file mode 100644
index 0000000..b8f441b
--- /dev/null
+++ b/main-service/src/test/java/ru/practicum/request/ParticipationRequestBatchUpdateIT.java
@@ -0,0 +1,136 @@
+package ru.practicum.request;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+import ru.practicum.category.model.Category;
+import ru.practicum.category.repository.CategoryRepository;
+import ru.practicum.event.model.Event;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.model.Location;
+import ru.practicum.event.repository.EventRepository;
+import ru.practicum.request.dto.EventRequestStatusUpdateRequest;
+import ru.practicum.request.dto.EventRequestStatusUpdateResult;
+import ru.practicum.request.dto.ParticipationRequestDto;
+import ru.practicum.request.model.EventRequestStatus;
+import ru.practicum.request.repository.ParticipationRequestRepository;
+import ru.practicum.request.service.ParticipationRequestService;
+import ru.practicum.user.model.User;
+import ru.practicum.user.repository.UserRepository;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.transaction.annotation.Transactional;
+
+@SpringBootTest
+@Transactional
+class ParticipationRequestBatchUpdateIT {
+
+ @Autowired private ParticipationRequestService requestService;
+
+ @Autowired private ParticipationRequestRepository requestRepository;
+
+ @Autowired private UserRepository userRepository;
+
+ @Autowired private CategoryRepository categoryRepository;
+
+ @Autowired private EventRepository eventRepository;
+
+ private User initiator;
+ private User u1;
+ private User u2;
+ private User u3;
+ private Category category;
+
+ @BeforeEach
+ void setUp() {
+ requestRepository.deleteAll();
+ eventRepository.deleteAll();
+ userRepository.deleteAll();
+ categoryRepository.deleteAll();
+
+ initiator = userRepository.save(User.builder().name("init").email("init@mail.com").build());
+ u1 = userRepository.save(User.builder().name("u1").email("u1@mail.com").build());
+ u2 = userRepository.save(User.builder().name("u2").email("u2@mail.com").build());
+ u3 = userRepository.save(User.builder().name("u3").email("u3@mail.com").build());
+ category = categoryRepository.save(Category.builder().name("cat").build());
+ }
+
+ @Test
+ void batchConfirm_shouldConfirmUpToLimit_andRejectRest_andRejectOtherPendingWhenLimitReached() {
+ Event event = eventRepository.save(buildEvent(EventState.PUBLISHED, 2, true));
+
+ ParticipationRequestDto r1 = requestService.createRequest(u1.getId(), event.getId());
+ ParticipationRequestDto r2 = requestService.createRequest(u2.getId(), event.getId());
+ ParticipationRequestDto r3 = requestService.createRequest(u3.getId(), event.getId());
+
+ EventRequestStatusUpdateResult result =
+ requestService.updateEventRequestsStatus(
+ initiator.getId(),
+ event.getId(),
+ new EventRequestStatusUpdateRequest(
+ List.of(r1.id(), r2.id(), r3.id()), EventRequestStatus.CONFIRMED));
+
+ assertEquals(2, result.confirmedRequests().size());
+ assertEquals(1, result.rejectedRequests().size());
+
+ List all =
+ requestService.getEventRequestsByInitiator(initiator.getId(), event.getId());
+
+ long confirmed =
+ all.stream()
+ .filter(r -> r.status().equals(EventRequestStatus.CONFIRMED.name()))
+ .count();
+ long rejected =
+ all.stream()
+ .filter(r -> r.status().equals(EventRequestStatus.REJECTED.name()))
+ .count();
+
+ assertEquals(2, confirmed);
+ assertEquals(1, rejected);
+ }
+
+ @Test
+ void batchReject_shouldRejectOnlyProvidedPending() {
+ Event event = eventRepository.save(buildEvent(EventState.PUBLISHED, 10, true));
+
+ ParticipationRequestDto r1 = requestService.createRequest(u1.getId(), event.getId());
+ ParticipationRequestDto r2 = requestService.createRequest(u2.getId(), event.getId());
+
+ EventRequestStatusUpdateResult result =
+ requestService.updateEventRequestsStatus(
+ initiator.getId(),
+ event.getId(),
+ new EventRequestStatusUpdateRequest(
+ List.of(r1.id(), r2.id()), EventRequestStatus.REJECTED));
+
+ assertEquals(0, result.confirmedRequests().size());
+ assertEquals(2, result.rejectedRequests().size());
+ }
+
+ private Event buildEvent(EventState state, int participantLimit, boolean moderation) {
+ return Event.builder()
+ .title("title")
+ .annotation("ann")
+ .description("desc")
+ .createdOn(LocalDateTime.now().minusMinutes(1))
+ .eventDate(LocalDateTime.now().plusDays(1))
+ .paid(false)
+ .participantLimit(participantLimit)
+ .requestModeration(moderation)
+ .state(state)
+ .initiator(initiator)
+ .category(category)
+ .location(
+ Location.builder()
+ .lat(BigDecimal.valueOf(55.755800))
+ .lon(BigDecimal.valueOf(37.617300))
+ .build())
+ .build();
+ }
+}
diff --git a/main-service/src/test/java/ru/practicum/request/ParticipationRequestServiceIT.java b/main-service/src/test/java/ru/practicum/request/ParticipationRequestServiceIT.java
new file mode 100644
index 0000000..8965c6f
--- /dev/null
+++ b/main-service/src/test/java/ru/practicum/request/ParticipationRequestServiceIT.java
@@ -0,0 +1,122 @@
+package ru.practicum.request;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+import ru.practicum.category.model.Category;
+import ru.practicum.category.repository.CategoryRepository;
+import ru.practicum.event.model.Event;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.model.Location;
+import ru.practicum.event.repository.EventRepository;
+import ru.practicum.exception.ConflictException;
+import ru.practicum.request.dto.ParticipationRequestDto;
+import ru.practicum.request.model.EventRequestStatus;
+import ru.practicum.request.repository.ParticipationRequestRepository;
+import ru.practicum.request.service.ParticipationRequestService;
+import ru.practicum.user.model.User;
+import ru.practicum.user.repository.UserRepository;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.transaction.annotation.Transactional;
+
+@SpringBootTest
+@Transactional
+class ParticipationRequestServiceIT {
+
+ @Autowired private ParticipationRequestService requestService;
+
+ @Autowired private ParticipationRequestRepository requestRepository;
+
+ @Autowired private UserRepository userRepository;
+
+ @Autowired private CategoryRepository categoryRepository;
+
+ @Autowired private EventRepository eventRepository;
+
+ private User initiator;
+ private User requester;
+ private Category category;
+
+ @BeforeEach
+ void setUp() {
+ requestRepository.deleteAll();
+ eventRepository.deleteAll();
+ userRepository.deleteAll();
+ categoryRepository.deleteAll();
+
+ initiator = userRepository.save(User.builder().name("init").email("init@mail.com").build());
+ requester = userRepository.save(User.builder().name("req").email("req@mail.com").build());
+ category = categoryRepository.save(Category.builder().name("cat").build());
+ }
+
+ @Test
+ void createRequest_shouldConfirmImmediately_whenModerationOff() {
+ Event event = eventRepository.save(buildEvent(EventState.PUBLISHED, 10, false));
+
+ ParticipationRequestDto dto =
+ requestService.createRequest(requester.getId(), event.getId());
+
+ assertEquals(event.getId(), dto.event());
+ assertEquals(requester.getId(), dto.requester());
+ assertEquals(EventRequestStatus.CONFIRMED.name(), dto.status());
+ assertNotNull(dto.created());
+ assertTrue(requestRepository.existsById(dto.id()));
+ }
+
+ @Test
+ void createRequest_shouldThrowConflict_whenEventNotPublished() {
+ Event event = eventRepository.save(buildEvent(EventState.PENDING, 10, true));
+
+ assertThrows(
+ ConflictException.class,
+ () -> requestService.createRequest(requester.getId(), event.getId()));
+ }
+
+ @Test
+ void createRequest_shouldThrowConflict_whenRequesterIsInitiator() {
+ Event event = eventRepository.save(buildEvent(EventState.PUBLISHED, 10, true));
+
+ assertThrows(
+ ConflictException.class,
+ () -> requestService.createRequest(initiator.getId(), event.getId()));
+ }
+
+ @Test
+ void cancelRequest_shouldSetCanceledStatus() {
+ Event event = eventRepository.save(buildEvent(EventState.PUBLISHED, 10, true));
+
+ ParticipationRequestDto created =
+ requestService.createRequest(requester.getId(), event.getId());
+ ParticipationRequestDto canceled =
+ requestService.cancelRequest(requester.getId(), created.id());
+
+ assertEquals(EventRequestStatus.CANCELED.name(), canceled.status());
+ }
+
+ private Event buildEvent(EventState state, int participantLimit, boolean moderation) {
+ return Event.builder()
+ .title("title")
+ .annotation("ann")
+ .description("desc")
+ .createdOn(LocalDateTime.now().minusMinutes(1))
+ .eventDate(LocalDateTime.now().plusDays(1))
+ .paid(false)
+ .participantLimit(participantLimit)
+ .requestModeration(moderation)
+ .state(state)
+ .initiator(initiator)
+ .category(category)
+ .location(
+ Location.builder()
+ .lat(BigDecimal.valueOf(55.755800))
+ .lon(BigDecimal.valueOf(37.617300))
+ .build())
+ .build();
+ }
+}
diff --git a/main-service/src/test/java/ru/practicum/request/ParticipationRequestsControllerIT.java b/main-service/src/test/java/ru/practicum/request/ParticipationRequestsControllerIT.java
new file mode 100644
index 0000000..c031aa5
--- /dev/null
+++ b/main-service/src/test/java/ru/practicum/request/ParticipationRequestsControllerIT.java
@@ -0,0 +1,421 @@
+package ru.practicum.request;
+
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+
+import ru.practicum.category.model.Category;
+import ru.practicum.category.repository.CategoryRepository;
+import ru.practicum.event.model.Event;
+import ru.practicum.event.model.EventState;
+import ru.practicum.event.model.Location;
+import ru.practicum.event.repository.EventRepository;
+import ru.practicum.request.model.EventRequestStatus;
+import ru.practicum.request.repository.ParticipationRequestRepository;
+import ru.practicum.user.model.User;
+import ru.practicum.user.repository.UserRepository;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.transaction.annotation.Transactional;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+@SpringBootTest
+@AutoConfigureMockMvc
+@ActiveProfiles("dev")
+@Transactional
+class ParticipationRequestsIT {
+
+ @Autowired MockMvc mvc;
+ @Autowired ObjectMapper om;
+
+ @Autowired UserRepository userRepository;
+ @Autowired CategoryRepository categoryRepository;
+ @Autowired EventRepository eventRepository;
+ @Autowired ParticipationRequestRepository requestRepository;
+
+ private Long initiatorId;
+ private Long requesterId;
+ private Long publishedEventId;
+
+ @BeforeEach
+ void setUp() {
+ User initiator =
+ userRepository.save(
+ User.builder().name("initiator").email("initiator@mail.com").build());
+ User requester =
+ userRepository.save(
+ User.builder().name("requester").email("requester@mail.com").build());
+
+ Category category =
+ categoryRepository.save(
+ Category.builder().name("cat-" + System.nanoTime()).build());
+
+ Event published =
+ eventRepository.save(
+ Event.builder()
+ .annotation("annotation-annotation-annotation")
+ .category(category)
+ .createdOn(LocalDateTime.now())
+ .description("description-description-description")
+ .eventDate(LocalDateTime.now().plusDays(3))
+ .initiator(initiator)
+ .location(
+ Location.builder()
+ .lat(new BigDecimal("55.750000"))
+ .lon(new BigDecimal("37.610000"))
+ .build())
+ .paid(false)
+ .participantLimit(10)
+ .requestModeration(true)
+ .state(EventState.PUBLISHED)
+ .title("title")
+ .publishedOn(LocalDateTime.now())
+ .build());
+
+ initiatorId = initiator.getId();
+ requesterId = requester.getId();
+ publishedEventId = published.getId();
+ }
+
+ @Test
+ void createRequest_ok_pending() throws Exception {
+ mvc.perform(
+ post("/users/{userId}/requests", requesterId)
+ .queryParam("eventId", String.valueOf(publishedEventId))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$.id", notNullValue()))
+ .andExpect(jsonPath("$.event", is(publishedEventId.intValue())))
+ .andExpect(jsonPath("$.requester", is(requesterId.intValue())))
+ .andExpect(jsonPath("$.created", notNullValue()))
+ .andExpect(jsonPath("$.status", is(EventRequestStatus.PENDING.name())));
+ }
+
+ @Test
+ void createRequest_withoutEventId_400() throws Exception {
+ mvc.perform(
+ post("/users/{userId}/requests", requesterId)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void createRequest_duplicate_409() throws Exception {
+ createRequestAndGetId(requesterId, publishedEventId);
+
+ mvc.perform(
+ post("/users/{userId}/requests", requesterId)
+ .queryParam("eventId", String.valueOf(publishedEventId))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ void createRequest_byInitiator_409() throws Exception {
+ mvc.perform(
+ post("/users/{userId}/requests", initiatorId)
+ .queryParam("eventId", String.valueOf(publishedEventId))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ void createRequest_onNotPublished_409() throws Exception {
+ User initiator = userRepository.findById(initiatorId).orElseThrow();
+ Category category = categoryRepository.findAll().stream().findFirst().orElseThrow();
+
+ Event pending =
+ eventRepository.save(
+ Event.builder()
+ .annotation("annotation-annotation-annotation")
+ .category(category)
+ .createdOn(LocalDateTime.now())
+ .description("description-description-description")
+ .eventDate(LocalDateTime.now().plusDays(3))
+ .initiator(initiator)
+ .location(
+ Location.builder()
+ .lat(new BigDecimal("55.750000"))
+ .lon(new BigDecimal("37.610000"))
+ .build())
+ .paid(false)
+ .participantLimit(10)
+ .requestModeration(true)
+ .state(EventState.PENDING)
+ .title("title")
+ .build());
+
+ mvc.perform(
+ post("/users/{userId}/requests", requesterId)
+ .queryParam("eventId", String.valueOf(pending.getId()))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ void createRequest_limitReached_409() throws Exception {
+ User initiator = userRepository.findById(initiatorId).orElseThrow();
+ Category category = categoryRepository.findAll().stream().findFirst().orElseThrow();
+
+ Event event =
+ eventRepository.save(
+ Event.builder()
+ .annotation("annotation-annotation-annotation")
+ .category(category)
+ .createdOn(LocalDateTime.now())
+ .description("description-description-description")
+ .eventDate(LocalDateTime.now().plusDays(3))
+ .initiator(initiator)
+ .location(
+ Location.builder()
+ .lat(new BigDecimal("55.750000"))
+ .lon(new BigDecimal("37.610000"))
+ .build())
+ .paid(false)
+ .participantLimit(1)
+ .requestModeration(true)
+ .state(EventState.PUBLISHED)
+ .title("title")
+ .publishedOn(LocalDateTime.now())
+ .build());
+
+ long r1 = createRequestAndGetId(requesterId, event.getId());
+ String body =
+ om.writeValueAsString(Map.of("requestIds", List.of(r1), "status", "CONFIRMED"));
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ event.getId())
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(body)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk());
+
+ User requester2 =
+ userRepository.save(User.builder().name("r2").email("r2@mail.com").build());
+
+ mvc.perform(
+ post("/users/{userId}/requests", requester2.getId())
+ .queryParam("eventId", String.valueOf(event.getId()))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ void createRequest_moderationFalse_autoConfirmed() throws Exception {
+ User initiator = userRepository.findById(initiatorId).orElseThrow();
+ Category category = categoryRepository.findAll().stream().findFirst().orElseThrow();
+
+ Event event =
+ eventRepository.save(
+ Event.builder()
+ .annotation("annotation-annotation-annotation")
+ .category(category)
+ .createdOn(LocalDateTime.now())
+ .description("description-description-description")
+ .eventDate(LocalDateTime.now().plusDays(3))
+ .initiator(initiator)
+ .location(
+ Location.builder()
+ .lat(new BigDecimal("55.750000"))
+ .lon(new BigDecimal("37.610000"))
+ .build())
+ .paid(false)
+ .participantLimit(10)
+ .requestModeration(false)
+ .state(EventState.PUBLISHED)
+ .title("title")
+ .publishedOn(LocalDateTime.now())
+ .build());
+
+ mvc.perform(
+ post("/users/{userId}/requests", requesterId)
+ .queryParam("eventId", String.valueOf(event.getId()))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.status", is(EventRequestStatus.CONFIRMED.name())));
+ }
+
+ @Test
+ void getUserRequests_ok_containsCreated() throws Exception {
+ long reqId = createRequestAndGetId(requesterId, publishedEventId);
+
+ mvc.perform(get("/users/{userId}/requests", requesterId).accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
+ .andExpect(jsonPath("$[*].id", hasItem((int) reqId)));
+ }
+
+ @Test
+ void cancelRequest_ok_canceled() throws Exception {
+ long reqId = createRequestAndGetId(requesterId, publishedEventId);
+
+ mvc.perform(
+ patch("/users/{userId}/requests/{requestId}/cancel", requesterId, reqId)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.id", is((int) reqId)))
+ .andExpect(jsonPath("$.status", is(EventRequestStatus.CANCELED.name())));
+ }
+
+ @Test
+ void getEventRequestsByInitiator_ok_containsCreated() throws Exception {
+ long reqId = createRequestAndGetId(requesterId, publishedEventId);
+
+ mvc.perform(
+ get(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$[*].id", hasItem((int) reqId)));
+ }
+
+ @Test
+ void updateRequestsStatus_confirm_ok() throws Exception {
+ long reqId = createRequestAndGetId(requesterId, publishedEventId);
+
+ String body =
+ om.writeValueAsString(Map.of("requestIds", List.of(reqId), "status", "CONFIRMED"));
+
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(body)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.confirmedRequests[0].id", is((int) reqId)))
+ .andExpect(
+ jsonPath(
+ "$.confirmedRequests[0].status",
+ is(EventRequestStatus.CONFIRMED.name())));
+ }
+
+ @Test
+ void updateRequestsStatus_reject_ok() throws Exception {
+ long reqId = createRequestAndGetId(requesterId, publishedEventId);
+
+ String body =
+ om.writeValueAsString(Map.of("requestIds", List.of(reqId), "status", "REJECTED"));
+
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(body)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.rejectedRequests[0].id", is((int) reqId)))
+ .andExpect(
+ jsonPath(
+ "$.rejectedRequests[0].status",
+ is(EventRequestStatus.REJECTED.name())));
+ }
+
+ @Test
+ void updateRequestsStatus_nonPending_409() throws Exception {
+ long reqId = createRequestAndGetId(requesterId, publishedEventId);
+
+ String confirm =
+ om.writeValueAsString(Map.of("requestIds", List.of(reqId), "status", "CONFIRMED"));
+
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(confirm)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk());
+
+ String reject =
+ om.writeValueAsString(Map.of("requestIds", List.of(reqId), "status", "REJECTED"));
+
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(reject)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isConflict());
+ }
+
+ @Test
+ void updateRequestsStatus_emptyIds_400() throws Exception {
+ String body = om.writeValueAsString(Map.of("requestIds", List.of(), "status", "CONFIRMED"));
+
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(body)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isBadRequest());
+ }
+
+ @Test
+ void updateRequestsStatus_nullStatus_400() throws Exception {
+ String body =
+ "{\"requestIds\":["
+ + createRequestAndGetId(requesterId, publishedEventId)
+ + "],\"status\":null}";
+
+ mvc.perform(
+ patch(
+ "/users/{userId}/events/{eventId}/requests",
+ initiatorId,
+ publishedEventId)
+ .contentType(MediaType.APPLICATION_JSON)
+ .content(body)
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isBadRequest());
+ }
+
+ private long createRequestAndGetId(long userId, long eventId) throws Exception {
+ String json =
+ mvc.perform(
+ post("/users/{userId}/requests", userId)
+ .queryParam("eventId", String.valueOf(eventId))
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andReturn()
+ .getResponse()
+ .getContentAsString();
+
+ JsonNode node = om.readTree(json);
+ return node.get("id").asLong();
+ }
+}
diff --git a/main-service/src/test/resources/application-test.yaml b/main-service/src/test/resources/application-test.yaml
new file mode 100644
index 0000000..a7b2c55
--- /dev/null
+++ b/main-service/src/test/resources/application-test.yaml
@@ -0,0 +1,26 @@
+spring:
+ datasource:
+ url: jdbc:h2:mem:ewm-test-db;MODE=PostgreSQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE
+ driver-class-name: org.h2.Driver
+ username: sa
+ password:
+
+ sql:
+ init:
+ mode: always
+ schema-locations: classpath:schema.sql
+
+ jpa:
+ hibernate:
+ ddl-auto: none
+ properties:
+ hibernate:
+ format_sql: true
+
+logging:
+ level:
+ org.hibernate.SQL: debug
+
+stats-server:
+ url: http://localhost:9090
+ app: ewm-main-service
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index bf1fdc6..268da56 100644
--- a/pom.xml
+++ b/pom.xml
@@ -25,8 +25,19 @@
21
UTF-8
+ 5.1.0
+
+
+
+ com.querydsl
+ querydsl-jpa
+ ${querydsl.version}
+ jakarta
+
+
+
diff --git a/postman/ewm-main-service.json b/postman/ewm-main-service.json
new file mode 100644
index 0000000..62b600e
--- /dev/null
+++ b/postman/ewm-main-service.json
@@ -0,0 +1,14630 @@
+{
+ "info": {
+ "_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": "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": [
+ {
+ "name": "Validation",
+ "item": [
+ {
+ "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": [
+ {
+ "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",
+ " 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",
+ "\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",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "query": [
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество элементов в наборе",
+ "disabled": true
+ }
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "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",
+ " 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",
+ " } 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}}/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "events"
+ ],
+ "query": [
+ {
+ "key": "text",
+ "value": "0",
+ "description": "текст для поиска в содержимом аннотации и подробном описании события",
+ "disabled": true
+ },
+ {
+ "key": "categories",
+ "value": "0",
+ "description": "список идентификаторов категорий в которых будет вестись поиск",
+ "disabled": true
+ },
+ {
+ "key": "paid",
+ "value": "true",
+ "description": "поиск только платных/бесплатных событий",
+ "disabled": true
+ },
+ {
+ "key": "rangeStart",
+ "value": "2022-01-06%2013%3A30%3A38",
+ "description": "дата и время не раньше которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "rangeEnd",
+ "value": "2097-09-06%2013%3A30%3A38",
+ "description": "дата и время не позже которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "onlyAvailable",
+ "value": "false",
+ "description": "только события у которых не исчерпан лимит запросов на участие",
+ "disabled": true
+ },
+ {
+ "key": "sort",
+ "value": "EVENT_DATE",
+ "description": "Вариант сортировки: по дате события или по количеству просмотров",
+ "disabled": true
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество событий, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество событий в наборе",
+ "disabled": true
+ }
+ ]
+ },
+ "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики"
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Required params in body",
+ "item": [
+ {
+ "name": "Добавление события без поля description",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " delete event[\"description\"];\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ " event[\"description\"] = '';\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ " event[\"description\"] = ' ';\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление события без поля annotation",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " delete event[\"annotation\"];\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ " event[\"annotation\"] = '';\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ " event[\"annotation\"] = ' ';\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ " event[\"participantLimit\"] = -6;\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ " pm.request.body.update({\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify({\r",
+ " participantLimit : -156\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(\"Ответ должен содержать код статуса 400 и данные в формате json\", function () {\r",
+ " pm.response.to.have.status(400);\r",
+ " pm.response.to.be.withBody;\r",
+ " pm.response.to.be.json;\r",
+ "});\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": "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 new_event, event;\r",
+ " try {\r",
+ " const user = await api.addUser(rnd.getUser());\r",
+ " const category = await api.addCategory(rnd.getCategory());\r",
+ " event = await api.addEvent(user.id, rnd.getEvent(category.id));\r",
+ " await pm.sendRequest({\r",
+ " url : \"http://localhost:8080/admin/events/\" + event.id,\r",
+ " method : \"PATCH\",\r",
+ " header: { \"Content-Type\": \"application/json\" },\r",
+ " body: JSON.stringify({\r",
+ " stateAction: \"REJECT_EVENT\"\r",
+ " })\r",
+ " }, (error, response) => {\r",
+ "\r",
+ " });\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",
+ " stateAction: \"SEND_TO_REVIEW\"\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(\"Ответ должен содержать код статуса 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(\"Событие должно иметь статус CANCELED при возвращении от администратора и статус PENDING после выполнения запроса\", function () {\r",
+ " pm.expect(target.state).equal(\"PENDING\");\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 event = await api.addEvent(user.id, rnd.getEvent(category.id));\r",
+ " pm.collectionVariables.set(\"eid\", event.id)\r",
+ " pm.collectionVariables.set('response', event);\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(\"Ответ должен содержать код статуса 404 и данные в формате json\", function () {\r",
+ " pm.response.to.be.notFound; \r",
+ " pm.response.to.be.withBody;\r",
+ " pm.response.to.be.json;\r",
+ "});\r",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/events/:id",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "events",
+ ":id"
+ ],
+ "variable": [
+ {
+ "key": "id",
+ "value": "{{eid}}",
+ "description": "(Required) id события"
+ }
+ ]
+ },
+ "description": "Обратите внимание:\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 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": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " try {\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",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/events?text=0&categories=0&paid=true&rangeStart=2022-01-06%2013%3A30%3A38&rangeEnd=2007-09-06%2013%3A30%3A38&onlyAvailable=false&sort=EVENT_DATE&from=0&size=1000",
+ "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": "2007-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- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики"
+ },
+ "response": []
+ },
+ {
+ "name": "Проверка работы поля views",
+ "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",
+ " await api.findEvent(event.id)\r",
+ " await api.findEvent(event.id)\r",
+ " pm.collectionVariables.set(\"eid\", event.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",
+ "\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.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(\"Значение поля views должно увеличится на 1 после выполнения GET запроса с уникального IP к событию\", function () {\r",
+ " pm.expect(target.views).equal(1);\r",
+ " \r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/events/:id",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "events",
+ ":id"
+ ],
+ "variable": [
+ {
+ "key": "id",
+ "value": "{{eid}}",
+ "description": "(Required) id события"
+ }
+ ]
+ },
+ "description": "Обратите внимание:\n- событие должно быть опубликовано\n- информация о событии должна включать в себя количество просмотров и количество подтвержденных запросов\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление запроса на участие при participantLimit == 0",
+ "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'] = 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",
+ "};\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(\"Ответ должен содержать код статуса 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": "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",
+ " 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": [
+ "{{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",
+ " 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",
+ " 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",
+ " 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",
+ ""
+ ],
+ "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",
+ " 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",
+ " event.eventDate = \"2020-12-31 15:10:05\";\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",
+ "\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.have.status(400);\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "String length restrictions",
+ "item": [
+ {
+ "name": "Добавление нового события с description.length < 20",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.description = rnd.getWord(19);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с description.length < 20",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.description = rnd.getWord(19);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с annotation.length < 20",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.annotation = rnd.getWord(19);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с annotation.length > 2000",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.annotation = rnd.getWord(2001);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с description.length == 20 && annotation.length == 20 && title.length == 3",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.description = rnd.getWord(20);\r",
+ " event.annotation = rnd.getWord(20);\r",
+ " event.title = rnd.getWord(3);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с description.length == 7000 && annotation.length == 2000 && title.length == 120",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.description = rnd.getWord(7000);\r",
+ " event.annotation = rnd.getWord(2000);\r",
+ " event.title = rnd.getWord(120);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с title.length < 3",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.title = rnd.getWord(2);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление нового события с title.length > 120",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " event.title = rnd.getWord(121);\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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Изменение заголовка события с title.length < 3 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.title = rnd.getWord(2);\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",
+ "\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": [
+ {
+ "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": "Изменение заголовка события с title.length > 120 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.title = rnd.getWord(121);\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",
+ "\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": [
+ {
+ "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": "Изменение описания события с description.length < 20 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.description = rnd.getWord(19);\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",
+ "\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": [
+ {
+ "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": "Изменение описания события с description.length > 7000 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.description = rnd.getWord(7001);\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",
+ "\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": [
+ {
+ "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": "Изменение аннотации события с annotation.length < 20 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.annotation = rnd.getWord(19);\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",
+ "\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": [
+ {
+ "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": "Изменение аннотации события с annotation.length > 2000 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.annotation = rnd.getWord(2001);\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",
+ "\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": [
+ {
+ "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": "Изменение события с description.length == 20 && annotation.length == 20 && title.length == 3 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.annotation = rnd.getWord(20);\r",
+ " event2.description = rnd.getWord(20);\r",
+ " event2.title = rnd.getWord(3);\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",
+ "\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": [
+ {
+ "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": "Изменение события с description.length == 7000 && annotation.length == 2000 && title.length == 120 (admin endpoint)",
+ "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",
+ " let event2 = rnd.getEvent(category.id)\r",
+ " event2.annotation = rnd.getWord(2000);\r",
+ " event2.description = rnd.getWord(7000);\r",
+ " event2.title = rnd.getWord(120);\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",
+ "\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": [
+ {
+ "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": "Изменение заголовка события с title.length < 3 (user endpoint)",
+ "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",
+ " 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({\r",
+ " title: rnd.getWord(2)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение заголовка события с title.length > 120 (user endpoint)",
+ "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",
+ " 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({\r",
+ " title: rnd.getWord(121)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение описания события с description.length < 20 (user endpoint)",
+ "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",
+ " 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({\r",
+ " description: rnd.getWord(19)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение описания события с description.length > 7000 (user endpoint)",
+ "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",
+ " 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({\r",
+ " description: rnd.getWord(7001)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение аннотации события с annotation.length < 20 (user endpoint)",
+ "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",
+ " 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({\r",
+ " annotation: rnd.getWord(19)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение аннотации события с annotation.length > 2000 (user endpoint)",
+ "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",
+ " 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({\r",
+ " annotation: rnd.getWord(2001)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение события с description.length == 20 && annotation.length == 20 && title.length == 3 (user endpoint)",
+ "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",
+ " 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({\r",
+ " description: rnd.getWord(20),\r",
+ " annotation: rnd.getWord(20),\r",
+ " title: rnd.getWord(3)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Изменение события с description.length == 7000 && annotation.length == 2000 && title.length == 120 (user endpoint)",
+ "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",
+ " 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({\r",
+ " description: rnd.getWord(7000),\r",
+ " annotation: rnd.getWord(2000),\r",
+ " title: rnd.getWord(120)\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(\"Ответ должен содержать код статуса 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": [
+ {
+ "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": "Default values check",
+ "item": [
+ {
+ "name": "Добавление нового события без paid, participantLimit, requestModeration",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " let usersArr = Array.from({length: 10}, () => rnd.getUser());\r",
+ " let categoriesArr = Array.from({length: 10}, () => rnd.getCategory());\r",
+ " let usersResponseArr = [], categoriesResponseArr = [], eventArr, eventResponseArr = [];\r",
+ " try {\r",
+ " for (const u of usersArr){\r",
+ " usersResponseArr.push(await api.addUser(u));\r",
+ " }\r",
+ " for (const c of categoriesArr){\r",
+ " categoriesResponseArr.push(await api.addCategory(c));\r",
+ " }\r",
+ " eventArr = Array.from(categoriesResponseArr, (x) => rnd.getEvent(x.id));\r",
+ " for (let i = 0; i < 10; i++){\r",
+ " delete eventArr[i].requestModeration;\r",
+ " delete eventArr[i].paid;\r",
+ " delete eventArr[i].participantLimit;\r",
+ " }\r",
+ " for (let i = 0; i < 10; i++){\r",
+ " eventResponseArr.push(await api.addEvent(usersResponseArr[i].id, eventArr[i]));\r",
+ " }\r",
+ " pm.collectionVariables.set('responseArr', eventResponseArr)\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",
+ " } 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",
+ "\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(\"У каждого созданного события paid должно принять значение по умолчанию(false)\", function () {\r",
+ " source.forEach(function(x){pm.expect(x.paid).to.be.equal(false)});\r",
+ "});\r",
+ "\r",
+ "pm.test(\"У каждого созданного события participantLimit должен принять значение по умолчанию(0)\", function () {\r",
+ " source.forEach(function(x){pm.expect(x.participantLimit).to.be.equal(0)});\r",
+ "});\r",
+ "\r",
+ "pm.test(\"У каждого созданного события requestModeration должно принять значение по умолчанию(true)\", function () {\r",
+ " source.forEach(function(x){pm.expect(x.requestModeration).to.be.equal(true)});\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}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ },
+ "description": "Обратите внимание: дата и время на которые намечено событие не может быть раньше, чем через два часа от текущего момента"
+ },
+ "response": []
+ },
+ {
+ "name": "Проверка на значения по-умолчанию from и size(event)",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " let user, category, eventArr;\r",
+ " try {\r",
+ " user = await api.addUser(rnd.getUser());\r",
+ " category = await api.addCategory(rnd.getCategory());\r",
+ " eventArr = Array.from({length:11}, () => rnd.getEvent(category.id));\r",
+ " for (let i = 0; i < 11; i++){\r",
+ " await api.addEvent(user.id, eventArr[i]);\r",
+ " }\r",
+ " await pm.sendRequest({\r",
+ " url : \"http://localhost:8080/admin/events?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}}/admin/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "events"
+ ],
+ "query": [
+ {
+ "key": "users",
+ "value": "0",
+ "description": "список id пользователей, чьи события нужно найти",
+ "disabled": true
+ },
+ {
+ "key": "states",
+ "value": "PUBLISHED",
+ "description": "список состояний в которых находятся искомые события",
+ "disabled": true
+ },
+ {
+ "key": "categories",
+ "value": "0",
+ "description": "список id категорий в которых будет вестись поиск",
+ "disabled": true
+ },
+ {
+ "key": "rangeStart",
+ "value": "2022-01-06%2013%3A30%3A38",
+ "description": "дата и время не раньше которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "rangeEnd",
+ "value": "2097-09-06%2013%3A30%3A38",
+ "description": "дата и время не позже которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество событий, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество событий в наборе",
+ "disabled": true
+ }
+ ]
+ },
+ "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия"
+ },
+ "response": []
+ },
+ {
+ "name": "Получение событий, добавленных текущим пользователем. Проверка на значения по-умолчанию size и from",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " let eventArr, user, category, eventResponseArr = [];\r",
+ " try {\r",
+ " user = await api.addUser(rnd.getUser());\r",
+ " category = await api.addCategory(rnd.getCategory());\r",
+ " eventArr = Array.from({length:11}, () => rnd.getEvent(category.id));\r",
+ " for (let i = 0; i < 11; i++){\r",
+ " eventResponseArr.push(await api.addEvent(user.id, eventArr[i]));\r",
+ " }\r",
+ " pm.collectionVariables.set('responseArr', eventResponseArr)\r",
+ " pm.collectionVariables.set(\"uid\", user.id)\r",
+ " await pm.sendRequest({\r",
+ " url : \"http://localhost:8080/users/\" + user.id + \"/events?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",
+ " 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('responseArr');\r",
+ "newSourceArr = Array.from(source, (x) => x.id);\r",
+ "const responseWithFrom = pm.collectionVariables.get('source');\r",
+ "\r",
+ "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r",
+ " pm.expect(target.length).to.equal(10);\r",
+ "});\r",
+ "\r",
+ "pm.test(\"Значение from по-умолчанию должно быть равным 0\", function () {\r",
+ " pm.expect(target[0].id).to.be.equal(responseWithFrom[0].id, 'Запросы с from=0 и без него должны начинаться с одного и того же события');\r",
+ "});\r",
+ "\r",
+ "pm.test(\"Все найденные события должны быть в списке добавленных\", function () {\r",
+ " source.forEach(function(x){pm.expect(newSourceArr).to.include(x.id)});\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "query": [
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество элементов в наборе",
+ "disabled": true
+ }
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Получение событий с возможностью фильтрации. Проверка на значение по-умолчанию size",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " let user, category, eventArr, eventResponseArr = [], publishEventResponseArr = [];\r",
+ " try {\r",
+ " user = await api.addUser(rnd.getUser());\r",
+ " category = await api.addCategory(rnd.getCategory());\r",
+ " eventArr = Array.from({length:11}, () => rnd.getEvent(category.id));\r",
+ " for (let i = 0; i < 11; i++){\r",
+ " eventResponseArr.push(await api.addEvent(user.id, eventArr[i]));\r",
+ " }\r",
+ " for (let i = 0; i < 11; i++){\r",
+ " publishEventResponseArr.push(await api.publishEvent(eventResponseArr[i].id));\r",
+ " }\r",
+ " pm.collectionVariables.set('responseArr', eventResponseArr);\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",
+ " // выполняем наш скрипт\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('responseArr');\r",
+ "newSourceArr = Array.from(source, (x) => x.id);\r",
+ "\r",
+ "pm.test(\"Значение size по-умолчанию должно быть равным 10\", function () {\r",
+ " pm.expect(target.length).to.equal(10);\r",
+ "});\r",
+ "\r",
+ "pm.test(\"Все найденные события должны быть в списке добавленных\", function () {\r",
+ " source.forEach(function(x){pm.expect(newSourceArr).to.include(x.id)});\r",
+ "});\r",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/events?categories={{catid}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "events"
+ ],
+ "query": [
+ {
+ "key": "text",
+ "value": "0",
+ "description": "текст для поиска в содержимом аннотации и подробном описании события",
+ "disabled": true
+ },
+ {
+ "key": "categories",
+ "value": "{{catid}}",
+ "description": "список идентификаторов категорий в которых будет вестись поиск"
+ },
+ {
+ "key": "paid",
+ "value": "true",
+ "description": "поиск только платных/бесплатных событий",
+ "disabled": true
+ },
+ {
+ "key": "rangeStart",
+ "value": "2022-01-06%2013%3A30%3A38",
+ "description": "дата и время не раньше которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "rangeEnd",
+ "value": "2097-09-06%2013%3A30%3A38",
+ "description": "дата и время не позже которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "onlyAvailable",
+ "value": "false",
+ "description": "только события у которых не исчерпан лимит запросов на участие",
+ "disabled": true
+ },
+ {
+ "key": "sort",
+ "value": "EVENT_DATE",
+ "description": "Вариант сортировки: по дате события или по количеству просмотров",
+ "disabled": true
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество событий, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество событий в наборе",
+ "disabled": true
+ }
+ ]
+ },
+ "description": "Обратите внимание: \n- это публичный эндпоинт, соответственно в выдаче должны быть только опубликованные события\n- текстовый поиск (по аннотации и подробному описанию) должен быть без учета регистра букв\n- если в запросе не указан диапазон дат [rangeStart-rangeEnd], то нужно выгружать события, которые произойдут позже текущей даты и времени\n- информация о каждом событии должна включать в себя количество просмотров и количество уже одобренных заявок на участие\n- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики"
+ },
+ "response": []
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Users",
+ "item": [
+ {
+ "name": "Required query params",
+ "item": []
+ },
+ {
+ "name": "Unrequired 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",
+ " let compilation;\r",
+ " try {\r",
+ " const user = await api.addUser(rnd.getUser());\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",
+ " 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}}/admin/users?ids={{uid}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ],
+ "query": [
+ {
+ "key": "ids",
+ "value": "{{uid}}",
+ "description": "id пользователей"
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "10",
+ "description": "количество элементов в наборе",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Поиск пользователей без параметра ids",
+ "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",
+ " let user1 = rnd.getUser();\r",
+ " user1 = await api.addUser(user1);\r",
+ "\r",
+ " let user2 = rnd.getUser()\r",
+ " user2 = await api.addUser(user2);\r",
+ " //pm.collectionVariables.set('fromId', user1.id);\r",
+ " pm.collectionVariables.set('source1', user1);\r",
+ " pm.collectionVariables.set('source2', user2);\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(\"Ответ должен содержать код статуса 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 source1 = pm.collectionVariables.get('source1');\r",
+ "const source2 = pm.collectionVariables.get('source2');\r",
+ "\r",
+ "pm.test(\"Пользователи должны содержать поля: id, name, email\", function () {\r",
+ " pm.expect(target[target.length-2]).to.have.property('id');\r",
+ " pm.expect(target[target.length-2]).to.have.property('name');\r",
+ " pm.expect(target[target.length-2]).to.have.property('email');\r",
+ " pm.expect(target[target.length-1]).to.have.property('id');\r",
+ " pm.expect(target[target.length-1]).to.have.property('name');\r",
+ " pm.expect(target[target.length-1]).to.have.property('email');\r",
+ "});\r",
+ "\r",
+ "pm.test(\"Данные последних двух пользователей должны совпадать с данными добавленных пользователей\", function () {\r",
+ " pm.expect(target[target.length-2].id).to.equal(source1.id);\r",
+ " pm.expect(target[target.length-2].name).to.equal(source1.name);\r",
+ " pm.expect(target[target.length-2].email).to.equal(source1.email);\r",
+ " pm.expect(target[target.length-1].id).to.equal(source2.id);\r",
+ " pm.expect(target[target.length-1].name).to.equal(source2.name);\r",
+ " pm.expect(target[target.length-1].email).to.equal(source2.email);\r",
+ "});\r",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/admin/users?from={{fromId}}&size=100000",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ],
+ "query": [
+ {
+ "key": "ids",
+ "value": "{{uid}}",
+ "description": "id пользователей",
+ "disabled": true
+ },
+ {
+ "key": "from",
+ "value": "{{fromId}}",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора"
+ },
+ {
+ "key": "size",
+ "value": "100000",
+ "description": "количество элементов в наборе"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "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",
+ " let event = await api.addEvent(user.id, rnd.getEvent(category.id));\r",
+ " event = await api.publishEvent(event.id);\r",
+ " pm.request.removeQueryParams(['users', 'categories']);\r",
+ " pm.request.addQueryParams([`users=` + user.id, 'categories=' + 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",
+ " // выполняем наш скрипт\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}}/admin/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "events"
+ ],
+ "query": [
+ {
+ "key": "users",
+ "value": "0",
+ "description": "список id пользователей, чьи события нужно найти",
+ "disabled": true
+ },
+ {
+ "key": "states",
+ "value": "PUBLISHED",
+ "description": "список состояний в которых находятся искомые события",
+ "disabled": true
+ },
+ {
+ "key": "categories",
+ "value": "0",
+ "description": "список id категорий в которых будет вестись поиск",
+ "disabled": true
+ },
+ {
+ "key": "rangeStart",
+ "value": "2022-01-06%2013%3A30%3A38",
+ "description": "дата и время не раньше которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "rangeEnd",
+ "value": "2097-09-06%2013%3A30%3A38",
+ "description": "дата и время не позже которых должно произойти событие",
+ "disabled": true
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество событий, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество событий в наборе",
+ "disabled": true
+ }
+ ]
+ },
+ "description": "Эндпоинт возвращает полную информацию обо всех событиях подходящих под переданные условия"
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Required params in body",
+ "item": [
+ {
+ "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.email = \" \";\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(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.have.status(400);\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",
+ " let user;\r",
+ " try {\r",
+ " user = rnd.getUser();\r",
+ " user.email = \"\";\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(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.have.status(400);\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": "Добавление пользователя без поля email",
+ "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",
+ " delete user.email;\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(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.have.status(400);\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",
+ " let user;\r",
+ " try {\r",
+ " user = rnd.getUser();\r",
+ " user.name = \"\";\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(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.have.status(400);\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",
+ " let user;\r",
+ " try {\r",
+ " user = rnd.getUser();\r",
+ " user.name = \" \";\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(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.have.status(400);\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": "Добавление пользователя без поля 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",
+ " delete user.name;\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(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.have.status(400);\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": "Misc tests",
+ "item": []
+ },
+ {
+ "name": "String length restrictions",
+ "item": [
+ {
+ "name": "Добавление пользователя с name.length < 2",
+ "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(1);\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с name.length == 2",
+ "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(2);\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с name.length > 250",
+ "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(251);\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с name.length == 250",
+ "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(250);\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с email.length < 6",
+ "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.email = rnd.getWord(1) + '@a.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",
+ "\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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с email.length == 6",
+ "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.email = rnd.getWord(1) + '@a.ru';\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с email.localpart.length > 64",
+ "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.email = rnd.getWord(65) + '@a.ru';\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с email.localpart.length == 64",
+ "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.email = rnd.getWord(59) + '@a.ru';\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с domain.part.length > 63",
+ "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.email = rnd.getWord(1) + '@' + rnd.getWord(64) + '.ru';\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с domain.part.length == 63",
+ "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.email = rnd.getWord(1) + '@' + rnd.getWord(60) + '.ru';\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с email.length > 254",
+ "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.email = rnd.getWord(1) + '@' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(61);\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление пользователя с email.length == 254",
+ "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.email = rnd.getWord(1) + '@' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(63) + '.' + rnd.getWord(60);\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Default values check",
+ "item": [
+ {
+ "name": "Проверка на значения по-умолчанию from и size(user)",
+ "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.addUser(rnd.getUser());\r",
+ " }\r",
+ " await pm.sendRequest({\r",
+ " url : \"http://localhost:8080/admin/users?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",
+ " 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}}/admin/users",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ],
+ "query": [
+ {
+ "key": "ids",
+ "value": "{{uid}}",
+ "description": "id пользователей",
+ "disabled": true
+ },
+ {
+ "key": "ids",
+ "value": "-10833646",
+ "description": "id пользователей",
+ "disabled": true
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "10",
+ "description": "количество элементов в наборе",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Category",
+ "item": [
+ {
+ "name": "Required query params",
+ "item": []
+ },
+ {
+ "name": "Unrequired params in body",
+ "item": [
+ {
+ "name": "Получение категорий без нескольких Query params",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "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}}/categories",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "categories"
+ ],
+ "query": [
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество категорий, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "10000",
+ "description": "количество категорий в наборе",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Required params in body",
+ "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 = {name: ' '};\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(\"Ответ должен содержать код статуса 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": [],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/categories",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "categories"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление категории с пустым полем 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 = {name: ''};\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(\"Ответ должен содержать код статуса 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": [],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/categories",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "categories"
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление категории без поля 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 = {};\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(\"Ответ должен содержать код статуса 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": [],
+ "body": {
+ "mode": "raw",
+ "raw": ""
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/categories",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "categories"
+ ]
+ }
+ },
+ "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",
+ " let category, categoryObj\r",
+ " try {\r",
+ " category = rnd.getCategory();\r",
+ " categoryObj = await api.addCategory(category);\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r",
+ " }\r",
+ " pm.collectionVariables.set(\"catid\", Number(categoryObj.id))\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(\"Ответ должен содержать код статуса 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"
+ }
+ }
+ ],
+ "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": "String length restrictions",
+ "item": [
+ {
+ "name": "Добавление новой категории с name.length > 50",
+ "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 = {'name': rnd.getWord(51)};\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(\"Ответ должен содержать код статуса 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": "{{request_body}}",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/admin/categories",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "categories"
+ ]
+ },
+ "description": "Обратите внимание: имя категории должно быть уникальным"
+ },
+ "response": []
+ },
+ {
+ "name": "Добавление новой категории с name.length == 50",
+ "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 = {'name': rnd.getWord(50)};\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",
+ ""
+ ],
+ "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": "Изменение имени категории с name.length > 50",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ " let category\r",
+ " try {\r",
+ " category = await api.addCategory(rnd.getCategory());\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.getWord(51)\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(\"Ответ должен содержать код статуса 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/categories/:catId",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "categories",
+ ":catId"
+ ],
+ "variable": [
+ {
+ "key": "catId",
+ "value": "{{catid}}"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Изменение имени категории с name.length == 50",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ " let category\r",
+ " try {\r",
+ " category = await api.addCategory(rnd.getCategory());\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.getWord(50)\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(\"Ответ должен содержать код статуса 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/categories/:catId",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "categories",
+ ":catId"
+ ],
+ "variable": [
+ {
+ "key": "catId",
+ "value": "{{catid}}"
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Default values check",
+ "item": [
+ {
+ "name": "Проверка на значения по-умолчанию from и size(category)",
+ "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.addCategory(rnd.getCategory());\r",
+ " }\r",
+ " await pm.sendRequest({\r",
+ " url : \"http://localhost:8080/categories?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",
+ " 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}}/categories",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "categories"
+ ],
+ "query": [
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество категорий, которые нужно пропустить для формирования текущего набора",
+ "disabled": true
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество категорий в наборе",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ }
+ ]
+ },
+ {
+ "name": "Compilation",
+ "item": [
+ {
+ "name": "Required query params",
+ "item": []
+ },
+ {
+ "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"
+ }
+ }
+ ],
+ "request": {
+ "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": "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 category\r",
+ " try {\r",
+ " category = await api.addCategory(rnd.getCategory());\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",
+ "\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 = 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": "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": "Users",
+ "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",
+ " try {\r",
+ " const user = await api.addUser(rnd.getUser());\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",
+ " 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",
+ "\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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/admin/users?ids={{uid}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users"
+ ],
+ "query": [
+ {
+ "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
+ }
+ ]
+ }
+ },
+ "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",
+ " } 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(\"Ответ должен содержать код статуса 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"
+ }
+ }
+ ],
+ "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": "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": {
+ "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 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",
+ "};\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"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/admin/users/:userId",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "users",
+ ":userId"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id пользователя"
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ]
+ },
+ {
+ "name": "Event",
+ "item": [
+ {
+ "name": "Добавление нового события",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " 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",
+ " } 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",
+ "\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, 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"
+ }
+ }
+ ],
+ "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}}/users/:userId/events",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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",
+ " 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",
+ "};\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(\"Ответ должен содержать код статуса 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"
+ }
+ }
+ ],
+ "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(['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",
+ "};\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",
+ "});"
+ ],
+ "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": [
+ {
+ "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",
+ " 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",
+ "\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 target = pm.response.json()[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",
+ "});\r",
+ "\r",
+ "pm.test(\"Данные в ответе должны соответствовать данным в запросе\", function () {\r",
+ " pm.expect(target.id).to.not.be.null;\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/events?from=0&size=1000",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "events"
+ ],
+ "query": [
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора"
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество элементов в наборе"
+ }
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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 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",
+ " } 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\", 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.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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/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",
+ "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- информацию о том, что по этому эндпоинту был осуществлен и обработан запрос, нужно сохранить в сервисе статистики"
+ },
+ "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.collectionVariables.set('response', event);\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();\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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/events/:id",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "events",
+ ":id"
+ ],
+ "variable": [
+ {
+ "key": "id",
+ "value": "{{eid}}",
+ "description": "(Required) id события"
+ }
+ ]
+ },
+ "description": "Обратите внимание:\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",
+ " 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",
+ " } 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 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",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/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 события"
+ }
+ ]
+ }
+ },
+ "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",
+ " 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",
+ "};\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, 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(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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/requests",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "requests"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "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 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', user.id);\r",
+ " pm.collectionVariables.set('eid', event.id);\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.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, 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(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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/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 события"
+ }
+ ]
+ }
+ },
+ "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",
+ " 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",
+ "\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();\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",
+ "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",
+ " 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",
+ " 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",
+ "};\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",
+ "\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": "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",
+ " 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",
+ "\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('response');\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, а при удалении - 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(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"
+ }
+ }
+ ],
+ "request": {
+ "method": "PATCH",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/users/:userId/requests/:requestId/cancel",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "users",
+ ":userId",
+ "requests",
+ ":requestId",
+ "cancel"
+ ],
+ "variable": [
+ {
+ "key": "userId",
+ "value": "{{uid}}",
+ "description": "(Required) id текущего пользователя"
+ },
+ {
+ "key": "requestId",
+ "value": "{{reqid}}",
+ "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 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', 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",
+ "};\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()[\"rejectedRequests\"][0];\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(\"Данные в ответе должны соответствовать данным в запросе\", function () {\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"
+ }
+ }
+ ],
+ "request": {
+ "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}}/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",
+ " 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",
+ "};\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()[\"confirmedRequests\"][0];\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(\"Данные в ответе должны соответствовать данным в запросе\", function () {\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"
+ }
+ }
+ ],
+ "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": "Compilation",
+ "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",
+ " 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",
+ " } 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.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": "Получение подборки событий по её id",
+ "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",
+ " pm.collectionVariables.set('response', compilation);\r",
+ " pm.collectionVariables.set('compid', compilation.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();\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(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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/compilations/:compId",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "compilations",
+ ":compId"
+ ],
+ "variable": [
+ {
+ "key": "compId",
+ "value": "{{compid}}",
+ "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 user = await api.addUser(rnd.getUser());\r",
+ " const category = await api.addCategory(rnd.getCategory());\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",
+ "};\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();\r",
+ "let founded;\r",
+ "target.forEach(function(element){if (element.id == source.id) founded = element});\r",
+ "\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(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"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/compilations?pinned=true&from=0&size=1000",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "compilations"
+ ],
+ "query": [
+ {
+ "key": "pinned",
+ "value": "true",
+ "description": "искать только закрепленные/не закрепленные подборки"
+ },
+ {
+ "key": "from",
+ "value": "0",
+ "description": "количество элементов, которые нужно пропустить для формирования текущего набора"
+ },
+ {
+ "key": "size",
+ "value": "1000",
+ "description": "количество элементов в наборе"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "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": {
+ "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",
+ " 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",
+ "};\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"
+ }
+ }
+ ],
+ "request": {
+ "method": "DELETE",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/admin/compilations/:compId",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "admin",
+ "compilations",
+ ":compId"
+ ],
+ "variable": [
+ {
+ "key": "compId",
+ "value": "{{compid}}",
+ "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 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 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",
+ " events : [event.id],\r",
+ " pinned: true,\r",
+ " title: rnd.getCompilation().name\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\", function () {\r",
+ " pm.response.to.be.ok; \r",
+ "});\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.events.length).equal(0);\r",
+ "});\r",
+ "\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"
+ }
+ }
+ ],
+ "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": []
+ }
+ ]
+ }
+ ],
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ "API = class {",
+ " constructor(postman, verbose = false, baseUrl = \"http://localhost:8080\") {",
+ " this.baseUrl = baseUrl;",
+ " this.pm = postman;",
+ " this._verbose = verbose;",
+ " }",
+ "",
+ " async addUser(user, verbose=null) {",
+ " return this.post(\"/admin/users\", user, \"Ошибка при добавлении нового пользователя: \", verbose);",
+ " }",
+ "",
+ " async addCategory(category, verbose=null) {",
+ " return this.post(\"/admin/categories\", category, \"Ошибка при добавлении новой категории: \", verbose);",
+ " }",
+ "",
+ " async addEvent(userId, event, verbose=null) {",
+ " return this.post(\"/users/\" + userId + \"/events\", event, \"Ошибка при добавлении нового события: \", verbose);",
+ " }",
+ "",
+ " async addCompilation(compilation, verbose=null) {",
+ " return this.post(\"/admin/compilations\", compilation, \"Ошибка при добавлении новой подборки: \", verbose);",
+ " }",
+ "",
+ " async publishParticipationRequest(eventId, userId, verbose=null) {",
+ " return this.post('/users/' + userId + '/requests?eventId=' + eventId, null, \"Ошибка при добавлении нового запроса на участие в событии\", verbose);",
+ " }",
+ "",
+ " async publishEvent(eventId, verbose=null) {",
+ " return this.patch('/admin/events/' + eventId, {stateAction: \"PUBLISH_EVENT\"}, \"Ошибка при публикации события\", verbose);",
+ " }",
+ " ",
+ " async rejectEvent(eventId, verbose=null) {",
+ " return this.patch('/admin/events/' + eventId, {stateAction: \"REJECT_EVENT\"}, \"Ошибка при отмене события\", verbose);",
+ " }",
+ "",
+ " async acceptParticipationRequest(eventId, userId, reqId, verbose=null) {",
+ " return this.patch('/users/' + userId + '/events/' + eventId + '/requests', {requestIds:[reqId], status: \"CONFIRMED\"}, \"Ошибка при принятии заявки на участие в событии\", verbose);",
+ " }",
+ "",
+ " async findCategory(catId, verbose=null) {",
+ " return this.get('/categories/' + catId, null, \"Ошибка при поиске категории по id\", verbose);",
+ " }",
+ "",
+ " async findCompilation(compId, verbose=null) {",
+ " return this.get('/compilations/' + compId, null, \"Ошибка при поиске подборки по id\", verbose);",
+ " }",
+ "",
+ " async findEvent(eventId, verbose=null) {",
+ " return this.get('/events/' + eventId, null, \"Ошибка при поиске события по id\", verbose);",
+ " }",
+ "",
+ " async findUser(userId, verbose=null) {",
+ " return this.get('/admin/users?ids=' + userId, null, \"Ошибка при поиске пользователя по id\", verbose);",
+ " }",
+ "",
+ " async post(path, body, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {",
+ " return this.sendRequest(\"POST\", path, body, errorText, verbose);",
+ " }",
+ "",
+ " async patch(path, body = null, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {",
+ " return this.sendRequest(\"PATCH\", path, body, errorText, verbose);",
+ " }",
+ "",
+ " async get(path, body = null, errorText = \"Ошибка при выполнении get-запроса: \", verbose=null) {",
+ " return this.sendRequest(\"GET\", path, body, errorText, verbose);",
+ " }",
+ " async sendRequest(method, path, body=null, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {",
+ " return new Promise((resolve, reject) => {",
+ " verbose = verbose == null ? this._verbose : verbose;",
+ " const request = {",
+ " url: this.baseUrl + path,",
+ " method: method,",
+ " body: body == null ? \"\" : JSON.stringify(body),",
+ " header: { \"Content-Type\": \"application/json\" },",
+ " };",
+ " if(verbose) {",
+ " console.log(\"Отправляю запрос: \", request);",
+ " }",
+ "",
+ " try {",
+ " this.pm.sendRequest(request, (error, response) => {",
+ " if(error || (response.code >= 400 && response.code <= 599)) {",
+ " let err = error ? error : JSON.stringify(response.json());",
+ " console.error(\"При выполнении запроса к серверу возникла ошика.\\n\", err,",
+ " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + ",
+ " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));",
+ "",
+ " reject(new Error(errorText + err));",
+ " }",
+ " if(verbose) {",
+ " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());",
+ " }",
+ " if (response.stream.length === 0){",
+ " reject(new Error('Отправлено пустое тело ответа'))",
+ " }else{",
+ " resolve(response.json());",
+ " }",
+ " });",
+ " ",
+ " } catch(err) {",
+ " if(verbose) {",
+ " console.error(errorText, err);",
+ " }",
+ " return Promise.reject(err);",
+ " }",
+ " });",
+ " }",
+ "};",
+ "",
+ "RandomUtils = class {",
+ " constructor() {}",
+ "",
+ " getUser() {",
+ " return {",
+ " name: pm.variables.replaceIn('{{$randomFullName}}'),",
+ " email: pm.variables.replaceIn('{{$randomEmail}}')",
+ " };",
+ " }",
+ "",
+ " getCategory() {",
+ " return {",
+ " name: pm.variables.replaceIn('{{$randomWord}}') + Math.floor(Math.random() * 10000 * Math.random()).toString()",
+ " };",
+ " }",
+ "",
+ " getEvent(categoryId) {",
+ " return {",
+ " annotation: pm.variables.replaceIn('{{$randomLoremParagraph}}'),",
+ " category: categoryId,",
+ " description: pm.variables.replaceIn('{{$randomLoremParagraphs}}'),",
+ " eventDate: this.getFutureDateTime(),",
+ " location: {",
+ " lat: parseFloat(pm.variables.replaceIn('{{$randomLatitude}}')),",
+ " lon: parseFloat(pm.variables.replaceIn('{{$randomLongitude}}')),",
+ " },",
+ " paid: pm.variables.replaceIn('{{$randomBoolean}}'),",
+ " participantLimit: pm.variables.replaceIn('{{$randomInt}}'),",
+ " requestModeration: pm.variables.replaceIn('{{$randomBoolean}}'),",
+ " title: pm.variables.replaceIn('{{$randomLoremSentence}}'),",
+ " }",
+ " }",
+ "",
+ " getCompilation(...eventIds) {",
+ " return {",
+ " title: pm.variables.replaceIn('{{$randomLoremSentence}}').slice(0, 50),",
+ " pinned: pm.variables.replaceIn('{{$randomBoolean}}'),",
+ " events: eventIds",
+ " };",
+ " }",
+ "",
+ "",
+ " getFutureDateTime(hourShift = 5, minuteShift=0, yearShift=0) {",
+ " let moment = require('moment');",
+ "",
+ " let m = moment();",
+ " m.add(hourShift, 'hour');",
+ " m.add(minuteShift, 'minute');",
+ " m.add(yearShift, 'year');",
+ "",
+ " return m.format('YYYY-MM-DD HH:mm:ss');",
+ " }",
+ "",
+ " getWord(length = 1) {",
+ " let result = '';",
+ " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';",
+ " const charactersLength = characters.length;",
+ " let counter = 0;",
+ " while (counter < length) {",
+ " result += characters.charAt(Math.floor(Math.random() * charactersLength));",
+ " counter += 1;",
+ " }",
+ " return result;",
+ " }",
+ "}"
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "key": "baseUrl",
+ "value": "http://localhost:8080",
+ "type": "string"
+ },
+ {
+ "key": "name",
+ "value": ""
+ },
+ {
+ "key": "usersCount",
+ "value": 1,
+ "type": "number"
+ },
+ {
+ "key": "catid",
+ "value": 1,
+ "type": "number"
+ },
+ {
+ "key": "request_body",
+ "value": ""
+ },
+ {
+ "key": "mail",
+ "value": ""
+ },
+ {
+ "key": "response",
+ "value": ""
+ },
+ {
+ "key": "uid",
+ "value": 1,
+ "type": "number"
+ },
+ {
+ "key": "catname",
+ "value": ""
+ },
+ {
+ "key": "eid",
+ "value": 1,
+ "type": "number"
+ },
+ {
+ "key": "compid",
+ "value": 1,
+ "type": "number"
+ },
+ {
+ "key": "toCheck",
+ "value": ""
+ },
+ {
+ "key": "newDataToSet",
+ "value": ""
+ },
+ {
+ "key": "uid1",
+ "value": ""
+ },
+ {
+ "key": "reqid",
+ "value": 1,
+ "type": "number"
+ },
+ {
+ "key": "catId",
+ "value": ""
+ },
+ {
+ "key": "confirmedRequests",
+ "value": ""
+ },
+ {
+ "key": "responseArr",
+ "value": ""
+ },
+ {
+ "key": "source1",
+ "value": ""
+ },
+ {
+ "key": "source2",
+ "value": ""
+ },
+ {
+ "key": "fromId",
+ "value": "0"
+ },
+ {
+ "key": "source",
+ "value": ""
+ }
+ ]
+}
diff --git a/postman/ewm-stat-service.json b/postman/ewm-stat-service.json
new file mode 100644
index 0000000..08614b5
--- /dev/null
+++ b/postman/ewm-stat-service.json
@@ -0,0 +1,1027 @@
+{
+ "info": {
+ "_postman_id": "f5371ebb-b14e-45d3-bf4c-f72a79f56ea0",
+ "name": "\"Explore with me\" API статистика",
+ "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-f5371ebb-b14e-45d3-bf4c-f72a79f56ea0?action=share&source=collection_link&creator=27311667"
+ },
+ "item": [
+ {
+ "name": "Сохранение информации о том, что к эндпоинту был запрос",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " let post;\r",
+ " try {\r",
+ " post = rnd.getPost();\r",
+ " } catch(err) {\r",
+ " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r",
+ " }\r",
+ "\r",
+ " pm.request.body.update({\r",
+ " mode: 'raw',\r",
+ " raw: JSON.stringify(post),\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",
+ "});\r",
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "POST",
+ "header": [
+ {
+ "key": "Content-Type",
+ "value": "application/json"
+ }
+ ],
+ "body": {
+ "mode": "raw",
+ "raw": "",
+ "options": {
+ "raw": {
+ "language": "json"
+ }
+ }
+ },
+ "url": {
+ "raw": "{{baseUrl}}/hit",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "hit"
+ ]
+ },
+ "description": "Сохранение информации о том, что на uri конкретного сервиса был отправлен запрос пользователем. Название сервиса, uri и ip пользователя указаны в теле запроса."
+ },
+ "response": []
+ },
+ {
+ "name": "Получение статистики по посещениям. Обратите внимание: значение даты и времени нужно закодировать (например используя java.net.URLEncoder.encode)(Тест на /events/{id})",
+ "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(\"uri\", '/events/' + event.id);\r",
+ "\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events/\" + event.id,\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:9090/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=/events/\" + event.id.toString() + \"&unique=false\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.collectionVariables.set('source', response.json());\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events/\" + event.id,\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " });\r",
+ " });\r",
+ " });\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(\"Ответ должен содержать код статуса 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()[0];\r",
+ "\r",
+ "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r",
+ " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r",
+ "});\r",
+ "\r",
+ "const source = pm.collectionVariables.get(\"source\")[0];\r",
+ "\r",
+ "\r",
+ "pm.test(\"После выполнения запроса GET /events/{id} должно увеличиться количество хитов.\", function(){\r",
+ " pm.expect(source.hits + 1).equal(target.hits, 'Количество хитов после выполнения запроса GET /events/{id} должно быть больше на 1.');\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris={{uri}}&unique=false",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "end",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)"
+ }
+ ]
+ }
+ },
+ "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 event1 = await api.addEvent(user.id, rnd.getEvent(category.id));\r",
+ " event1 = await api.publishEvent(event1.id);\r",
+ " let event2 = await api.addEvent(user.id, rnd.getEvent(category.id));\r",
+ " event2 = await api.publishEvent(event2.id);\r",
+ " pm.collectionVariables.set(\"uri\", '/events/' + event1.id + '&uris=/events/' + event2.id);\r",
+ "\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events/\" + event1.id,\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events/\" + event2.id,\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events/\" + event2.id,\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " });\r",
+ " });\r",
+ " });\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(\"Ответ должен содержать код статуса 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(\"Посты должны содержать поля: app, uri, hits\", function () {\r",
+ " pm.expect(target[0]).to.have.all.keys('app', 'uri', 'hits');\r",
+ " pm.expect(target[1]).to.have.all.keys('app', 'uri', 'hits');\r",
+ "});\r",
+ "\r",
+ "pm.test(\"В теле ответа должна соблюдаться сортировка по убыванию количества просмотров\", function(){\r",
+ " pm.expect(target[0].hits).to.be.above(target[1].hits);\r",
+ "});\r",
+ "\r",
+ "pm.test(\"Проверка соответствия реального количества просмотров событий и сохраненных хитов\", function(){\r",
+ " pm.expect(target[0].hits).equal(2);\r",
+ " pm.expect(target[1].hits).equal(1);\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris={{uri}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "end",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "uris",
+ "value": "aliqua o",
+ "description": "Список uri для которых нужно выгрузить статистику",
+ "disabled": true
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Получение статистики по посещениям. Обратите внимание: значение даты и времени нужно закодировать (например используя java.net.URLEncoder.encode)(Тест на /events)",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " try { \r",
+ " pm.collectionVariables.set(\"uri\", '/events'); \r",
+ " \r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " \r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:9090/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=/events&unique=false\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.collectionVariables.set('source', response.json());\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " });\r",
+ " });\r",
+ " });\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(\"Ответ должен содержать код статуса 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()[0];\r",
+ "\r",
+ "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r",
+ " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r",
+ "});\r",
+ "\r",
+ "const source = pm.collectionVariables.get(\"source\")[0];\r",
+ "\r",
+ "\r",
+ "pm.test(\"После выполнения запроса GET /events должно увеличиться количество хитов.\", function(){\r",
+ " pm.expect(source.hits + 1).equal(target.hits, 'Количество хитов после выполнения запроса GET /events должно быть больше на 1.');\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris={{uri}}&unique=false",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "end",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)"
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Получение статистики по посещениям. (Тест на опциональность и работу параметра unique)",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ "const main = async () => {\r",
+ " const api = new API(pm);\r",
+ " const rnd = new RandomUtils();\r",
+ "\r",
+ " try { \r",
+ " pm.collectionVariables.set(\"uri\", '/events'); \r",
+ " \r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:8080/events\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " \r",
+ " pm.sendRequest({\r",
+ " url : \"http://localhost:9090/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris=/events&unique=true\",\r",
+ " method : \"GET\",\r",
+ " header: { \"Content-Type\": \"application/json\" }\r",
+ " }, (error, response) => {\r",
+ " pm.collectionVariables.set('source', response.json());\r",
+ " });\r",
+ " });\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(\"Ответ должен содержать код статуса 200 и данные в формате json при запросе без опционального параметра unique\", 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()[0];\r",
+ "\r",
+ "pm.test(\"Посты должны содержать поля: app, uri, hits\", function () {\r",
+ " pm.expect(target).to.have.all.keys('app', 'uri', 'hits');\r",
+ "});\r",
+ "\r",
+ "const source = pm.collectionVariables.get(\"source\")[0];\r",
+ "\r",
+ "pm.test(\"Количество уникальных просмотров с одного ip должно равняться 1\", function () {\r",
+ " pm.expect(source.hits == 1);\r",
+ "})\r",
+ "\r",
+ "pm.test(\"Количество просмотров с одного ip должно быть больше 1\", function () {\r",
+ " pm.expect(target.hits > 1);\r",
+ "})"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?start=2020-05-05 00:00:00&end=2035-05-05 00:00:00&uris={{uri}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "end",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "uris",
+ "value": "aliqua o",
+ "description": "Список uri для которых нужно выгрузить статистику",
+ "disabled": true
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Получение статистики по посещениям. (Тест на верную обработку запроса с неверными датами начала и конца диапазона времени)",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.be.badRequest;\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?start=2035-05-05 00:00:00&end=2020-05-05 00:00:00&uris={{uri}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "end",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Тест на верную обработку запроса без даты начала",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.be.badRequest;\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?end=2020-05-05 00:00:00&uris={{uri}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")",
+ "disabled": true
+ },
+ {
+ "key": "end",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ },
+ {
+ "name": "Тест на верную обработку запроса без даты конца",
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "exec": [
+ ""
+ ],
+ "type": "text/javascript"
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "exec": [
+ "pm.test(\"Ответ должен содержать код статуса 400\", function () {\r",
+ " pm.response.to.be.badRequest;\r",
+ "});"
+ ],
+ "type": "text/javascript"
+ }
+ }
+ ],
+ "request": {
+ "method": "GET",
+ "header": [
+ {
+ "key": "Accept",
+ "value": "application/json"
+ }
+ ],
+ "url": {
+ "raw": "{{baseUrl}}/stats?start=2035-05-05 00:00:00&uris={{uri}}",
+ "host": [
+ "{{baseUrl}}"
+ ],
+ "path": [
+ "stats"
+ ],
+ "query": [
+ {
+ "key": "start",
+ "value": "2035-05-05 00:00:00",
+ "description": "(Required) Дата и время начала диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")"
+ },
+ {
+ "key": "end",
+ "value": "2020-05-05 00:00:00",
+ "description": "(Required) Дата и время конца диапазона за который нужно выгрузить статистику (в формате \"yyyy-MM-dd HH:mm:ss\")",
+ "disabled": true
+ },
+ {
+ "key": "uris",
+ "value": "{{uri}}",
+ "description": "Список uri для которых нужно выгрузить статистику"
+ },
+ {
+ "key": "unique",
+ "value": "false",
+ "description": "Нужно ли учитывать только уникальные посещения (только с уникальным ip)",
+ "disabled": true
+ }
+ ]
+ }
+ },
+ "response": []
+ }
+ ],
+ "event": [
+ {
+ "listen": "prerequest",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ "API = class {",
+ " constructor(postman, verbose = false, baseUrl = \"http://localhost:9090\") {",
+ " this.baseUrl = baseUrl;",
+ " this.pm = postman;",
+ " this._verbose = verbose;",
+ " }",
+ "",
+ " async addPost(post, verbose=null) {",
+ " return this.post(\"/hit\", post, \"Ошибка при сохранении информации о запросе к эндпойнту: \", verbose);",
+ " }",
+ "",
+ " async addUser(user, verbose=null) {",
+ " return this.post(\"/admin/users\", user, \"http://localhost:8080\", \"Ошибка при добавлении нового пользователя: \", verbose);",
+ " }",
+ "",
+ " async addCategory(category, verbose=null) {",
+ " return this.post(\"/admin/categories\", category, \"http://localhost:8080\", \"Ошибка при добавлении новой категории: \", verbose);",
+ " }",
+ "",
+ " async addEvent(userId, event, verbose=null) {",
+ " return this.post(\"/users/\" + userId + \"/events\", event, \"http://localhost:8080\", \"Ошибка при добавлении нового события: \", verbose);",
+ " }",
+ "",
+ " async publishEvent(eventId, verbose=null) {",
+ " return this.patch('/admin/events/' + eventId, {stateAction: \"PUBLISH_EVENT\"},\"Ошибка при публикации события\", verbose);",
+ " }",
+ "",
+ " async patch(path, body = null, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {",
+ " return this.sendRequest(\"PATCH\", path, \"http://localhost:8080\", body, errorText);",
+ " }",
+ "",
+ " async post(path, body, newBaseUrl=null, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {",
+ " return this.sendRequest(\"POST\", path, newBaseUrl, body, errorText);",
+ " }",
+ "",
+ " async sendRequest(method, path, newBaseUrl=null, body=null, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {",
+ " return new Promise((resolve, reject) => {",
+ " verbose = verbose == null ? this._verbose : verbose;",
+ " let request;",
+ " if (newBaseUrl==null)",
+ " request = {",
+ " url: this.baseUrl + path,",
+ " method: method,",
+ " body: body == null ? \"\" : JSON.stringify(body),",
+ " header: { \"Content-Type\": \"application/json\" },",
+ " };",
+ " else",
+ " request = {",
+ " url: newBaseUrl + path,",
+ " method: method,",
+ " body: body == null ? \"\" : JSON.stringify(body),",
+ " header: { \"Content-Type\": \"application/json\" },",
+ " };",
+ "",
+ " if(verbose) {",
+ " console.log(\"Отправляю запрос: \", request);",
+ " }",
+ "",
+ " try {",
+ " this.pm.sendRequest(request, (error, response) => {",
+ " if(error || (response.code >= 400 && response.code <= 599)) {",
+ " let err = error ? error : JSON.stringify(response.json());",
+ " console.error(\"При выполнении запроса к серверу возникла ошика.\\n\", err,",
+ " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + ",
+ " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));",
+ "",
+ " reject(new Error(errorText + err));",
+ " }",
+ "",
+ " if(verbose) {",
+ " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());",
+ " }",
+ " try{",
+ " resolve(response.json());",
+ " } catch(err){",
+ " resolve(response);",
+ " }",
+ " ",
+ " });",
+ " } catch(err) {",
+ " if(verbose) {",
+ " console.error(errorText, err);",
+ " }",
+ " return Promise.reject(err);",
+ " }",
+ " });",
+ " }",
+ "};",
+ "",
+ "RandomUtils = class {",
+ " constructor() {}",
+ "",
+ " getPost() {",
+ " return {",
+ " app: \"ewm-main-service\",",
+ " uri: \"/events/\" + pm.variables.replaceIn('{{$randomInt}}'),",
+ " ip: pm.variables.replaceIn('{{$randomIP}}'),",
+ " timestamp: this.getPastDateTime()",
+ " }",
+ " }",
+ "",
+ " getUser() {",
+ " return {",
+ " name: pm.variables.replaceIn('{{$randomFullName}}'),",
+ " email: pm.variables.replaceIn('{{$randomEmail}}')",
+ " };",
+ " }",
+ "",
+ " getCategory() {",
+ " return {",
+ " name: pm.variables.replaceIn('{{$randomWord}}') + Math.floor(Math.random() * 100).toString()",
+ " };",
+ " }",
+ "",
+ " getEvent(categoryId) {",
+ " return {",
+ " annotation: pm.variables.replaceIn('{{$randomLoremParagraph}}'),",
+ " category: categoryId,",
+ " description: pm.variables.replaceIn('{{$randomLoremParagraphs}}'),",
+ " eventDate: this.getFutureDateTime(),",
+ " location: {",
+ " lat: parseFloat(pm.variables.replaceIn('{{$randomLatitude}}')),",
+ " lon: parseFloat(pm.variables.replaceIn('{{$randomLongitude}}')),",
+ " },",
+ " paid: pm.variables.replaceIn('{{$randomBoolean}}'),",
+ " participantLimit: pm.variables.replaceIn('{{$randomInt}}'),",
+ " requestModeration: pm.variables.replaceIn('{{$randomBoolean}}'),",
+ " title: pm.variables.replaceIn('{{$randomLoremSentence}}'),",
+ " }",
+ " }",
+ " ",
+ " getCompilation(...eventIds) { ",
+ " return { ",
+ " title: pm.variables.replaceIn('{{$randomLoremSentence}}'), ",
+ " pinned: pm.variables.replaceIn('{{$randomBoolean}}'), ",
+ " events: eventIds ",
+ " }; ",
+ " }",
+ "",
+ " getPastDateTime(hourShift = 5, minuteShift=0, yearShift=0) {",
+ " let moment = require('moment');",
+ "",
+ " let m = moment();",
+ " m.subtract(hourShift, 'hour');",
+ " m.subtract(minuteShift, 'minute');",
+ " m.subtract(yearShift, 'year');",
+ "",
+ " return m.format('YYYY-MM-DD HH:mm:ss');",
+ " }",
+ "",
+ " getFutureDateTime(hourShift = 5, minuteShift=0, yearShift=0) {",
+ " let moment = require('moment');",
+ "",
+ " let m = moment();",
+ " m.add(hourShift, 'hour');",
+ " m.add(minuteShift, 'minute');",
+ " m.add(yearShift, 'year');",
+ "",
+ " return m.format('YYYY-MM-DD HH:mm:ss');",
+ " }",
+ "}"
+ ]
+ }
+ },
+ {
+ "listen": "test",
+ "script": {
+ "type": "text/javascript",
+ "exec": [
+ ""
+ ]
+ }
+ }
+ ],
+ "variable": [
+ {
+ "key": "baseUrl",
+ "value": "http://localhost:9090",
+ "type": "string"
+ },
+ {
+ "key": "uri",
+ "value": "1"
+ },
+ {
+ "key": "source",
+ "value": ""
+ }
+ ]
+}
\ No newline at end of file
diff --git a/stat/stat-server/src/main/resources/schema.sql b/stat/stat-server/src/main/resources/schema.sql
index c54a611..59c5a23 100644
--- a/stat/stat-server/src/main/resources/schema.sql
+++ b/stat/stat-server/src/main/resources/schema.sql
@@ -1,7 +1,8 @@
-CREATE TABLE IF NOT EXISTS stat (
- id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
- app VARCHAR(255) NOT NULL,
- uri VARCHAR(512) NOT NULL,
- ip VARCHAR(45) NOT NULL,
- created TIMESTAMP NOT NULL
+CREATE TABLE IF NOT EXISTS stat
+(
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
+ app VARCHAR(255) NOT NULL,
+ uri VARCHAR(512) NOT NULL,
+ ip VARCHAR(45) NOT NULL,
+ created TIMESTAMP NOT NULL
);