Skip to content

Commit bf8a76e

Browse files
committed
fix coderabbitai review
1 parent fab8398 commit bf8a76e

3 files changed

Lines changed: 108 additions & 78 deletions

File tree

.github/workflows/integration-functional.yml

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,28 @@ name: Integration functional
77
on:
88
push:
99
branches: [main, develop]
10-
pull_request_target:
10+
pull_request:
1111
branches: [main, develop]
12-
types: [labeled, synchronize, opened, reopened]
1312

1413
jobs:
1514
integration-functional:
1615
runs-on: ubuntu-latest
1716
timeout-minutes: 25
18-
if: >-
19-
github.event_name == 'push'
20-
|| contains(github.event.pull_request.labels.*.name, 'safe-to-test')
21-
env:
22-
# Use a repository *secret* (Settings → Secrets → Actions), not a variable.
23-
GH_TEST_REPO_TOKEN: ${{ secrets.GH_TEST_REPO_TOKEN }}
2417
steps:
2518
# actions/checkout v6.0.2
2619
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd
2720
with:
28-
ref: ${{ github.event.pull_request.head.sha || github.sha }}
2921
persist-credentials: false
3022

3123
# actions/setup-python v6.2.0
3224
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405
3325
with:
3426
python-version: '3.12'
3527

36-
- name: Check GitHub test token availability
37-
run: |
38-
set -euo pipefail
39-
if [ -n "${GH_TEST_REPO_TOKEN:-}" ]; then
40-
echo "GH_TEST_REPO_TOKEN is set (${#GH_TEST_REPO_TOKEN} characters)."
41-
else
42-
echo "::warning::GH_TEST_REPO_TOKEN is empty. E2E/Celery functional tests will be skipped."
43-
echo "Add a classic PAT with the 'repo' scope as repository secret GH_TEST_REPO_TOKEN."
44-
fi
45-
4628
- name: Run integration functional tests
29+
env:
30+
# Use a repository *secret* (Settings → Secrets → Actions), not a variable.
31+
GH_TEST_REPO_TOKEN: ${{ secrets.GH_TEST_REPO_TOKEN }}
4732
run: bash scripts/integration-functional.sh
4833

4934
- name: Upload logs on failure

scripts/integration-functional.sh

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,13 @@ export WEBLATE_COMPOSE_FILE="${COMPOSE_FILE}"
4040
export WEBLATE_COMPOSE_PROJECT="${COMPOSE_PROJECT_NAME}"
4141

4242
echo "=== Extracting Weblate SSH public key ==="
43-
export WEBLATE_SSH_PUBKEY="$(compose exec -T weblate cat /app/data/ssh/id_rsa.pub)"
43+
TMP_WEBLATE_SSH_PUBKEY="$(compose exec -T weblate cat /app/data/ssh/id_rsa.pub)"
44+
if [[ -z "${TMP_WEBLATE_SSH_PUBKEY}" ]]; then
45+
echo "ERROR: Failed to read Weblate SSH public key from container." >&2
46+
exit 1
47+
fi
48+
export WEBLATE_SSH_PUBKEY="${TMP_WEBLATE_SSH_PUBKEY}"
49+
unset TMP_WEBLATE_SSH_PUBKEY
4450

4551
if [[ -n "${GH_TEST_REPO_TOKEN:-}" ]]; then
4652
export GH_TEST_REPO_TOKEN

tests/integration/test_functional.py

Lines changed: 97 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from __future__ import annotations
1212

1313
import json
14+
from dataclasses import dataclass
1415

1516
import pytest
1617

@@ -31,6 +32,36 @@
3132
ZH_HANS_TRANSLATION = "复杂 QuickBook 测试夹具"
3233

3334

