Skip to content

Commit 51eab32

Browse files
committed
fix: make live upload integration tests rerun-safe
Give the live upload tests unique per-run file names and content so leftover pages on the shared test wiki can no longer trigger fileexists-no-change errors, and move the wiki-side cleanup of the target-fpt test into a finally block so failed runs do not leak state.
1 parent 13968ad commit 51eab32

5 files changed

Lines changed: 96 additions & 24 deletions

File tree

.github/workflows/integration.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Integration tests against a live test wiki. Credentials come from the
2+
# INTEGR_TEST_* secrets; where they are unavailable (e.g. forks), the
3+
# credential fixtures make the tests skip instead of fail.
4+
5+
name: Integration
6+
7+
on:
8+
push:
9+
branches: [main]
10+
workflow_dispatch:
11+
12+
concurrency:
13+
# runs mutate the shared test wiki, so never run two in parallel
14+
group: integration
15+
cancel-in-progress: false
16+
17+
jobs:
18+
integration-tests:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Check out
22+
uses: actions/checkout@v4
23+
with:
24+
# deep clone incl. tags so hatch-vcs can derive the version
25+
fetch-depth: 0
26+
27+
- name: Set up the environment
28+
uses: ./.github/actions/setup-python-env
29+
30+
- name: Run integration tests
31+
env:
32+
WIKI_DOMAIN: ${{ secrets.INTEGR_TEST_WIKI_DOMAIN }}
33+
WIKI_USERNAME: ${{ secrets.INTEGR_TEST_WIKI_USERNAME }}
34+
WIKI_PASSWORD: ${{ secrets.INTEGR_TEST_WIKI_PASSWORD }}
35+
run: make test-integration

Makefile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,16 @@ test: ## Test the code with pytest (integration tests excluded, see pyproject.to
2222
@echo ">> Testing code: Running pytest"
2323
@uv run python -m pytest --cov --cov-config=pyproject.toml --cov-report=xml
2424

25+
.PHONY: test-integration
26+
test-integration: ## Run integration tests (set WIKI_DOMAIN, WIKI_USERNAME, WIKI_PASSWORD [, DB_USERNAME, DB_PASSWORD]; tests skip where credentials are missing)
27+
@echo ">> Testing code: Running pytest on tests/integration"
28+
@uv run python -m pytest tests/integration -o addopts="" \
29+
$(if $(WIKI_DOMAIN),--wiki_domain "$(WIKI_DOMAIN)") \
30+
$(if $(WIKI_USERNAME),--wiki_username "$(WIKI_USERNAME)") \
31+
$(if $(WIKI_PASSWORD),--wiki_password "$(WIKI_PASSWORD)") \
32+
$(if $(DB_USERNAME),--db_username "$(DB_USERNAME)") \
33+
$(if $(DB_PASSWORD),--db_password "$(DB_PASSWORD)")
34+
2535
.PHONY: build
2636
build: clean-build ## Build wheel and sdist
2737
@echo ">> Creating wheel and sdist files"

docs/dev.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,25 @@ excluded by default:
6161

6262
### Integration tests
6363

64-
Integration tests run against a live wiki and need credentials:
64+
Integration tests run against a live wiki and need credentials; tests
65+
whose credentials are missing skip instead of fail. CI runs them on
66+
every push to `main` (workflow `integration.yml`).
6567

66-
```bash
67-
uv run pytest tests/integration -o addopts="" \
68-
--wiki_domain "<domain>" \
69-
--wiki_username "<login>" \
70-
--wiki_password "<password>"
71-
```
68+
=== "make"
69+
70+
```bash
71+
WIKI_DOMAIN="<domain>" WIKI_USERNAME="<login>" WIKI_PASSWORD="<password>" \
72+
make test-integration
73+
```
74+
75+
=== "manual"
76+
77+
```bash
78+
uv run pytest tests/integration -o addopts="" \
79+
--wiki_domain "<domain>" \
80+
--wiki_username "<login>" \
81+
--wiki_password "<password>"
82+
```
7283

7384
## Documentation
7485

