2

I have this ruby library which establishes telnet or ssh connections to remote devices. All seems to work. The testing framework (which works for telnet) defines device simulators which bind to a port, and the test connection bind to that same port. The whole payload exchange then goes through that channel, the authentication step as well.

With SSH, I'm having problems adapting this, because apparently, mimicking the authentication step of the protocol is not that easy. Since we use net-ssh, the client expects all the authentication steps (ssh version, alghoritm negotiation, key exchange,...) and I don't know the formats of the messages in detail (I don't find a lot of examples anywhere).

What I'm expecting is a way of actually signaling the client that channel can be unencrypted (for tests, not an issue) and I want to receive the prompts (password-based, I don't need the authorized_keys process for the test environment).

Is there an easy implementation / an existing implementation for such an ordeal?

ChuckE
  • 5,610
  • 4
  • 31
  • 59
  • 1
    Could you add a bit more detail about the architecture of your system? Are you developing an SSH server? Or are you making a custom SSH client? – John Bachir Mar 15 '14 at 18:03
  • What kind of protocol are you planning to run across SSH? If it is remotely http-like running it as a Sinatra application might be an option. – Patru Mar 15 '14 at 23:50
  • I'm making a custom SSH/Telnet Client to a specific vendor firmware. My custom dummy server only processes the expected command and generates some output that mimicks the vendor remote device output for such a command. That part is sort of figured out (it works for the telnet). Just the connection has to be established, and the custom client uses Net::SSH as the session layer for SSH. – ChuckE Mar 16 '14 at 03:07
  • @ChuckE, check out my answer, let me know if I missed the ballpark, but it seems the good general direction – bbozo Mar 19 '14 at 09:05

1 Answers1

0

Yes,

for unit testing mock Net::SSH, for integration testing do a test of full stack (Net::SSH + your app + your infrastructure)

Unit tests

For unit tests, replace Net::SSH with a Struct, OpenStruct or Plain Old Ruby Object of same name which implements methods you are using. This mock class doesn't need to connect to an actual SSH server, it just needs to quack like its connected to one, and it can store its input and output into instance variables so you can assert on them.

This way you can test if app sends correct username/pass/ip/executes and parses commands properly/etc instead of testing if Net::SSH is capable of SSH connections (this would be responsibility of Net::SSH gem).

And the tests will be much faster then connecting to the real thing.

This is the basic idea behind the VCR gem - don't make the server quack, make the client quack.

Once you do this, if you make a nice API for it, you could submit it to the good people on https://github.com/net-ssh/net-ssh/, it's a useful thing to have and it seems to be missing at the moment.

Integration tests

Keep integration tests very basic.

You might want to check if Net::SSH + your app + your server infrastructure is compatible, by having one additional hard-coded integration test which connects to an actual server in your infrastructure and establishes a connection and executes a command.

Upgrading SSH daemon or hardening your infrastructure may break function of your app - for example if you introduce selinux, disable weak ciphers, switch to a funny OS like Solaris or Net::SSH people forget to introduce a workaround for a bug for a certain architecture you might be using (solaris SSH cypher issues, ubuntu 10 known_hosts issues, ...).

bbozo
  • 7,075
  • 3
  • 30
  • 56
  • Sorry for not commenting before. I've been following this direction. The only culprit I find is having to create a Mock transport object which abstracts the various features of the internal Net::Telnet or Net::SSH client (waiting for output, timing out, waiting a few more seconds for more output). This is the hardest, because your client expects those, and you can of have to reproduce them. It's like rewriting Net::Telnet without sockets, just message passing. I don't know how much of a veredict I can get in 2 days, though. – ChuckE Mar 19 '14 at 09:35
  • @ChuckE, if you need that kind of detail in your tests, you'll probably have best long term results with a mock client, rather then a mock server. Once you have a mock client, it should be pretty easy to abstract it so you can use it for Net::Telnet as well without setting up dummy servers in your tests so that's a good thing I guess – bbozo Mar 20 '14 at 12:14
  • I agree with you. Just creating a mock client for these types of clients is a bit "overheadish"... Which I'm trying to fight against, but you know, preparing myself for all additional options of sending a remote command to a client, telnet or ssh (waiting for more output, timeout, match against prompt, mock exceptions, etc...) feels almost like rewriting the clients WITHOUT the sockets. – ChuckE Mar 21 '14 at 08:49
  • 1
    It would be nice if the libraries provided such mock implementations, though :) – ChuckE Mar 21 '14 at 08:49