StrictDoc Documentation
tests/unit/strictdoc/core/transforms/test_update_requirement.py
Source file coverage
Path:
tests/unit/strictdoc/core/transforms/test_update_requirement.py
Lines:
520
Non-empty lines:
459
Non-empty lines covered with requirements:
459 / 459 (100.0%)
Functions:
7
Functions covered by requirements:
7 / 7 (100.0%)
1
"""
2
@relation(SDOC-SRS-55, scope=file)
3
"""
4
 
5
from strictdoc.backend.sdoc.constants import SDocMarkup
6
from strictdoc.backend.sdoc.document_reference import DocumentReference
7
from strictdoc.backend.sdoc.models.grammar_element import (
8
    GrammarElementRelationChild,
9
    GrammarElementRelationParent,
10
)
11
from strictdoc.core.document_tree import DocumentTree
12
from strictdoc.core.project_config import ProjectConfig
13
from strictdoc.core.traceability_index import TraceabilityIndex
14
from strictdoc.core.traceability_index_builder import TraceabilityIndexBuilder
15
from strictdoc.core.transforms.update_requirement import (
16
    CreateOrUpdateNodeCommand,
17
    UpdateNodeInfo,
18
)
19
from strictdoc.export.html.form_objects.requirement_form_object import (
20
    RequirementFormObject,
21
    RequirementReferenceFormField,
22
)
23
from strictdoc.helpers.mid import MID
24
from tests.unit.helpers.document_builder import DocumentBuilder
25
 
26
MARKDOWN_TABLE = (
27
    "|   | Action | Verify |\n"
28
    "|---|--------|--------|\n"
29
    "| 1 | Do the thing | |\n"
30
    "| 2 | Do another thing | |\n"
31
)
32
 
33
 
34
def test_01_single_document_add_first_parent_relation_with_no_role():
35
    document_builder = DocumentBuilder()
36
    requirement1 = document_builder.add_requirement("REQ-001")
37
    requirement2 = document_builder.add_requirement("REQ-002")
38
    assert len(requirement2.relations) == 0
39
 
40
    document_1 = document_builder.build()
41
 
42
    file_tree = []
43
    document_list = [document_1]
44
    map_docs_by_paths = {}
45
    document_tree = DocumentTree(
46
        file_tree=file_tree,
47
        document_list=document_list,
48
        map_docs_by_paths=map_docs_by_paths,
49
        map_docs_by_rel_paths={},
50
        map_grammars_by_filenames={},
51
    )
52
    traceability_index: TraceabilityIndex = (
53
        TraceabilityIndexBuilder.create_from_document_tree(
54
            document_tree, project_config=document_builder.project_config
55
        )
56
    )
57
    traceability_index.document_tree = document_tree
58
    assert traceability_index.get_parent_requirements(requirement1) == []
59
 
60
    requirement2_parents = list(
61
        traceability_index.get_parent_relations_with_roles(requirement2)
62
    )
63
    assert requirement2_parents == []
64
 
65
    form_object: RequirementFormObject = (
66
        RequirementFormObject.create_from_requirement(
67
            requirement=requirement2,
68
            revision=0,
69
            context_document_mid=document_1.reserved_mid,
70
        )
71
    )
72
    form_object.reference_fields.append(
73
        RequirementReferenceFormField(
74
            field_mid=MID.create(),
75
            field_type=RequirementReferenceFormField.FieldType.PARENT,
76
            field_value="REQ-001",
77
            field_role=None,
78
        )
79
    )
80
    update_command = CreateOrUpdateNodeCommand(
81
        form_object=form_object,
82
        node_info=UpdateNodeInfo(requirement2),
83
        traceability_index=traceability_index,
84
        project_config=ProjectConfig.default_config(),
85
    )
86
    update_command.perform()
87
 
88
    assert len(requirement2.relations) == 1
89
    requirement2_parents = list(
90
        traceability_index.get_parent_relations_with_roles(requirement2)
91
    )
