codeplain 0.1.8__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,532 @@
1
+ import os
2
+ import re
3
+ from unittest.mock import patch
4
+ from urllib.parse import urlparse
5
+
6
+ import pytest
7
+ from liquid2 import Environment, RenderContext, TemplateSource
8
+
9
+ import file_utils
10
+ import plain_file
11
+ import plain_spec
12
+ from plain2code_exceptions import PlainSyntaxError
13
+
14
+
15
+ def test_regular_plain_source(get_test_data_path):
16
+ _, plain_sections, _ = plain_file.plain_file_parser(
17
+ "regular_plain_source.plain",
18
+ [get_test_data_path("data/plainfileparser")],
19
+ )
20
+ assert plain_sections == {
21
+ "definitions": [],
22
+ "technical specs": [
23
+ {"markdown": "- First non-functional requirement."},
24
+ {"markdown": "- Second non-functional requirement."},
25
+ ],
26
+ "functional specs": [{"markdown": '- Display "hello, world"'}],
27
+ }
28
+
29
+
30
+ def test_unknown_section():
31
+ plain_source = """
32
+ ***definitions***
33
+
34
+ ***Unknown Section:***
35
+ """
36
+ with pytest.raises(
37
+ Exception,
38
+ match=re.escape(
39
+ "Syntax error at line 3: Invalid specification heading (`Unknown Section:`). Allowed headings: definitions, technical specs, test specs, functional specs, acceptance tests"
40
+ ),
41
+ ):
42
+ plain_file.parse_plain_source(plain_source, {}, [], [], [])
43
+
44
+
45
+ def test_duplicate_section():
46
+ plain_source = """
47
+ ***definitions***
48
+
49
+ ***definitions***
50
+ """
51
+ with pytest.raises(
52
+ Exception,
53
+ match=re.escape("Syntax error at line 3: Duplicate specification heading (`definitions`)"),
54
+ ):
55
+ plain_file.parse_plain_source(plain_source, {}, [], [], [])
56
+
57
+
58
+ def test_invalid_top_level_element():
59
+ plain_source = """
60
+ ***definitions***
61
+ ```
62
+ code block
63
+ ```
64
+ """
65
+ with pytest.raises(
66
+ Exception,
67
+ match=re.escape("Syntax error at line 2: Invalid source structure (`code block`)"),
68
+ ):
69
+ plain_file.parse_plain_source(plain_source, {}, [], [], [])
70
+
71
+
72
+ def test_plain_file_parser_with_comments(get_test_data_path):
73
+ _, plain_sections, _ = plain_file.plain_file_parser(
74
+ "plain_file_parser_with_comments.plain",
75
+ [get_test_data_path("data/plainfileparser")],
76
+ )
77
+ assert plain_sections == {
78
+ "definitions": [],
79
+ "technical specs": [{"markdown": "- Second non-functional requirement."}],
80
+ "functional specs": [{"markdown": '- Display "hello, world"'}],
81
+ }
82
+
83
+
84
+ def test_plain_file_parser_with_comments_indented(get_test_data_path):
85
+ _, plain_sections, _ = plain_file.plain_file_parser(
86
+ "plain_file_with_comments_indented.plain",
87
+ [get_test_data_path("data/plainfileparser")],
88
+ )
89
+ assert plain_sections == {
90
+ "definitions": [],
91
+ "technical specs": [
92
+ {"markdown": "- First non-functional requirement."},
93
+ {"markdown": "- Second non-functional requirement."},
94
+ ],
95
+ "functional specs": [{"markdown": '- Display "hello, world"'}],
96
+ }
97
+
98
+
99
+ def test_invalid_url_link(get_test_data_path):
100
+ with pytest.raises(Exception, match="Only relative links are allowed."):
101
+ plain_file.plain_file_parser("plain_source_with_url_link.plain", [get_test_data_path("data/plainfile")])
102
+
103
+
104
+ def test_invalid_absolute_link(get_test_data_path):
105
+ with pytest.raises(Exception, match="Only relative links are allowed."):
106
+ plain_file.plain_file_parser(
107
+ "plain_source_with_absolute_link.plain",
108
+ [get_test_data_path("data/plainfile")],
109
+ )
110
+
111
+
112
+ def test_reference_link_parsing(get_test_data_path):
113
+ _, plain_sections, _ = plain_file.plain_file_parser(
114
+ "task_manager_with_reference_links.plain",
115
+ [get_test_data_path("data/plainfile")],
116
+ )
117
+ asserted_resources = [
118
+ "task_list_ui_specification.yaml",
119
+ "add_new_task_modal_specification.yaml",
120
+ ]
121
+ for functional_requirement in plain_sections[plain_spec.FUNCTIONAL_REQUIREMENTS]:
122
+ if "linked_resources" not in functional_requirement:
123
+ continue
124
+
125
+ for resource in functional_requirement["linked_resources"]:
126
+ assert resource["target"] in asserted_resources
127
+ del asserted_resources[asserted_resources.index(resource["target"])]
128
+
129
+ parsed_url = urlparse(resource["target"])
130
+ assert parsed_url.scheme == ""
131
+
132
+ assert not os.path.isabs(resource["target"])
133
+
134
+ assert asserted_resources == []
135
+
136
+ assert "`[resource]task_list_ui_specification.yaml`" in "\n".join(
137
+ [item["markdown"] for item in plain_sections[plain_spec.FUNCTIONAL_REQUIREMENTS]]
138
+ )
139
+ assert "`[resource]add_new_task_modal_specification.yaml`" in "\n".join(
140
+ [item["markdown"] for item in plain_sections[plain_spec.FUNCTIONAL_REQUIREMENTS]]
141
+ )
142
+
143
+ assert plain_sections[plain_spec.FUNCTIONAL_REQUIREMENTS][1]["linked_resources"] == [
144
+ {
145
+ "text": "task_list_ui_specification.yaml",
146
+ "target": "task_list_ui_specification.yaml",
147
+ }
148
+ ]
149
+ assert plain_sections[plain_spec.FUNCTIONAL_REQUIREMENTS][2]["linked_resources"] == [
150
+ {
151
+ "text": "add_new_task_modal_specification.yaml",
152
+ "target": "add_new_task_modal_specification.yaml",
153
+ }
154
+ ]
155
+
156
+
157
+ def test_invalid_specification_order(get_test_data_path):
158
+ with pytest.raises(
159
+ Exception,
160
+ match="Syntax error at line 6: Definitions specification must be the first specification in the section.",
161
+ ):
162
+ plain_file.plain_file_parser("invalid_specification_order.plain", [get_test_data_path("data/plainfile")])
163
+
164
+
165
+ def test_duplicate_specification_heading(get_test_data_path):
166
+ with pytest.raises(
167
+ Exception,
168
+ match=re.escape("Syntax error at line 6: Duplicate specification heading (`definitions`)"),
169
+ ):
170
+ plain_file.plain_file_parser(
171
+ "duplicate_specification_heading.plain",
172
+ [get_test_data_path("data/plainfile")],
173
+ )
174
+
175
+
176
+ def test_missing_non_functional_requirements(get_test_data_path):
177
+ with pytest.raises(
178
+ Exception,
179
+ match="Syntax error: Functional requirement with no non-functional requirements specified.",
180
+ ):
181
+ plain_file.plain_file_parser(
182
+ "missing_non_functional_requirements.plain",
183
+ [get_test_data_path("data/plainfile")],
184
+ )
185
+
186
+
187
+ def test_without_non_functional_requirement(get_test_data_path):
188
+ with pytest.raises(
189
+ Exception,
190
+ match="Syntax error: Functional requirement with no non-functional requirements specified.",
191
+ ):
192
+ plain_file.plain_file_parser(
193
+ "without_non_functional_requirement.plain",
194
+ [get_test_data_path("data/plainfile")],
195
+ )
196
+
197
+
198
+ def test_indented_include_tags():
199
+ plain_source = """# Main
200
+
201
+ ***definitions***
202
+
203
+ - This is a definition.
204
+
205
+ ***technical specs***
206
+ - First non-functional requirement.
207
+ - Second non-functional requirement.
208
+
209
+ > This is a comment
210
+ > This is a with an include tag {% include "template.plain" %}
211
+
212
+ ***functional specs***
213
+ - Implement {% include "implement.plain" %}
214
+ {% include "template.plain" %}
215
+ - Display "hello, world"
216
+ {% include "template.plain" %}
217
+ {% include "template.plain" %}
218
+ - Implement {% include "implement.plain" %}
219
+ """
220
+ loaded_templates = {
221
+ "template.plain": "- This is a functional requirement inside a template.",
222
+ "implement.plain": """something nice and useful
223
+ - the nice thing should be really nice
224
+ - the useful thing should be really useful""",
225
+ }
226
+
227
+ expected_rendered_plain_source = """# Main
228
+
229
+ ***definitions***
230
+
231
+ - This is a definition.
232
+
233
+ ***technical specs***
234
+ - First non-functional requirement.
235
+ - Second non-functional requirement.
236
+
237
+ > This is a comment
238
+ > This is a with an include tag {% include 'template.plain' %}
239
+
240
+ ***functional specs***
241
+ - Implement something nice and useful
242
+ - the nice thing should be really nice
243
+ - the useful thing should be really useful
244
+ - This is a functional requirement inside a template.
245
+ - Display "hello, world"
246
+ - This is a functional requirement inside a template.
247
+ - This is a functional requirement inside a template.
248
+ - Implement something nice and useful
249
+ - the nice thing should be really nice
250
+ - the useful thing should be really useful
251
+ """
252
+ rendered_plain_source = plain_file.render_plain_source(plain_source, loaded_templates, {})
253
+ assert rendered_plain_source == expected_rendered_plain_source
254
+
255
+ def mock_get_source(
256
+ env: Environment,
257
+ template_name: str,
258
+ *,
259
+ context: RenderContext | None = None,
260
+ **kwargs: object,
261
+ ):
262
+ return TemplateSource(
263
+ loaded_templates[template_name],
264
+ template_name,
265
+ lambda: True,
266
+ )
267
+
268
+ with patch(
269
+ "liquid2.builtin.loaders.file_system_loader.FileSystemLoader.get_source",
270
+ side_effect=mock_get_source,
271
+ ):
272
+ plain_source_result, loaded_templates_result = file_utils.get_loaded_templates(["."], plain_source)
273
+ assert plain_source_result == expected_rendered_plain_source
274
+ assert loaded_templates_result == loaded_templates
275
+
276
+
277
+ def test_code_variables(load_test_data, get_test_data_path):
278
+ plain_source = load_test_data("data/templates/code_variables.plain")
279
+ loaded_templates = {
280
+ "implement.plain": load_test_data("data/templates/implement.plain"),
281
+ }
282
+
283
+ code_variables = {}
284
+ rendered_plain_source = plain_file.render_plain_source(plain_source, loaded_templates, code_variables)
285
+ keys = list(code_variables.keys())
286
+
287
+ expected_rendered_plain_source = f"""***definitions***
288
+
289
+ - :concept: is a concept.
290
+
291
+ ***technical specs***
292
+ - First non-functional requirement.
293
+ - Second non-functional requirement.
294
+
295
+ ***functional specs***
296
+ - Implement something nice and useful
297
+ - the nice thing should be really {keys[0]}
298
+ - the useful thing should be really useful
299
+ """
300
+
301
+ assert rendered_plain_source == expected_rendered_plain_source
302
+
303
+ _, plain_source, _ = plain_file.plain_file_parser("code_variables.plain", [get_test_data_path("data/templates")])
304
+ expected_plain_source = {
305
+ "definitions": [{"markdown": "- :concept: is a concept."}],
306
+ "technical specs": [
307
+ {"markdown": "- First non-functional requirement."},
308
+ {"markdown": "- Second non-functional requirement."},
309
+ ],
310
+ "functional specs": [
311
+ {
312
+ "markdown": "- Implement something nice and useful\n - the nice thing should be really {{ variable_name }}\n - the useful thing should be really useful",
313
+ "code_variables": [{"name": "variable_name", "value": "nice"}],
314
+ }
315
+ ],
316
+ }
317
+
318
+ assert plain_source == expected_plain_source
319
+
320
+ plain_source = load_test_data("data/templates/template_include.plain")
321
+ loaded_templates = {
322
+ "header.plain": load_test_data("data/templates/header.plain"),
323
+ "implement_2.plain": load_test_data("data/templates/implement_2.plain"),
324
+ }
325
+
326
+ code_variables = {}
327
+ rendered_plain_source = plain_file.render_plain_source(plain_source, loaded_templates, code_variables)
328
+ keys = list(code_variables.keys())
329
+ expected_rendered_plain_source = f"""***definitions***
330
+
331
+ - :concept: is a concept.
332
+
333
+ ***technical specs***
334
+ - First non-functional requirement {keys[0]}.
335
+ - Second non-functional requirement {keys[1]}.
336
+
337
+ ***functional specs***
338
+ - Implement something nice and useful
339
+ - the nice thing should be really {keys[2]}
340
+ - the useful thing should be really useful
341
+ """
342
+
343
+ assert rendered_plain_source == expected_rendered_plain_source
344
+
345
+ _, plain_source, _ = plain_file.plain_file_parser("template_include.plain", [get_test_data_path("data/templates")])
346
+ expected_plain_source = {
347
+ "definitions": [{"markdown": "- :concept: is a concept."}],
348
+ "technical specs": [
349
+ {
350
+ "markdown": "- First non-functional requirement {{ variable_name_1 }}.",
351
+ "code_variables": [{"name": "variable_name_1", "value": "nice_1"}],
352
+ },
353
+ {
354
+ "markdown": "- Second non-functional requirement {{ variable_name_1 }}.",
355
+ "code_variables": [{"name": "variable_name_1", "value": "nice_2"}],
356
+ },
357
+ ],
358
+ "functional specs": [
359
+ {
360
+ "markdown": "- Implement something nice and useful\n - the nice thing should be really {{ variable_name_1 }}\n - the useful thing should be really useful",
361
+ "code_variables": [{"name": "variable_name_1", "value": "nice"}],
362
+ }
363
+ ],
364
+ }
365
+
366
+ assert plain_source == expected_plain_source
367
+
368
+
369
+ def test_acceptance_tests_block_include_with_trailing_newline_keeps_structure_and_ignores_quote(get_test_data_path):
370
+ """
371
+ Ensures that a block-level include inside ***acceptance tests*** whose template ends
372
+ with a trailing newline does not terminate the list, and that a following '> ...' line
373
+ is treated as a comment and ignored. The parser should not raise a syntax error.
374
+ It's an error we encountered while implementing custom rendering and should break in case of regression.
375
+ """
376
+ plain_file.plain_file_parser("block_level_include.plain", [get_test_data_path("data/templates")])
377
+
378
+
379
+ def test_concept_validation_definitions(get_test_data_path):
380
+ plain_file.plain_file_parser(
381
+ "concept_validation_definition.plain",
382
+ [get_test_data_path("data/plainfileparser")],
383
+ )
384
+
385
+ with pytest.raises(PlainSyntaxError):
386
+ plain_file.plain_file_parser(
387
+ "concept_validation_noconcepts.plain",
388
+ [get_test_data_path("data/plainfileparser")],
389
+ )
390
+
391
+
392
+ def test_concept_validation_usage(get_test_data_path):
393
+ plain_file.plain_file_parser(
394
+ "concept_validation_valid.plain",
395
+ [get_test_data_path("data/plainfileparser")],
396
+ )
397
+
398
+ with pytest.raises(PlainSyntaxError):
399
+ plain_file.plain_file_parser(
400
+ "concept_validation_nondefined.plain",
401
+ [get_test_data_path("data/plainfileparser")],
402
+ )
403
+
404
+ with pytest.raises(PlainSyntaxError):
405
+ plain_file.plain_file_parser(
406
+ "concept_validation_defined_nondefined.plain",
407
+ [get_test_data_path("data/plainfileparser")],
408
+ )
409
+
410
+ with pytest.raises(PlainSyntaxError):
411
+ plain_file.plain_file_parser(
412
+ "concept_validation_defined_nondefined_2.plain",
413
+ [get_test_data_path("data/plainfileparser")],
414
+ )
415
+
416
+
417
+ def test_concept_validation_redefinition(get_test_data_path):
418
+ with pytest.raises(PlainSyntaxError):
419
+ plain_file.plain_file_parser(
420
+ "concept_validation_redefinition.plain",
421
+ [get_test_data_path("data/plainfileparser")],
422
+ )
423
+
424
+
425
+ def test_concept_validation_non_concept_usage(get_test_data_path):
426
+ plain_file.plain_file_parser(
427
+ "concept_validation_nonconcept.plain",
428
+ [get_test_data_path("data/plainfileparser")],
429
+ )
430
+
431
+
432
+ def test_concept_validation_several_concepts(get_test_data_path):
433
+ plain_file.plain_file_parser(
434
+ "concept_validation_several_concepts.plain",
435
+ [get_test_data_path("data/plainfileparser")],
436
+ )
437
+
438
+
439
+ def test_concept_validation_acceptance_tests(get_test_data_path):
440
+ plain_file.plain_file_parser(
441
+ "concept_validation_acceptance_tests.plain",
442
+ [get_test_data_path("data/plainfileparser")],
443
+ )
444
+
445
+ with pytest.raises(PlainSyntaxError):
446
+ plain_file.plain_file_parser(
447
+ "concept_validation_acceptance_tests_nondefined.plain",
448
+ [get_test_data_path("data/plainfileparser")],
449
+ )
450
+
451
+
452
+ def test_required_concepts(get_test_data_path):
453
+ plain_file.plain_file_parser(
454
+ "required_concepts_example.plain",
455
+ [get_test_data_path("data/plainfileparser")],
456
+ )
457
+
458
+ plain_file.plain_file_parser(
459
+ "required_concepts_module.plain",
460
+ [get_test_data_path("data/plainfileparser")],
461
+ )
462
+
463
+ plain_file.plain_file_parser(
464
+ "required_concepts_partial.plain",
465
+ [get_test_data_path("data/plainfileparser")],
466
+ )
467
+
468
+ with pytest.raises(PlainSyntaxError):
469
+ plain_file.plain_file_parser(
470
+ "required_concepts_missing.plain",
471
+ [get_test_data_path("data/plainfileparser")],
472
+ )
473
+
474
+ with pytest.raises(PlainSyntaxError):
475
+ plain_file.plain_file_parser(
476
+ "required_concepts_partial_duplicate.plain",
477
+ [get_test_data_path("data/plainfileparser")],
478
+ )
479
+
480
+
481
+ def test_exported_concepts(get_test_data_path):
482
+ plain_file.plain_file_parser(
483
+ "exported_concepts_example.plain",
484
+ [get_test_data_path("data/plainfileparser")],
485
+ )
486
+
487
+ plain_file.plain_file_parser(
488
+ "exported_concepts_nested_example.plain",
489
+ [get_test_data_path("data/plainfileparser")],
490
+ )
491
+
492
+ with pytest.raises(PlainSyntaxError):
493
+ plain_file.plain_file_parser(
494
+ "exported_concepts_missing_example.plain",
495
+ [get_test_data_path("data/plainfileparser")],
496
+ )
497
+
498
+ with pytest.raises(PlainSyntaxError):
499
+ plain_file.plain_file_parser(
500
+ "exported_concepts_transitive_example.plain",
501
+ [get_test_data_path("data/plainfileparser")],
502
+ )
503
+
504
+
505
+ def test_topological_sort(get_test_data_path):
506
+ _, plain_source, _ = plain_file.plain_file_parser(
507
+ "topological_sort.plain", [get_test_data_path("data/plainfileparser")]
508
+ )
509
+ assert plain_spec.DEFINITIONS in plain_source
510
+ assert plain_source[plain_spec.DEFINITIONS] == [
511
+ {"markdown": "- :Concept1: is a concept."},
512
+ {"markdown": "- :Concept2: is a concept that depends on the :Concept1: concept."},
513
+ {"markdown": "- :Concept3: is a concept that depends on both the :Concept1: and :Concept2: concepts."},
514
+ ]
515
+
516
+ _, plain_source, _ = plain_file.plain_file_parser(
517
+ "topological_sort_not_referenced.plain",
518
+ [get_test_data_path("data/plainfileparser")],
519
+ )
520
+ assert plain_spec.DEFINITIONS in plain_source
521
+ assert plain_source[plain_spec.DEFINITIONS] == [
522
+ {"markdown": "- :Concept1: is a concept."},
523
+ {"markdown": "- :NonReferencedConcept: is a concept."},
524
+ {"markdown": "- :Concept7: is a concept."},
525
+ {"markdown": "- :Concept2: is a concept that depends on the :Concept1: concept."},
526
+ {"markdown": "- :Concept8: is a concept that depends on the :Concept1: concept."},
527
+ {"markdown": "- :Concept5: is a concept that depends on the :Concept1: concept."},
528
+ {"markdown": "- :Concept3: is a concept that depends on both the :Concept1: and :Concept2: concepts."},
529
+ {"markdown": "- :Concept9: is a concept that depends on the :Concept8:."},
530
+ {"markdown": "- :Concept4: is a concept that depends on the :Concept3: concept."},
531
+ {"markdown": "- :Concept6: is a concept that depends on the :Concept1: and :Concept4: concepts."},
532
+ ]
@@ -0,0 +1,67 @@
1
+ import pytest
2
+
3
+ import plain_file
4
+ import plain_spec
5
+
6
+
7
+ def test_get_linked_resources_invalid_input():
8
+ with pytest.raises(ValueError, match="plain_source_tree must be a dictionary."):
9
+ plain_spec.collect_linked_resources([], [], None, True)
10
+
11
+
12
+ def test_get_frids_simple(get_test_data_path):
13
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
14
+
15
+ frids = list(plain_spec.get_frids(plain_source))
16
+
17
+ assert frids == ["1"]
18
+
19
+
20
+ def test_get_first_functional_requirement_simple(get_test_data_path):
21
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
22
+
23
+ assert plain_spec.get_first_frid(plain_source) == "1"
24
+
25
+
26
+ def test_get_next_frid_not_exists(get_test_data_path):
27
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
28
+
29
+ with pytest.raises(Exception, match="Functional requirement 2 does not exist."):
30
+ plain_spec.get_next_frid(plain_source, "2")
31
+
32
+
33
+ def test_get_next_frid_simple(get_test_data_path):
34
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
35
+
36
+ assert plain_spec.get_next_frid(plain_source, "1") is None
37
+
38
+
39
+ def test_get_previous_frid_not_exists(get_test_data_path):
40
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
41
+
42
+ with pytest.raises(Exception, match="Functional requirement 2 does not exist."):
43
+ plain_spec.get_previous_frid(plain_source, "2")
44
+
45
+
46
+ def test_get_previous_frid_no_previous_frid(get_test_data_path):
47
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
48
+
49
+ assert plain_spec.get_previous_frid(plain_source, "1") is None
50
+
51
+
52
+ def test_get_specifications_simple(get_test_data_path):
53
+ _, plain_source, _ = plain_file.plain_file_parser("simple.plain", [get_test_data_path("data/")])
54
+
55
+ with pytest.raises(Exception, match="Functional requirement a does not exist."):
56
+ plain_spec.get_specifications_for_frid(plain_source, "a")
57
+
58
+ frid = plain_spec.get_first_frid(plain_source)
59
+
60
+ specifications, _ = plain_spec.get_specifications_for_frid(plain_source, frid)
61
+
62
+ assert specifications == {
63
+ "definitions": [],
64
+ "technical specs": ["- Simple non-functional requirement"],
65
+ "test specs": [],
66
+ "functional specs": ["- Simple functional requirement"],
67
+ }
tests/test_requires.py ADDED
@@ -0,0 +1,27 @@
1
+ import pytest
2
+
3
+ import plain_file
4
+
5
+
6
+ def test_non_existent_require(get_test_data_path):
7
+ with pytest.raises(Exception, match="Required module not found"):
8
+ plain_file.plain_file_parser("non_existent_require.plain", [get_test_data_path("data/requires")])
9
+
10
+
11
+ def test_independent_requires(get_test_data_path):
12
+ with pytest.raises(Exception, match="There must be a fixed order how required modules are dependent"):
13
+ plain_file.plain_file_parser("independent_requires_main.plain", [get_test_data_path("data/requires")])
14
+
15
+
16
+ def test_diamond_requires(get_test_data_path):
17
+ with pytest.raises(Exception, match="There must be a fixed order how required modules are dependent"):
18
+ plain_file.plain_file_parser("diamond_requires_main.plain", [get_test_data_path("data/requires")])
19
+
20
+
21
+ def test_circular_requires(get_test_data_path):
22
+ with pytest.raises(Exception, match="Circular required module detected"):
23
+ plain_file.plain_file_parser("circular_requires_main.plain", [get_test_data_path("data/requires")])
24
+
25
+
26
+ def test_normal_requires(get_test_data_path):
27
+ plain_file.plain_file_parser("normal_requires_main.plain", [get_test_data_path("data/requires")])
@@ -1,42 +0,0 @@
1
- code_complexity
2
- codeplain
3
- codeplain_REST_api
4
- codeplain_constants
5
- codeplain_local_api
6
- codeplain_models
7
- codeplain_types
8
- codeplain_utils
9
- concept_utils
10
- config
11
- content_extractor
12
- event_bus
13
- file_utils
14
- git_utils
15
- hash_key
16
- llm
17
- llm_exceptions
18
- llm_handler
19
- llm_selector
20
- memory_management
21
- module_renderer
22
- plain2code
23
- plain2code_arguments
24
- plain2code_console
25
- plain2code_events
26
- plain2code_exceptions
27
- plain2code_logger
28
- plain2code_nodes
29
- plain2code_read_config
30
- plain2code_state
31
- plain2code_tui
32
- plain2code_utils
33
- plain_file
34
- plain_modules
35
- plain_spec
36
- render_cache
37
- render_machine
38
- spinner
39
- standard_template_library
40
- system_config
41
- tui
42
- tui_components