Skip to content

Commit 4373b4a

Browse files
committed
Add ssh integration test
1 parent 5d295e1 commit 4373b4a

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

test/examples/ssh_usage_test.exs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
defmodule MyApp.SSH do
2+
@moduledoc false
3+
4+
# A minimal SSH client — the kind of module a library consumer would write
5+
# and want to test against TestServer.SSH.
6+
def run(host, port, command, opts \\ []) do
7+
user = Keyword.get(opts, :user, "deploy")
8+
password = Keyword.get(opts, :password)
9+
10+
connect_opts =
11+
[
12+
user: String.to_charlist(user),
13+
silently_accept_hosts: true,
14+
user_interaction: false,
15+
save_accepted_host: false
16+
]
17+
|> then(fn o ->
18+
if password, do: Keyword.put(o, :password, String.to_charlist(password)), else: o
19+
end)
20+
21+
with {:ok, conn} <- :ssh.connect(String.to_charlist(host), port, connect_opts) do
22+
{:ok, ch} = :ssh_connection.session_channel(conn, :infinity)
23+
:success = :ssh_connection.exec(conn, ch, String.to_charlist(command), :infinity)
24+
result = collect(ch, {0, "", ""})
25+
:ssh.close(conn)
26+
result
27+
end
28+
end
29+
30+
defp collect(ch, {code, out, err}) do
31+
receive do
32+
{:ssh_cm, _, {:data, ^ch, 0, data}} -> collect(ch, {code, out <> data, err})
33+
{:ssh_cm, _, {:data, ^ch, 1, data}} -> collect(ch, {code, out, err <> data})
34+
{:ssh_cm, _, {:exit_status, ^ch, c}} -> collect(ch, {c, out, err})
35+
{:ssh_cm, _, {:closed, ^ch}} -> {:ok, code, out, err}
36+
after
37+
5_000 -> {:error, :timeout}
38+
end
39+
end
40+
end
41+
42+
defmodule MyApp.SSHUsageTest do
43+
use ExUnit.Case
44+
45+
# Demonstrates the intended public API of TestServer.SSH from a consumer's perspective.
46+
47+
test "runs a remote command and returns output" do
48+
TestServer.SSH.exec(to: fn "deploy", state -> {:reply, {0, "deployed v1.0\n", ""}, state} end)
49+
{host, port} = TestServer.SSH.address()
50+
assert {:ok, 0, "deployed v1.0\n", ""} = MyApp.SSH.run(host, port, "deploy")
51+
end
52+
53+
test "propagates non-zero exit codes" do
54+
TestServer.SSH.exec(to: fn _, state -> {:reply, {1, "", "permission denied\n"}, state} end)
55+
{host, port} = TestServer.SSH.address()
56+
assert {:ok, 1, "", "permission denied\n"} = MyApp.SSH.run(host, port, "restricted")
57+
end
58+
59+
test "sequential deploys consume handlers in FIFO order" do
60+
TestServer.SSH.exec(to: fn _, state -> {:reply, {0, "deploy 1\n", ""}, state} end)
61+
TestServer.SSH.exec(to: fn _, state -> {:reply, {0, "deploy 2\n", ""}, state} end)
62+
{host, port} = TestServer.SSH.address()
63+
assert {:ok, 0, "deploy 1\n", ""} = MyApp.SSH.run(host, port, "deploy")
64+
assert {:ok, 0, "deploy 2\n", ""} = MyApp.SSH.run(host, port, "deploy")
65+
end
66+
67+
test "accepts client with correct password" do
68+
{:ok, instance} = TestServer.SSH.start(credentials: [{"deploy", "hunter2"}])
69+
TestServer.SSH.exec(instance, to: fn _, state -> {:reply, {0, "ok\n", ""}, state} end)
70+
{host, port} = TestServer.SSH.address(instance)
71+
72+
assert {:ok, 0, "ok\n", ""} =
73+
MyApp.SSH.run(host, port, "deploy", user: "deploy", password: "hunter2")
74+
end
75+
76+
test "rejects client with wrong password" do
77+
{:ok, _instance} = TestServer.SSH.start(credentials: [{"deploy", "hunter2"}])
78+
{host, port} = TestServer.SSH.address()
79+
assert {:error, _} = MyApp.SSH.run(host, port, "anything", user: "deploy", password: "wrong")
80+
end
81+
end

0 commit comments

Comments
 (0)