92
    assert requirement2_parents == [
93
        (requirement1, None),
94
    ]
95
 
96
 
97
def test_02_single_document_add_second_parent_relation_with_role():
98
    document_builder = DocumentBuilder()
99
    requirement1 = document_builder.add_requirement("REQ-001")
100
    requirement2 = document_builder.add_requirement("REQ-002")
101
    document_builder.add_requirement_relation(
102
        relation_type="Parent",
103
        source_requirement_id="REQ-002",
104
        target_requirement_id="REQ-001",
105
        role="Refines",
106
    )
107
    assert len(requirement2.relations) == 1
108
 
109
    document_1 = document_builder.build()
110
    requirement_grammar_element = document_1.grammar.elements_by_type[
111
        "REQUIREMENT"
112
    ]
113
    requirement_grammar_element.relations.append(
114
        GrammarElementRelationParent(
115
            requirement_grammar_element, "Parent", "Refines"
116
        )
117
    )
118
    file_tree = []
119
    document_list = [document_1]
120
    map_docs_by_paths = {}
121
    document_tree = DocumentTree(
122
        file_tree=file_tree,
123
        document_list=document_list,
124
        map_docs_by_paths=map_docs_by_paths,
125
        map_docs_by_rel_paths={},
126
        map_grammars_by_filenames={},
127
    )
128
    traceability_index: TraceabilityIndex = (
129
        TraceabilityIndexBuilder.create_from_document_tree(
130
            document_tree, project_config=document_builder.project_config
131
        )
132
    )
133
    traceability_index.document_tree = document_tree
134
    assert traceability_index.get_parent_requirements(requirement1) == []
135
 
136
    requirement2_parents = list(
137
        traceability_index.get_parent_relations_with_roles(requirement2)
138
    )
139
    assert requirement2_parents == [(requirement1, "Refines")]
140
 
141
    form_object: RequirementFormObject = (
142
        RequirementFormObject.create_from_requirement(
143
            requirement=requirement2,
144
            revision=0,
145
            context_document_mid=document_1.reserved_mid,
146
        )
147
    )
148
    update_command = CreateOrUpdateNodeCommand(
149
        form_object=form_object,
150
        node_info=UpdateNodeInfo(requirement2),
151
        traceability_index=traceability_index,
152
        project_config=ProjectConfig.default_config(),
153
    )
154
    update_command.perform()
155
    assert len(requirement2.relations) == 1
156
 
157
    form_object.reference_fields.append(
158
        RequirementReferenceFormField(
159
            field_mid=MID.create(),
160
            field_type=RequirementReferenceFormField.FieldType.PARENT,
161
            field_value="REQ-001",
162
            field_role="Implements",
163
        )
164
    )
165
    update_command.perform()
166
 
167
    assert len(requirement2.relations) == 2
168
    requirement2_parents = list(
169
        traceability_index.get_parent_relations_with_roles(requirement2)
170
    )
171
    assert requirement2_parents == [
172
        (requirement1, "Refines"),
173
        (requirement1, "Implements"),
174
    ]
175
 
176
 
177
def test_20_single_document_add_second_child_relation_with_role():
178
    document_builder = DocumentBuilder()
179
    requirement1 = document_builder.add_requirement("REQ-001")
180
    requirement2 = document_builder.add_requirement("REQ-002")
181
    document_builder.add_requirement_relation(
182
        relation_type="Child",
183
        source_requirement_id="REQ-002",
184
        target_requirement_id="REQ-001",
185
        role="IsRefinedBy",
186
    )
187
 
188
    assert len(requirement2.relations) == 1
189
 
190
    document_1 = document_builder.build()
191
    requirement_grammar_element = document_1.grammar.elements_by_type[
192
        "REQUIREMENT"
193
    ]
194
    requirement_grammar_element.relations.append(
195
        GrammarElementRelationChild(
196
            requirement_grammar_element, "Child", "IsRefinedBy"
197
        )
198
    )
199
    file_tree = []
