2024-06-24 13:08:07 +00:00
|
|
|
|
import io
|
2024-02-06 18:19:27 +00:00
|
|
|
|
import json
|
2024-02-11 18:36:56 +00:00
|
|
|
|
import os
|
2024-04-30 16:00:46 +00:00
|
|
|
|
import re
|
2024-02-13 17:52:25 +00:00
|
|
|
|
import shutil
|
2024-06-24 13:08:07 +00:00
|
|
|
|
from datetime import date as Date
|
2024-02-03 12:58:13 +00:00
|
|
|
|
|
|
|
|
|
import pydantic
|
2024-01-18 22:38:19 +00:00
|
|
|
|
import pytest
|
2024-02-18 16:19:59 +00:00
|
|
|
|
import ruamel.yaml
|
2024-06-24 13:08:07 +00:00
|
|
|
|
import time_machine
|
2024-01-18 17:24:30 +00:00
|
|
|
|
|
2024-01-18 22:38:19 +00:00
|
|
|
|
from rendercv import data_models as dm
|
2024-01-18 17:24:30 +00:00
|
|
|
|
|
2024-03-12 17:19:14 +00:00
|
|
|
|
from .conftest import update_testdata
|
2024-02-18 16:19:59 +00:00
|
|
|
|
|
2024-01-28 20:14:25 +00:00
|
|
|
|
|
2024-02-03 12:58:13 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"date, expected_date_object, expected_error",
|
|
|
|
|
[
|
|
|
|
|
("2020-01-01", Date(2020, 1, 1), None),
|
|
|
|
|
("2020-01", Date(2020, 1, 1), None),
|
|
|
|
|
("2020", Date(2020, 1, 1), None),
|
|
|
|
|
(2020, Date(2020, 1, 1), None),
|
|
|
|
|
("present", Date(2024, 1, 1), None),
|
|
|
|
|
("invalid", None, ValueError),
|
2024-02-08 19:12:47 +00:00
|
|
|
|
("20222", None, ValueError),
|
|
|
|
|
("202222-20200", None, ValueError),
|
|
|
|
|
("202222-12-20", None, ValueError),
|
|
|
|
|
("2022-20-20", None, ValueError),
|
2024-02-03 12:58:13 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
@time_machine.travel("2024-01-01")
|
|
|
|
|
def test_get_date_object(date, expected_date_object, expected_error):
|
|
|
|
|
if expected_error:
|
|
|
|
|
with pytest.raises(expected_error):
|
|
|
|
|
dm.get_date_object(date)
|
|
|
|
|
else:
|
|
|
|
|
assert dm.get_date_object(date) == expected_date_object
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"date, expected_date_string",
|
|
|
|
|
[
|
2024-06-06 14:22:41 +00:00
|
|
|
|
(Date(2020, 1, 1), "Jan 2020"),
|
|
|
|
|
(Date(2020, 2, 1), "Feb 2020"),
|
|
|
|
|
(Date(2020, 3, 1), "Mar 2020"),
|
|
|
|
|
(Date(2020, 4, 1), "Apr 2020"),
|
2024-02-03 12:58:13 +00:00
|
|
|
|
(Date(2020, 5, 1), "May 2020"),
|
|
|
|
|
(Date(2020, 6, 1), "June 2020"),
|
|
|
|
|
(Date(2020, 7, 1), "July 2020"),
|
2024-06-06 14:22:41 +00:00
|
|
|
|
(Date(2020, 8, 1), "Aug 2020"),
|
|
|
|
|
(Date(2020, 9, 1), "Sept 2020"),
|
|
|
|
|
(Date(2020, 10, 1), "Oct 2020"),
|
|
|
|
|
(Date(2020, 11, 1), "Nov 2020"),
|
|
|
|
|
(Date(2020, 12, 1), "Dec 2020"),
|
2024-02-03 12:58:13 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_format_date(date, expected_date_string):
|
|
|
|
|
assert dm.format_date(date) == expected_date_string
|
|
|
|
|
|
|
|
|
|
|
2024-04-30 16:00:46 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"key, value",
|
|
|
|
|
[
|
|
|
|
|
("cv.phone", "+905555555555"),
|
|
|
|
|
("cv.email", "test@example.com"),
|
2024-05-19 14:43:10 +00:00
|
|
|
|
("cv.sections.education.0.degree", "PhD"),
|
|
|
|
|
("cv.sections.education.0.highlights.0", "Did this."),
|
2024-04-30 17:52:33 +00:00
|
|
|
|
("cv.sections.this_is_a_new_section", '["This is a text entry."]'),
|
2024-04-30 16:00:46 +00:00
|
|
|
|
("design.page_size", "a4paper"),
|
2024-04-30 17:52:33 +00:00
|
|
|
|
("design", '{"theme": "engineeringresumes"}'),
|
2024-04-30 16:00:46 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_set_or_update_a_value(rendercv_data_model, key, value):
|
|
|
|
|
dm.set_or_update_a_value(rendercv_data_model, key, value)
|
|
|
|
|
|
|
|
|
|
# replace with regex pattern:
|
2024-04-30 17:52:33 +00:00
|
|
|
|
key = re.sub(r"sections\.([^\.]*)", 'sections_input["\\1"]', key)
|
|
|
|
|
key = re.sub(r"\.(\d+)", "[\\1]", key)
|
|
|
|
|
|
|
|
|
|
if value.startswith("{") and value.endswith("}"):
|
|
|
|
|
value = eval(value)
|
|
|
|
|
elif value.startswith("[") and value.endswith("]"):
|
|
|
|
|
value = eval(value)
|
2024-04-30 16:00:46 +00:00
|
|
|
|
|
|
|
|
|
assert eval(f"rendercv_data_model.{key}") == value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"key, value",
|
|
|
|
|
[
|
|
|
|
|
("cv.phones", "+905555555555"),
|
|
|
|
|
("cv.emssdsail", ""),
|
|
|
|
|
("cv.sections.education.99.degree", "PhD"),
|
|
|
|
|
("dessssign.page_size", "a4paper"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_set_or_update_a_value_invalid_keys(rendercv_data_model, key, value):
|
|
|
|
|
with pytest.raises((ValueError, KeyError, IndexError, AttributeError)):
|
|
|
|
|
dm.set_or_update_a_value(rendercv_data_model, key, value)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"key, value",
|
|
|
|
|
[
|
|
|
|
|
("cv.phone", "+9999995555555555"),
|
|
|
|
|
("cv.email", "notanemail***"),
|
2024-05-19 14:43:10 +00:00
|
|
|
|
("cv.sections.education.0.highlights", "this is not a list"),
|
2024-04-30 16:00:46 +00:00
|
|
|
|
("design.page_size", "invalid_page_size"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_set_or_update_a_value_invalid_values(rendercv_data_model, key, value):
|
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
dm.set_or_update_a_value(rendercv_data_model, key, value)
|
|
|
|
|
|
|
|
|
|
|
2024-02-06 18:19:27 +00:00
|
|
|
|
def test_read_input_file(input_file_path):
|
2024-03-12 17:19:14 +00:00
|
|
|
|
# Update the auxiliary files if update_testdata is True
|
|
|
|
|
if update_testdata:
|
2024-03-17 19:10:43 +00:00
|
|
|
|
# create testdata directory if it doesn't exist
|
|
|
|
|
if not input_file_path.parent.exists():
|
|
|
|
|
input_file_path.parent.mkdir()
|
|
|
|
|
|
2024-02-18 16:19:59 +00:00
|
|
|
|
input_dictionary = {
|
|
|
|
|
"cv": {
|
|
|
|
|
"name": "John Doe",
|
|
|
|
|
},
|
|
|
|
|
"design": {
|
|
|
|
|
"theme": "classic",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# dump the dictionary to a yaml file
|
2024-03-08 17:12:01 +00:00
|
|
|
|
yaml_object = ruamel.yaml.YAML()
|
|
|
|
|
yaml_object.dump(input_dictionary, input_file_path)
|
2024-02-18 16:19:59 +00:00
|
|
|
|
|
2024-02-14 18:50:00 +00:00
|
|
|
|
data_model = dm.read_input_file(input_file_path)
|
2024-02-18 16:19:59 +00:00
|
|
|
|
|
2024-02-14 18:50:00 +00:00
|
|
|
|
assert isinstance(data_model, dm.RenderCVDataModel)
|
2024-02-04 20:50:12 +00:00
|
|
|
|
|
|
|
|
|
|
2024-05-19 10:55:01 +00:00
|
|
|
|
def test_read_input_file_directly_with_contents(input_file_path):
|
|
|
|
|
input_dictionary = {
|
|
|
|
|
"cv": {
|
|
|
|
|
"name": "John Doe",
|
|
|
|
|
},
|
|
|
|
|
"design": {
|
|
|
|
|
"theme": "classic",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# dump the dictionary to a yaml file
|
|
|
|
|
yaml_object = ruamel.yaml.YAML()
|
|
|
|
|
yaml_object.width = 60
|
|
|
|
|
yaml_object.indent(mapping=2, sequence=4, offset=2)
|
|
|
|
|
with io.StringIO() as string_stream:
|
|
|
|
|
yaml_object.dump(input_dictionary, string_stream)
|
|
|
|
|
yaml_string = string_stream.getvalue()
|
|
|
|
|
|
|
|
|
|
data_model = dm.read_input_file(yaml_string)
|
|
|
|
|
|
|
|
|
|
assert isinstance(data_model, dm.RenderCVDataModel)
|
2024-02-09 19:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
2024-02-11 18:36:56 +00:00
|
|
|
|
def test_read_input_file_invalid_file(tmp_path):
|
|
|
|
|
invalid_file_path = tmp_path / "invalid.extension"
|
|
|
|
|
invalid_file_path.write_text("dummy content", encoding="utf-8")
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
dm.read_input_file(invalid_file_path)
|
|
|
|
|
|
|
|
|
|
|
2024-06-01 12:56:48 +00:00
|
|
|
|
def test_read_input_file_that_doesnt_exist(tmp_path):
|
|
|
|
|
non_existent_file_path = tmp_path / "non_existent_file.yaml"
|
|
|
|
|
with pytest.raises(FileNotFoundError):
|
|
|
|
|
dm.read_input_file(non_existent_file_path)
|
|
|
|
|
|
|
|
|
|
|
2024-02-25 14:15:54 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"theme",
|
|
|
|
|
dm.available_themes,
|
|
|
|
|
)
|
|
|
|
|
def test_get_a_sample_data_model(theme):
|
|
|
|
|
data_model = dm.get_a_sample_data_model("John Doe", theme)
|
2024-02-04 20:50:12 +00:00
|
|
|
|
assert isinstance(data_model, dm.RenderCVDataModel)
|
|
|
|
|
|
2024-02-06 18:19:27 +00:00
|
|
|
|
|
2024-02-25 14:15:54 +00:00
|
|
|
|
def test_get_a_sample_data_model_invalid_theme():
|
|
|
|
|
with pytest.raises(ValueError):
|
|
|
|
|
dm.get_a_sample_data_model("John Doe", "invalid")
|
|
|
|
|
|
|
|
|
|
|
2024-02-06 18:19:27 +00:00
|
|
|
|
def test_generate_json_schema():
|
|
|
|
|
schema = dm.generate_json_schema()
|
|
|
|
|
assert isinstance(schema, dict)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_generate_json_schema_file(tmp_path):
|
|
|
|
|
schema_file_path = tmp_path / "schema.json"
|
|
|
|
|
dm.generate_json_schema_file(schema_file_path)
|
|
|
|
|
|
|
|
|
|
assert schema_file_path.exists()
|
|
|
|
|
|
2024-02-11 13:37:10 +00:00
|
|
|
|
schema_text = schema_file_path.read_text(encoding="utf-8")
|
2024-02-06 18:19:27 +00:00
|
|
|
|
schema = json.loads(schema_text)
|
|
|
|
|
|
|
|
|
|
assert isinstance(schema, dict)
|
|
|
|
|
|
|
|
|
|
|
2024-05-20 20:08:53 +00:00
|
|
|
|
# def test_if_the_schema_is_the_latest(root_directory_path):
|
|
|
|
|
# original_schema_file_path = root_directory_path / "schema.json"
|
|
|
|
|
# original_schema_text = original_schema_file_path.read_text()
|
|
|
|
|
# original_schema = json.loads(original_schema_text)
|
2024-02-06 18:19:27 +00:00
|
|
|
|
|
2024-05-20 20:08:53 +00:00
|
|
|
|
# new_schema = dm.generate_json_schema()
|
2024-02-06 18:19:27 +00:00
|
|
|
|
|
2024-05-20 20:08:53 +00:00
|
|
|
|
# assert original_schema == new_schema
|
2024-02-06 18:19:27 +00:00
|
|
|
|
|
2024-02-04 20:50:12 +00:00
|
|
|
|
|
2024-01-28 20:14:25 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"start_date, end_date, date, expected_date_string, expected_date_string_only_years,"
|
|
|
|
|
" expected_time_span",
|
2024-01-28 20:14:25 +00:00
|
|
|
|
[
|
2024-02-11 18:36:56 +00:00
|
|
|
|
(
|
|
|
|
|
"2020-01-01",
|
|
|
|
|
"2021-01-01",
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Jan 2020 – Jan 2021",
|
|
|
|
|
"2020 – 2021",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"1 year 1 month",
|
|
|
|
|
),
|
2024-05-17 12:29:44 +00:00
|
|
|
|
(
|
|
|
|
|
Date(2020, 1, 1),
|
|
|
|
|
Date(2021, 1, 1),
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Jan 2020 – Jan 2021",
|
|
|
|
|
"2020 – 2021",
|
2024-05-17 12:29:44 +00:00
|
|
|
|
"1 year 1 month",
|
|
|
|
|
),
|
2024-02-11 18:36:56 +00:00
|
|
|
|
(
|
|
|
|
|
"2020-01",
|
|
|
|
|
"2021-01",
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Jan 2020 – Jan 2021",
|
|
|
|
|
"2020 – 2021",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"1 year 1 month",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"2020-01",
|
|
|
|
|
"2021-01-01",
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Jan 2020 – Jan 2021",
|
|
|
|
|
"2020 – 2021",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"1 year 1 month",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"2020-01-01",
|
|
|
|
|
"2021-01",
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Jan 2020 – Jan 2021",
|
|
|
|
|
"2020 – 2021",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"1 year 1 month",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"2020-01-01",
|
|
|
|
|
None,
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Jan 2020 – present",
|
|
|
|
|
"2020 – present",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"4 years 1 month",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"2020-02-01",
|
|
|
|
|
"present",
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Feb 2020 – present",
|
|
|
|
|
"2020 – present",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"3 years 11 months",
|
|
|
|
|
),
|
2024-06-06 14:22:41 +00:00
|
|
|
|
("2020-01-01", "2021-01-01", "2023-02-01", "Feb 2023", "2023", ""),
|
2024-06-10 17:53:13 +00:00
|
|
|
|
("2020", "2021", None, "2020 – 2021", "2020 – 2021", "1 year"),
|
|
|
|
|
("2020", None, None, "2020 – present", "2020 – present", "4 years"),
|
|
|
|
|
("2020-10-10", "2022", None, "Oct 2020 – 2022", "2020 – 2022", "2 years"),
|
2024-02-11 18:36:56 +00:00
|
|
|
|
(
|
|
|
|
|
"2020-10-10",
|
|
|
|
|
"2020-11-05",
|
|
|
|
|
None,
|
2024-06-10 17:53:13 +00:00
|
|
|
|
"Oct 2020 – Nov 2020",
|
|
|
|
|
"2020 – 2020",
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"1 month",
|
|
|
|
|
),
|
2024-06-10 17:53:13 +00:00
|
|
|
|
("2022", "2023-10-10", None, "2022 – Oct 2023", "2022 – 2023", "1 year"),
|
2024-02-11 18:36:56 +00:00
|
|
|
|
(
|
|
|
|
|
"2020-01-01",
|
|
|
|
|
"present",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"2020-01-01",
|
|
|
|
|
None,
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
None,
|
|
|
|
|
None,
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
None,
|
|
|
|
|
"2020-01-01",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"My Custom Date",
|
|
|
|
|
"",
|
|
|
|
|
),
|
2024-06-06 14:22:41 +00:00
|
|
|
|
(None, None, "2020-01-01", "Jan 2020", "2020", ""),
|
|
|
|
|
(None, None, "2020-09", "Sept 2020", "2020", ""),
|
|
|
|
|
(None, None, Date(2020, 1, 1), "Jan 2020", "2020", ""),
|
2024-02-11 18:36:56 +00:00
|
|
|
|
(None, None, None, "", "", ""),
|
2024-06-06 14:22:41 +00:00
|
|
|
|
(None, "2020-01-01", None, "Jan 2020", "2020", ""),
|
|
|
|
|
(None, "present", None, "Jan 2024", "2024", ""),
|
2024-05-17 11:30:26 +00:00
|
|
|
|
("2002", "2020", "2024", "2024", "2024", ""),
|
2024-01-28 20:14:25 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
@time_machine.travel("2024-01-01")
|
2024-02-11 18:36:56 +00:00
|
|
|
|
def test_dates(
|
|
|
|
|
start_date,
|
|
|
|
|
end_date,
|
|
|
|
|
date,
|
|
|
|
|
expected_date_string,
|
|
|
|
|
expected_date_string_only_years,
|
|
|
|
|
expected_time_span,
|
|
|
|
|
):
|
2024-01-28 20:14:25 +00:00
|
|
|
|
entry_base = dm.EntryBase(start_date=start_date, end_date=end_date, date=date)
|
|
|
|
|
|
|
|
|
|
assert entry_base.date_string == expected_date_string
|
2024-02-11 18:36:56 +00:00
|
|
|
|
assert entry_base.date_string_only_years == expected_date_string_only_years
|
2024-01-30 18:50:57 +00:00
|
|
|
|
assert entry_base.time_span_string == expected_time_span
|
2024-01-28 20:14:25 +00:00
|
|
|
|
|
2024-02-03 12:58:13 +00:00
|
|
|
|
|
2024-02-02 17:02:18 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"date, expected_date_string",
|
|
|
|
|
[
|
2024-06-06 14:22:41 +00:00
|
|
|
|
("2020-01-01", "Jan 2020"),
|
|
|
|
|
("2020-01", "Jan 2020"),
|
2024-02-02 17:02:18 +00:00
|
|
|
|
("2020", "2020"),
|
2024-02-03 12:58:13 +00:00
|
|
|
|
],
|
2024-02-02 17:02:18 +00:00
|
|
|
|
)
|
|
|
|
|
def test_publication_dates(publication_entry, date, expected_date_string):
|
|
|
|
|
publication_entry["date"] = date
|
|
|
|
|
publication_entry = dm.PublicationEntry(**publication_entry)
|
|
|
|
|
assert publication_entry.date_string == expected_date_string
|
|
|
|
|
|
2024-02-03 12:58:13 +00:00
|
|
|
|
|
2024-05-17 11:30:26 +00:00
|
|
|
|
@pytest.mark.parametrize("date", ["2025-23-23"])
|
2024-02-02 17:02:18 +00:00
|
|
|
|
def test_invalid_publication_dates(publication_entry, date):
|
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
publication_entry["date"] = date
|
|
|
|
|
dm.PublicationEntry(**publication_entry)
|
|
|
|
|
|
2024-01-28 20:14:25 +00:00
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"start_date, end_date, date",
|
|
|
|
|
[
|
|
|
|
|
("aaa", "2021-01-01", None),
|
|
|
|
|
("2020-01-01", "aaa", None),
|
|
|
|
|
("2023-01-01", "2021-01-01", None),
|
|
|
|
|
("2022", "2021", None),
|
2024-02-09 19:14:46 +00:00
|
|
|
|
("2025", "2021", None),
|
|
|
|
|
("2020-01-01", "invalid_end_date", None),
|
|
|
|
|
("invalid_start_date", "2021-01-01", None),
|
|
|
|
|
("2020-99-99", "2021-01-01", None),
|
|
|
|
|
("2020-10-12", "2020-99-99", None),
|
2024-03-10 17:13:32 +00:00
|
|
|
|
(None, None, "2020-20-20"),
|
2024-01-28 20:14:25 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_invalid_dates(start_date, end_date, date):
|
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
dm.EntryBase(start_date=start_date, end_date=end_date, date=date)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"doi, expected_doi_url",
|
|
|
|
|
[
|
|
|
|
|
("10.1109/TASC.2023.3340648", "https://doi.org/10.1109/TASC.2023.3340648"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_doi_url(publication_entry, doi, expected_doi_url):
|
|
|
|
|
publication_entry["doi"] = doi
|
|
|
|
|
publication_entry = dm.PublicationEntry(**publication_entry)
|
|
|
|
|
assert publication_entry.doi_url == expected_doi_url
|
|
|
|
|
|
|
|
|
|
|
2024-02-09 19:14:46 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"network, username",
|
2024-04-16 11:41:24 +00:00
|
|
|
|
[
|
|
|
|
|
("Mastodon", "invalidmastodon"),
|
|
|
|
|
("Mastodon", "@inva@l@id"),
|
|
|
|
|
("Mastodon", "@invalid@ne<>twork.com"),
|
2024-05-25 12:53:12 +00:00
|
|
|
|
("StackOverflow", "invalidusername"),
|
|
|
|
|
("StackOverflow", "invalidusername//"),
|
|
|
|
|
("StackOverflow", "invalidusername/invalid"),
|
2024-06-04 22:14:08 +00:00
|
|
|
|
("YouTube", "@invalidusername"),
|
2024-04-16 11:41:24 +00:00
|
|
|
|
],
|
2024-02-09 19:14:46 +00:00
|
|
|
|
)
|
|
|
|
|
def test_invalid_social_networks(network, username):
|
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
dm.SocialNetwork(network=network, username=username)
|
|
|
|
|
|
|
|
|
|
|
2024-01-28 20:14:25 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"network, username, expected_url",
|
|
|
|
|
[
|
|
|
|
|
("LinkedIn", "myusername", "https://linkedin.com/in/myusername"),
|
|
|
|
|
("GitHub", "myusername", "https://github.com/myusername"),
|
|
|
|
|
("Instagram", "myusername", "https://instagram.com/myusername"),
|
2024-05-26 19:54:35 +00:00
|
|
|
|
("ORCID", "myusername", "https://orcid.org/myusername"),
|
2024-04-16 11:41:24 +00:00
|
|
|
|
("Mastodon", "@myusername@test.org", "https://test.org/@myusername"),
|
2024-05-23 05:18:53 +00:00
|
|
|
|
(
|
|
|
|
|
"StackOverflow",
|
|
|
|
|
"4567/myusername",
|
|
|
|
|
"https://stackoverflow.com/users/4567/myusername",
|
|
|
|
|
),
|
2024-05-25 12:53:12 +00:00
|
|
|
|
(
|
|
|
|
|
"GitLab",
|
|
|
|
|
"myusername",
|
|
|
|
|
"https://gitlab.com/myusername",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"ResearchGate",
|
|
|
|
|
"myusername",
|
|
|
|
|
"https://researchgate.net/profile/myusername",
|
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"YouTube",
|
2024-06-04 22:14:08 +00:00
|
|
|
|
"myusername",
|
2024-05-25 12:53:12 +00:00
|
|
|
|
"https://youtube.com/@myusername",
|
|
|
|
|
),
|
2024-05-29 13:05:51 +00:00
|
|
|
|
(
|
|
|
|
|
"Google Scholar",
|
|
|
|
|
"myusername",
|
|
|
|
|
"https://scholar.google.com/citations?user=myusername",
|
|
|
|
|
),
|
2024-01-28 20:14:25 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_social_network_url(network, username, expected_url):
|
|
|
|
|
social_network = dm.SocialNetwork(network=network, username=username)
|
|
|
|
|
assert str(social_network.url) == expected_url
|
|
|
|
|
|
|
|
|
|
|
2024-02-09 19:14:46 +00:00
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"entry, expected_entry_type, expected_section_type",
|
|
|
|
|
[
|
|
|
|
|
(
|
|
|
|
|
"publication_entry",
|
|
|
|
|
"PublicationEntry",
|
2024-03-10 17:13:32 +00:00
|
|
|
|
"SectionWithPublicationEntries",
|
2024-02-09 19:14:46 +00:00
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"experience_entry",
|
|
|
|
|
"ExperienceEntry",
|
2024-03-10 17:13:32 +00:00
|
|
|
|
"SectionWithExperienceEntries",
|
2024-02-09 19:14:46 +00:00
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"education_entry",
|
|
|
|
|
"EducationEntry",
|
2024-03-10 17:13:32 +00:00
|
|
|
|
"SectionWithEducationEntries",
|
2024-02-09 19:14:46 +00:00
|
|
|
|
),
|
|
|
|
|
(
|
|
|
|
|
"normal_entry",
|
|
|
|
|
"NormalEntry",
|
2024-03-10 17:13:32 +00:00
|
|
|
|
"SectionWithNormalEntries",
|
2024-02-09 19:14:46 +00:00
|
|
|
|
),
|
2024-03-10 17:13:32 +00:00
|
|
|
|
("one_line_entry", "OneLineEntry", "SectionWithOneLineEntries"),
|
|
|
|
|
("text_entry", "TextEntry", "SectionWithTextEntries"),
|
2024-03-10 18:08:18 +00:00
|
|
|
|
("bullet_entry", "BulletEntry", "SectionWithBulletEntries"),
|
2024-02-09 19:14:46 +00:00
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_get_entry_and_section_type(
|
2024-03-12 18:02:06 +00:00
|
|
|
|
entry, expected_entry_type, expected_section_type, request: pytest.FixtureRequest
|
2024-02-09 19:14:46 +00:00
|
|
|
|
):
|
|
|
|
|
entry = request.getfixturevalue(entry)
|
|
|
|
|
entry_type, section_type = dm.get_entry_and_section_type(entry)
|
|
|
|
|
assert entry_type == expected_entry_type
|
2024-03-10 17:13:32 +00:00
|
|
|
|
assert section_type.__name__ == expected_section_type
|
2024-02-09 19:14:46 +00:00
|
|
|
|
|
|
|
|
|
# initialize the entry with the entry type
|
2024-03-10 17:13:32 +00:00
|
|
|
|
if entry_type != "TextEntry":
|
2024-02-09 19:14:46 +00:00
|
|
|
|
entry = eval(f"dm.{entry_type}(**entry)")
|
|
|
|
|
entry_type, section_type = dm.get_entry_and_section_type(entry)
|
|
|
|
|
assert entry_type == expected_entry_type
|
2024-03-10 17:13:32 +00:00
|
|
|
|
assert section_type.__name__ == expected_section_type
|
2024-02-09 19:14:46 +00:00
|
|
|
|
|
|
|
|
|
|
2024-02-14 18:50:00 +00:00
|
|
|
|
def test_sections(
|
2024-01-28 20:14:25 +00:00
|
|
|
|
education_entry,
|
|
|
|
|
experience_entry,
|
|
|
|
|
publication_entry,
|
|
|
|
|
normal_entry,
|
|
|
|
|
one_line_entry,
|
|
|
|
|
text_entry,
|
|
|
|
|
):
|
|
|
|
|
input = {
|
|
|
|
|
"name": "John Doe",
|
|
|
|
|
"sections": {
|
2024-02-14 18:50:00 +00:00
|
|
|
|
"arbitrary_title": [
|
|
|
|
|
education_entry,
|
|
|
|
|
education_entry,
|
|
|
|
|
],
|
|
|
|
|
"arbitrary_title_2": [
|
|
|
|
|
experience_entry,
|
|
|
|
|
experience_entry,
|
|
|
|
|
],
|
|
|
|
|
"arbitrary_title_3": [
|
|
|
|
|
publication_entry,
|
|
|
|
|
publication_entry,
|
|
|
|
|
],
|
|
|
|
|
"arbitrary_title_4": [
|
|
|
|
|
normal_entry,
|
|
|
|
|
normal_entry,
|
|
|
|
|
],
|
|
|
|
|
"arbitrary_title_5": [
|
|
|
|
|
one_line_entry,
|
|
|
|
|
one_line_entry,
|
|
|
|
|
],
|
|
|
|
|
"arbitrary_title_6": [
|
|
|
|
|
text_entry,
|
|
|
|
|
text_entry,
|
2024-01-28 20:14:25 +00:00
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cv = dm.CurriculumVitae(**input)
|
2024-02-14 18:50:00 +00:00
|
|
|
|
assert len(cv.sections) == 6
|
|
|
|
|
for section in cv.sections:
|
|
|
|
|
assert len(section.entries) == 2
|
2024-01-31 17:26:10 +00:00
|
|
|
|
|
|
|
|
|
|
2024-02-14 18:50:00 +00:00
|
|
|
|
def test_sections_with_invalid_entries():
|
2024-01-31 17:26:10 +00:00
|
|
|
|
input = {"name": "John Doe", "sections": dict()}
|
2024-02-18 16:19:59 +00:00
|
|
|
|
input["sections"]["section_title"] = [
|
|
|
|
|
{
|
|
|
|
|
"this": "is",
|
|
|
|
|
"an": "invalid",
|
|
|
|
|
"entry": 10,
|
|
|
|
|
}
|
|
|
|
|
]
|
2024-01-31 17:26:10 +00:00
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
dm.CurriculumVitae(**input)
|
2024-02-11 18:36:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"invalid_custom_theme_name",
|
|
|
|
|
[
|
|
|
|
|
"pathdoesntexist",
|
|
|
|
|
"invalid_theme_name",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_invalid_custom_theme(invalid_custom_theme_name):
|
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
2024-02-18 16:19:59 +00:00
|
|
|
|
dm.RenderCVDataModel(
|
|
|
|
|
**{
|
|
|
|
|
"cv": {"name": "John Doe"},
|
|
|
|
|
"design": {"theme": invalid_custom_theme_name},
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-02-11 18:36:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_custom_theme_with_missing_files(tmp_path):
|
|
|
|
|
custom_theme_path = tmp_path / "customtheme"
|
|
|
|
|
custom_theme_path.mkdir()
|
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
os.chdir(tmp_path)
|
2024-02-18 16:19:59 +00:00
|
|
|
|
dm.RenderCVDataModel(
|
|
|
|
|
**{ # type: ignore
|
|
|
|
|
"cv": {"name": "John Doe"},
|
|
|
|
|
"design": {"theme": "customtheme"},
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2024-03-12 17:19:14 +00:00
|
|
|
|
def test_custom_theme(testdata_directory_path):
|
2024-02-18 16:19:59 +00:00
|
|
|
|
os.chdir(
|
2024-03-12 17:19:14 +00:00
|
|
|
|
testdata_directory_path
|
2024-02-18 16:19:59 +00:00
|
|
|
|
/ "test_copy_theme_files_to_output_directory_custom_theme"
|
|
|
|
|
)
|
|
|
|
|
data_model = dm.RenderCVDataModel(
|
|
|
|
|
**{ # type: ignore
|
2024-02-11 18:36:56 +00:00
|
|
|
|
"cv": {"name": "John Doe"},
|
2024-02-18 16:19:59 +00:00
|
|
|
|
"design": {"theme": "dummytheme"},
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-02-11 18:36:56 +00:00
|
|
|
|
|
|
|
|
|
assert data_model.design.theme == "dummytheme"
|
2024-02-13 17:52:25 +00:00
|
|
|
|
|
|
|
|
|
|
2024-03-12 17:19:14 +00:00
|
|
|
|
def test_custom_theme_without_init_file(tmp_path, testdata_directory_path):
|
2024-02-18 16:19:59 +00:00
|
|
|
|
reference_custom_theme_path = (
|
2024-03-12 17:19:14 +00:00
|
|
|
|
testdata_directory_path
|
2024-02-18 16:19:59 +00:00
|
|
|
|
/ "test_copy_theme_files_to_output_directory_custom_theme"
|
|
|
|
|
/ "dummytheme"
|
|
|
|
|
)
|
2024-02-13 17:52:25 +00:00
|
|
|
|
|
|
|
|
|
# copy the directory to tmp_path:
|
|
|
|
|
custom_theme_path = tmp_path / "dummytheme"
|
|
|
|
|
shutil.copytree(reference_custom_theme_path, custom_theme_path, dirs_exist_ok=True)
|
|
|
|
|
|
|
|
|
|
# remove the __init__.py file:
|
|
|
|
|
init_file = custom_theme_path / "__init__.py"
|
|
|
|
|
init_file.unlink()
|
|
|
|
|
|
|
|
|
|
os.chdir(tmp_path)
|
2024-02-18 16:19:59 +00:00
|
|
|
|
data_model = dm.RenderCVDataModel(
|
|
|
|
|
**{ # type: ignore
|
|
|
|
|
"cv": {"name": "John Doe"},
|
|
|
|
|
"design": {"theme": "dummytheme"},
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-02-13 17:52:25 +00:00
|
|
|
|
|
|
|
|
|
assert data_model.design.theme == "dummytheme"
|
2024-03-17 19:46:37 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_custom_theme_with_broken_init_file(tmp_path, testdata_directory_path):
|
|
|
|
|
reference_custom_theme_path = (
|
|
|
|
|
testdata_directory_path
|
|
|
|
|
/ "test_copy_theme_files_to_output_directory_custom_theme"
|
|
|
|
|
/ "dummytheme"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# copy the directory to tmp_path:
|
|
|
|
|
custom_theme_path = tmp_path / "dummytheme"
|
|
|
|
|
shutil.copytree(reference_custom_theme_path, custom_theme_path, dirs_exist_ok=True)
|
|
|
|
|
|
2024-06-01 12:56:48 +00:00
|
|
|
|
# overwrite the __init__.py file (syntax error)
|
2024-03-17 19:46:37 +00:00
|
|
|
|
init_file = custom_theme_path / "__init__.py"
|
|
|
|
|
init_file.write_text("invalid python code", encoding="utf-8")
|
|
|
|
|
|
|
|
|
|
os.chdir(tmp_path)
|
2024-06-01 12:56:48 +00:00
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
dm.RenderCVDataModel(
|
|
|
|
|
**{ # type: ignore
|
|
|
|
|
"cv": {"name": "John Doe"},
|
|
|
|
|
"design": {"theme": "dummytheme"},
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# overwrite the __init__.py file (import error)
|
|
|
|
|
init_file = custom_theme_path / "__init__.py"
|
|
|
|
|
init_file.write_text("from ... import test", encoding="utf-8")
|
|
|
|
|
|
|
|
|
|
os.chdir(tmp_path)
|
2024-03-17 19:46:37 +00:00
|
|
|
|
with pytest.raises(pydantic.ValidationError):
|
|
|
|
|
dm.RenderCVDataModel(
|
|
|
|
|
**{ # type: ignore
|
|
|
|
|
"cv": {"name": "John Doe"},
|
|
|
|
|
"design": {"theme": "dummytheme"},
|
|
|
|
|
}
|
|
|
|
|
)
|
2024-05-19 10:55:01 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_locale_catalog():
|
|
|
|
|
data_model = dm.get_a_sample_data_model("John Doe")
|
|
|
|
|
data_model.locale_catalog = dm.LocaleCatalog(
|
|
|
|
|
month="a",
|
|
|
|
|
months="b",
|
|
|
|
|
year="c",
|
|
|
|
|
years="d",
|
|
|
|
|
present="e",
|
|
|
|
|
to="f",
|
|
|
|
|
abbreviations_for_months=[
|
|
|
|
|
"1",
|
|
|
|
|
"2",
|
|
|
|
|
"3",
|
|
|
|
|
"4",
|
|
|
|
|
"5",
|
|
|
|
|
"6",
|
|
|
|
|
"7",
|
|
|
|
|
"8",
|
|
|
|
|
"9",
|
|
|
|
|
"10",
|
|
|
|
|
"11",
|
|
|
|
|
"12",
|
|
|
|
|
],
|
2024-06-20 12:55:45 +00:00
|
|
|
|
full_names_of_months=[
|
|
|
|
|
"1",
|
|
|
|
|
"2",
|
|
|
|
|
"3",
|
|
|
|
|
"4",
|
|
|
|
|
"5",
|
|
|
|
|
"6",
|
|
|
|
|
"7",
|
|
|
|
|
"8",
|
|
|
|
|
"9",
|
|
|
|
|
"10",
|
|
|
|
|
"11",
|
|
|
|
|
"12",
|
|
|
|
|
],
|
2024-05-19 10:55:01 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert dm.locale_catalog == data_model.locale_catalog.model_dump()
|
2024-05-19 12:53:12 +00:00
|
|
|
|
|
2024-05-25 12:53:12 +00:00
|
|
|
|
|
|
|
|
|
def test_if_local_catalog_resets():
|
|
|
|
|
data_model = dm.get_a_sample_data_model("John Doe")
|
|
|
|
|
|
|
|
|
|
data_model.locale_catalog = dm.LocaleCatalog(
|
|
|
|
|
month="a",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
assert dm.locale_catalog["month"] == "a"
|
|
|
|
|
|
|
|
|
|
data_model = dm.get_a_sample_data_model("John Doe")
|
|
|
|
|
|
|
|
|
|
assert dm.locale_catalog["month"] == "month"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_dictionary_to_yaml():
|
|
|
|
|
input_dictionary = {
|
|
|
|
|
"test_list": [
|
|
|
|
|
"a",
|
|
|
|
|
"b",
|
|
|
|
|
"c",
|
2024-05-19 12:53:12 +00:00
|
|
|
|
],
|
2024-05-25 12:53:12 +00:00
|
|
|
|
"test_dict": {
|
|
|
|
|
"a": 1,
|
|
|
|
|
"b": 2,
|
|
|
|
|
},
|
2024-05-19 12:53:12 +00:00
|
|
|
|
}
|
2024-05-25 12:53:12 +00:00
|
|
|
|
yaml_string = dm.dictionary_to_yaml(input_dictionary)
|
|
|
|
|
|
|
|
|
|
# load the yaml string
|
|
|
|
|
yaml_object = ruamel.yaml.YAML()
|
|
|
|
|
output_dictionary = yaml_object.load(yaml_string)
|
|
|
|
|
|
|
|
|
|
assert input_dictionary == output_dictionary
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_create_a_sample_yaml_input_file(tmp_path):
|
|
|
|
|
input_file_path = tmp_path / "input.yaml"
|
|
|
|
|
yaml_contents = dm.create_a_sample_yaml_input_file(input_file_path)
|
|
|
|
|
|
|
|
|
|
assert input_file_path.exists()
|
|
|
|
|
assert yaml_contents == input_file_path.read_text(encoding="utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_default_input_file_doesnt_have_local_catalog():
|
|
|
|
|
yaml_contents = dm.create_a_sample_yaml_input_file()
|
|
|
|
|
assert "locale_catalog" not in yaml_contents
|
2024-06-10 14:31:13 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
"key, expected_section_title",
|
|
|
|
|
[
|
|
|
|
|
("this_is_a_test", "This Is A Test"),
|
|
|
|
|
("welcome_to_RenderCV!", "Welcome To RenderCV!"),
|
|
|
|
|
("\\faGraduationCap_education", "\\faGraduationCap Education"),
|
|
|
|
|
("Hello_World", "Hello World"),
|
|
|
|
|
("Hello World", "Hello World"),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
def test_dictionary_key_to_proper_section_title(key, expected_section_title):
|
|
|
|
|
assert dm.dictionary_key_to_proper_section_title(key) == expected_section_title
|