tests/integration/test_express.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"""
2424

2525
import os
26+
import uuid
2627
from contextlib import contextmanager
2728
from pathlib import Path
2829

@@ -173,8 +174,9 @@ def test_file_upload_download(wiki_domain, wiki_username, wiki_password):
173174
assert osw_express_and_credentials(
174175
osw_express, wiki_domain, wiki_username, wiki_password
175176
)
176-
# Test file upload
177-
file_path = Path.cwd() / "test_file.txt"
177+
# Test file upload; unique name per run so leftovers on the shared
178+
# test wiki never collide with a re-upload (fileexists-no-change)
179+
file_path = Path.cwd() / f"test_file_{uuid.uuid4().hex[:8]}.txt"
178180
create_dummy_file(file_path)
179181
# Upload a file to an OSW instance
180182
wiki_file = osw.express.osw_upload_file(file_path, osw_express=osw_express)

tests/integration/test_express_init.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"""
2020

2121
import os
22+
import uuid
2223
import warnings
2324
from contextlib import contextmanager
2425
from pathlib import Path
@@ -1130,9 +1131,11 @@ def test_live_upload_download_via_instance(
11301131
osw_obj = osw.express.OswExpress(
11311132
domain=wiki_domain, cred_filepath=cred_filepath
11321133
)
1133-
# Create dummy file
1134-
source_file = Path.cwd() / "test_instance_upload.txt"
1135-
source_file.write_text("Instance upload test content")
1134+
# Create dummy file; unique name/content per run so leftovers
1135+
# on the shared test wiki never collide with a re-upload
1136+
uid = uuid.uuid4().hex[:8]
1137+
source_file = Path.cwd() / f"test_instance_upload_{uid}.txt"
1138+
source_file.write_text(f"Instance upload test content {uid}")
11361139

11371140
try:
11381141
# Upload via instance method
@@ -1146,7 +1149,9 @@ def test_live_upload_download_via_instance(
11461149
overwrite=True,
11471150
)
11481151
assert local_file.path.exists()
1149-
assert local_file.path.read_text() == "Instance upload test content"
1152+
assert local_file.path.read_text() == (
1153+
f"Instance upload test content {uid}"
1154+
)
11501155

11511156
# Cleanup
11521157
local_file.close()
@@ -1175,14 +1180,22 @@ def test_live_upload_with_target_fpt(
11751180
osw_obj = osw.express.OswExpress(
11761181
domain=wiki_domain, cred_filepath=cred_filepath
11771182
)
1178-
source_file = Path.cwd() / "test_target_fpt_upload.txt"
1179-
source_file.write_text("Target FPT test content")
1180-
1183+
# Unique name and content per run: the shared test wiki keeps
1184+
# pages between runs, and re-uploading identical content raises
1185+
# a fileexists-no-change API error.
1186+
uid = uuid.uuid4().hex[:8]
1187+
content = f"Target FPT test content {uid}"
1188+
source_file = Path.cwd() / f"test_target_fpt_upload_{uid}.txt"
1189+
source_file.write_text(content)
1190+
title = f"File:TestTargetFptUpload{uid}.txt"
1191+
1192+
wiki_file = None
1193+
local_file = None
11811194
try:
11821195
wiki_file = osw_obj.upload_file(
11831196
source=source_file,
1184-
url_or_title="File:TestTargetFptUpload.txt",
1185-
target_fpt="File:TestTargetFptUpload.txt",
1197+
url_or_title=title,
1198+
target_fpt=title,
11861199
)
11871200
assert wiki_file is not None
11881201
assert wiki_file.change_id is not None
@@ -1193,13 +1206,14 @@ def test_live_upload_with_target_fpt(
11931206
url_or_title=wiki_file.url_or_title,
11941207
overwrite=True,
11951208
)
1196-
assert local_file.path.read_text() == "Target FPT test content"
1197-
1198-
# Cleanup
1199-
local_file.close()
1200-
local_file.delete()
1201-
wiki_file.delete()
1209+
assert local_file.path.read_text() == content
12021210
finally:
1211+
# best-effort cleanup so no state leaks onto the shared wiki
1212+
if local_file is not None:
1213+
local_file.close()
1214+
local_file.delete()
1215+
if wiki_file is not None:
1216+
wiki_file.delete()
12031217
if source_file.exists():
12041218
source_file.unlink()
12051219
osw_obj.shut_down()

0 commit comments

Comments
 (0)