Coverage for tests/test_running.py: 100.00%

58 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-17 17:18 +0200

1"""Tests about running duties.""" 

2 

3from __future__ import annotations 

4 

5from typing import NoReturn 

6from unittest.mock import NonCallableMock 

7 

8import pytest 

9 

10from duty.collection import Collection, Duty 

11from duty.decorator import duty as decorate 

12from duty.exceptions import DutyFailure 

13 

14INTERRUPT_CODE = 130 

15 

16 

17def test_run_duty() -> None: 

18 """Run a duty.""" 

19 duty = Duty("name", "description", lambda ctx: 1) 

20 assert duty.run() is None # type: ignore[func-returns-value] 

21 assert duty(duty.context) is None # type: ignore[func-returns-value] 

22 

23 

24def test_run_pre_post_duties_lambdas() -> None: 

25 """Run pre- and post- duties as lambdas.""" 

26 pre_calls = [] 

27 post_calls = [] 

28 

29 duty = Duty( 

30 "name", 

31 "description", 

32 lambda ctx: None, 

33 pre=[lambda ctx: pre_calls.append(True)], 

34 post=[lambda ctx: post_calls.append(True)], 

35 ) 

36 

37 duty.run() 

38 

39 assert pre_calls[0] is True 

40 assert post_calls[0] is True 

41 

42 

43def test_run_pre_post_duties_instances() -> None: 

44 """Run pre- and post- duties as duties.""" 

45 pre_calls = [] 

46 post_calls = [] 

47 

48 pre_duty = Duty("pre", "", lambda ctx: pre_calls.append(True)) 

49 post_duty = Duty("post", "", lambda ctx: post_calls.append(True)) 

50 

51 duty = Duty( 

52 name="name", 

53 description="description", 

54 function=lambda ctx: None, 

55 pre=[pre_duty], 

56 post=[post_duty], 

57 ) 

58 

59 duty.run() 

60 

61 assert pre_calls[0] is True 

62 assert post_calls[0] is True 

63 

64 

65def test_run_pre_post_duties_refs() -> None: 

66 """Run pre- and post- duties as duties references.""" 

67 pre_calls = [] 

68 post_calls = [] 

69 

70 collection = Collection() 

71 collection.add(decorate(lambda ctx: pre_calls.append(True), name="pre")) # type: ignore[call-overload] 

72 collection.add(decorate(lambda ctx: post_calls.append(True), name="post")) # type: ignore[call-overload] 

73 

74 duty = Duty("name", "description", lambda ctx: None, collection=collection, pre=["pre"], post=["post"]) 

75 duty.run() 

76 

77 assert pre_calls[0] is True 

78 assert post_calls[0] is True 

79 

80 

81def test_dont_run_other_pre_post_duties() -> None: 

82 """Don't run other types of pre- and post- duties.""" 

83 pre_duty = NonCallableMock() 

84 post_duty = NonCallableMock() 

85 

86 duty = Duty("name", "description", lambda ctx: 0, pre=[pre_duty], post=[post_duty]) 

87 duty.run() 

88 

89 assert not pre_duty.called 

90 assert not post_duty.called 

91 

92 

93def test_code_when_keyboard_interrupt() -> None: 

94 """Return a code 130 on keyboard interruption.""" 

95 

96 def interrupt() -> NoReturn: 

97 raise KeyboardInterrupt 

98 

99 with pytest.raises(DutyFailure) as excinfo: 

100 Duty("name", "description", lambda ctx: ctx.run(interrupt)).run() 

101 assert excinfo.value.code == INTERRUPT_CODE 

102 

103 

104def test_dont_raise_duty_failure() -> None: 

105 """Don't raise a duty failure on success.""" 

106 duty = Duty("n", "d", lambda ctx: ctx.run(lambda: 0)) 

107 assert not duty.run() # type: ignore[func-returns-value] 

108 

109 

110def test_cant_find_duty_without_collection() -> None: 

111 """Check that we can't find a duty with its name without a collection.""" 

112 duty = decorate(lambda ctx: None, name="duty1", post=["duty2"]) # type: ignore[call-overload] 

113 with pytest.raises(RuntimeError): 

114 duty.run()