Add tests for import/export errors
[tinc] / test / integration / cmd_import.py
1 #!/usr/bin/env python3
2
3 """Test import/export error conditions."""
4
5 import os
6
7 from testlib import check, cmd, util
8 from testlib.log import log
9 from testlib.proc import Tinc
10 from testlib.test import Test
11
12 SEPARATOR = f"#{'-' * 63}#"
13
14 MULTI_HOST = f"""
15 Name = node0
16 Address = sun
17 {SEPARATOR}
18 Name = node1
19 Address = moon
20 {SEPARATOR}
21 """.strip()
22
23 MAX_PATH = 255 if os.name == "nt" else os.pathconf("/", "PC_PATH_MAX")
24 LONG_NAME = MAX_PATH * "x"
25
26
27 def init(ctx: Test) -> Tinc:
28     """Initialize a node."""
29
30     node = ctx.node()
31     stdin = f"""
32         init {node}
33         set Port 0
34         set Address localhost
35         set DeviceType dummy
36         set AutoConnect no
37     """
38     node.cmd(stdin=stdin)
39     return node
40
41
42 def test_import(foo: Tinc) -> None:
43     """Run tests for command 'import'."""
44
45     _, err = foo.cmd("import", "foo", code=1)
46     check.is_in("Too many arguments", err)
47
48     _, err = foo.cmd("import", code=1)
49     check.is_in("No host configuration files imported", err)
50
51     for prefix in "fred", "Name fred", "name = fred", "name=fred":
52         log.info("testing prefix '%s'", prefix)
53         _, err = foo.cmd("import", stdin=prefix, code=1)
54         check.is_in("Junk at the beginning", err)
55
56     _, err = foo.cmd("import", stdin="Name = !@#", code=1)
57     check.is_in("Invalid Name in input", err)
58
59     _, err = foo.cmd("import", stdin=f"Name = {LONG_NAME}", code=1)
60     check.is_in("Filename too long", err)
61
62     log.info("make sure no address for imported nodes is present")
63     for node in "node0", "node1":
64         foo.cmd("get", f"{node}.Address", code=1)
65
66     _, err = foo.cmd("import", stdin=MULTI_HOST)
67     check.is_in("Imported 2 host configuration files", err)
68
69     log.info("check imported nodes addresses")
70     check.equals("sun", cmd.get(foo, "node0.Address"))
71     check.equals("moon", cmd.get(foo, "node1.Address"))
72
73     _, err = foo.cmd("import", stdin="Name = node0", code=1)
74     check.is_in("node0 already exists", err)
75
76     if os.name != "nt":
77         log.info("import to inaccessible hosts subdirectory")
78         os.chmod(foo.sub("hosts"), 0)
79         _, err = foo.cmd("import", stdin="Name = vinny", code=1)
80         check.is_in("Error creating configuration", err)
81
82
83 def test_export(foo: Tinc) -> None:
84     """Run tests for command 'export'."""
85
86     _, err = foo.cmd("export", "foo", code=1)
87     check.is_in("Too many arguments", err)
88
89     os.remove(foo.sub(f"hosts/{foo}"))
90     _, err = foo.cmd("export", code=1)
91     check.is_in("Could not open configuration", err)
92
93     util.write_text(foo.sub("tinc.conf"), "")
94     _, err = foo.cmd("export", code=1)
95     check.is_in("Could not find Name", err)
96
97     os.remove(foo.sub("tinc.conf"))
98     _, err = foo.cmd("export", code=1)
99     check.is_in("Could not open", err)
100
101
102 def test_exchange(foo: Tinc) -> None:
103     """Run tests for command 'exchange'."""
104
105     log.info("make sure exchange does not import if export fails")
106     util.write_text(foo.sub("tinc.conf"), "")
107     host_foo = "Name = foo\nAddress = 1.1.1.1"
108     _, err = foo.cmd("exchange", stdin=host_foo, code=1)
109     assert "Imported" not in err
110
111
112 def test_exchange_all(foo: Tinc) -> None:
113     """Run tests for command 'exchange'."""
114
115     log.info("make sure exchange-all does not import if export fails")
116     host_bar = foo.sub("hosts/bar")
117     util.write_text(host_bar, "")
118     os.chmod(host_bar, 0)
119     host_foo = "Name = foo\nAddress = 1.1.1.1"
120     _, err = foo.cmd("exchange-all", stdin=host_foo, code=1)
121     assert "Imported" not in err
122
123
124 def test_export_all(foo: Tinc) -> None:
125     """Run tests for command 'export-all'."""
126
127     _, err = foo.cmd("export-all", "foo", code=1)
128     check.is_in("Too many arguments", err)
129
130     host_foo = foo.sub("hosts/foo")
131     util.write_text(host_foo, "Name = foo")
132     os.chmod(host_foo, 0)
133
134     host_bar = foo.sub("hosts/bar")
135     util.write_text(host_bar, "Host = bar\nAddress = 1.1.1.1")
136
137     host_invalid = foo.sub("hosts/xi-Eb-Vx-k3")
138     util.write_text(host_invalid, "Host = invalid")
139
140     out, err = foo.cmd("export-all", code=1)
141     check.is_in("Could not open configuration", err)
142
143     log.info("checking bad node name in export")
144     assert "xi-Eb-Vx-k3" not in out
145
146     for want in "Host = bar", "Address = 1.1.1.1", SEPARATOR:
147         check.is_in(want, out)
148
149     log.info("verify that separators are used on separate lines")
150     lines = out.splitlines()
151     separators = list(filter(lambda line: line == SEPARATOR, lines))
152     if len(separators) != 2:
153         log.info("unexpected number of separators: %s", lines)
154         assert False
155
156     if os.name != "nt":
157         os.chmod(foo.sub("hosts"), 0)
158         _, err = foo.cmd("export-all", code=1)
159         check.is_in("Could not open host configuration", err)
160
161
162 with Test("test 'import' command") as context:
163     test_import(init(context))
164
165 with Test("test 'export' command") as context:
166     test_export(init(context))
167
168 with Test("test 'exchange' command") as context:
169     test_exchange(init(context))
170
171 if os.name != "nt":
172     with Test("test 'exchange-all' command") as context:
173         test_exchange_all(init(context))
174
175     with Test("test 'export-all' command") as context:
176         test_export_all(init(context))