200
    document_list = [document_1]
201
    map_docs_by_paths = {}
202
    document_tree = DocumentTree(
203
        file_tree=file_tree,
204
        document_list=document_list,
205
        map_docs_by_paths=map_docs_by_paths,
206
        map_docs_by_rel_paths={},
207
        map_grammars_by_filenames={},
208
    )
209
    traceability_index: TraceabilityIndex = (
210
        TraceabilityIndexBuilder.create_from_document_tree(
211
            document_tree, project_config=document_builder.project_config
212
        )
213
    )
214
    traceability_index.document_tree = document_tree
215
    requirement1_parents = list(
216
        traceability_index.get_parent_relations_with_roles(requirement1)
217
    )
218
    assert requirement1_parents == [(requirement2, "IsRefinedBy")]
219
 
220
    requirement2_children = list(
221
        traceability_index.get_child_relations_with_roles(requirement2)
222
    )
223
    assert requirement2_children == [(requirement1, "IsRefinedBy")]
224
 
225
    form_object: RequirementFormObject = (
226
        RequirementFormObject.create_from_requirement(
227
            requirement=requirement2,
228
            revision=0,
229
            context_document_mid=document_1.reserved_mid,
230
        )
231
    )
232
    form_object.reference_fields.append(
233
        RequirementReferenceFormField(
234
            field_mid=MID.create(),
235
            field_type=RequirementReferenceFormField.FieldType.CHILD,
236
            field_value="REQ-001",
237
            field_role="IsImplementedBy",
238
        )
239
    )
240
    update_command = CreateOrUpdateNodeCommand(
241
        form_object=form_object,
242
        node_info=UpdateNodeInfo(requirement2),
243
        traceability_index=traceability_index,
244
        project_config=ProjectConfig.default_config(),
245
    )
246
    update_command.perform()
247
 
248
    requirement1_parents = list(
249
        traceability_index.get_parent_relations_with_roles(requirement1)
250
    )
251
    assert requirement1_parents == [
252
        (requirement2, "IsRefinedBy"),
253
        (requirement2, "IsImplementedBy"),
254
    ]
255
 
256
    requirement2_children = list(
257
        traceability_index.get_child_relations_with_roles(requirement2)
258
    )
259
    assert requirement2_children == [
260
        (requirement1, "IsRefinedBy"),
261
        (requirement1, "IsImplementedBy"),
262
    ]
263
 
264
 
265
def test_25_single_document_remove_child_relation():
266
    document_builder = DocumentBuilder()
267
    requirement1 = document_builder.add_requirement("REQ-001")
268
    requirement2 = document_builder.add_requirement("REQ-002")
269
    document_builder.add_requirement_relation(
270
        relation_type="Child",
271
        source_requirement_id="REQ-002",
272
        target_requirement_id="REQ-001",
273
        role="IsRefinedBy",
274
    )
275
 
276
    assert len(requirement2.relations) == 1
277
 
278
    document_1 = document_builder.build()
279
    requirement_grammar_element = document_1.grammar.elements_by_type[
280
        "REQUIREMENT"
281
    ]
282
    requirement_grammar_element.relations.append(
283
        GrammarElementRelationChild(
284
            requirement_grammar_element, "Child", "IsRefinedBy"
285
        )
286
    )
287
 
288
    file_tree = []
289
    document_list = [document_1]
290
    map_docs_by_paths = {}
291
    document_tree = DocumentTree(
292
        file_tree=file_tree,
293
        document_list=document_list,
294
        map_docs_by_paths=map_docs_by_paths,
295
        map_docs_by_rel_paths={},
296
        map_grammars_by_filenames={},
297
    )
298
    traceability_index: TraceabilityIndex = (
299
        TraceabilityIndexBuilder.create_from_document_tree(
300
            document_tree, project_config=document_builder.project_config
301
        )
302
    )
303
    traceability_index.document_tree = document_tree
304
    requirement1_parents = list(
305
        traceability_index.get_parent_relations_with_roles(requirement1)
306
    )
