mirror of
https://github.com/nshttpd/mikrotik-exporter.git
synced 2024-12-13 14:38:44 +01:00
451 lines
8.8 KiB
Go
451 lines
8.8 KiB
Go
package routeros_test
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"testing"
|
|
|
|
"gopkg.in/routeros.v2"
|
|
"gopkg.in/routeros.v2/proto"
|
|
)
|
|
|
|
func TestLogin(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/login @ []")
|
|
s.writeSentence(t, "!done", "=ret=abc123")
|
|
s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
|
|
s.writeSentence(t, "!done")
|
|
}()
|
|
|
|
err := c.Login("userTest", "passTest")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLoginIncorrect(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/login @ []")
|
|
s.writeSentence(t, "!done", "=ret=abc123")
|
|
s.readSentence(t, "/login @ [{`name` `userTest`} {`response` `0021277bff9ac7caf06aa608e46616d47f`}]")
|
|
s.writeSentence(t, "!trap", "=message=incorrect login")
|
|
}()
|
|
|
|
err := c.Login("userTest", "passTest")
|
|
if err == nil {
|
|
t.Fatalf("Login succeeded; want error")
|
|
}
|
|
if err.Error() != "from RouterOS device: incorrect login" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLoginNoChallenge(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/login @ []")
|
|
s.writeSentence(t, "!done")
|
|
}()
|
|
|
|
err := c.Login("userTest", "passTest")
|
|
if err == nil {
|
|
t.Fatalf("Login succeeded; want error")
|
|
}
|
|
if err.Error() != "RouterOS: /login: no ret (challenge) received" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLoginInvalidChallenge(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/login @ []")
|
|
s.writeSentence(t, "!done", "=ret=Invalid Hex String")
|
|
}()
|
|
|
|
err := c.Login("userTest", "passTest")
|
|
if err == nil {
|
|
t.Fatalf("Login succeeded; want error")
|
|
}
|
|
if err.Error() != "RouterOS: /login: invalid ret (challenge) hex string received: encoding/hex: invalid byte: U+0049 'I'" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestLoginEOF(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
s.Close()
|
|
|
|
err := c.Login("userTest", "passTest")
|
|
if err == nil {
|
|
t.Fatalf("Login succeeded; want error")
|
|
}
|
|
if err.Error() != "io: read/write on closed pipe" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestCloseTwice(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer s.Close()
|
|
c.Close()
|
|
c.Close()
|
|
}
|
|
|
|
func TestAsyncTwice(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
defer s.Close()
|
|
|
|
c.Async()
|
|
|
|
errC := c.Async()
|
|
err := <-errC
|
|
want := "Async() has already been called"
|
|
if err.Error() != want {
|
|
t.Fatalf("Second Async()=%#q; want %#q", err, want)
|
|
}
|
|
|
|
err = <-errC
|
|
if err != nil {
|
|
t.Fatalf("Async() channel should be closed after error; got %#q", err)
|
|
}
|
|
}
|
|
|
|
func TestRun(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
s.writeSentence(t, "!re", "=address=1.2.3.4/32")
|
|
s.writeSentence(t, "!done")
|
|
}()
|
|
|
|
sen, err := c.Run("/ip/address")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
|
|
if sen.String() != want {
|
|
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
|
|
}
|
|
}
|
|
|
|
func TestRunWithListen(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @l1 []")
|
|
s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
|
|
s.writeSentence(t, "!done", ".tag=l1")
|
|
}()
|
|
|
|
listen, err := c.Listen("/ip/address")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
sen := <-listen.Chan()
|
|
want := "!re @l1 [{`address` `1.2.3.4/32`}]"
|
|
if fmt.Sprintf("%s", sen) != want {
|
|
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
|
|
}
|
|
|
|
sen = <-listen.Chan()
|
|
if sen != nil {
|
|
t.Fatalf("Listen() channel should be closed after EOF; got %#q", sen)
|
|
}
|
|
err = listen.Err()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunAsync(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
c.Async()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @r1 []")
|
|
s.writeSentence(t, "!re", ".tag=r1", "=address=1.2.3.4/32")
|
|
s.writeSentence(t, "!done", ".tag=r1")
|
|
}()
|
|
|
|
sen, err := c.Run("/ip/address")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := "!re @r1 [{`address` `1.2.3.4/32`}]\n!done @r1 []"
|
|
if sen.String() != want {
|
|
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
|
|
}
|
|
}
|
|
|
|
func TestRunEmptySentence(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
s.writeSentence(t)
|
|
s.writeSentence(t, "!re", "=address=1.2.3.4/32")
|
|
s.writeSentence(t, "!done")
|
|
}()
|
|
|
|
sen, err := c.Run("/ip/address")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := "!re @ [{`address` `1.2.3.4/32`}]\n!done @ []"
|
|
if sen.String() != want {
|
|
t.Fatalf("/ip/address (%s); want (%s)", sen, want)
|
|
}
|
|
}
|
|
|
|
func TestRunEOF(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
}()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunEOFAsync(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
c.Async()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @r1 []")
|
|
s.writeSentence(t, "!re", "=address=1.2.3.4/32")
|
|
}()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err != io.EOF {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunInvalidSentence(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
s.writeSentence(t, "!xxx")
|
|
}()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err.Error() != "unknown RouterOS reply word: !xxx" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunTrap(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
s.writeSentence(t, "!trap", "=message=Some device error message")
|
|
}()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err.Error() != "from RouterOS device: Some device error message" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunMesagelessTrap(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
s.writeSentence(t, "!trap", "=some=unknown key")
|
|
}()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err.Error() != "from RouterOS device: unknown error: !trap @ [{`some` `unknown key`}]" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunFatal(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address @ []")
|
|
s.writeSentence(t, "!fatal", "=message=Some device error message")
|
|
}()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err.Error() != "from RouterOS device: Some device error message" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestRunAfterClose(t *testing.T) {
|
|
c, s := newPair(t)
|
|
c.Close()
|
|
s.Close()
|
|
|
|
_, err := c.Run("/ip/address")
|
|
if err == nil {
|
|
t.Fatalf("Run succeeded; want error")
|
|
}
|
|
if err.Error() != "io: read/write on closed pipe" {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func TestListen(t *testing.T) {
|
|
c, s := newPair(t)
|
|
defer c.Close()
|
|
|
|
go func() {
|
|
defer s.Close()
|
|
s.readSentence(t, "/ip/address/listen @l1 []")
|
|
s.writeSentence(t, "!re", ".tag=l1", "=address=1.2.3.4/32")
|
|
s.readSentence(t, "/cancel @r2 [{`tag` `l1`}]")
|
|
s.writeSentence(t, "!trap", "=category=2", ".tag=l1")
|
|
s.writeSentence(t, "!done", "=tag=r2")
|
|
s.writeSentence(t, "!done", "=tag=l1")
|
|
}()
|
|
|
|
c.Queue = 1
|
|
listen, err := c.Listen("/ip/address/listen")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
reC := listen.Chan()
|
|
|
|
listen.Cancel()
|
|
|
|
sen := <-reC
|
|
want := "!re @l1 [{`address` `1.2.3.4/32`}]"
|
|
if fmt.Sprintf("%s", sen) != want {
|
|
t.Fatalf("/ip/address/listen (%s); want (%s)", sen, want)
|
|
}
|
|
|
|
sen = <-reC
|
|
if sen != nil {
|
|
t.Fatalf("Listen() channel should be closed after Close(); got %#q", sen)
|
|
}
|
|
err = listen.Err()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
type conn struct {
|
|
*io.PipeReader
|
|
*io.PipeWriter
|
|
}
|
|
|
|
func (c *conn) Close() error {
|
|
c.PipeReader.Close()
|
|
c.PipeWriter.Close()
|
|
return nil
|
|
}
|
|
|
|
func newPair(t *testing.T) (*routeros.Client, *fakeServer) {
|
|
ar, aw := io.Pipe()
|
|
br, bw := io.Pipe()
|
|
|
|
c, err := routeros.NewClient(&conn{ar, bw})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
s := &fakeServer{
|
|
proto.NewReader(br),
|
|
proto.NewWriter(aw),
|
|
&conn{br, aw},
|
|
}
|
|
|
|
return c, s
|
|
}
|
|
|
|
type fakeServer struct {
|
|
r proto.Reader
|
|
w proto.Writer
|
|
io.Closer
|
|
}
|
|
|
|
func (f *fakeServer) readSentence(t *testing.T, want string) {
|
|
sen, err := f.r.ReadSentence()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if sen.String() != want {
|
|
t.Fatalf("Sentence (%s); want (%s)", sen.String(), want)
|
|
}
|
|
t.Logf("< %s\n", sen)
|
|
}
|
|
|
|
func (f *fakeServer) writeSentence(t *testing.T, sentence ...string) {
|
|
t.Logf("> %#q\n", sentence)
|
|
f.w.BeginSentence()
|
|
for _, word := range sentence {
|
|
f.w.WriteWord(word)
|
|
}
|
|
err := f.w.EndSentence()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|