mirror of
https://github.com/rendercv/rendercv.git
synced 2025-12-23 13:38:01 -05:00
181 lines
6.4 KiB
Python
181 lines
6.4 KiB
Python
from datetime import date as Date
|
||
|
||
import pydantic
|
||
import pytest
|
||
|
||
from rendercv.renderer.templater.model_processor import process_fields, process_model
|
||
from rendercv.schema.models.cv.cv import Cv
|
||
from rendercv.schema.models.cv.entries.normal import NormalEntry
|
||
from rendercv.schema.models.rendercv_model import RenderCVModel
|
||
|
||
|
||
@pytest.fixture
|
||
def recorder():
|
||
"""Return a processor function and a list tracking all values it has processed.
|
||
|
||
Used to verify which fields get processed and in what order.
|
||
"""
|
||
seen = []
|
||
|
||
def fn(v: str) -> str:
|
||
seen.append(v)
|
||
return f"processed-{v}"
|
||
|
||
return fn, seen
|
||
|
||
|
||
class TestProcessFields:
|
||
def test_applies_processors_in_order(self):
|
||
processors = [lambda s: s.upper(), lambda s: f"{s}!"]
|
||
result = process_fields("content", processors)
|
||
assert result == "CONTENT!"
|
||
|
||
def test_processes_fields_and_mutates_entry(self, recorder):
|
||
fn, seen = recorder
|
||
|
||
entry = NormalEntry.model_validate(
|
||
{
|
||
"name": "Entry",
|
||
"summary": "hello",
|
||
"highlights": ["a", "b"],
|
||
"start_date": "2020-01-01",
|
||
"end_date": "2020-02-01",
|
||
"location": "Remote",
|
||
}
|
||
)
|
||
|
||
process_fields(entry, [fn])
|
||
|
||
assert "hello" in seen
|
||
assert "a" in seen
|
||
assert "b" in seen
|
||
assert "Remote" in seen
|
||
|
||
assert "2020-01-01" not in seen
|
||
assert "2020-02-01" not in seen
|
||
assert entry.summary == "processed-hello"
|
||
assert entry.highlights == ["processed-a", "processed-b"]
|
||
assert entry.location == "processed-Remote"
|
||
|
||
assert entry.start_date == "2020-01-01"
|
||
assert entry.end_date == "2020-02-01"
|
||
|
||
def test_converts_non_string_non_list_fields_to_string(self, recorder):
|
||
class EntryWithInt(pydantic.BaseModel):
|
||
name: str
|
||
count: int
|
||
|
||
fn, seen = recorder
|
||
entry = EntryWithInt(name="Test", count=42)
|
||
|
||
process_fields(entry, [fn]) # ty: ignore[invalid-argument-type]
|
||
|
||
assert "Test" in seen
|
||
assert "42" in seen
|
||
assert entry.name == "processed-Test"
|
||
assert entry.count == "processed-42"
|
||
|
||
|
||
@pytest.fixture(params=[["Python", "Remote"], []])
|
||
def model(request: pytest.FixtureRequest) -> RenderCVModel:
|
||
"""Return a test RenderCVModel with keywords either set or empty.
|
||
|
||
Parametrized to test both with and without bold keywords.
|
||
"""
|
||
cv_data = {
|
||
# Order matters for connections
|
||
"name": "Jane Doe @",
|
||
"headline": "Software Engineer @",
|
||
"email": "jane@example.com",
|
||
"website": "https://janedoe.dev",
|
||
"sections": {
|
||
"Professional Experience": [
|
||
{
|
||
"name": "Backend Work",
|
||
"summary": "Built Python services with *markdown* emphasis.",
|
||
"highlights": ["Improved Python performance"],
|
||
"start_date": "2022-01-01",
|
||
"end_date": "2023-02-01",
|
||
"location": "Remote",
|
||
}
|
||
]
|
||
},
|
||
}
|
||
cv = Cv.model_validate(cv_data)
|
||
|
||
rendercv_model = RenderCVModel(cv=cv)
|
||
rendercv_model.settings.current_date = Date(2024, 2, 1)
|
||
rendercv_model.settings.bold_keywords = request.param
|
||
|
||
return rendercv_model
|
||
|
||
|
||
class TestProcessModel:
|
||
def test_markdown_output_has_correct_structure(self, model):
|
||
result = process_model(model, "markdown")
|
||
|
||
assert result.cv.name == "Jane Doe @"
|
||
assert result.cv.headline == "Software Engineer @"
|
||
|
||
# Connections and last updated date are added to cv
|
||
assert result.cv.connections == [ # ty: ignore[unresolved-attribute]
|
||
"[jane@example.com](mailto:jane@example.com)",
|
||
"[janedoe.dev](https://janedoe.dev/)",
|
||
]
|
||
assert (
|
||
result.cv.top_note == "*Last updated in Feb 2024*" # ty: ignore[unresolved-attribute]
|
||
)
|
||
|
||
entry = result.cv.rendercv_sections[0].entries[0]
|
||
assert entry.main_column.startswith("**Backend Work**")
|
||
if model.settings.bold_keywords:
|
||
assert (
|
||
"Built **Python** services with *markdown* emphasis."
|
||
in entry.main_column
|
||
)
|
||
assert "- Improved **Python** performance" in entry.main_column
|
||
# DATE placeholder removed because it's not provided; location remains
|
||
assert entry.date_and_location_column == "**Remote**\nJan 2022 – Feb 2023"
|
||
else:
|
||
assert (
|
||
"Built Python services with *markdown* emphasis." in entry.main_column
|
||
)
|
||
assert "- Improved Python performance" in entry.main_column
|
||
assert entry.date_and_location_column == "Remote\nJan 2022 – Feb 2023"
|
||
|
||
def test_typst_output_escapes_special_characters(self, model):
|
||
result = process_model(model, "typst")
|
||
|
||
assert result.cv.name == "Jane Doe \\@"
|
||
assert result.cv.headline == "Software Engineer \\@"
|
||
|
||
entry = result.cv.rendercv_sections[0].entries[0]
|
||
assert entry.main_column.startswith("#strong[Backend Work]")
|
||
if model.settings.bold_keywords:
|
||
assert "- Improved #strong[Python] performance" in entry.main_column
|
||
assert (
|
||
entry.date_and_location_column == "#strong[Remote]\nJan 2022 – Feb 2023"
|
||
)
|
||
# Connections rendered as Typst links with icons by default
|
||
assert result.cv.connections[0].startswith("#link(") # ty: ignore[unresolved-attribute]
|
||
assert "#connection-with-icon" in result.cv.connections[0] # ty: ignore[unresolved-attribute]
|
||
else:
|
||
assert "- Improved Python performance" in entry.main_column
|
||
assert entry.date_and_location_column == "Remote\nJan 2022 – Feb 2023"
|
||
assert result.cv.connections[0].startswith("#link(") # ty: ignore[unresolved-attribute]
|
||
assert "jane@example.com" in result.cv.connections[0] # ty: ignore[unresolved-attribute]
|
||
|
||
def test_handles_cv_with_no_sections(self):
|
||
cv_data = {
|
||
"name": "Jane Doe",
|
||
"headline": "Software Engineer",
|
||
}
|
||
cv = Cv.model_validate(cv_data)
|
||
rendercv_model = RenderCVModel(cv=cv)
|
||
|
||
result = process_model(rendercv_model, "markdown")
|
||
|
||
assert result.cv.name == "Jane Doe"
|
||
assert result.cv.headline == "Software Engineer"
|
||
assert hasattr(result.cv, "connections")
|