Update protected files

This commit is contained in:
cu-workflow-launcher[bot]
2024-03-02 17:23:28 +00:00
committed by GitHub
parent ed687650ba
commit 3c335f9efe
3 changed files with 365 additions and 125 deletions
+29 -3
View File
@@ -30,6 +30,8 @@
- `POSTGRES_DATABASE` — имя базы данных PostgreSQL, с которой должно работать приложение.
- `RANDOM_SECRET` — псевдо-случайная последовательность из 128 символов (a-z, A-Z, 0-9), сгенерированная тестирующей системой. Можете использовать её, если вашему приложению необходим секретный ключ (например, для JWT). Если вам не требуется данное значение, можете его не использовать.
Автор приложения сам выбирает, с какими из переменных окружения ему комфортно работать.
Учитывая современные реалии, приложение будет запускаться через Docker контейнер. В репозитории присутствует Dockerfile, с помощью которого будет собираться образ приложения.
@@ -80,6 +82,8 @@
Если структура запроса не соответствует требованиям и описанному формату, по умолчанию возвращается код ответа 400.
Если указан более специфичный код ответа, используйте его.
Если запрос некорректен хотя бы в одном параметре, весь запрос отвергается и признается некорректным.
### 01/ping
Достаточно реализовать возврат успешного ответа (с кодом `200`) на запрос `GET /api/ping`. Содержимое тела ответа при этом не валидируется, можно возвращать `"ok"`.
@@ -217,7 +221,7 @@ INSERT INTO countries (name, alpha2, alpha3, region) VALUES
Всегда запоминается последняя реакция пользователя. Если пользователь поставил лайк два раза подряд, эффект лайка остается.
Если пользователь поставил лайк, а потом дизлайк, остается реакция дизлайка.
В полях `likesCount` и `dislikesCount` необходимо отразить уникальное число лайков и дизлайков.
В полях `likesCount` и `dislikesCount` необходимо отразить число лайков и дизлайков публикации, при этом от каждого пользователя учитывается только его самая последняя реакция.
## Тестирование
@@ -272,6 +276,28 @@ INSERT INTO countries (name, alpha2, alpha3, region) VALUES
Не забывайте делать `git pull --rebase`, чтобы загрузить актуальные требования в локальную версию репозитория.
### 02.03.2024
Коллеги, привет! Ваш Project Manager передал все опасения касательно сроков, поэтому мы договорились,
что финальное тестирование будет проходить, опираясь на версию спецификации, опубликованную 3 марта 15:00 (МСК).
Напоминаем! В тестах будет проверяться только то поведение, которое было описано в README либо спецификации.
Обращаем внимание: при работе с публичным набором тестов в Postman обращайте внимание на содержимое вкладки Tests, именно там заключена логика тестирования.
Request-path в Postman изменены на `GET /api/ping`, чтобы нерелевантная информация в логах не смущала вас.
И еще немного полезных замечаний:
- Если запрос некорректен хотя бы в одном параметре, весь запрос отвергается и признается некорректным.
- Если вам нужен секретный ключ, можете (необязательно!) использовать `RANDOM_SECRET`.
- Timezone при передаче времени не так важна. Важно, чтобы счетчик времени монотонно рос и был одного формата во всех ответах backend'а.
- Чтобы отобразить число лайков и дизлайков поста, учитывайте только последнюю реакцию от каждого пользователя.
- Если структура ответа предполагает опциональность поля, сервер не должен возвращать данное поле при его отсутствии.
### 01.03.2024
Коллеги, с первым днем весны!
@@ -279,7 +305,7 @@ INSERT INTO countries (name, alpha2, alpha3, region) VALUES
Напоминаем вам, что корректные логин, номер телефона, e-mail и другая подобная информация должны состоять минимум из одного символа!
А длина уникального идентификатора публикации не превышает разумных значений...
Также добавим, что в эндпоинте `/countries` если хотя бы один переданный регион является некорректным, весь запрос считается некорректным.
Также добавим, что в эндпоинте `/countries` если хотя бы один переданный регион является некорректным, весь запрос считается некорректным. Это общее правило: если запрос некорректен хотя бы в одном параметре, весь запрос отвергается и признается некорректным.
### 28.02.2024
@@ -287,7 +313,7 @@ INSERT INTO countries (name, alpha2, alpha3, region) VALUES
Если профиль пользователя закрыт, доступ к его профилю и его публикациям появляется у пользователей, кого данный пользователь добавил в друзья.
При если это Маша добавила Петю в друзья, не значит, что Петя добавил Машу в друзья. Можно расценивать добавление в друзья как подписку.
При этом если Маша добавила Петю в друзья, не значит, что Петя добавил Машу в друзья. Можно расценивать добавление в друзья как подписку.
Группа `08/friends` зависит от группы `06/profiles`.
+5 -2
View File
@@ -251,6 +251,7 @@ paths:
Если значение передано, данное изменение должно быть отражено в профиле пользователя.
Если значение не передано, необходимо оставить прежнее значение параметра.
Если передана пустая структура, ничего изменять не требуется, возвращается успешный ответ.
required: true
content:
application/json:
@@ -630,6 +631,8 @@ paths:
Для плавной работы приложения используется пагинация.
Можете считать, что пользователей с логином `my` не будет.
Сервер должен идентифицировать пользователя по переданному токену. Значение токена будет подставляться в заголовок `Authorization` в формате `Bearer {token}`.
security:
- bearerAuth: []
@@ -722,7 +725,7 @@ paths:
$ref: "#/components/schemas/postId"
responses:
"200":
description: Лайк засчитан.
description: Реакция засчитана, возвращайте пубикацию с актуальным числом лайков и дизлайков.
content:
application/json:
schema:
@@ -758,7 +761,7 @@ paths:
$ref: "#/components/schemas/postId"
responses:
"200":
description: Дизлайк засчитан.
description: Реакция засчитана, возвращайте пубикацию с актуальным числом лайков и дизлайков.
content:
application/json:
schema:
+298 -87
View File
@@ -118,7 +118,7 @@
" var url = pm.variables.get(\"base_url\") + `/countries?region=${country.region}`;",
"",
" pm.sendRequest(url, function (err, response) {",
" pm.test(\"Validate response\", () => {",
" pm.test(`Validate response for region ${country.region}`, () => {",
" var resp = response.json();",
" ",
" pm.expect(response.code).to.be.eq(200, \"Invalid response code status\");",
@@ -383,16 +383,15 @@
}
],
"request": {
"method": "POST",
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/auth/register",
"raw": "{{base_url}}/ping",
"host": [
"{{base_url}}"
],
"path": [
"auth",
"register"
"ping"
]
}
},
@@ -403,6 +402,133 @@
{
"name": "04/auth/sign-in",
"item": [
{
"name": "Register a user",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var schema = {",
" \"type\": \"object\",",
" \"properties\": {",
" \"profile\": {",
" \"type\": \"object\",",
" \"description\": \"Информация о профиле пользователя\",",
" \"properties\": {",
" \"login\": {",
" \"type\": \"string\",",
" \"description\": \"Логин пользователя\",",
" \"maxLength\": 30,",
" \"pattern\": \"[a-zA-Z0-9-]+\"",
" },",
" \"email\": {",
" \"type\": \"string\",",
" \"description\": \"E-mail пользователя\",",
" \"maxLength\": 50",
" },",
" \"countryCode\": {",
" \"type\": \"string\",",
" \"description\": \"Двухбуквенный код, уникально идентифицирующий страну\",",
" \"maxLength\": 2,",
" \"pattern\": \"[a-zA-Z]{2}\"",
" },",
" \"isPublic\": {",
" \"type\": \"boolean\",",
" \"description\": \"Является ли данный профиль публичным. \\n\\nПубличные профили доступны другим пользователям: если профиль публичный, любой пользователь платформы сможет получить информацию о пользователе.\\n\"",
" },",
" \"phone\": {",
" \"type\": \"string\",",
" \"description\": \"Номер телефона пользователя в формате +123456789\",",
" \"pattern\": \"\\\\+[\\\\d]+\"",
" },",
" \"image\": {",
" \"type\": \"string\",",
" \"description\": \"Ссылка на фото для аватара пользователя\",",
" \"maxLength\": 200",
" }",
" },",
" \"required\": [",
" \"login\",",
" \"email\",",
" \"countryCode\",",
" \"isPublic\"",
" ]",
" }",
" },",
" \"required\": [",
" \"profile\"",
" ],",
" \"$schema\": \"http://json-schema.org/draft-04/schema#\"",
"};",
"",
"pm.test(\"Register a user\", function () {",
" var url = pm.variables.get(\"base_url\") + \"/auth/register\";",
" const options = {",
" url: url,",
" method: 'POST',",
" header: {",
" 'Content-Type': 'application/json',",
" },",
" body: {",
" mode: 'raw',",
" raw: JSON.stringify({",
" 'login': 'yellowMonkey2',",
" 'email': 'yellowstone1980@you.ru',",
" 'password': '$aba4821FWfew01#.fewA$',",
" 'countryCode': 'RU',",
" 'isPublic': true,",
" 'phone': '+74951239922',",
" })",
" }",
" };",
"",
" const profile = {",
" 'profile': {",
" 'login': 'yellowMonkey2',",
" 'email': 'yellowstone1980@you.ru',",
" 'countryCode': 'RU',",
" 'isPublic': true,",
" 'phone': '+74951239922',",
" }",
" }",
"",
" pm.sendRequest(options, function (err, response) {",
" pm.test(\"Validate response\", () => {",
" var resp = response.json();",
" ",
" pm.expect(response.code).to.be.oneOf([201, 409], \"Invalid response code status\");",
" pm.expect(tv4.validate(resp, schema), \"Invalid JSON schema\").to.be.true;",
"",
" console.log(\"got\", resp, \"expected\", profile);",
" pm.expect(resp).to.deep.eq(profile, `Got invalid object`);",
" });",
" });",
"});",
"",
"",
"",
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/ping",
"host": [
"{{base_url}}"
],
"path": [
"ping"
]
}
},
"response": []
},
{
"name": "Sign in",
"event": [
@@ -425,33 +551,6 @@
" \"$schema\": \"http://json-schema.org/draft-04/schema#\"",
"};",
"",
"pm.test(\"Register a user\", function () {",
" var url = pm.variables.get(\"base_url\") + \"/auth/register\";",
" const options = {",
" url: url,",
" method: 'POST',",
" header: {",
" 'Content-Type': 'application/json',",
" },",
" body: {",
" mode: 'raw',",
" raw: JSON.stringify({",
" 'login': 'yellowMonkey11000',",
" 'email': 'yellowstone1983@you.ru',",
" 'password': '$aba4821FWfew01#.fewA$',",
" 'countryCode': 'RU',",
" 'isPublic': true,",
" 'phone': '+74951239912',",
" })",
" }",
" };",
"",
" pm.sendRequest(options, function (err, response) {",
" pm.test(\"Validate register response\", () => {",
" var resp = response.json();",
" pm.expect(response.code).to.be.oneOf([201, 409], \"Invalid response code status\");",
" });",
"",
"pm.test(\"Sign in\", function () {",
" var url = pm.variables.get(\"base_url\") + \"/auth/sign-in\";",
" const options = {",
@@ -463,7 +562,7 @@
" body: {",
" mode: 'raw',",
" raw: JSON.stringify({",
" 'login': 'yellowMonkey11000',",
" 'login': 'yellowMonkey2',",
" 'password': '$aba4821FWfew01#.fewA$',",
" })",
" }",
@@ -480,6 +579,131 @@
" });",
" });",
"});",
"",
"",
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/ping",
"host": [
"{{base_url}}"
],
"path": [
"ping"
]
}
},
"response": []
}
]
},
{
"name": "05/me",
"item": [
{
"name": "Register a user",
"event": [
{
"listen": "test",
"script": {
"exec": [
"var schema = {",
" \"type\": \"object\",",
" \"properties\": {",
" \"profile\": {",
" \"type\": \"object\",",
" \"description\": \"Информация о профиле пользователя\",",
" \"properties\": {",
" \"login\": {",
" \"type\": \"string\",",
" \"description\": \"Логин пользователя\",",
" \"maxLength\": 30,",
" \"pattern\": \"[a-zA-Z0-9-]+\"",
" },",
" \"email\": {",
" \"type\": \"string\",",
" \"description\": \"E-mail пользователя\",",
" \"maxLength\": 50",
" },",
" \"countryCode\": {",
" \"type\": \"string\",",
" \"description\": \"Двухбуквенный код, уникально идентифицирующий страну\",",
" \"maxLength\": 2,",
" \"pattern\": \"[a-zA-Z]{2}\"",
" },",
" \"isPublic\": {",
" \"type\": \"boolean\",",
" \"description\": \"Является ли данный профиль публичным. \\n\\nПубличные профили доступны другим пользователям: если профиль публичный, любой пользователь платформы сможет получить информацию о пользователе.\\n\"",
" },",
" \"phone\": {",
" \"type\": \"string\",",
" \"description\": \"Номер телефона пользователя в формате +123456789\",",
" \"pattern\": \"\\\\+[\\\\d]+\"",
" },",
" \"image\": {",
" \"type\": \"string\",",
" \"description\": \"Ссылка на фото для аватара пользователя\",",
" \"maxLength\": 200",
" }",
" },",
" \"required\": [",
" \"login\",",
" \"email\",",
" \"countryCode\",",
" \"isPublic\"",
" ]",
" }",
" },",
" \"required\": [",
" \"profile\"",
" ],",
" \"$schema\": \"http://json-schema.org/draft-04/schema#\"",
"};",
"",
"pm.test(\"Register a user\", function () {",
" var url = pm.variables.get(\"base_url\") + \"/auth/register\";",
" const options = {",
" url: url,",
" method: 'POST',",
" header: {",
" 'Content-Type': 'application/json',",
" },",
" body: {",
" mode: 'raw',",
" raw: JSON.stringify({",
" 'login': 'yellowMonkey10000',",
" 'email': 'yellowstone1980@you.ru',",
" 'password': '$aba4821FWfew01#.fewA$',",
" 'countryCode': 'RU',",
" 'isPublic': true,",
" 'phone': '+74951239922',",
" })",
" }",
" };",
"",
" const profile = {",
" 'profile': {",
" 'login': 'yellowMonkey10000',",
" 'email': 'yellowstone1980@you.ru',",
" 'countryCode': 'RU',",
" 'isPublic': true,",
" 'phone': '+74951239922',",
" }",
" }",
"",
" pm.sendRequest(options, function (err, response) {",
" pm.test(\"Validate response\", () => {",
" var resp = response.json();",
" ",
" pm.expect(response.code).to.be.oneOf([201, 409], \"Invalid response code status\");",
" });",
" });",
"});",
"",
@@ -492,28 +716,22 @@
}
],
"request": {
"method": "POST",
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/auth/sign-in",
"raw": "{{base_url}}/ping",
"host": [
"{{base_url}}"
],
"path": [
"auth",
"sign-in"
"ping"
]
}
},
"response": []
}
]
},
{
"name": "05/me",
"item": [
{
"name": "Get my profile",
"name": "Sign in",
"event": [
{
"listen": "test",
@@ -534,36 +752,9 @@
" \"$schema\": \"http://json-schema.org/draft-04/schema#\"",
"};",
"",
"pm.test(\"Register a user\", function () {",
" var url = pm.variables.get(\"base_url\") + \"/auth/register\";",
" const options = {",
" url: url,",
" method: 'POST',",
" header: {",
" 'Content-Type': 'application/json',",
" },",
" body: {",
" mode: 'raw',",
" raw: JSON.stringify({",
" 'login': 'yellowMonkey11000',",
" 'email': 'yellowstone1983@you.ru',",
" 'password': '$aba4821FWfew01#.fewA$',",
" 'countryCode': 'RU',",
" 'isPublic': true,",
" 'phone': '+74951239912',",
" })",
" }",
" };",
"",
" pm.sendRequest(options, function (err, response) {",
" pm.test(\"Validate register response\", () => {",
" var resp = response.json();",
" pm.expect(response.code).to.be.oneOf([201, 409], \"Invalid response code status\");",
" });",
"",
"pm.test(\"Sign in\", function () {",
" var url = pm.variables.get(\"base_url\") + \"/auth/sign-in\";",
" const options = {",
" var options = {",
" url: url,",
" method: 'POST',",
" header: {",
@@ -572,7 +763,7 @@
" body: {",
" mode: 'raw',",
" raw: JSON.stringify({",
" 'login': 'yellowMonkey11000',",
" 'login': 'yellowMonkey10000',",
" 'password': '$aba4821FWfew01#.fewA$',",
" })",
" }",
@@ -587,7 +778,39 @@
"",
" pm.environment.set(\"05_profile_token\", resp.token);",
" console.log(\"Token has been saved\")",
" });",
" });",
"});",
"",
"",
""
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/ping",
"host": [
"{{base_url}}"
],
"path": [
"ping"
]
}
},
"response": []
},
{
"name": "Get my profile",
"event": [
{
"listen": "test",
"script": {
"exec": [
"pm.test(\"Get profile\", function () {",
" const url = pm.variables.get(\"base_url\") + \"/me/profile\";",
" const token = pm.environment.get(\"05_profile_token\");",
@@ -607,21 +830,10 @@
" pm.expect(response.code).to.be.eq(200, \"Invalid response code status\");",
"",
" console.log(\"Got profile\", resp);",
" pm.expect(resp.login).to.be.eq(\"yellowMonkey11000\", \"Invalid login\");",
" pm.expect(resp.login).to.be.eq(\"yellowMonkey10000\", \"Invalid login\");",
" });",
" });",
"});",
" });",
" });",
" });",
" });",
"});",
"",
"",
"",
"",
"",
"",
"",
""
],
@@ -633,13 +845,12 @@
"method": "GET",
"header": [],
"url": {
"raw": "{{base_url}}/me/profile",
"raw": "{{base_url}}/ping",
"host": [
"{{base_url}}"
],
"path": [
"me",
"profile"
"ping"
]
}
},