feat: Add test file for run 1757081046

This commit is contained in:
2025-09-05 17:04:06 +03:00
parent 4a6a2a5d68
commit 4d7fee3b47
3 changed files with 575 additions and 0 deletions

444
gitea-client.zsh Normal file
View File

@@ -0,0 +1,444 @@
#!/usr/bin/env zsh
# [PACKAGE: 'homebox_lens']
# [FILE: 'gitea-client.zsh']
# [SEMANTICS]
# [ENTITY: 'File'('gitea-client.zsh')]
# [ENTITY: 'Function'('api_request')]
# [ENTITY: 'Function'('find_tasks')]
# [ENTITY: 'Function'('update_task_status')]
# [ENTITY: 'Function'('create_pr')]
# [ENTITY: 'Function'('create_task')]
# [ENTITY: 'Function'('add_comment')]
# [ENTITY: 'Function'('merge_and_complete')]
# [ENTITY: 'Function'('return_to_dev')]
# [ENTITY: 'EntryPoint'('main_dispatch')]
# [ENTITY: 'Configuration'('GITEA_URL')]
# [ENTITY: 'Configuration'('GITEA_TOKEN')]
# [ENTITY: 'Configuration'('GITEA_OWNER')]
# [ENTITY: 'Configuration'('GITEA_REPO')]
# [ENTITY: 'ExternalCommand'('jq')]
# [ENTITY: 'ExternalCommand'('curl')]
# [RELATION: 'File'('gitea-client.zsh')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'File'('gitea-client.zsh')] -> [DEPENDS_ON] -> ['ExternalCommand'('curl')]
# [RELATION: 'Function'('api_request')] -> [DEPENDS_ON] -> ['ExternalCommand'('curl')]
# [RELATION: 'Function'('api_request')] -> [READS_FROM] -> ['Configuration'('GITEA_URL')]
# [RELATION: 'Function'('api_request')] -> [READS_FROM] -> ['Configuration'('GITEA_TOKEN')]
# [RELATION: 'Function'('find_tasks')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('update_task_status')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('update_task_status')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'Function'('create_pr')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('create_pr')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'Function'('create_task')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('create_task')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'Function'('add_comment')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('add_comment')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'Function'('merge_and_complete')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('merge_and_complete')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'Function'('return_to_dev')] -> [CALLS] -> ['Function'('api_request')]
# [RELATION: 'Function'('return_to_dev')] -> [DEPENDS_ON] -> ['ExternalCommand'('jq')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('find_tasks')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('update_task_status')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('create_pr')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('create_task')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('add_comment')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('merge_and_complete')]
# [RELATION: 'EntryPoint'('main_dispatch')] -> [CALLS] -> ['Function'('return_to_dev')]
# [END_SEMANTICS]
set -x
# [DEPENDENCIES]
# Gitea Client Script
# Version: 1.0
if ! command -v jq &> /dev/null;
then
echo "jq could not be found. Please install jq to use this script."
exit 1
fi
# [END_DEPENDENCIES]
# [CONFIGURATION]
# IMPORTANT: Replace with your Gitea URL, API Token, repository owner and repository name.
# You can also set these as environment variables: GITEA_URL, GITEA_TOKEN, GITEA_OWNER, GITEA_REPO
: ${GITEA_URL:="https://gitea.bebesh.ru"}
: ${GITEA_TOKEN:="c6fb6d73a18b2b4ddf94b67f2da6b6bb832164ce"}
: ${GITEA_OWNER:="busya"}
: ${GITEA_REPO:="gitea-client-tests"} # <-- Убедитесь, что здесь тестовый репозиторий
# [END_CONFIGURATION]
# [HELPERS]
# [ENTITY: 'Function'('api_request')]
# [CONTRACT]
# Generic function to make requests to the Gitea API.
# This is the central communication point with the Gitea instance.
#
# @param $1: method - The HTTP method (GET, POST, PATCH, DELETE).
# @param $2: endpoint - The API endpoint (e.g., "repos/owner/repo/issues").
# @param $3: json_data - The JSON payload for POST/PATCH requests.
#
# @stdout The body of the API response on success.
# @stderr Error messages on failure.
#
# @returns 0 on success, 1 on unsupported method. Curl exit code on curl failure.
# [/CONTRACT]
function api_request() {
local method="$1"
local endpoint="$2"
local data="$3"
local url="$GITEA_URL/api/v1/$endpoint"
local -a curl_opts
curl_opts=("-s" "-H" "Authorization: token $GITEA_TOKEN" "-H" "Content-Type: application/json")
case "$method" in
GET)
curl "${curl_opts[@]}" "$url"
;;
POST|PATCH)
curl "${curl_opts[@]}" -X "$method" -d @- "$url" <<< "$data"
;;
DELETE)
# Для DELETE запросов тело не нужно, и curl вернет ошибку если его передать
# Gitea на успешное удаление часто возвращает код 204 No Content с пустым телом
curl -o /dev/null -w "%{http_code}" "${curl_opts[@]}" -X "$method" "$url"
;;
*)
echo "Unsupported HTTP method: $method" >&2
return 1
;;
esac
}
# [END_ENTITY: 'Function'('api_request')]
# [END_HELPERS]
# [COMMANDS]
# [ENTITY: 'Function'('find_tasks')]
# [CONTRACT]
# Finds open issues with a specific type and 'status::pending' label.
#
# @param --type: The label to filter issues by (e.g., "type::development").
#
# @stdout A JSON array of Gitea issues matching the criteria.
# [/CONTRACT]
function find_tasks() {
local type=""
# Parsing arguments like --type "type::development"
while [[ $# -gt 0 ]]; do
case "$1" in
--type) type="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
local labels="type::development,status::pending"
if [[ -n "$type" ]]; then
labels="status::pending,${type}"
fi
api_request "GET" "repos/$GITEA_OWNER/$GITEA_REPO/issues?labels=$labels&state=open"
}
# [END_ENTITY: 'Function'('find_tasks')]
# [ENTITY: 'Function'('update_task_status')]
# [CONTRACT]
# Atomically changes the status of a task by removing an old status label and adding a new one.
#
# @param --issue-id: The ID of the issue to update.
# @param --old: The old status label to remove (e.g., "status::pending").
# @param --new: The new status label to add (e.g., "status::in-progress").
#
# @stdout The JSON representation of the updated issue.
# [/CONTRACT]
function update_task_status() {
local issue_id=""
local old_status=""
local new_status=""
while [[ $# -gt 0 ]]; do
case "$1" in
--issue-id) issue_id="$2"; shift 2 ;;
--old) old_status="$2"; shift 2 ;;
--new) new_status="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
if [[ -z "$issue_id" || -z "$old_status" || -z "$new_status" ]]; then
echo "Usage: update-task-status --issue-id <id> --old <old_status> --new <new_status>" >&2
return 1
fi
local issue_data=$(api_request "GET" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$issue_id")
if [[ -z "$issue_data" ]]; then
echo "Error: Could not retrieve issue data for issue ID $issue_id." >&2
return 1
fi
local existing_labels=$(echo "$issue_data" | jq -r '.labels | .[].name')
local -a new_labels
for label in ${=existing_labels};
do
if [[ "$label" != "$old_status" ]]; then
new_labels+=($label)
fi
done
new_labels+=($new_status)
local new_labels_json=$(printf '%s\n' "${new_labels[@]}" | jq -R . | jq -s .)
local data=$(jq -n --argjson labels "$new_labels_json" '{labels: $labels}')
api_request "PATCH" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$issue_id" "$data"
}
# [END_ENTITY: 'Function'('update_task_status')]
# [ENTITY: 'Function'('create_pr')]
# [CONTRACT]
# Creates a new Pull Request in the repository.
#
# @param --title: The title of the pull request.
# @param --head: The source branch for the pull request.
# @param --body: (Optional) The body/description of the pull request.
# @param --base: (Optional) The target branch. Defaults to 'main'.
#
# @stdout The JSON representation of the newly created pull request.
# [/CONTRACT]
function create_pr() {
local title=""
local body=""
local head_branch=""
local base_branch="main"
while [[ $# -gt 0 ]]; do
case "$1" in
--title) title="$2"; shift 2 ;;
--body) body="$2"; shift 2 ;;
--head) head_branch="$2"; shift 2 ;;
--base) base_branch="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
if [[ -z "$title" || -z "$head_branch" ]]; then
echo "Usage: create-pr --title <title> --head <head_branch> [--body <body>] [--base <base_branch>]" >&2
return 1
fi
local data=$(jq -n \
--arg title "$title" \
--arg body "$body" \
--arg head "$head_branch" \
--arg base "$base_branch" \
'{title: $title, body: $body, head: $head, base: $base}')
api_request "POST" "repos/$GITEA_OWNER/$GITEA_REPO/pulls" "$data"
}
# [END_ENTITY: 'Function'('create_pr')]
# [ENTITY: 'Function'('create_task')]
# [CONTRACT]
# Creates a new issue (task) in the repository.
#
# @param --title: The title of the issue.
# @param --body: (Optional) The body/description of the issue.
# @param --assignee: (Optional) Comma-separated list of usernames to assign.
# @param --labels: (Optional) Comma-separated list of labels to add.
#
# @stdout The JSON representation of the newly created issue.
# [/CONTRACT]
function create_task() {
local title=""
local body=""
local assignee=""
local labels=""
while [[ $# -gt 0 ]]; do
case "$1" in
--title) title="$2"; shift 2 ;;
--body) body="$2"; shift 2 ;;
--assignee) assignee="$2"; shift 2 ;;
--labels) labels="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
if [[ -z "$title" ]]; then
echo "Usage: create-task --title <title> [--body <body>] [--assignee <assignee>] [--labels <labels>]" >&2
return 1
fi
local labels_json="[]"
if [[ -n "$labels" ]]; then
local -a labels_arr
IFS=',' read -rA labels_arr <<< "$labels"
labels_json=$(printf '%s\n' "${labels_arr[@]}" | jq -R . | jq -s .)
fi
local assignees_json="[]"
if [[ -n "$assignee" ]]; then
local -a assignees_arr
IFS=',' read -rA assignees_arr <<< "$assignee"
assignees_json=$(printf '%s\n' "${assignees_arr[@]}" | jq -R . | jq -s .)
fi
local data=$(jq -n \
--arg title "$title" \
--arg body "$body" \
--argjson assignees "$assignees_json" \
--argjson labels "$labels_json" \
'{title: $title, body: $body, assignees: $assignees, labels: $labels}')
api_request "POST" "repos/$GITEA_OWNER/$GITEA_REPO/issues" "$data"
}
# [END_ENTITY: 'Function'('create_task')]
# [ENTITY: 'Function'('add_comment')]
# [CONTRACT]
# Adds a comment to an existing issue or pull request.
#
# @param --issue-id: The ID of the issue/PR to comment on.
# @param --body: The content of the comment.
#
# @stdout The JSON representation of the newly created comment.
# [/CONTRACT]
function add_comment() {
local issue_id=""
local comment_body=""
while [[ $# -gt 0 ]]; do
case "$1" in
--issue-id) issue_id="$2"; shift 2 ;;
--body) comment_body="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
if [[ -z "$issue_id" || -z "$comment_body" ]]; then
echo "Usage: add-comment --issue-id <id> --body <comment_body>" >&2
return 1
fi
local data=$(jq -n --arg body "$comment_body" '{body: $body}')
api_request "POST" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$issue_id/comments" "$data"
}
# [END_ENTITY: 'Function'('add_comment')]
# [ENTITY: 'Function'('merge_and_complete')]
# [CONTRACT]
# Atomic operation to merge a PR, delete its source branch, and close the associated issue.
#
# @param --issue-id: The ID of the issue to close.
# @param --pr-id: The ID of the pull request to merge.
# @param --branch: The name of the source branch to delete after merging.
#
# @stderr Log messages indicating the progress of each step.
# @returns 1 on failure to merge or close the issue.
# [/CONTRACT]
function merge_and_complete() {
local issue_id=""
local pr_id=""
local branch_to_delete=""
while [[ $# -gt 0 ]]; do
case "$1" in
--issue-id) issue_id="$2"; shift 2 ;;
--pr-id) pr_id="$2"; shift 2 ;;
--branch) branch_to_delete="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
if [[ -z "$issue_id" || -z "$pr_id" || -z "$branch_to_delete" ]]; then
echo "Usage: merge-and-complete --issue-id <issue_id> --pr-id <pr_id> --branch <branch_to_delete>" >&2
return 1
fi
# 1. Merge the PR
echo "Attempting to merge PR #$pr_id..."
local merge_data=$(jq -n '{Do: "merge"}' ) # Gitea API expects a MergePullRequestOption object
local merge_response=$(api_request "POST" "repos/$GITEA_OWNER/$GITEA_REPO/pulls/$pr_id/merge" "$merge_data")
if echo "$merge_response" | jq -e '.merged' > /dev/null; then
echo "PR #$pr_id merged successfully."
else
echo "Error merging PR #$pr_id: $merge_response" >&2
return 1
fi
# 2. Delete the branch
echo "Attempting to delete branch $branch_to_delete..."
local delete_http_code=$(api_request "DELETE" "repos/$GITEA_OWNER/$GITEA_REPO/branches/$branch_to_delete")
if [[ "$delete_http_code" == "204" ]]; then # 204 No Content is success
echo "Branch $branch_to_delete deleted successfully."
else
echo "Warning: Failed to delete branch $branch_to_delete (HTTP code: $delete_http_code). It might be protected or already deleted." >&2
# Не возвращаем ошибку, т.к. главная задача (мерж) выполнена
fi
# 3. Close the associated issue
echo "Attempting to close issue #$issue_id..."
local close_issue_data=$(jq -n '{state: "closed"}')
local close_response=$(api_request "PATCH" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$issue_id" "$close_issue_data")
if echo "$close_response" | jq -e '.state == "closed"' > /dev/null; then
echo "Issue #$issue_id closed successfully."
else
echo "Error closing issue #$issue_id: $close_response" >&2
return 1
fi
}
# [END_ENTITY: 'Function'('merge_and_complete')]
# [ENTITY: 'Function'('return_to_dev')]
# [CONTRACT]
# Returns an issue to development by adding a comment and changing its status.
# It specifically changes the status from 'status::in-review' to 'status::in-progress'.
#
# @param --issue-id: The ID of the issue to update.
# @param --comment: The comment explaining why the issue is being returned.
#
# @stderr Log messages indicating the progress of each step.
# @returns 1 on failure to add comment or update status.
# [/CONTRACT]
function return_to_dev() {
local issue_id=""
local comment_body=""
while [[ $# -gt 0 ]]; do
case "$1" in
--issue-id) issue_id="$2"; shift 2 ;;
--comment) comment_body="$2"; shift 2 ;;
*) echo "Unknown parameter: $1"; return 1 ;;
esac
done
if [[ -z "$issue_id" || -z "$comment_body" ]]; then
echo "Usage: return-to-dev --issue-id <id> --comment <comment_body>" >&2
return 1
fi
# 1. Add the comment
echo "Adding comment to issue #$issue_id..."
local add_comment_response
add_comment_response=$(add_comment --issue-id "$issue_id" --body "$comment_body")
if ! echo "$add_comment_response" | jq -e '.id' > /dev/null; then
echo "Error: Failed to add comment to issue #$issue_id. Response: $add_comment_response" >&2
return 1
fi
# 2. Update the status
echo "Updating status for issue #$issue_id..."
local update_status_response
update_status_response=$(update_task_status --issue-id "$issue_id" --old "status::in-review" --new "status::in-progress")
if ! echo "$update_status_response" | jq -e '.id' > /dev/null; then
echo "Error: Failed to update status for issue #$issue_id. Response: $update_status_response" >&2
return 1
fi
echo "Issue #$issue_id returned to development."
}
# [END_ENTITY: 'Function'('return_to_dev')]
# Здесь может быть функция main_dispatch, если она вам нужна