35+
@dataclass(frozen=True)
36+
class CreatedProjectComponent:
37+
project_slug: str
38+
component_slug: str
39+
40+
41+
@pytest.fixture(scope="class")
42+
def created_project_component(weblate_api: WeblateAPI) -> CreatedProjectComponent:
43+
"""Project + QuickBook component for round-trip tests."""
44+
project_slug = WeblateAPI.unique_slug("func-qbk")
45+
component_slug = "qbk-fixture"
46+
weblate_api.create_project("Functional QBK", project_slug)
47+
# Docfile multipart upload (no empty local VCS template paths).
48+
created = weblate_api.create_component_from_docfile(
49+
project_slug,
50+
name="QBK Fixture",
51+
slug=component_slug,
52+
file_format="quickbook",
53+
docfile_path=QBK_FIXTURE,
54+
filemask="doc/*.qbk",
55+
language_regex=f"^{TEST_LANG_CODE}$",
56+
)
57+
component_slug = str(created.get("slug", component_slug))
58+
weblate_api.ensure_translation(project_slug, component_slug, TEST_LANG_CODE)
59+
return CreatedProjectComponent(
60+
project_slug=project_slug,
61+
component_slug=component_slug,
62+
)
63+
64+
3465
# ---------------------------------------------------------------------------
3566
# P1: QuickBook round-trip via Weblate REST API
3667
# ---------------------------------------------------------------------------
@@ -39,34 +70,15 @@
3970
class TestQuickBookRoundTrip:
4071
"""Upload QBK, translate a unit, download translated file."""
4172

42-
project_slug: str = ""
43-
component_slug: str = ""
44-
unit_url: str = ""
45-
46-
def test_create_project_and_component(self, weblate_api: WeblateAPI) -> None:
47-
project_slug = WeblateAPI.unique_slug("func-qbk")
48-
component_slug = "qbk-fixture"
49-
weblate_api.create_project("Functional QBK", project_slug)
50-
# Docfile multipart upload (no empty local VCS template paths).
51-
created = weblate_api.create_component_from_docfile(
52-
project_slug,
53-
name="QBK Fixture",
54-
slug=component_slug,
55-
file_format="quickbook",
56-
docfile_path=QBK_FIXTURE,
57-
filemask="doc/*.qbk",
58-
language_regex=f"^{TEST_LANG_CODE}$",
59-
)
60-
component_slug = str(created.get("slug", component_slug))
61-
weblate_api.ensure_translation(project_slug, component_slug, TEST_LANG_CODE)
62-
63-
type(self).project_slug = project_slug
64-
type(self).component_slug = component_slug
65-
66-
def test_units_extracted(self, weblate_api: WeblateAPI) -> None:
67-
assert self.project_slug and self.component_slug
73+
def test_units_extracted(
74+
self,
75+
weblate_api: WeblateAPI,
76+
created_project_component: CreatedProjectComponent,
77+
) -> None:
6878
units = weblate_api.list_units(
69-
self.project_slug, self.component_slug, TEST_LANG_CODE
79+
created_project_component.project_slug,
80+
created_project_component.component_slug,
81+
TEST_LANG_CODE,
7082
)
7183
assert len(units) > 0
7284
sources = [
@@ -75,27 +87,36 @@ def test_units_extracted(self, weblate_api: WeblateAPI) -> None:
7587
]
7688
assert any(KNOWN_SOURCE_STRING in s for s in sources), sources[:5]
7789

78-
def test_submit_translation(self, weblate_api: WeblateAPI) -> None:
79-
assert self.project_slug and self.component_slug
90+
def test_submit_translation(
91+
self,
92+
weblate_api: WeblateAPI,
93+
created_project_component: CreatedProjectComponent,
94+
) -> None:
8095
units = weblate_api.list_units(
81-
self.project_slug, self.component_slug, TEST_LANG_CODE
96+
created_project_component.project_slug,
97+
created_project_component.component_slug,
98+
TEST_LANG_CODE,
8299
)
83100
match = next(
84101
(u for u in units if KNOWN_SOURCE_STRING in str(u.get("source", ""))),
85102
None,
86103
)
87104
assert match is not None
88105
unit_url = WeblateAPI.unit_api_url(match)
89-
type(self).unit_url = unit_url
90106
weblate_api.submit_translation(unit_url, ZH_HANS_TRANSLATION)
91107

