927f56704fc30b2164a34fbc6ebcd74c1b40c2ec
[tinc] / test / integration / cmd_misc.py
1 #!/usr/bin/env python3
2
3 """Test miscellaneous commands."""
4 import os
5 import typing as T
6
7 from testlib import check, cmd
8 from testlib.log import log
9 from testlib.proc import Tinc, Script
10 from testlib.test import Test
11
12 SUBNETS_BAR = ("10.20.30.40", "fe80::")
13
14
15 def configure_nodes(ctx: Test) -> T.Tuple[Tinc, Tinc]:
16     """Create and configure nodes."""
17
18     log.info("initialize nodes")
19     foo, bar = ctx.node(init=True), ctx.node(init=True)
20
21     log.info("configure and start nodes")
22     foo.cmd("add", "Subnet", "1.2.3.4")
23     foo.add_script(Script.TINC_UP)
24     foo.add_script(bar.script_up)
25     foo.start()
26
27     for sub in SUBNETS_BAR:
28         bar.cmd("add", "Subnet", sub)
29     bar.start()
30
31     log.info("connect nodes")
32     cmd.exchange(foo, bar)
33     foo.cmd("add", "ConnectTo", bar.name)
34     foo.cmd("retry")
35     foo[bar.script_up].wait()
36
37     return foo, bar
38
39
40 def test_version(foo: Tinc) -> None:
41     """Test command 'version'."""
42
43     log.info("test command 'version' with redundant arguments")
44     _, err = foo.cmd("version", "foo", code=1)
45     check.is_in("Too many arguments", err)
46
47     log.info("test command 'version'")
48     out, _ = foo.cmd("version")
49     check.has_prefix(out, "tinc version ")
50
51
52 def test_help(foo: Tinc) -> None:
53     """Test command 'help'."""
54
55     log.info("test command 'help'")
56     out, _ = foo.cmd("help")
57     check.is_in("Valid options are", out)
58
59     out, _ = foo.cmd("help", "foobar")
60     check.is_in("Valid options are", out)
61
62
63 def test_info(foo: Tinc, bar: Tinc) -> None:
64     """Test command 'info'."""
65
66     log.info("info invalid arguments")
67     _, err = foo.cmd("info", code=1)
68     check.is_in("Invalid number of arguments", err)
69
70     log.info("info unknown node")
71     _, err = foo.cmd("info", "foobar", code=1)
72     check.is_in("Unknown node foobar", err)
73
74     log.info("info own node")
75     out, _ = foo.cmd("info", foo.name)
76     check.is_in("can reach itself", out)
77
78     log.info("info peer node")
79     out, _ = foo.cmd("info", bar.name)
80     check.is_in(bar.name, out)
81     for sub in SUBNETS_BAR:
82         check.is_in(sub, out)
83
84     log.info("info unknown subnet")
85     for sub in "1.1.1.1", "fe82:42::":
86         _, err = foo.cmd("info", sub, code=1)
87         check.is_in("Unknown address", err)
88
89     log.info("info own valid subnet")
90     out, _ = foo.cmd("info", "1.2.3.4")
91     check.is_in("Subnet: 1.2.3.4", out)
92     check.is_in(f"Owner:  {foo}", out)
93
94     for sub in SUBNETS_BAR:
95         log.info("info peer's valid subnet %s", sub)
96         out, _ = foo.cmd("info", sub)
97         check.is_in(f"Subnet: {sub}", out)
98         check.is_in(f"Owner:  {bar}", out)
99
100
101 def test_pid(foo: Tinc) -> None:
102     """Test command 'pid'."""
103
104     log.info("test pid with too many arguments")
105     _, err = foo.cmd("pid", "foo", code=1)
106     check.is_in("Too many arguments", err)
107
108     log.info("test pid without arguments")
109     out, _ = foo.cmd("pid")
110     check.equals(foo.pid, int(out.strip()))
111
112
113 def test_debug(foo: Tinc) -> None:
114     """Test command 'debug'."""
115
116     for args in ("debug",), ("debug", "1", "2"):
117         _, err = foo.cmd(*args, code=1)
118         check.is_in("Invalid number of arguments", err)
119
120     _, err = foo.cmd("debug", "5")
121     check.is_in("new level 5", err)
122
123
124 def test_log(foo: Tinc) -> None:
125     """Test command 'log'."""
126
127     log.info("test with too many arguments")
128     _, err = foo.cmd("log", "foo", "bar", code=1)
129     check.is_in("Too many arguments", err)
130
131     log.info("test correct call")
132     log_client = foo.tinc("log")
133     foo.cmd("set", "LogLevel", "10")
134     foo.cmd("reload")
135
136     foo.add_script(Script.TINC_DOWN)
137     foo.cmd("stop")
138     foo[Script.TINC_DOWN].wait()
139
140     out, _ = log_client.communicate()
141     check.true(out)
142
143
144 def test_restart(foo: Tinc) -> None:
145     """Test command 'restart'."""
146
147     log.info("restart without arguments")
148     foo.cmd("restart")
149     foo[Script.TINC_UP].wait()
150
151     log.info("restart with an argument")
152     foo.cmd("restart", "-d3")
153     foo[Script.TINC_UP].wait()
154
155     # Checking the error message is unreliable since
156     # it's provided by getopt() and differs from OS to OS.
157     log.info("restart with invalid options")
158     foo.cmd("restart", "--fake-invalid-option", code=1)
159
160     log.info("restart with invalid arguments")
161     _, err = foo.cmd("restart", "bad-incorrect-argument", code=1)
162     check.is_in("unrecognized argument", err)
163
164
165 def test_shell(foo: Tinc) -> None:
166     """Test shell."""
167
168     log.info("indented comments are not ignored")
169     _, err = foo.cmd(stdin=" # ", code=1)
170     check.is_in("Unknown command", err)
171
172     log.info("comments are ignored")
173     _, err = foo.cmd(stdin="# this_will_fail unless comments are ignored")
174     assert "this_will_fail" not in err
175
176     log.info("inline comments are treated as arguments")
177     _, err = foo.cmd(stdin="version # inline comments are not ignored", code=1)
178     check.is_in("Too many arguments", err)
179
180     log.info("check exit commands")
181     for command in "exit", "quit":
182         out, _ = foo.cmd(stdin=command)
183         check.blank(out)
184
185
186 def run_tests(ctx: Test) -> None:
187     """Run tests."""
188
189     foo, bar = configure_nodes(ctx)
190     test_shell(foo)
191     test_version(foo)
192     test_help(foo)
193     test_info(foo, bar)
194     test_pid(foo)
195     test_debug(foo)
196     test_log(foo)
197
198     # Too unstable on Windows because of how it works with services (impossible to
199     # start the service if it has been marked for deletion, but not yet deleted).
200     # Since lots of things can prevent service removal (like opened task manager or
201     # services.msc) the `restart` command is inherently unreliable.
202     if os.name != "nt":
203         test_restart(foo)
204
205
206 with Test("run tests") as context:
207     run_tests(context)