307
    assert requirement1_parents == [(requirement2, "IsRefinedBy")]
308
 
309
    requirement2_children = list(
310
        traceability_index.get_child_relations_with_roles(requirement2)
311
    )
312
    assert requirement2_children == [(requirement1, "IsRefinedBy")]
313
 
314
    form_object: RequirementFormObject = (
315
        RequirementFormObject.create_from_requirement(
316
            requirement=requirement2,
317
            revision=0,
318
            context_document_mid=document_1.reserved_mid,
319
        )
320
    )
321
 
322
    # Form object has no relations.
323
    form_object.reference_fields.clear()
324
 
325
    update_command = CreateOrUpdateNodeCommand(
326
        form_object=form_object,
327
        node_info=UpdateNodeInfo(requirement2),
328
        traceability_index=traceability_index,
329
        project_config=ProjectConfig.default_config(),
330
    )
331
    update_command.perform()
332
 
333
    requirement1_parents = list(
334
        traceability_index.get_parent_relations_with_roles(requirement1)
335
    )
336
    assert requirement1_parents == []
337
 
338
    requirement2_children = list(
339
        traceability_index.get_child_relations_with_roles(requirement2)
340
    )
341
    assert requirement2_children == []
342
 
343
 
344
def test_26_two_documents_remove_child_relation():
345
    # Document 1
346
    document_builder = DocumentBuilder("DOC-1")
347
    requirement1 = document_builder.add_requirement("REQ-001")
348
    document_1 = document_builder.build()
349
 
350
    # Document 2
351
    document_builder = DocumentBuilder("DOC-2")
352
    requirement2 = document_builder.add_requirement("REQ-002")
353
    document_builder.add_requirement_relation(
354
        relation_type="Child",
355
        source_requirement_id="REQ-002",
356
        target_requirement_id="REQ-001",
357
        role="IsRefinedBy",
358
    )
359
    assert len(requirement2.relations) == 1
360
    document_2 = document_builder.build()
361
 
362
    document_2_requirement_grammar_element = (
363
        document_2.grammar.elements_by_type["REQUIREMENT"]
364
    )
365
    document_2_requirement_grammar_element.relations.append(
366
        GrammarElementRelationChild(
367
            document_2_requirement_grammar_element, "Child", "IsRefinedBy"
368
        )
369
    )
370
    assert requirement1.get_document() != requirement2.get_document()
371
 
372
    file_tree = []
373
    document_list = [document_1, document_2]
374
    map_docs_by_paths = {}
375
    document_tree = DocumentTree(
376
        file_tree=file_tree,
377
        document_list=document_list,
378
        map_docs_by_paths=map_docs_by_paths,
379
        map_docs_by_rel_paths={},
380
        map_grammars_by_filenames={},
381
    )
382
    traceability_index: TraceabilityIndex = (
383
        TraceabilityIndexBuilder.create_from_document_tree(
384
            document_tree,
385
            project_config=ProjectConfig.default_config(),
386
        )
387
    )
388
    traceability_index.document_tree = document_tree
389
 
390
    requirement2_children = list(
391
        traceability_index.get_child_relations_with_roles(requirement2)
392
    )
393
    assert requirement2_children == [(requirement1, "IsRefinedBy")]
394
 
395
    form_object: RequirementFormObject = (
396
        RequirementFormObject.create_from_requirement(
397
            requirement=requirement2,
398
            revision=0,
399
            context_document_mid=document_2.reserved_mid,
400
        )
401
    )
402
 
403
    # Form object has no relations.
404
    form_object.reference_fields.clear()
405
 
406
    update_command = CreateOrUpdateNodeCommand(
407
        form_object=form_object,
408
        node_info=UpdateNodeInfo(requirement2),
409
        traceability_index=traceability_index,
410
        project_config=ProjectConfig.default_config(),
411
    )
412
    update_command.perform()
413
 
414
    requirement1_parents = list(
415
        traceability_index.get_parent_relations_with_roles(requirement1)
416
    )
