Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,19 @@
package; this file stays here so PyInstaller (server.spec), run-app.bat and
the Electron shell keep working unchanged.
"""
import sys
from datetime import datetime

from app import create_app
from app.services.ocr import tesseract_available
# On Windows, stdout/stderr use the legacy code page (cp1252) when they are a
# pipe, as when Electron launches the packaged backend. Printing any emoji
# (startup banner, OCR progress messages) then raises UnicodeEncodeError and
# kills the server, so force UTF-8 before anything else is imported.
for _stream in (sys.stdout, sys.stderr):
if _stream is not None and hasattr(_stream, 'reconfigure'):
_stream.reconfigure(encoding='utf-8', errors='replace')

from app import create_app # noqa: E402
from app.services.ocr import tesseract_available # noqa: E402

app = create_app()

Expand Down
44 changes: 44 additions & 0 deletions backend/tests/test_entrypoint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""The real server.py entrypoint, started the way the Electron shell starts it."""
import json
import os
import subprocess
import sys
import time
import urllib.request

BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PORT = 5098


def test_server_starts_with_legacy_windows_encoding_and_piped_output(tmp_path):
# Electron pipes stdout; on Windows that means cp1252, which cannot encode
# the emoji in the startup banner and used to crash the backend.
env = {
**os.environ,
'PYTHONIOENCODING': 'cp1252',
'LIBRADIGIT_PORT': str(PORT),
'LIBRADIGIT_DATABASE': str(tmp_path / 'db.sqlite'),
'LIBRADIGIT_UPLOAD_FOLDER': str(tmp_path / 'uploads'),
'LIBRADIGIT_ARCHIVE_FOLDER': str(tmp_path / 'Archive'),
}
proc = subprocess.Popen([sys.executable, 'server.py'], cwd=BACKEND_DIR, env=env,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
try:
health = None
deadline = time.time() + 30
while time.time() < deadline and proc.poll() is None:
try:
with urllib.request.urlopen(f'http://127.0.0.1:{PORT}/api/health', timeout=1) as res:
health = json.load(res)
break
except OSError:
time.sleep(0.3)
if health is None:
proc.kill()
output = proc.communicate(timeout=10)[0].decode('utf-8', 'replace')
raise AssertionError(f'backend did not start (exit {proc.returncode}):\n{output[-2000:]}')
assert health['status'] == 'healthy'
finally:
if proc.poll() is None:
proc.terminate()
proc.wait(timeout=10)
6 changes: 6 additions & 0 deletions scripts/smoke-test-backend.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,16 @@ try {
$health = $null
for ($i = 0; $i -lt 90 -and -not $health; $i++) {
Start-Sleep -Seconds 1
# A crashed backend will never answer; stop waiting and show why.
if ($proc.HasExited) { break }
try { $health = Invoke-RestMethod 'http://127.0.0.1:5001/api/health' } catch { }
}
if (-not $health) {
Write-Host '--- backend stdout ---'
Get-Content (Join-Path $env:TEMP 'backend-out.txt') -ErrorAction SilentlyContinue | Select-Object -Last 40
Write-Host '--- backend stderr ---'
Get-Content (Join-Path $env:TEMP 'backend-err.txt') -ErrorAction SilentlyContinue | Select-Object -Last 40
if ($proc.HasExited) { throw "Backend exited with code $($proc.ExitCode) before becoming healthy" }
throw 'Backend did not become healthy'
}
Write-Host "Health: $($health | ConvertTo-Json -Compress)"
Expand Down
Loading