fix(hub): метки проектов в базовой JQL в кавычках, запрет \ и управляющих символов (I-2, #61)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ec4fb8b1e7
commit
1f2fc142b7
@ -38,13 +38,14 @@
|
|||||||
Отдельная таблица, а не ключ настроек: проект — сущность со своим жизненным циклом, а настройки уходят агенту целиком
|
Отдельная таблица, а не ключ настроек: проект — сущность со своим жизненным циклом, а настройки уходят агенту целиком
|
||||||
и поднимают `settings_version` при каждой правке. Создание проекта `settings_version` не поднимает: агенту проекты не
|
и поднимают `settings_version` при каждой правке. Создание проекта `settings_version` не поднимает: агенту проекты не
|
||||||
нужны, доски он получает в параметрах команд (решение 7).
|
нужны, доски он получает в параметрах команд (решение 7).
|
||||||
2. **Метка проекта** — непустая строка без пробельных символов, запятых, кавычек и скобок (метка Jira не может
|
2. **Метка проекта** — непустая строка без пробельных и управляющих символов (категория Unicode `Cc`), запятых,
|
||||||
содержать пробел; остальное исключено, чтобы метку можно было без экранирования вставить в JQL `labels in (…)`), до
|
кавычек, скобок и обратной косой черты (метка Jira не может содержать пробел; остальное исключено, чтобы метку можно
|
||||||
100 символов. Регистр сохраняется как введён; совпадение с существующей без учёта регистра — 409 (Jira сравнивает
|
было вставить в JQL в двойных кавычках без экранирования), до 100 символов. Регистр сохраняется как введён; совпадение с существующей без учёта регистра — 409 (Jira сравнивает
|
||||||
метки без учёта регистра в JQL). Название — непустое после обрезки пробелов, до 100 символов. `board_id` — целое > 0
|
метки без учёта регистра в JQL). Название — непустое после обрезки пробелов, до 100 символов. `board_id` — целое > 0
|
||||||
или отсутствует.
|
или отсутствует.
|
||||||
3. **Базовая JQL собирается, а не хранится.** `base_jql = "project = <project_key> AND labels in (<метки по
|
3. **Базовая JQL собирается, а не хранится.** `base_jql = 'project = <project_key> AND labels in ("<метка>", …)'` (каждая метка в двойных
|
||||||
created_at, затем label>)"`; функция в `settings` (или новом модуле `projects`), её используют `POST /refresh
|
кавычках, чтобы `EMPTY`, `null` и прочие зарезервированные слова JQL были значением; метки по created_at, затем
|
||||||
|
label); функция в `settings` (или новом модуле `projects`), её используют `POST /refresh
|
||||||
{full}`, автообновление, `is_full_refresh`, `mirror.board` и `GET /settings` (поле `base_jql` в ответе — вычисленное,
|
{full}`, автообновление, `is_full_refresh`, `mirror.board` и `GET /settings` (поле `base_jql` в ответе — вычисленное,
|
||||||
только для чтения; `PUT /settings` с `base_jql` или `board_id` — 422 с указанием, что JQL собирается из проектов, а
|
только для чтения; `PUT /settings` с `base_jql` или `board_id` — 422 с указанием, что JQL собирается из проектов, а
|
||||||
доска — свойство проекта). Без проектов `base_jql = null`: `POST /refresh {full}` — 422 «нет проектов», автообновление
|
доска — свойство проекта). Без проектов `base_jql = null`: `POST /refresh {full}` — 422 «нет проектов», автообновление
|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
- **Проекты в хабе.** Новая таблица `project` (схема v4): метка Jira (ключ, не меняется), название, номер доски Jira
|
- **Проекты в хабе.** Новая таблица `project` (схема v4): метка Jira (ключ, не меняется), название, номер доски Jira
|
||||||
(может отсутствовать), тип доски (`scrum` / `kanban`, определяет агент). Маршруты `GET /projects`, `POST /projects`.
|
(может отсутствовать), тип доски (`scrum` / `kanban`, определяет агент). Маршруты `GET /projects`, `POST /projects`.
|
||||||
Миграция заводит проект AWPCA «АРМ КА» с доской 4216 из нынешних настроек.
|
Миграция заводит проект AWPCA «АРМ КА» с доской 4216 из нынешних настроек.
|
||||||
- **Базовая JQL собирается из проектов** — `project = <project_key> AND labels in (<метки всех проектов>)`; настройки
|
- **Базовая JQL собирается из проектов** — `project = <project_key> AND labels in ("<метка>", …)`; настройки
|
||||||
`base_jql` и `board_id` уходят из `setting`. Создание проекта ставит полный `refresh`.
|
`base_jql` и `board_id` уходят из `setting`. Создание проекта ставит полный `refresh`.
|
||||||
- **Разрез по проекту в API.** `GET /board?project=` и `GET /issues?project=` отдают задачи проекта и их соседей
|
- **Разрез по проекту в API.** `GET /board?project=` и `GET /issues?project=` отдают задачи проекта и их соседей
|
||||||
(задачи других меток, связанные с ними, и их эпики) с признаком `neighbor` относительно проекта.
|
(задачи других меток, связанные с ними, и их эпики) с признаком `neighbor` относительно проекта.
|
||||||
|
|||||||
@ -3,12 +3,13 @@
|
|||||||
### Requirement: Проекты
|
### Requirement: Проекты
|
||||||
Таблица `project` SHALL хранить проекты моста — подмножества задач Jira-проекта `project_key`, выделенные меткой:
|
Таблица `project` SHALL хранить проекты моста — подмножества задач Jira-проекта `project_key`, выделенные меткой:
|
||||||
`label` (PK, метка Jira), `name`, `board_id` (номер доски Jira или `NULL`), `board_type` (`scrum` | `kanban` | `NULL` —
|
`label` (PK, метка Jira), `name`, `board_id` (номер доски Jira или `NULL`), `board_type` (`scrum` | `kanban` | `NULL` —
|
||||||
тип ещё не известен), `created_at`. Метка SHALL быть непустой строкой до 100 символов без пробельных символов, запятых,
|
тип ещё не известен), `created_at`. Метка SHALL быть непустой строкой до 100 символов без пробельных и управляющих символов (категория
|
||||||
кавычек и скобок; две метки, совпадающие без учёта регистра, MUST NOT существовать одновременно. Название SHALL быть
|
Unicode `Cc`), запятых, кавычек, скобок и обратной косой черты; две метки, совпадающие без учёта регистра, MUST NOT существовать одновременно. Название SHALL быть
|
||||||
непустым после обрезки пробелов, до 100 символов. `board_id` — целое больше нуля или `NULL`. `board_type` SHALL
|
непустым после обрезки пробелов, до 100 символов. `board_id` — целое больше нуля или `NULL`. `board_type` SHALL
|
||||||
записываться только по результату `refresh` от агента (см. `agent`). Метку, название и доску проекта после создания
|
записываться только по результату `refresh` от агента (см. `agent`). Метку, название и доску проекта после создания
|
||||||
хаб не меняет (переименования и удаления нет). Базовая JQL SHALL вычисляться из проектов:
|
хаб не меняет (переименования и удаления нет). Базовая JQL SHALL вычисляться из проектов:
|
||||||
`project = <project_key> AND labels in (<метки проектов по created_at, затем label через ", ">)`; без проектов базовой JQL
|
`project = <project_key> AND labels in ("<метка>", …)` — каждая метка в двойных кавычках, метки по created_at, затем
|
||||||
|
label, через `, `; без проектов базовой JQL
|
||||||
нет (`null`).
|
нет (`null`).
|
||||||
|
|
||||||
Для проекта P задачи зеркала делятся на *свои* — с меткой P в `labels` (точное совпадение) — и *соседей* — задачи без
|
Для проекта P задачи зеркала делятся на *свои* — с меткой P в `labels` (точное совпадение) — и *соседей* — задачи без
|
||||||
@ -17,7 +18,7 @@
|
|||||||
|
|
||||||
#### Scenario: Базовая JQL из двух проектов
|
#### Scenario: Базовая JQL из двух проектов
|
||||||
- **WHEN** заведены проекты `AWPCA` (раньше) и `STOCKS_DRRK`, `project_key = AURORA`
|
- **WHEN** заведены проекты `AWPCA` (раньше) и `STOCKS_DRRK`, `project_key = AURORA`
|
||||||
- **THEN** базовая JQL — `project = AURORA AND labels in (AWPCA, STOCKS_DRRK)`
|
- **THEN** базовая JQL — `project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")`
|
||||||
|
|
||||||
#### Scenario: Свои и соседи
|
#### Scenario: Свои и соседи
|
||||||
- **WHEN** у AURORA-1 метка `STOCKS_DRRK` и эпик AURORA-9 (метка `AWPCA`), связь AURORA-1 → AURORA-2 (метка `AWPCA`),
|
- **WHEN** у AURORA-1 метка `STOCKS_DRRK` и эпик AURORA-9 (метка `AWPCA`), связь AURORA-1 → AURORA-2 (метка `AWPCA`),
|
||||||
@ -28,6 +29,14 @@
|
|||||||
- **WHEN** создаётся проект с меткой `STOCKS DRRK`
|
- **WHEN** создаётся проект с меткой `STOCKS DRRK`
|
||||||
- **THEN** отказ с указанием поля `label`, проекта нет
|
- **THEN** отказ с указанием поля `label`, проекта нет
|
||||||
|
|
||||||
|
#### Scenario: Метка с обратной косой чертой или управляющим символом
|
||||||
|
- **WHEN** создаётся проект с меткой `A\B` или `A<перевод строки>B`
|
||||||
|
- **THEN** отказ с указанием поля `label`, проекта нет
|
||||||
|
|
||||||
|
#### Scenario: Метка — зарезервированное слово JQL
|
||||||
|
- **WHEN** заведён проект с меткой `EMPTY`
|
||||||
|
- **THEN** базовая JQL — `project = AURORA AND labels in ("EMPTY")`, метка не расширяет выборку
|
||||||
|
|
||||||
#### Scenario: Та же метка другим регистром
|
#### Scenario: Та же метка другим регистром
|
||||||
- **WHEN** есть проект `AWPCA`, создаётся проект `awpca`
|
- **WHEN** есть проект `AWPCA`, создаётся проект `awpca`
|
||||||
- **THEN** отказ «проект с такой меткой уже есть», проект один
|
- **THEN** отказ «проект с такой меткой уже есть», проект один
|
||||||
@ -71,12 +80,12 @@
|
|||||||
- **WHEN** хаб стартует с базой `schema_version = 3`, `base_jql = "project = AURORA AND labels = AWPCA"`, `board_id = 4216`,
|
- **WHEN** хаб стартует с базой `schema_version = 3`, `base_jql = "project = AURORA AND labels = AWPCA"`, `board_id = 4216`,
|
||||||
в `sync_run` полный прогон с этой `query`
|
в `sync_run` полный прогон с этой `query`
|
||||||
- **THEN** в `backups/` копия базы v3; проект `AWPCA` «АРМ КА» с `board_id = 4216`, `board_type = NULL`; ключи `base_jql`
|
- **THEN** в `backups/` копия базы v3; проект `AWPCA` «АРМ КА» с `board_id = 4216`, `board_type = NULL`; ключи `base_jql`
|
||||||
и `board_id` в `setting` на месте; `query` того прогона — `project = AURORA AND labels in (AWPCA)`; задачи, заметки,
|
и `board_id` в `setting` на месте; `query` того прогона — `project = AURORA AND labels in ("AWPCA")`; задачи, заметки,
|
||||||
пакеты на месте; `schema_version = 4`
|
пакеты на месте; `schema_version = 4`
|
||||||
|
|
||||||
#### Scenario: refresh в очереди при миграции
|
#### Scenario: refresh в очереди при миграции
|
||||||
- **WHEN** в базе v3 стоит `queued` `refresh {jql: "project = AURORA AND labels = AWPCA"}`
|
- **WHEN** в базе v3 стоит `queued` `refresh {jql: "project = AURORA AND labels = AWPCA"}`
|
||||||
- **THEN** после миграции его `params.jql` — `project = AURORA AND labels in (AWPCA)`, и по завершении он считается
|
- **THEN** после миграции его `params.jql` — `project = AURORA AND labels in ("AWPCA")`, и по завершении он считается
|
||||||
полным прогоном
|
полным прогоном
|
||||||
|
|
||||||
#### Scenario: Два соединения к базе v1
|
#### Scenario: Два соединения к базе v1
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
#### Scenario: Новый проект
|
#### Scenario: Новый проект
|
||||||
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", board_id: 5120, author: "viewer"}`, есть проект `AWPCA`
|
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК", board_id: 5120, author: "viewer"}`, есть проект `AWPCA`
|
||||||
- **THEN** 201; `GET /projects` отдаёт два проекта, у `STOCKS_DRRK` `board_type = null`; в очереди пакет автора `viewer`
|
- **THEN** 201; `GET /projects` отдаёт два проекта, у `STOCKS_DRRK` `board_type = null`; в очереди пакет автора `viewer`
|
||||||
с `refresh {jql: "project = AURORA AND labels in (AWPCA, STOCKS_DRRK)"}`
|
с `refresh {jql: 'project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")'}`
|
||||||
|
|
||||||
#### Scenario: Проект без доски
|
#### Scenario: Проект без доски
|
||||||
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК"}`
|
- **WHEN** `POST /projects {label: "STOCKS_DRRK", name: "Запасы ДРРК"}`
|
||||||
@ -146,7 +146,7 @@
|
|||||||
|
|
||||||
#### Scenario: Два проекта
|
#### Scenario: Два проекта
|
||||||
- **WHEN** интервал прошёл, очередь пуста, проекты `AWPCA` и `STOCKS_DRRK`
|
- **WHEN** интервал прошёл, очередь пуста, проекты `AWPCA` и `STOCKS_DRRK`
|
||||||
- **THEN** поставлен один `refresh` с JQL `project = AURORA AND labels in (AWPCA, STOCKS_DRRK)`
|
- **THEN** поставлен один `refresh` с JQL `project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")`
|
||||||
|
|
||||||
### Requirement: Снимок доски
|
### Requirement: Снимок доски
|
||||||
Хаб SHALL предоставлять `GET /board?project=` под клиентским токеном — согласованный снимок зеркала для доски связей,
|
Хаб SHALL предоставлять `GET /board?project=` под клиентским токеном — согласованный снимок зеркала для доски связей,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
"""Проекты-метки моста: таблица project, проверка метки, базовая JQL из меток, доски, миграция v3 → v4."""
|
"""Проекты-метки моста: таблица project, проверка метки, базовая JQL из меток, доски, миграция v3 → v4."""
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
import unicodedata
|
||||||
|
|
||||||
from . import db, settings
|
from . import db, settings
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ LEGACY_LABEL = "AWPCA"
|
|||||||
LEGACY_NAME = "АРМ КА"
|
LEGACY_NAME = "АРМ КА"
|
||||||
MAX_LEN = 100
|
MAX_LEN = 100
|
||||||
BOARD_TYPES = ("scrum", "kanban")
|
BOARD_TYPES = ("scrum", "kanban")
|
||||||
_FORBIDDEN = ',"\'()'
|
_FORBIDDEN = ',"\'()\\'
|
||||||
_FIELDS = "label, name, board_id, board_type, created_at"
|
_FIELDS = "label, name, board_id, board_type, created_at"
|
||||||
|
|
||||||
|
|
||||||
@ -32,8 +33,8 @@ def _label_error(label) -> str | None:
|
|||||||
return "ожидается непустая строка"
|
return "ожидается непустая строка"
|
||||||
if len(label) > MAX_LEN:
|
if len(label) > MAX_LEN:
|
||||||
return f"длиннее {MAX_LEN} символов"
|
return f"длиннее {MAX_LEN} символов"
|
||||||
if any(c.isspace() or c in _FORBIDDEN for c in label):
|
if any(c.isspace() or c in _FORBIDDEN or unicodedata.category(c) == "Cc" for c in label):
|
||||||
return "пробелы и символы , \" ' ( ) недопустимы"
|
return "пробелы, управляющие символы и символы , \" ' ( ) \\ недопустимы"
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ def base_jql(conn) -> str | None:
|
|||||||
if not found:
|
if not found:
|
||||||
return None
|
return None
|
||||||
key = settings.get(conn, "project_key") or "AURORA"
|
key = settings.get(conn, "project_key") or "AURORA"
|
||||||
return f"project = {key} AND labels in ({', '.join(found)})"
|
return f"project = {key} AND labels in ({', '.join(f'"{x}"' for x in found)})"
|
||||||
|
|
||||||
|
|
||||||
def boards(conn) -> list[dict]:
|
def boards(conn) -> list[dict]:
|
||||||
|
|||||||
@ -131,7 +131,7 @@ def test_interface_routes(hub, add_project):
|
|||||||
retry = c.post(f"/commands/{cmd['id']}/retry", {"author": "claude"})
|
retry = c.post(f"/commands/{cmd['id']}/retry", {"author": "claude"})
|
||||||
assert c.get(f"/batches/{retry['id']}")["title"] == f"Повтор команды {cmd['id']}"
|
assert c.get(f"/batches/{retry['id']}")["title"] == f"Повтор команды {cmd['id']}"
|
||||||
full = c.post("/refresh", {"full": True})
|
full = c.post("/refresh", {"full": True})
|
||||||
assert c.get(f"/batches/{full['id']}")["commands"][0]["params"]["jql"] == "project = AURORA AND labels in (AWPCA)"
|
assert c.get(f"/batches/{full['id']}")["commands"][0]["params"]["jql"] == 'project = AURORA AND labels in ("AWPCA")'
|
||||||
keys = c.post("/refresh", {"keys": ["A-1"]})
|
keys = c.post("/refresh", {"keys": ["A-1"]})
|
||||||
assert c.get(f"/batches/{keys['id']}")["commands"][0]["params"] == {"keys": ["A-1"]}
|
assert c.get(f"/batches/{keys['id']}")["commands"][0]["params"] == {"keys": ["A-1"]}
|
||||||
|
|
||||||
@ -552,7 +552,7 @@ def test_settings_get_base_jql_from_projects(hub, add_project):
|
|||||||
c = hub["client"]
|
c = hub["client"]
|
||||||
assert c.get("/settings")["base_jql"] is None
|
assert c.get("/settings")["base_jql"] is None
|
||||||
_add_project(hub, add_project)
|
_add_project(hub, add_project)
|
||||||
assert c.get("/settings")["base_jql"] == "project = AURORA AND labels in (AWPCA)"
|
assert c.get("/settings")["base_jql"] == 'project = AURORA AND labels in ("AWPCA")'
|
||||||
|
|
||||||
|
|
||||||
def test_settings_put_returns_all_settings(hub):
|
def test_settings_put_returns_all_settings(hub):
|
||||||
@ -794,9 +794,9 @@ def test_create_project(hub): # сце
|
|||||||
assert [p["label"] for p in got] == ["AWPCA", "STOCKS_DRRK"] and got[1]["issues_count"] == 0
|
assert [p["label"] for p in got] == ["AWPCA", "STOCKS_DRRK"] and got[1]["issues_count"] == 0
|
||||||
b = c.get(f"/batches/{out['batch_id']}")
|
b = c.get(f"/batches/{out['batch_id']}")
|
||||||
assert b["author"] == "viewer" and b["title"] == "Новый проект STOCKS_DRRK" and b["project"] is None
|
assert b["author"] == "viewer" and b["title"] == "Новый проект STOCKS_DRRK" and b["project"] is None
|
||||||
assert b["commands"][0]["params"] == {"jql": "project = AURORA AND labels in (AWPCA, STOCKS_DRRK)"}
|
assert b["commands"][0]["params"] == {"jql": 'project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")'}
|
||||||
assert c.get("/settings")["settings_version"] == version # «Создание проекта не трогает версию настроек»
|
assert c.get("/settings")["settings_version"] == version # «Создание проекта не трогает версию настроек»
|
||||||
assert c.get("/settings")["base_jql"] == "project = AURORA AND labels in (AWPCA, STOCKS_DRRK)"
|
assert c.get("/settings")["base_jql"] == 'project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")'
|
||||||
|
|
||||||
|
|
||||||
def test_create_project_without_board(hub):
|
def test_create_project_without_board(hub):
|
||||||
@ -811,6 +811,9 @@ def test_create_project_without_board(hub):
|
|||||||
({"label": "AWPCA", "name": "x"}, 409, "label"),
|
({"label": "AWPCA", "name": "x"}, 409, "label"),
|
||||||
({"label": "awpca", "name": "x"}, 409, "label"),
|
({"label": "awpca", "name": "x"}, 409, "label"),
|
||||||
({"label": "STOCKS DRRK", "name": "x"}, 422, "label"),
|
({"label": "STOCKS DRRK", "name": "x"}, 422, "label"),
|
||||||
|
({"label": "A\\B", "name": "x"}, 422, "label"),
|
||||||
|
({"label": "A\nB", "name": "x"}, 422, "label"),
|
||||||
|
({"label": "A\tB", "name": "x"}, 422, "label"),
|
||||||
({"label": "STOCKS_DRRK", "name": " "}, 422, "name"),
|
({"label": "STOCKS_DRRK", "name": " "}, 422, "name"),
|
||||||
({"label": "STOCKS_DRRK", "name": "x", "board_id": "5120"}, 422, "board_id"),
|
({"label": "STOCKS_DRRK", "name": "x", "board_id": "5120"}, 422, "board_id"),
|
||||||
({"label": "STOCKS_DRRK", "name": "x", "author": 5}, 422, None),
|
({"label": "STOCKS_DRRK", "name": "x", "author": 5}, 422, None),
|
||||||
|
|||||||
@ -257,7 +257,7 @@ def test_migrates_v3_creates_awpca(tmp_path):
|
|||||||
path = tmp_path / "hub.sqlite"
|
path = tmp_path / "hub.sqlite"
|
||||||
_v3_database(path)
|
_v3_database(path)
|
||||||
conn = db.connect(path)
|
conn = db.connect(path)
|
||||||
new = "project = AURORA AND labels in (AWPCA)"
|
new = 'project = AURORA AND labels in ("AWPCA")'
|
||||||
assert projects.list_projects(conn)[0] | {"created_at": None, "issues_count": None} == {
|
assert projects.list_projects(conn)[0] | {"created_at": None, "issues_count": None} == {
|
||||||
"label": "AWPCA", "name": "АРМ КА", "board_id": 4216, "board_type": None, "created_at": None, "issues_count": None}
|
"label": "AWPCA", "name": "АРМ КА", "board_id": 4216, "board_type": None, "created_at": None, "issues_count": None}
|
||||||
assert settings.get(conn, "base_jql") == "project = AURORA AND labels = AWPCA" # ключи на месте
|
assert settings.get(conn, "base_jql") == "project = AURORA AND labels = AWPCA" # ключи на месте
|
||||||
|
|||||||
@ -35,7 +35,7 @@ def test_light_list_same_order_no_count(conn):
|
|||||||
assert all("issues_count" not in p and set(p) == {"label", "board_id", "board_type"} for p in got)
|
assert all("issues_count" not in p and set(p) == {"label", "board_id", "board_type"} for p in got)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("label", ["", "STOCKS DRRK", "A\tB", "A,B", 'A"B', "A'B", "A(B", "A)B", "X" * 101, 5, None])
|
@pytest.mark.parametrize("label", ["", "STOCKS DRRK", "A\tB", "A\nB", "A\\B", "A\x00B", "A\x07B", "A\x7fB", "A,B", 'A"B', "A'B", "A(B", "A)B", "X" * 101, 5, None])
|
||||||
def test_bad_label(conn, label):
|
def test_bad_label(conn, label):
|
||||||
with pytest.raises(projects.ProjectError) as e:
|
with pytest.raises(projects.ProjectError) as e:
|
||||||
projects.create(conn, label, "Название")
|
projects.create(conn, label, "Название")
|
||||||
@ -85,11 +85,18 @@ def test_same_label_other_case(conn):
|
|||||||
def test_base_jql(conn):
|
def test_base_jql(conn):
|
||||||
assert projects.base_jql(conn) is None
|
assert projects.base_jql(conn) is None
|
||||||
projects.create(conn, "AWPCA", "АРМ КА"); projects.create(conn, "STOCKS_DRRK", "Запасы ДРРК")
|
projects.create(conn, "AWPCA", "АРМ КА"); projects.create(conn, "STOCKS_DRRK", "Запасы ДРРК")
|
||||||
assert projects.base_jql(conn) == "project = AURORA AND labels in (AWPCA, STOCKS_DRRK)"
|
assert projects.base_jql(conn) == 'project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")'
|
||||||
settings.set_(conn, "project_key", "AUR")
|
settings.set_(conn, "project_key", "AUR")
|
||||||
assert projects.base_jql(conn).startswith("project = AUR AND")
|
assert projects.base_jql(conn).startswith("project = AUR AND")
|
||||||
|
|
||||||
|
|
||||||
|
def test_base_jql_single_and_reserved_words(conn):
|
||||||
|
projects.create(conn, "AWPCA", "АРМ КА")
|
||||||
|
assert projects.base_jql(conn) == 'project = AURORA AND labels in ("AWPCA")'
|
||||||
|
projects.create(conn, "EMPTY", "Пустой") # зарезервированное слово JQL — в кавычках
|
||||||
|
assert projects.base_jql(conn) == 'project = AURORA AND labels in ("AWPCA", "EMPTY")'
|
||||||
|
|
||||||
|
|
||||||
def test_boards_dedup_and_types(conn):
|
def test_boards_dedup_and_types(conn):
|
||||||
projects.create(conn, "AWPCA", "a", 4216); projects.create(conn, "B", "b", 5120)
|
projects.create(conn, "AWPCA", "a", 4216); projects.create(conn, "B", "b", 5120)
|
||||||
projects.create(conn, "C", "c", 4216); projects.create(conn, "D", "d")
|
projects.create(conn, "C", "c", 4216); projects.create(conn, "D", "d")
|
||||||
|
|||||||
@ -170,7 +170,7 @@ def test_auto_refresh_two_projects(conn, add_project): # сцен
|
|||||||
add_project(conn, "STOCKS_DRRK", board_id=None)
|
add_project(conn, "STOCKS_DRRK", board_id=None)
|
||||||
queue.claim(conn, agent_version=1, settings_version=1)
|
queue.claim(conn, agent_version=1, settings_version=1)
|
||||||
b = queue.maybe_auto_refresh(conn)
|
b = queue.maybe_auto_refresh(conn)
|
||||||
assert queue.batch_view(conn, b["id"])["commands"][0]["params"] == {"jql": "project = AURORA AND labels in (AWPCA, STOCKS_DRRK)"}
|
assert queue.batch_view(conn, b["id"])["commands"][0]["params"] == {"jql": 'project = AURORA AND labels in ("AWPCA", "STOCKS_DRRK")'}
|
||||||
|
|
||||||
|
|
||||||
def test_auto_refresh_conditions(conn, add_project):
|
def test_auto_refresh_conditions(conn, add_project):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user