92-
def test_download_translated_qbk(self, weblate_api: WeblateAPI) -> None:
93-
assert self.project_slug and self.component_slug
108+
def test_download_translated_qbk(
109+
self,
110+
weblate_api: WeblateAPI,
111+
created_project_component: CreatedProjectComponent,
112+
) -> None:
94113
raw = weblate_api.download_file(
95-
self.project_slug, self.component_slug, TEST_LANG_CODE
114+
created_project_component.project_slug,
115+
created_project_component.component_slug,
116+
TEST_LANG_CODE,
96117
)
97118
text = raw.decode("utf-8", errors="replace")
98-
assert ZH_HANS_TRANSLATION in text or KNOWN_SOURCE_STRING in text
119+
assert ZH_HANS_TRANSLATION in text, "translated QBK content was not found"
99120

100121

101122
# ---------------------------------------------------------------------------
@@ -229,37 +250,55 @@ def test_idempotency(self, exec_python, test_repo: EphemeralGitHubRepo) -> None:
229250
# ---------------------------------------------------------------------------
230251

231252

253+
@dataclass(frozen=True)
254+
class AddOrUpdateTask:
255+
task_id: str
256+
http_code: int
257+
response: dict
258+
259+
260+
@pytest.fixture(scope="class")
261+
def add_or_update_task(
262+
api_token: str, test_repo: EphemeralGitHubRepo
263+
) -> AddOrUpdateTask:
264+
"""Accepted add-or-update request and Celery task id."""
265+
owner = test_repo.resolve_owner()
266+
body = {
267+
"organization": owner,
268+
"version": TEST_VERSION,
269+
"add_or_update": {TEST_LANG_CODE: [test_repo.repo_name]},
270+
}
271+
code, data = http_json(
272+
"POST",
273+
"/boost-endpoint/add-or-update/",
274+
token=api_token,
275+
body=body,
276+
)
277+
assert code == 202, f"expected 202: {code} {data}"
278+
assert isinstance(data, dict)
279+
assert data.get("status") == "accepted"
280+
assert data.get("task_id")
281+
return AddOrUpdateTask(
282+
task_id=str(data["task_id"]),
283+
http_code=code,
284+
response=data,
285+
)
286+
287+
232288
class TestAddOrUpdateCeleryFlow:
233289
"""POST /boost-endpoint/add-or-update/ and poll Celery completion."""
234290

235291
def test_add_or_update_returns_202(
236-
self, api_token: str, test_repo: EphemeralGitHubRepo
292+
self, add_or_update_task: AddOrUpdateTask
237293
) -> None:
238-
owner = test_repo.resolve_owner()
239-
body = {
240-
"organization": owner,
241-
"version": TEST_VERSION,
242-
"add_or_update": {TEST_LANG_CODE: [test_repo.repo_name]},
243-
}
244-
code, data = http_json(
245-
"POST",
246-
"/boost-endpoint/add-or-update/",
247-
token=api_token,
248-
body=body,
249-
)
250-
assert code == 202, f"expected 202: {code} {data}"
251-
assert isinstance(data, dict)
252-
assert data.get("status") == "accepted"
253-
assert data.get("task_id")
254-
type(self).task_id = str(data["task_id"])
294+
assert add_or_update_task.http_code == 202
295+
assert add_or_update_task.response.get("status") == "accepted"
296+
assert add_or_update_task.task_id
255297

256298
def test_add_or_update_task_completes(
257-
self, weblate_api: WeblateAPI, test_repo: EphemeralGitHubRepo
299+
self, weblate_api: WeblateAPI, add_or_update_task: AddOrUpdateTask
258300
) -> None:
259-
task_id = getattr(self, "task_id", None)
260-
if not task_id:
261-
pytest.skip("depends on test_add_or_update_returns_202")
262-
result = weblate_api.poll_celery_task(task_id, timeout=300.0)
301+
result = weblate_api.poll_celery_task(add_or_update_task.task_id, timeout=300.0)
263302
assert isinstance(result, dict)
264303
lang_result = result.get(TEST_LANG_CODE)
265304
assert lang_result is not None

0 commit comments

Comments
 (0)