417
    assert requirement1_parents == []
418
 
419
    requirement2_children = list(
420
        traceability_index.get_child_relations_with_roles(requirement2)
421
    )
422
    assert requirement2_children == []
423
 
424
 
425
def test_30_markdown_document_updates_field_with_markdown_table():
426
    document_builder = DocumentBuilder()
427
    requirement = document_builder.add_requirement("REQ-001")
428
 
429
    document_1 = document_builder.build()
430
    document_1.config.markup = SDocMarkup.MARKDOWN
431
 
432
    document_tree = DocumentTree(
433
        file_tree=[],
434
        document_list=[document_1],
435
        map_docs_by_paths={},
436
        map_docs_by_rel_paths={},
437
        map_grammars_by_filenames={},
438
    )
439
    traceability_index: TraceabilityIndex = (
440
        TraceabilityIndexBuilder.create_from_document_tree(
441
            document_tree, project_config=document_builder.project_config
442
        )
443
    )
444
    traceability_index.document_tree = document_tree
445
 
446
    form_object: RequirementFormObject = (
447
        RequirementFormObject.create_from_requirement(
448
            requirement=requirement,
449
            revision=0,
450
            context_document_mid=document_1.reserved_mid,
451
        )
452
    )
453
    for field in form_object.fields["STATEMENT"]:
454
        field.field_value = MARKDOWN_TABLE
455
 
456
    update_command = CreateOrUpdateNodeCommand(
457
        form_object=form_object,
458
        node_info=UpdateNodeInfo(requirement),
459
        traceability_index=traceability_index,
460
        project_config=ProjectConfig.default_config(),
461
    )
462
    result = update_command.perform()
463
 
464
    assert result is not None
465
    assert not form_object.any_errors()
466
    assert requirement.reserved_statement == MARKDOWN_TABLE
467
 
468
 
469
def test_31_included_markdown_document_uses_own_markup_when_updated_from_parent():
470
    parent_document_builder = DocumentBuilder(uid="PARENT")
471
    parent_document = parent_document_builder.build()
472
    parent_document.config.markup = SDocMarkup.RST
473
 
474
    included_document_builder = DocumentBuilder(uid="INCLUDED")
475
    requirement = included_document_builder.add_requirement("REQ-001")
476
    included_document = included_document_builder.build()
477
    included_document.config.markup = SDocMarkup.MARKDOWN
478
 
479
    included_document.ng_including_document_reference = DocumentReference()
480
    included_document.ng_including_document_reference.set_document(
481
        parent_document
482
    )
483
    parent_document.included_documents.append(included_document)
484
 
485
    document_tree = DocumentTree(
486
        file_tree=[],
487
        document_list=[parent_document, included_document],
488
        map_docs_by_paths={},
489
        map_docs_by_rel_paths={},
490
        map_grammars_by_filenames={},
491
    )
492
    traceability_index: TraceabilityIndex = (
493
        TraceabilityIndexBuilder.create_from_document_tree(
494
            document_tree,
495
            project_config=included_document_builder.project_config,
496
        )
497
    )
498
    traceability_index.document_tree = document_tree
499
 
500
    form_object: RequirementFormObject = (
501
        RequirementFormObject.create_from_requirement(
502
            requirement=requirement,
503
            revision=0,
504
            context_document_mid=parent_document.reserved_mid,
505
        )
506
    )
507
    for field in form_object.fields["STATEMENT"]:
508
        field.field_value = MARKDOWN_TABLE
509
 
510
    update_command = CreateOrUpdateNodeCommand(
511
        form_object=form_object,
512
        node_info=UpdateNodeInfo(requirement),
513
        traceability_index=traceability_index,
514
        project_config=ProjectConfig.default_config(),
515
    )
516
    result = update_command.perform()
517
 
518
    assert result is not None
519
    assert not form_object.any_errors()
520
    assert requirement.reserved_statement == MARKDOWN_TABLE