Python · 3698 bytes Raw Blame History
1 """Verify dlm_factory produces structurally-valid .dlm text.
2
3 Until Sprint 03's parser lands, we assert shape (frontmatter delimiters,
4 section fences, Q/A markers). The parser tests in Sprint 03 will
5 round-trip these blobs as the real acceptance check.
6 """
7
8 from __future__ import annotations
9
10 import re
11
12 from tests.fixtures.dlm_factory import instruction, make_dlm, preference, prose
13
14
15 class TestMakeDlm:
16 def test_default_has_frontmatter_and_body(self) -> None:
17 text = make_dlm()
18 assert text.startswith("---\n")
19 fm_end = text.index("\n---\n", 4)
20 body = text[fm_end + len("\n---\n") :]
21 assert body.strip(), "body must not be empty"
22
23 def test_explicit_dlm_id_is_preserved(self) -> None:
24 text = make_dlm(dlm_id="01HZ000000000000000000000X")
25 assert "dlm_id: 01HZ000000000000000000000X" in text
26
27 def test_base_model_reflects_arg(self) -> None:
28 text = make_dlm(base_model="hf:org/custom-model")
29 assert (
30 "base_model: hf:org/custom-model" in text or 'base_model: "hf:org/custom-model"' in text
31 )
32
33 def test_instruction_section_emits_q_a_pairs(self) -> None:
34 text = make_dlm(
35 sections=[instruction(("Q1?", "A1."), ("Q2?", "A2."))],
36 )
37 assert "::instruction::" in text
38 # Count Q/A headers — must match pair count.
39 assert text.count("### Q") == 2
40 assert text.count("### A") == 2
41 assert "Q1?" in text
42 assert "A1." in text
43 assert "Q2?" in text
44 assert "A2." in text
45
46 def test_preference_section_emits_triples(self) -> None:
47 text = make_dlm(
48 sections=[preference(("prompt", "good", "bad"))],
49 )
50 assert "::preference::" in text
51 assert "### Prompt" in text
52 assert "### Chosen" in text
53 assert "### Rejected" in text
54 for piece in ("prompt", "good", "bad"):
55 assert piece in text
56
57 def test_prose_section_is_verbatim(self) -> None:
58 body = "# heading\n\nparagraph with **markdown**.\n"
59 text = make_dlm(sections=[prose(body)])
60 assert body in text
61
62 def test_mixed_sections_in_order(self) -> None:
63 text = make_dlm(
64 sections=[
65 prose("intro.\n"),
66 instruction(("q", "a")),
67 preference(("p", "c", "r")),
68 ],
69 )
70 # Assert positional order via index.
71 i_intro = text.index("intro.")
72 i_instr = text.index("::instruction::")
73 i_pref = text.index("::preference::")
74 assert i_intro < i_instr < i_pref
75
76 def test_training_override_appears_in_frontmatter(self) -> None:
77 text = make_dlm(training_overrides={"lora_r": 16, "learning_rate": 1e-3})
78 assert re.search(r"^\s*lora_r:\s*16$", text, re.MULTILINE)
79 assert re.search(r"^\s*learning_rate:\s*0\.001$", text, re.MULTILINE)
80
81 def test_system_prompt_emitted_as_block_scalar(self) -> None:
82 text = make_dlm(system_prompt="line one\nline two")
83 assert "system_prompt: |\n line one\n line two" in text
84
85 def test_generated_output_ends_with_newline(self) -> None:
86 text = make_dlm()
87 assert text.endswith("\n")
88
89 def test_no_explicit_id_generates_ulid(self) -> None:
90 text_a = make_dlm()
91 text_b = make_dlm()
92 # Two calls should produce different ULIDs.
93 id_a = re.search(r"^dlm_id:\s*(\S+)$", text_a, re.MULTILINE)
94 id_b = re.search(r"^dlm_id:\s*(\S+)$", text_b, re.MULTILINE)
95 assert id_a is not None
96 assert id_b is not None
97 assert id_a.group(1) != id_b.group(1)
98 assert len(id_a.group(1)) == 26 # ULID canonical length