Passed
Pull Request — rhel7-branch (#72)
by Matěj
55s
created

test_rule_handling.rule_data()   A

Complexity

Conditions 1

Size

Total Lines 3
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
cc 1
nop 0
1
import pytest
2
3
import mock
4
5
from org_fedora_oscap import rule_handling, common
6
7
8
@pytest.fixture()
9
def part_rules():
10
    rules = rule_handling.PartRules()
11
    rules.ensure_mount_point("/tmp")
12
    return rules
13
14
15
# simple tests, shouldn't raise exceptions
16
def test_part_rules_getitem(part_rules):
17
    part_rules["/tmp"]
18
19
20
def test_part_rules_setitem(part_rules):
21
    rule = rule_handling.PartRule("/var/log")
22
    part_rules["/var/log"] = rule
23
24
25
def test_part_rules_len(part_rules):
26
    assert len(part_rules) == 1
27
28
29
def test_part_rules_contains(part_rules):
30
    assert "/tmp" in part_rules
31
32
33
def test_part_rules_delitem(part_rules):
34
    del(part_rules["/tmp"])
35
    assert "/tmp" not in part_rules
36
37
38
@pytest.fixture()
39
def rule_data():
40
    return rule_handling.RuleData()
41
42
43
def test_rule_data_artificial(rule_data):
44
    rule_data.new_rule("  part /tmp --mountoptions=nodev,noauto")
45
    rule_data.new_rule("part /var/log  ")
46
    rule_data.new_rule(" passwd   --minlen=14 ")
47
    rule_data.new_rule("package --add=iptables")
48
    rule_data.new_rule(" package --add=firewalld --remove=telnet")
49
    rule_data.new_rule("package --remove=rlogin --remove=sshd")
50
    rule_data.new_rule("bootloader --passwd")
51
52
    # both partitions should appear in rule_data._part_rules
53
    assert "/tmp" in rule_data._part_rules
54
    assert "/var/log" in rule_data._part_rules
55
56
    # mount options should be parsed
57
    assert "nodev" in rule_data._part_rules["/tmp"]._mount_options
58
    assert "noauto" in rule_data._part_rules["/tmp"]._mount_options
59
60
    # no mount options for /var/log
61
    assert not rule_data._part_rules["/var/log"]._mount_options
62
63
    # minimal password length should be parsed and stored correctly
64
    assert rule_data._passwd_rules._minlen == 14
65
66
    # packages should be parsed correctly
67
    assert "iptables" in rule_data._package_rules._add_pkgs
68
    assert "firewalld" in rule_data._package_rules._add_pkgs
69
    assert "telnet" in rule_data._package_rules._remove_pkgs
70
    assert "rlogin" in rule_data._package_rules._remove_pkgs
71
    assert "sshd" in rule_data._package_rules._remove_pkgs
72
73
    # bootloader should require password
74
    assert rule_data._bootloader_rules._require_password
75
76
77
def test_rule_data_quoted_opt_values(rule_data):
78
    rule_data.new_rule('part /tmp --mountoptions="nodev,noauto"')
79
80
    assert "nodev" in rule_data._part_rules["/tmp"]._mount_options
81
    assert "noauto" in rule_data._part_rules["/tmp"]._mount_options
82
    assert '"' not in rule_data._part_rules["/tmp"]._mount_options
83
84
85
def test_rule_data_real_output(rule_data):
86
    output = """
87
    part /tmp
88
89
    part /tmp --mountoptions=nodev
90
    """
91
    for line in output.splitlines():
92
        rule_data.new_rule(line)
93
94
    assert "/tmp" in rule_data._part_rules
95
    assert "nodev" in rule_data._part_rules["/tmp"]._mount_options
96
97
    # should be stripped and merged
98
    assert str(rule_data._part_rules) == "part /tmp --mountoptions=nodev"
99
100
101
@pytest.fixture()
102
def ksdata_mock():
103
    return mock.Mock()
104
105
106
@pytest.fixture()
107
def storage_mock():
108
    return mock.Mock()
109
110
111
def test_evaluation_existing_part_must_exist_rules(
112
        rule_data, ksdata_mock, storage_mock):
113
    rules = [
114
        "part /tmp",
115
        "part /",
116
    ]
117
    for rule in rules:
118
        rule_data.new_rule(rule)
119
120
    tmp_part_mock = mock.Mock()
121
    tmp_part_mock.format.options = "defaults"
122
    root_part_mock = mock.Mock()
123
    root_part_mock.format.options = "defaults"
124
125
    storage_mock.mountpoints = {
126
        "/tmp": tmp_part_mock,
127
        "/": root_part_mock,
128
    }
129
130
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
131
132
    # partitions exist --> no errors, warnings or additional info
133
    assert not messages
134
135
    # no additional mount options specified
136
    assert tmp_part_mock.format.options == "defaults"
137
    assert root_part_mock.format.options == "defaults"
138
139
140
def test_evaluation_nonexisting_part_must_exist(rule_data, ksdata_mock, storage_mock):
141
    rules = [
142
        "part /tmp",
143
        "part /",
144
    ]
145
    for rule in rules:
146
        rule_data.new_rule(rule)
147
148
    tmp_part_mock = mock.Mock()
149
    tmp_part_mock.format.options = "defaults"
150
151
    storage_mock.mountpoints = {
152
        "/tmp": tmp_part_mock,
153
    }
154
155
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
156
157
    # / mount point missing --> one error
158
    assert len(messages) == 1
159
    assert messages[0].type == common.MESSAGE_TYPE_FATAL
160
161
    # error has to mention the mount point
162
    assert "/" in messages[0].text
163
164
165
def get_partition_mocks(mount_options):
166
    tmp_part_mock = mock.Mock()
167
    tmp_part_mock.format.options = mount_options["/tmp"]
168
    root_part_mock = mock.Mock()
169
    root_part_mock.format.options = mount_options["/"]
170
171
    partition_mocks = {
172
        "/tmp": tmp_part_mock,
173
        "/": root_part_mock,
174
    }
175
    return partition_mocks
176
177
178
def set_mount_options_of_actual_mount_points(
179
        storage_mock, mount_options, actual_mountpoints):
180
    storage_mock.mountpoints = {}
181
    for mountpoint, value in mount_options.items():
182
        if mountpoint in actual_mountpoints:
183
            storage_mock.mountpoints[mountpoint] = value
184
185
186
def get_messages_for_partition_rules(
187
        rule_data, ksdata_mock, storage_mock,
188
        rules,
189
        messages_evaluation_count=1,
190
        actual_mountpoints=("/tmp", "/"),
191
        mount_options=None,
192
        report_only=False,
193
        ):
194
195
    assert len(rules) == 2, \
196
        "We need rules for temp partition and root."
197
198
    if mount_options is None:
199
        mount_options = {
200
            "/": "defaults",
201
            "/tmp": "defaults",
202
        }
203
204
    for rule in rules:
205
        rule_data.new_rule(rule)
206
207
    mount_options = get_partition_mocks(mount_options)
208
209
    set_mount_options_of_actual_mount_points(storage_mock, mount_options, actual_mountpoints)
210
211
    messages = []
212
    for _ in range(messages_evaluation_count):
213
        messages = rule_data.eval_rules(ksdata_mock, storage_mock, report_only)
214
215
    return messages
216
217
218
def evaluation_add_mount_options(
219
        rule_data, ksdata_mock, storage_mock,
220
        messages_evaluation_count):
221
    rules = [
222
        "part /tmp --mountoptions=defaults,nodev",
223
        "part / --mountoptions=noauto",
224
    ]
225
226
    messages = get_messages_for_partition_rules(
227
        rule_data, ksdata_mock, storage_mock,
228
        rules, messages_evaluation_count)
229
230
    # two mount options added --> two info messages
231
    assert len(messages) == 2
232
    assert all(message.type == common.MESSAGE_TYPE_INFO for message in messages)
233
234
    # newly added mount options should be mentioned in the messages
235
    # together with their mount points
236
    nodev_found = False
237
    noauto_found = False
238
239
    for message in messages:
240
        if "'nodev'" in message.text:
241
            assert "/tmp" in message.text
242
            nodev_found = True
243
        elif "'noauto'" in message.text:
244
            assert "/" in message.text
245
            noauto_found = True
246
247
    assert all([nodev_found, noauto_found])
248
    assert storage_mock.mountpoints["/tmp"].format.options == "defaults,nodev"
249
    assert storage_mock.mountpoints["/"].format.options == "defaults,noauto"
250
251
252
def test_evaluation_add_mount_options(rule_data, ksdata_mock, storage_mock):
253
    evaluation_add_mount_options(rule_data, ksdata_mock, storage_mock, 1)
254
255
256
def test_evaluation_add_mount_options_no_duplicates(rule_data, ksdata_mock, storage_mock):
257
    evaluation_add_mount_options(rule_data, ksdata_mock, storage_mock, 2)
258
259
260
def test_evaluation_add_mount_options_report_only(rule_data, ksdata_mock, storage_mock):
261
    rules = [
262
        "part /tmp --mountoptions=nodev",
263
        "part / --mountoptions=noauto",
264
    ]
265
    messages = get_messages_for_partition_rules(
266
        rule_data, ksdata_mock, storage_mock,
267
        rules, 1, report_only=True)
268
269
    # two mount options added --> two info messages
270
    assert len(messages) == 2
271
    assert messages[0].type == common.MESSAGE_TYPE_INFO
272
    assert messages[1].type == common.MESSAGE_TYPE_INFO
273
274
    # newly added mount options should be mentioned in the messages
275
    # together with their mount points
276
    nodev_found = False
277
    noauto_found = False
278
279
    for message in messages:
280
        if "'nodev'" in message.text:
281
            assert "/tmp" in message.text
282
            nodev_found = True
283
        elif "'noauto'" in message.text:
284
            assert "/" in message.text
285
            noauto_found = True
286
287
    assert all([nodev_found, noauto_found])
288
289
    # no changes should be made
290
    assert storage_mock.mountpoints["/tmp"].format.options == "defaults"
291
    assert storage_mock.mountpoints["/"].format.options == "defaults"
292
293
294
def test_evaluation_add_mount_option_prefix(rule_data, ksdata_mock, storage_mock):
295
    rules = [
296
        "part /tmp --mountoptions=nodev",
297
        "part / --mountoptions=noauto",
298
    ]
299
    mount_options = {
300
        "/": "defaults",
301
        "/tmp": "defaults,nodevice",
302
    }
303
    messages = get_messages_for_partition_rules(
304
        rule_data, ksdata_mock, storage_mock,
305
        rules, mount_options=mount_options)
306
307
    # two mount options added (even though it is a prefix of another one)
308
    #   --> two info messages
309
    assert len(messages) == 2
310
    assert messages[0].type == common.MESSAGE_TYPE_INFO
311
    assert messages[1].type == common.MESSAGE_TYPE_INFO
312
313
    # the option should be added even though it is a prefix of another one
314
    assert storage_mock.mountpoints["/tmp"].format.options == "defaults,nodevice,nodev"
315
316
317
def test_evaluation_add_mount_options_nonexisting_part(rule_data, ksdata_mock, storage_mock):
318
    rules = [
319
        "part /tmp --mountoptions=nodev",
320
        "part / --mountoptions=noauto",
321
    ]
322
    messages = get_messages_for_partition_rules(
323
        rule_data, ksdata_mock, storage_mock,
324
        rules, actual_mountpoints=["/"])
325
326
    # one mount option added, one mount point missing (mount options
327
    # cannot be added) --> one info, one error
328
    assert len(messages) == 2
329
    assert any(message.type == common.MESSAGE_TYPE_INFO for message in messages)
330
    assert any(message.type == common.MESSAGE_TYPE_FATAL for message in messages)
331
332
    # the info message should report mount options added to the existing
333
    # mount point, the error message shoud contain the missing mount point
334
    # and not the mount option
335
    for message in messages:
336
        if message.type == common.MESSAGE_TYPE_INFO:
337
            assert "/" in message.text
338
            assert "'noauto'" in message.text
339
        elif message.type == common.MESSAGE_TYPE_FATAL:
340
            assert "/tmp" in message.text
341
            assert "'nodev'" not in message.text
342
343
344
def test_evaluation_passwd_minlen_no_passwd(rule_data, ksdata_mock, storage_mock):
345
    evaluation_passwd_minlen_no_passwd(rule_data, ksdata_mock, storage_mock, 8, (10, 11))
346
    evaluation_passwd_minlen_no_passwd(rule_data, ksdata_mock, storage_mock, 10, (8, 11))
347
    evaluation_passwd_minlen_no_passwd(rule_data, ksdata_mock, storage_mock, 11, (8, 10))
348
349
350
def evaluation_passwd_minlen_no_passwd(
351
        rule_data, ksdata_mock, storage_mock, min_password_length, check_against=tuple()):
352
    rule_data.new_rule("passwd --minlen={0}".format(min_password_length))
353
354
    ksdata_mock.rootpw.password = ""
355
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
356
357
    # minimal password length required --> one warning
358
    assert len(messages) == 1
359
    assert messages[0].type == common.MESSAGE_TYPE_WARNING
360
361
    # warning has to mention the length
362
    assert str(min_password_length) in messages[0].text
363
364
    for not_wanted in check_against:
365
        assert str(not_wanted) not in messages[0].text
366
367
368
class passwordTestData(object):
369
    def __init__(self, rule_data, ksdata_mock, storage_mock):
370
        self.password = None
371
        self.isCrypted = False
372
373
        self.rule_data = rule_data
374
        self.ksdata_mock = ksdata_mock
375
        self.storage_mock = storage_mock
376
377
    def set_rule(self, rule_string):
378
        self.rule_data.new_rule(rule_string)
379
380
    def get_messages(self, report_only=False):
381
        self.ksdata_mock.rootpw.password = self.password
382
        self.ksdata_mock.rootpw.isCrypted = self.isCrypted
383
        return self.rule_data.eval_rules(
384
            self.ksdata_mock, self.storage_mock, report_only=report_only)
385
386
387
@pytest.fixture()
388
def password_data(rule_data, ksdata_mock, storage_mock):
389
    return passwordTestData(rule_data, ksdata_mock, storage_mock)
390
391
392
def test_evaluation_passwd_minlen_short_passwd(password_data):
393
    password_data.set_rule("passwd --minlen=8")
394
    password_data.password = "aaaa"
395
396
    messages = password_data.get_messages()
397
398
    # minimal password length greater than actual length --> one warning
399
    assert len(messages) == 1
400
    assert messages[0].type == common.MESSAGE_TYPE_FATAL
401
402
    # warning has to mention the length
403
    assert "8" in messages[0].text
404
405
    # warning should mention that something is wrong with the old password
406
    assert "is" in messages[0].text
407
408
    # doing changes --> password should not be cleared
409
    assert password_data.ksdata_mock.rootpw.password == "aaaa"
410
411
412
def test_evaluation_passwd_minlen_short_passwd_report_only(password_data):
413
    password_data.set_rule("passwd --minlen=8")
414
    password_data.password = "aaaa"
415
416
    messages = password_data.get_messages(report_only=True)
417
418
    # minimal password length greater than actual length --> one warning
419
    assert len(messages) == 1
420
    assert messages[0].type == common.MESSAGE_TYPE_FATAL
421
422
    # warning has to mention the length
423
    assert "8" in messages[0].text
424
425
    # report only --> password shouldn't be cleared
426
    assert password_data.ksdata_mock.rootpw.password == "aaaa"
427
428
429
def test_evaluation_passwd_minlen_crypted_passwd(password_data):
430
    password_data.set_rule("passwd --minlen=8")
431
432
    password_data.password = "aaaa"
433
    password_data.isCrypted = True
434
435
    messages = password_data.get_messages()
436
437
    # minimal password length greater than actual length --> one warning
438
    assert len(messages) == 1
439
    assert messages[0].type == common.MESSAGE_TYPE_WARNING
440
441
    # warning has to mention that the password cannot be checked
442
    assert "cannot check" in messages[0].text
443
444
445
def test_evaluation_passwd_minlen_good_passwd(password_data):
446
    password_data.set_rule("passwd --minlen=8")
447
448
    password_data.password = "aaaaaaaaaaaaaaaaa"
449
450
    messages = password_data.get_messages()
451
452
    # minimal password length less than actual length --> no warning
453
    assert not messages
454
455
456
def test_evaluation_passwd_minlen_report_only_not_ignored(password_data):
457
    password_data.set_rule("passwd --minlen=8")
458
459
    password_data.password = "aaaaaaaaaaaaaaaaa"
460
461
    messages = password_data.get_messages()
462
463
    # Mock pw_policy returned by anaconda.pwpolicy.get_policy()
464
    pw_policy_mock = mock.Mock()
465
    pw_policy_mock.minlen = 6
466
    pw_policy_mock.strict = False
467
    password_data.ksdata_mock.anaconda.pwpolicy.get_policy.return_value = pw_policy_mock
468
469
    # call eval_rules with report_only=False
470
    # should set password minimal length to 8
471
    messages = password_data.get_messages()
472
473
    # Password Policy changed --> no warnings
474
    assert not messages
475
    assert password_data.rule_data._passwd_rules._orig_minlen == 6
476
    assert not password_data.rule_data._passwd_rules._orig_strict
477
    assert pw_policy_mock.minlen == 8
478
    assert pw_policy_mock.strict
479
    assert password_data.rule_data._passwd_rules._minlen == 8
480
481
    # call of eval_rules with report_only=True
482
    # should not change anything
483
    messages = password_data.get_messages(report_only=True)
484
    # Password Policy stayed the same --> no warnings
485
    assert not messages
486
487
    assert password_data.rule_data._passwd_rules._orig_minlen == 6
488
    assert not password_data.rule_data._passwd_rules._orig_strict
489
    assert pw_policy_mock.minlen == 8
490
    assert pw_policy_mock.strict
491
    assert password_data.rule_data._passwd_rules._minlen == 8
492
493
494
def _occurences_not_seen_in_strings(seeked, strings):
495
    found = set(seeked)
496
    for string in strings:
497
        for might_have_seen in seeked:
498
            if might_have_seen in string:
499
                found.add(string)
500
                break
501
    return set(seeked).difference(found)
502
503
504
def _quoted_keywords_not_seen_in_messages(keywords, messages):
505
    return _occurences_not_seen_in_strings(
506
        {"'{}'".format(kw) for kw in keywords},
507
        [m.text for m in messages],
508
    )
509
510
511
def test_evaluation_package_rules(rule_data, ksdata_mock, storage_mock):
512
    rule_data.new_rule("package --add=firewalld --remove=telnet --add=iptables --add=vim")
513
514
    ksdata_mock.packages.packageList = ["vim"]
515
    ksdata_mock.packages.excludedList = []
516
517
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
518
519
    # one info message for each (really) added/removed package
520
    assert len(messages) == 3
521
    assert all(message.type == common.MESSAGE_TYPE_INFO for message in messages)
522
523
    # all packages should appear in the messages
524
    not_seen = _quoted_keywords_not_seen_in_messages(
525
        {"firewalld", "telnet", "iptables"},
526
        messages,
527
    )
528
529
    assert not not_seen
530
    assert set(ksdata_mock.packages.packageList) == {"firewalld", "iptables", "vim"}
531
    assert set(ksdata_mock.packages.excludedList) == {"telnet"}
532
533
534
def test_evaluation_package_rules_report_only(rule_data, ksdata_mock, storage_mock):
535
    rule_data.new_rule("package --add=firewalld --remove=telnet --add=iptables")
536
537
    ksdata_mock.packages.packageList = []
538
    ksdata_mock.packages.excludedList = []
539
540
    messages = rule_data.eval_rules(ksdata_mock, storage_mock, report_only=True)
541
542
    # one info message for each added/removed package
543
    assert len(messages) == 3
544
    assert all(message.type == common.MESSAGE_TYPE_INFO for message in messages)
545
546
    not_seen = _quoted_keywords_not_seen_in_messages(
547
        {"firewalld", "telnet", "iptables"},
548
        messages,
549
    )
550
551
    assert not not_seen
552
553
    # report_only --> no packages should be added or excluded
554
    assert not ksdata_mock.packages.packageList
555
    assert not ksdata_mock.packages.excludedList
556
557
558
def test_evaluation_bootloader_passwd_not_set(rule_data, ksdata_mock, storage_mock):
559
    rule_data.new_rule("bootloader --passwd")
560
561
    storage_mock.bootloader.password = None
562
563
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
564
565
    # bootloader password not set --> one warning
566
    assert len(messages) == 1
567
    assert messages[0].type == common.MESSAGE_TYPE_WARNING
568
569
570
def test_evaluation_bootloader_passwd_set(rule_data, ksdata_mock, storage_mock):
571
    rule_data.new_rule("bootloader --passwd")
572
573
    storage_mock.bootloader.password = "aaaaa"
574
575
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
576
577
    # bootloader password set --> no warnings
578
    assert messages == []
579
580
581
def test_evaluation_various_rules(rule_data, ksdata_mock, storage_mock):
582
    for rule in ["part /tmp", "part /", "passwd --minlen=14",
583
                 "package --add=firewalld", ]:
584
        rule_data.new_rule(rule)
585
586
    storage_mock.mountpoints = dict()
587
    ksdata_mock.packages.packageList = []
588
    ksdata_mock.packages.excludedList = []
589
590
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
591
592
    # four rules, all fail --> four messages
593
    assert len(messages) == 4
594
595
596
def test_revert_mount_options_nonexistent(rule_data, ksdata_mock, storage_mock):
597
    rule_data.new_rule("part /tmp --mountoptions=nodev")
598
    storage_mock.mountpoints = dict()
599
600
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
601
602
    # mount point doesn't exist -> one message, nothing done
603
    assert len(messages) == 1
604
    assert storage_mock.mountpoints == dict()
605
606
    # mount point doesn't exist -> shouldn't do anything
607
    rule_data.revert_changes(ksdata_mock, storage_mock)
608
    assert storage_mock.mountpoints == dict()
609
610
611
def test_revert_mount_options(rule_data, ksdata_mock, storage_mock):
612
    rule_data.new_rule("part /tmp --mountoptions=nodev")
613
    storage_mock.mountpoints = dict()
614
    storage_mock.mountpoints["/tmp"] = mock.Mock()
615
    storage_mock.mountpoints["/tmp"].format.options = "defaults"
616
617
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
618
619
    # mount option added --> one message
620
    assert len(messages) == 1
621
622
    # "nodev" option should be added
623
    assert storage_mock.mountpoints["/tmp"].format.options, "defaults == nodev"
624
625
    rule_data.revert_changes(ksdata_mock, storage_mock)
626
627
    # should be reverted to the original value
628
    assert storage_mock.mountpoints["/tmp"].format.options == "defaults"
629
630
    # another cycle of the same #
631
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
632
633
    # mount option added --> one message
634
    assert len(messages) == 1
635
636
    # "nodev" option should be added
637
    assert storage_mock.mountpoints["/tmp"].format.options, "defaults == nodev"
638
639
    rule_data.revert_changes(ksdata_mock, storage_mock)
640
641
    # should be reverted to the original value
642
    assert storage_mock.mountpoints["/tmp"].format.options == "defaults"
643
644
645
def test_revert_password_policy_changes(rule_data, ksdata_mock, storage_mock):
646
    # FIXME: Add password policy changes to this test. It only checks
647
    # password length right now outside of policy changes.
648
    rule_data.new_rule("passwd --minlen=8")
649
650
    ksdata_mock.rootpw.password = "aaaa"
651
    ksdata_mock.rootpw.isCrypted = False
652
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
653
654
    # password error --> one message
655
    assert len(messages) == 1
656
    assert ksdata_mock.rootpw.password == "aaaa"
657
    assert ksdata_mock.rootpw.seen
658
659
    rule_data.revert_changes(ksdata_mock, storage_mock)
660
661
    # with long enough password this time #
662
    ksdata_mock.rootpw.password = "aaaaaaaaaaaaa"
663
664
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
665
666
    # long enough password
667
    # entered --> no message
668
    assert messages == []
669
670
671
def test_revert_package_rules(rule_data, ksdata_mock, storage_mock):
672
    rule_data.new_rule("package --add=firewalld --remove=telnet --add=iptables --add=vim")
673
674
    ksdata_mock.packages.packageList = ["vim"]
675
    ksdata_mock.packages.excludedList = []
676
677
    # run twice --> nothing should be different in the second run
678
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
679
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
680
681
    # one info message for each added/removed package
682
    assert len(messages) == 3
683
684
    rule_data.revert_changes(ksdata_mock, storage_mock)
685
686
    # (only) added and excluded packages should have been removed from the
687
    # list
688
    assert ksdata_mock.packages.packageList == ["vim"]
689
    assert ksdata_mock.packages.excludedList == []
690
691
    # now do the same again #
692
    messages = rule_data.eval_rules(ksdata_mock, storage_mock)
693
694
    # one info message for each added/removed package
695
    assert len(messages) == 3
696
697
    rule_data.revert_changes(ksdata_mock, storage_mock)
698
699
    # (only) added and excluded packages should have been removed from the
700
    # list
701
    assert ksdata_mock.packages.packageList == ["vim"]
702
    assert ksdata_mock.packages.excludedList == []
703