Files
noteflow/tests/test_constants_sync.py
Travis Vasceannie 61a4e69042 chore: update submodule and improve HTML export functionality
- Updated the client submodule to the latest commit for enhanced compatibility.
- Refactored HTML export logic to utilize a dedicated function for building HTML documents, improving code clarity and maintainability.
- Enhanced test assertions across various files to include descriptive messages, aiding in debugging and understanding test failures.

All quality checks pass.
2026-01-01 02:47:18 +00:00

132 lines
4.7 KiB
Python

"""Cross-language constant synchronization validation.
This module validates that constants defined in Python match their counterparts
in Rust (client/src-tauri/src/constants.rs) to prevent drift between codebases.
These tests serve as documentation and early warning for constant mismatches.
"""
from __future__ import annotations
import re
from pathlib import Path
import pytest
from noteflow.config.constants import (
DEFAULT_GRPC_PORT,
DEFAULT_SAMPLE_RATE,
SECONDS_PER_HOUR,
)
# Path to Rust constants file
RUST_CONSTANTS_PATH = Path(__file__).parent.parent / "client/src-tauri/src/constants.rs"
# Expected constant values for validation tests
EXPECTED_SAMPLE_RATE_HZ = 16000 # 16kHz standard for speech recognition
EXPECTED_GRPC_PORT = 50051 # Standard gRPC port convention
EXPECTED_SECONDS_PER_HOUR = 3600 # 60 * 60
def _parse_rust_constant(content: str, module: str, name: str) -> str | None:
"""Extract a constant value from Rust source.
Args:
content: Full Rust file content
module: Module name (e.g., 'audio', 'grpc', 'time')
name: Constant name (e.g., 'DEFAULT_SAMPLE_RATE')
Returns:
The constant value as a string, or None if not found
"""
# Find the module block
module_pattern = rf"pub mod {module}\s*\{{"
module_match = re.search(module_pattern, content)
if not module_match:
return None
# Find module end (matching braces)
start = module_match.end()
brace_count = 1
end = start
while brace_count > 0 and end < len(content):
if content[end] == "{":
brace_count += 1
elif content[end] == "}":
brace_count -= 1
end += 1
module_content = content[start:end]
# Find the constant within the module
const_pattern = rf"pub const {name}:\s*\w+\s*=\s*([^;]+);"
const_match = re.search(const_pattern, module_content)
if const_match:
return const_match.group(1).strip()
return None
@pytest.fixture
def rust_constants() -> str:
"""Load Rust constants file content."""
if not RUST_CONSTANTS_PATH.exists():
pytest.skip("Rust constants file not found (client not present)")
return RUST_CONSTANTS_PATH.read_text()
class TestCrossLanguageConstantSync:
"""Validate Python constants match Rust counterparts.
These tests ensure that shared constants remain synchronized across
the Python backend and Rust/Tauri client.
"""
def test_default_sample_rate_matches_rust(self, rust_constants: str) -> None:
"""DEFAULT_SAMPLE_RATE must match between Python and Rust."""
rust_value = _parse_rust_constant(rust_constants, "audio", "DEFAULT_SAMPLE_RATE")
assert rust_value is not None, "DEFAULT_SAMPLE_RATE not found in Rust constants"
assert int(rust_value) == DEFAULT_SAMPLE_RATE, (
f"Sample rate mismatch: Python={DEFAULT_SAMPLE_RATE}, Rust={rust_value}"
)
def test_default_grpc_port_matches_rust(self, rust_constants: str) -> None:
"""DEFAULT_GRPC_PORT must match between Python and Rust."""
rust_value = _parse_rust_constant(rust_constants, "grpc", "DEFAULT_PORT")
assert rust_value is not None, "DEFAULT_PORT not found in Rust constants"
assert int(rust_value) == DEFAULT_GRPC_PORT, (
f"gRPC port mismatch: Python={DEFAULT_GRPC_PORT}, Rust={rust_value}"
)
def test_seconds_per_hour_matches_rust(self, rust_constants: str) -> None:
"""SECONDS_PER_HOUR must match between Python and Rust."""
rust_value = _parse_rust_constant(rust_constants, "time", "SECONDS_PER_HOUR")
assert rust_value is not None, "SECONDS_PER_HOUR not found in Rust constants"
assert int(rust_value) == SECONDS_PER_HOUR, (
f"Seconds per hour mismatch: Python={SECONDS_PER_HOUR}, Rust={rust_value}"
)
class TestConstantValues:
"""Validate constant values are within expected ranges.
These tests document expected values and catch accidental changes.
"""
def test_sample_rate_is_16khz(self) -> None:
"""Standard sample rate for speech recognition is 16kHz."""
assert DEFAULT_SAMPLE_RATE == EXPECTED_SAMPLE_RATE_HZ, (
f"DEFAULT_SAMPLE_RATE should be {EXPECTED_SAMPLE_RATE_HZ} Hz"
)
def test_grpc_port_is_standard(self) -> None:
"""Default gRPC port follows convention (50051)."""
assert DEFAULT_GRPC_PORT == EXPECTED_GRPC_PORT, (
f"DEFAULT_GRPC_PORT should be {EXPECTED_GRPC_PORT}"
)
def test_seconds_per_hour_is_correct(self) -> None:
"""Seconds per hour must be 3600."""
assert SECONDS_PER_HOUR == EXPECTED_SECONDS_PER_HOUR, (
f"SECONDS_PER_HOUR should be {EXPECTED_SECONDS_PER_HOUR}"
)