131
live-test.zsh Executable file
View File

@@ -0,0 +1,131 @@
#!/usr/bin/env zsh
# Выходить из скрипта при любой ошибке
set -e
# --- КОНФИГУРАЦИЯ ---
# Подключаем основной скрипт с функциями
source ./gitea-client.zsh
# Уникальный идентификатор для этого тестового запуска, чтобы избежать конфликтов
# Он будет добавлен к названиям задач, веток и т.д.
TEST_RUN_ID=$(date +%s)
TEST_BRANCH_NAME="test/auto-branch-${TEST_RUN_ID}"
TEST_ISSUE_TITLE="[AUTO-TEST] Test Issue ${TEST_RUN_ID}"
TEST_COMMIT_MESSAGE="feat: Add test file for run ${TEST_RUN_ID}"
TEST_PR_TITLE="[AUTO-TEST] PR for issue ${TEST_RUN_ID}"
TEST_COMMENT="This is an automated test comment."
# Глобальные переменные для хранения ID созданных сущностей
# Это нужно для функции очистки
typeset -g TEST_ISSUE_ID=""
typeset -g TEST_PR_ID=""
# --- ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ ---
# Функции для красивого вывода
log_info() { echo "\e[34mINFO:\e[0m $1"; }
log_pass() { echo "\e[32mPASS:\e[0m $1"; }
log_fail() { echo "\e[31mFAIL:\e[0m $1"; }
# Функция очистки. Будет вызвана при любом завершении скрипта (успешном или нет)
cleanup() {
log_info "--- Запускаю процедуру очистки ---"
# Удаляем локальную тестовую ветку, если она есть
if git rev-parse --verify "$TEST_BRANCH_NAME" >/dev/null 2>&1; then
git checkout main # или master, в зависимости от вашей основной ветки
git branch -D "$TEST_BRANCH_NAME"
log_info "Локальная ветка '$TEST_BRANCH_NAME' удалена."
fi
# Удаляем ветку на сервере, если она там осталась
# Gitea API для удаления ветки может вернуть ошибку, если PR уже смержен, это нормально
api_request "DELETE" "repos/$GITEA_OWNER/$GITEA_REPO/branches/$TEST_BRANCH_NAME" >/dev/null 2>&1 || true
log_info "Попытка удаления удаленной ветки '$TEST_BRANCH_NAME' выполнена."
# Если была создана задача, но она еще не закрыта, принудительно закрываем ее
if [[ -n "$TEST_ISSUE_ID" ]]; then
local issue_state=$(api_request "GET" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$TEST_ISSUE_ID" | jq -r '.state')
if [[ "$issue_state" == "open" ]]; then
log_info "Задача #$TEST_ISSUE_ID все еще открыта. Закрываю принудительно..."
local close_data=$(jq -n '{state: "closed"}')
api_request "PATCH" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$TEST_ISSUE_ID" "$close_data" >/dev/null
log_pass "Задача #$TEST_ISSUE_ID закрыта."
fi
fi
log_info "--- Очистка завершена ---"
}
# Устанавливаем "ловушку": функция cleanup будет вызвана при выходе из скрипта
trap cleanup EXIT
# --- ОСНОВНОЙ СЦЕНАРИЙ ТЕСТИРОВАНИЯ ---
function run_e2e_workflow() {
log_info "Начало полного E2E (end-to-end) теста..."
log_info "Тестовый ID этого запуска: ${TEST_RUN_ID}"
# Шаг 1: Создание задачи
log_info "Шаг 1: Создание тестовой задачи..."
local issue_json=$(create_task --title "$TEST_ISSUE_TITLE" )
TEST_ISSUE_ID=$(echo "$issue_json" | jq -r '.number') # Используем .number для issue ID
if [[ -z "$TEST_ISSUE_ID" || "$TEST_ISSUE_ID" == "null" ]]; then
log_fail "Не удалось создать задачу. Ответ API: $issue_json"
exit 1
fi
log_pass "Задача успешно создана с ID: #$TEST_ISSUE_ID"
# Шаг 2: Создание ветки, коммита и push
log_info "Шаг 2: Создание локальной ветки и коммита..."
git checkout -b "$TEST_BRANCH_NAME"
touch "test_file_${TEST_RUN_ID}.txt"
git add .
git commit -m "$TEST_COMMIT_MESSAGE"
log_pass "Ветка '$TEST_BRANCH_NAME' и коммит созданы."
log_info "Отправка ветки на сервер Gitea..."
git push origin "$TEST_BRANCH_NAME"
log_pass "Ветка успешно отправлена."
# Шаг 3: Создание Pull Request
log_info "Шаг 3: Создание Pull Request..."
local pr_json=$(create_pr --title "$TEST_PR_TITLE" --head "$TEST_BRANCH_NAME" --base "main")
TEST_PR_ID=$(echo "$pr_json" | jq -r '.number') # Используем .number для PR ID
if [[ -z "$TEST_PR_ID" || "$TEST_PR_ID" == "null" ]]; then
log_fail "Не удалось создать Pull Request. Ответ API: $pr_json"
exit 1
fi
log_pass "Pull Request успешно создан с ID: #$TEST_PR_ID"
# Шаг 4: Добавление комментария
log_info "Шаг 4: Добавление комментария к задаче #$TEST_ISSUE_ID..."
add_comment --issue-id "$TEST_ISSUE_ID" --body "$TEST_COMMENT" > /dev/null
log_pass "Комментарий успешно добавлен."
# Шаг 5: Слияние PR и закрытие задачи
log_info "Шаг 5: Выполнение 'merge_and_complete'..."
merge_and_complete --issue-id "$TEST_ISSUE_ID" --pr-id "$TEST_PR_ID" --branch "$TEST_BRANCH_NAME"
log_pass "Слияние PR и закрытие задачи прошли успешно."
log_info "Проверка статуса задачи..."
local final_state=$(api_request "GET" "repos/$GITEA_OWNER/$GITEA_REPO/issues/$TEST_ISSUE_ID" | jq -r '.state')
if [[ "$final_state" == "closed" ]]; then
log_pass "Финальная проверка: задача #$TEST_ISSUE_ID имеет статус 'closed'."
else
log_fail "Финальная проверка: задача #$TEST_ISSUE_ID имеет статус '$final_state' вместо 'closed'."
exit 1
fi
}
# --- ТОЧКА ВХОДА ---
run_e2e_workflow
log_pass "====== Весь E2E сценарий успешно завершен! ======"

0
test_file_1757081046.txt Normal file
View File