Skip to content

Commit 5372277

Browse files
committed
add windows specific e2e test for hello world rendering
1 parent 544cd42 commit 5372277

4 files changed

Lines changed: 124 additions & 16 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,19 @@ jobs:
4545
name: e2e-outputs-linux
4646
path: /tmp/pytest-of-runner/**/container_work*/**
4747

48-
# Slice 1: smoke-test that install.ps1 runs non-interactively on a real
49-
# windows-latest runner. The pytest-based render/run flow will be added in a
50-
# follow-up commit once this is green.
5148
e2e-windows:
5249
runs-on: windows-latest
5350
timeout-minutes: 15
5451
steps:
5552
- uses: actions/checkout@v4
5653

57-
- name: Run install.ps1 non-interactively
54+
- uses: actions/setup-python@v5
55+
with:
56+
python-version: '3.12'
57+
58+
- run: pip install pytest
59+
60+
- name: Run E2E tests
5861
env:
5962
CODEPLAIN_API_KEY: ${{ secrets.CODEPLAIN_API_KEY }}
60-
CODEPLAIN_INSTALL_NONINTERACTIVE: '1'
61-
shell: pwsh
62-
run: ./install/powershell/install.ps1
63-
64-
- name: Verify codeplain is installed
65-
shell: pwsh
66-
run: |
67-
# install.ps1 persists PATH changes to the user registry but the
68-
# current pwsh session has a stale Path. Refresh it from the registry
69-
# so `codeplain` is discoverable here.
70-
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'User') + ';' + [Environment]::GetEnvironmentVariable('Path', 'Machine')
71-
codeplain --help
63+
run: pytest tests/e2e/ -v --tb=short

tests/e2e/conftest.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import os
22
import shutil
33
import subprocess
4+
import sys
45
import uuid
56
from pathlib import Path
67

78
import pytest
89

910
REPO_ROOT = Path(__file__).resolve().parents[2]
1011
CONTAINER_WORKDIR = "/home/e2e/work"
12+
INSTALL_PS1 = REPO_ROOT / "install" / "powershell" / "install.ps1"
1113

1214

1315
def _docker_available() -> bool:
@@ -106,3 +108,38 @@ def _copy(container_id: str, src: Path, dest: str = CONTAINER_WORKDIR + "/"):
106108
)
107109

108110
return _copy
111+
112+
113+
# No docker on this runner. The install.ps1 and Codeplain CLI runs directly on the windows VM.
114+
@pytest.fixture(scope="session")
115+
def codeplain_exe(api_key: str) -> Path:
116+
if sys.platform != "win32":
117+
pytest.skip("Windows-only fixture")
118+
119+
env = {
120+
**os.environ,
121+
"CODEPLAIN_API_KEY": api_key,
122+
"CODEPLAIN_INSTALL_NONINTERACTIVE": "1",
123+
}
124+
result = subprocess.run(
125+
["pwsh", "-NoProfile", "-File", str(INSTALL_PS1)],
126+
capture_output=True,
127+
text=True,
128+
timeout=600,
129+
env=env,
130+
check=False,
131+
)
132+
if result.returncode != 0:
133+
pytest.fail(
134+
f"install.ps1 failed (rc={result.returncode})\n" f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
135+
)
136+
137+
# uv tool installs binaries to %USERPROFILE%\.local\bin (see install.ps1
138+
# line ~102). Resolve the absolute path so our subprocess doesn't depend
139+
# on the current process's stale PATH — install.ps1 writes the user PATH
140+
# to the registry, but the python process running pytest already cached
141+
# its env at startup.
142+
exe = Path(os.environ["USERPROFILE"]) / ".local" / "bin" / "codeplain.exe"
143+
if not exe.exists():
144+
pytest.fail(f"codeplain.exe not found at expected location: {exe}")
145+
return exe

tests/e2e/test_hello_world_python.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
1+
import sys
12
from pathlib import Path
23

4+
import pytest
5+
6+
# The Linux flow drives codeplain via `docker exec`; Windows runners don't have
7+
# a Linux Docker daemon, so this whole file is skipped there. The Windows port
8+
# lives in test_hello_world_python_windows.py.
9+
pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="Linux/Docker-based e2e")
10+
311
REPO_ROOT = Path(__file__).resolve().parents[2]
412
EXAMPLE_PLAIN = REPO_ROOT / "examples" / "example_hello_world_python" / "hello_world_python.plain"
513

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import os
2+
import shutil
3+
import subprocess
4+
import sys
5+
from pathlib import Path
6+
7+
import pytest
8+
9+
pytestmark = pytest.mark.skipif(sys.platform != "win32", reason="Windows-only e2e")
10+
11+
REPO_ROOT = Path(__file__).resolve().parents[2]
12+
EXAMPLE_PLAIN = REPO_ROOT / "examples" / "example_hello_world_python" / "hello_world_python.plain"
13+
14+
# if it takes longer, it's stuck
15+
RENDER_TIMEOUT_SECONDS = 180
16+
17+
18+
def test_render_and_run_hello_world_python_windows(codeplain_exe: Path, api_key: str, tmp_path: Path):
19+
shutil.copy(EXAMPLE_PLAIN, tmp_path / "hello_world_python.plain")
20+
21+
env = {**os.environ, "CODEPLAIN_API_KEY": api_key}
22+
23+
result = subprocess.run(
24+
[str(codeplain_exe), "--help"],
25+
capture_output=True,
26+
text=True,
27+
timeout=60,
28+
env=env,
29+
check=False,
30+
)
31+
assert result.returncode == 0, (
32+
f"codeplain --help failed:\nstdout:\n{result.stdout}\nstderr:\n{result.stderr}"
33+
)
34+
35+
result = subprocess.run(
36+
[
37+
str(codeplain_exe),
38+
"--headless",
39+
"--build-folder",
40+
"build/",
41+
"hello_world_python.plain",
42+
],
43+
cwd=tmp_path,
44+
capture_output=True,
45+
text=True,
46+
timeout=RENDER_TIMEOUT_SECONDS,
47+
env=env,
48+
check=False,
49+
)
50+
assert result.returncode == 0, (
51+
f"codeplain render failed (rc={result.returncode}):\n"
52+
f"stdout:\n{result.stdout}\nstderr:\n{result.stderr}"
53+
)
54+
55+
generated = tmp_path / "build" / "hello_world_python" / "hello_world.py"
56+
assert generated.exists(), f"expected generated file at {generated} but it was not produced"
57+
58+
result = subprocess.run(
59+
["python", str(generated)],
60+
capture_output=True,
61+
text=True,
62+
timeout=30,
63+
env=env,
64+
check=False,
65+
)
66+
assert result.returncode == 0, (
67+
f"generated python failed (rc={result.returncode}):\nstderr:\n{result.stderr}"
68+
)
69+
assert "hello, world" in result.stdout.lower(), (
70+
f"unexpected stdout from generated app: {result.stdout!r}"
71+
)

0 commit comments

Comments
 (0)