Add unit tests and functional test in CI
* Unit tests Discover 90% The NmapRun function needs a refacto to make it use adaptors instead of directly calling exec.Command, exec.Command.StdoutPipe, exec.Command.Start, bufio.Scanner.Scan and bufio.Scanner.Err It makes me uncomfortable to push a test file that covers only 90%, but it's better than none, and the 10 missing %s are not very error-prone so it should be okay to delay this part a bit. For now it's more urgent to test as much of the code as possible * Unit tests Helpers 100% * Unit tests Loaders 100% - Attack 85% Once again, the Attack functions are not as simple as the rest to unit test, so I will refacto all of this to use a CURL adaptor later, but for now the total is of 88.6% of coverage, which is good enough for something I spent 2 hours on * Add testing to CI validation process * CI now does functional testing with RTSPATT * Change travis language to bash
This commit is contained in:
committed by
Brendan Le Glaunec
parent
be63c6a231
commit
cb74761675
+21
-2
@@ -1,4 +1,4 @@
|
||||
language: generic
|
||||
language: bash
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
@@ -11,7 +11,26 @@ install:
|
||||
- docker build -t cameradar .
|
||||
|
||||
script:
|
||||
- docker run cameradar
|
||||
- go get github.com/andelf/go-curl
|
||||
- go get github.com/pkg/errors
|
||||
- go get gopkg.in/go-playground/validator.v9
|
||||
- go get github.com/stretchr/testify/assert
|
||||
# Run unit tests
|
||||
- go test
|
||||
# Launch a fake camera to check if cameradar is able to access it
|
||||
- docker run -d --name=fake_camera -e RTSP_USERNAME=admin -e RTSP_PASSWORD=12345 -p 8554:8554 ullaakut/rtspatt
|
||||
# Launch cameradar on the local machine
|
||||
- docker run --net=host -t cameradar -t 0.0.0.0 -l > logs.txt
|
||||
- docker logs fake_camera > camera_logs.txt
|
||||
# Stop the fake camera
|
||||
- docker stop fake_camera
|
||||
# Print logs
|
||||
- cat camera_logs.txt
|
||||
- cat logs.txt
|
||||
# check if file contains more than one line
|
||||
# 1 line: Error message because no streams were found
|
||||
# More lines: Logs for all found cameras
|
||||
- if [[ $(wc -l <logs.txt) -lt 2 ]]; then exit 1; fi
|
||||
|
||||
after_success:
|
||||
- echo "Test Success - Branch($TRAVIS_BRANCH) Pull Request($TRAVIS_PULL_REQUEST) Tag($TRAVIS_TAG)"
|
||||
|
||||
@@ -190,7 +190,7 @@ func AttackCredentials(targets []Stream, credentials Credentials, timeout time.D
|
||||
}
|
||||
}
|
||||
if found == 0 {
|
||||
return targets, errors.New("No credentials found")
|
||||
return targets, errors.New("no credentials found")
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
@@ -225,7 +225,7 @@ func AttackRoute(targets []Stream, routes Routes, timeout time.Duration, log boo
|
||||
}
|
||||
}
|
||||
if found == 0 {
|
||||
return targets, errors.New("No routes found")
|
||||
return targets, errors.New("no routes found")
|
||||
}
|
||||
|
||||
return targets, nil
|
||||
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmrdr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Again, since these tests use the curl library, I don't want to spend ages trying to mock
|
||||
// the lib right now.
|
||||
|
||||
func TestAttackCredentials(t *testing.T) {
|
||||
validStream1 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
validStream2 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "differentFakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
fakeTargets := []Stream{validStream1, validStream2}
|
||||
fakeCredentials := Credentials{
|
||||
Usernames: []string{"admin", "root"},
|
||||
Passwords: []string{"12345", "root"},
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
targets []Stream
|
||||
credentials Credentials
|
||||
timeout time.Duration
|
||||
log bool
|
||||
|
||||
expectedStreams []Stream
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
targets: fakeTargets,
|
||||
credentials: fakeCredentials,
|
||||
timeout: 1 * time.Millisecond,
|
||||
log: true,
|
||||
|
||||
expectedStreams: fakeTargets,
|
||||
},
|
||||
// Valid baseline without logs
|
||||
{
|
||||
targets: fakeTargets,
|
||||
credentials: fakeCredentials,
|
||||
timeout: 1 * time.Millisecond,
|
||||
log: false,
|
||||
|
||||
expectedStreams: fakeTargets,
|
||||
},
|
||||
// TODO: Refacto and make tests with all possible error cases
|
||||
}
|
||||
for i, vector := range vectors {
|
||||
results, err := AttackCredentials(vector.targets, vector.credentials, vector.timeout, vector.log)
|
||||
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success in AttackCredentials test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error in AttackCredentials test, iteration %d: %v\n", i, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, stream := range vector.expectedStreams {
|
||||
foundStream := false
|
||||
for _, result := range results {
|
||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||
foundStream = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func TestAttackRoute(t *testing.T) {
|
||||
validStream1 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
validStream2 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "differentFakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
fakeTargets := []Stream{validStream1, validStream2}
|
||||
fakeRoutes := Routes{"live.sdp", "media.amp"}
|
||||
|
||||
vectors := []struct {
|
||||
targets []Stream
|
||||
routes Routes
|
||||
timeout time.Duration
|
||||
log bool
|
||||
|
||||
expectedStreams []Stream
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
targets: fakeTargets,
|
||||
routes: fakeRoutes,
|
||||
timeout: 1 * time.Millisecond,
|
||||
log: true,
|
||||
|
||||
expectedStreams: fakeTargets,
|
||||
},
|
||||
// Valid baseline without logs
|
||||
{
|
||||
targets: fakeTargets,
|
||||
routes: fakeRoutes,
|
||||
timeout: 1 * time.Millisecond,
|
||||
log: false,
|
||||
|
||||
expectedStreams: fakeTargets,
|
||||
},
|
||||
// TODO: Refacto and make tests with all possible error cases
|
||||
}
|
||||
for i, vector := range vectors {
|
||||
results, err := AttackRoute(vector.targets, vector.routes, vector.timeout, vector.log)
|
||||
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success in AttackRoute test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error in AttackRoute test, iteration %d: %v\n", i, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, stream := range vector.expectedStreams {
|
||||
foundStream := false
|
||||
for _, result := range results {
|
||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||
foundStream = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
+16
-8
@@ -17,7 +17,6 @@ import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os/exec"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -27,7 +26,7 @@ import (
|
||||
// These constants detail the different level of nmap speed presets
|
||||
// that determine the timeout values and wether or not nmap makes use of parallelism
|
||||
const (
|
||||
// PARANOID NO PARALLELISM | 5min timeout | 100ms to 10s round-trip time timeout | 5mn scan delay
|
||||
// PARANOIAC NO PARALLELISM | 5min timeout | 100ms to 10s round-trip time timeout | 5mn scan delay
|
||||
PARANOIAC = 0
|
||||
// SNEAKY NO PARALLELISM | 15sec timeout | 100ms to 10s round-trip time timeout | 15s scan delay
|
||||
SNEAKY = 1
|
||||
@@ -41,10 +40,19 @@ const (
|
||||
INSANE = 5
|
||||
)
|
||||
|
||||
// Allows unit tests to override the exec function to avoid launching a real command
|
||||
// during the tests. The NmapRun method will soon be refactored with an adaptor in order
|
||||
// to make it possible to mock all external calls.
|
||||
var execCommand = exec.Command
|
||||
|
||||
// NmapRun runs nmap on the specified targets's specified ports, using the given nmap speed.
|
||||
func NmapRun(targets, ports, resultFilePath string, nmapSpeed int, enableLogs bool) error {
|
||||
if nmapSpeed < PARANOIAC || nmapSpeed > INSANE {
|
||||
return fmt.Errorf("invalid nmap speed value '%d'. Should be between '%d' and '%d'", nmapSpeed, PARANOIAC, INSANE)
|
||||
}
|
||||
|
||||
// Prepare nmap command
|
||||
cmd := exec.Command(
|
||||
cmd := execCommand(
|
||||
"nmap",
|
||||
fmt.Sprintf("-T%d", nmapSpeed),
|
||||
"-A",
|
||||
@@ -58,24 +66,24 @@ func NmapRun(targets, ports, resultFilePath string, nmapSpeed int, enableLogs bo
|
||||
// Pipe stdout to be able to write the logs in realtime
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Couldn't get stdout pipe")
|
||||
return errors.Wrap(err, "couldn't get stdout pipe")
|
||||
}
|
||||
|
||||
// Execute the nmap command
|
||||
if err := cmd.Start(); err != nil {
|
||||
return errors.Wrap(err, "Coudln't run nmap command")
|
||||
return errors.Wrap(err, "coudln't run nmap command")
|
||||
}
|
||||
|
||||
// Scan the pipe until an end of file or an error occurs
|
||||
in := bufio.NewScanner(stdout)
|
||||
for in.Scan() {
|
||||
if enableLogs {
|
||||
log.Printf(in.Text())
|
||||
fmt.Printf(in.Text())
|
||||
}
|
||||
}
|
||||
if err := in.Err(); err != nil {
|
||||
if enableLogs {
|
||||
log.Printf("error: %s", err)
|
||||
fmt.Printf("error: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +98,7 @@ func NmapParseResults(nmapResultFilePath string) ([]Stream, error) {
|
||||
// Open & Read XML file
|
||||
content, err := ioutil.ReadFile(nmapResultFilePath)
|
||||
if err != nil {
|
||||
return streams, errors.Wrap(err, "Could not read nmap result file at "+nmapResultFilePath+":")
|
||||
return streams, errors.Wrap(err, "could not read nmap result file at "+nmapResultFilePath+":")
|
||||
}
|
||||
|
||||
// Unmarshal content of XML file into data structure
|
||||
|
||||
@@ -0,0 +1,715 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmrdr
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// HACK: See https://golang.org/src/os/exec/exec_test.go
|
||||
func fakeExecCommand(command string, args ...string) *exec.Cmd {
|
||||
cs := []string{"-test.run=TestExecCommandHelper", "--", command}
|
||||
cs = append(cs, args...)
|
||||
cmd := exec.Command(os.Args[0], cs...)
|
||||
cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1",
|
||||
"STDOUT= ",
|
||||
"EXIT_STATUS=0"}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestExecCommandHelper(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(os.Stdout, os.Getenv("STDOUT"))
|
||||
i, _ := strconv.Atoi(os.Getenv("EXIT_STATUS"))
|
||||
os.Exit(i)
|
||||
}
|
||||
|
||||
func TestNmapRun(t *testing.T) {
|
||||
execCommand = fakeExecCommand
|
||||
defer func() { execCommand = exec.Command }()
|
||||
|
||||
vectors := []struct {
|
||||
targets string
|
||||
ports string
|
||||
resultFilePath string
|
||||
nmapSpeed int
|
||||
enableLogs bool
|
||||
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// Valid baseline with logs enabled
|
||||
{
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: true,
|
||||
},
|
||||
// Invalid speed
|
||||
{
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: INSANE + 1,
|
||||
enableLogs: false,
|
||||
|
||||
expectedErrMsg: "invalid nmap speed value",
|
||||
},
|
||||
}
|
||||
for _, vector := range vectors {
|
||||
err := NmapRun(vector.targets, vector.ports, vector.resultFilePath, vector.nmapSpeed, vector.enableLogs)
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success. expected error: %s\n", vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNmapParseResults(t *testing.T) {
|
||||
validStream1 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
validStream2 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "differentFakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
invalidStreamNoPort := Stream{
|
||||
Device: "invalidDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 0,
|
||||
}
|
||||
|
||||
invalidStreamNoAddress := Stream{
|
||||
Device: "invalidDevice",
|
||||
Address: "",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
fileExists bool
|
||||
streamsXML *nmapResult
|
||||
|
||||
expectedStreams []Stream
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// File exists
|
||||
// Two valid streams, no error
|
||||
{
|
||||
expectedStreams: []Stream{validStream1, validStream2},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream1.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream1.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream1.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream2.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream2.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream2.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fileExists: true,
|
||||
},
|
||||
// File exists
|
||||
// Two invalid streams, no error
|
||||
{
|
||||
fileExists: true,
|
||||
expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: invalidStreamNoAddress.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: invalidStreamNoAddress.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: invalidStreamNoAddress.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: invalidStreamNoPort.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: invalidStreamNoPort.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: invalidStreamNoPort.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// File does not exist, error
|
||||
{
|
||||
fileExists: false,
|
||||
expectedErrMsg: "could not read nmap result file",
|
||||
},
|
||||
// No valid streams found
|
||||
{
|
||||
fileExists: true,
|
||||
expectedStreams: []Stream{},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: "Camera with closed ports",
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: 0,
|
||||
State: state{
|
||||
State: "closed",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: "Camera without closed ports",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: "Camera with closed ports",
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// XML Unmarshal error
|
||||
{
|
||||
fileExists: true,
|
||||
expectedStreams: []Stream{},
|
||||
expectedErrMsg: "expected element type <nmaprun> but have <failure>",
|
||||
},
|
||||
}
|
||||
for i, vector := range vectors {
|
||||
filePath := "/tmp/cameradar_test_parse_results_" + fmt.Sprint(i) + ".xml"
|
||||
|
||||
// create file
|
||||
if vector.fileExists {
|
||||
_, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("could not create xml file for NmapParseResults: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// marshal and write
|
||||
if vector.streamsXML != nil {
|
||||
streams, err := xml.Marshal(vector.streamsXML)
|
||||
if err != nil {
|
||||
fmt.Printf("invalid streams for NmapParseResults: %v. iteration: %d. streams: %v\n", err, i, vector.streamsXML)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filePath, streams, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write xml file for NmapParseResults: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
err := ioutil.WriteFile(filePath, []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><failure>"), 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write xml file for NmapParseResults: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results, err := NmapParseResults(filePath)
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success. expected error: %s\n", vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, stream := range vector.expectedStreams {
|
||||
foundStream := false
|
||||
for _, result := range results {
|
||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||
foundStream = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDiscover(t *testing.T) {
|
||||
execCommand = fakeExecCommand
|
||||
defer func() { execCommand = exec.Command }()
|
||||
|
||||
validStream1 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
validStream2 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "differentFakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
invalidStreamNoPort := Stream{
|
||||
Device: "invalidDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 0,
|
||||
}
|
||||
|
||||
invalidStreamNoAddress := Stream{
|
||||
Device: "invalidDevice",
|
||||
Address: "",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
targets string
|
||||
ports string
|
||||
resultFilePath string
|
||||
nmapSpeed int
|
||||
enableLogs bool
|
||||
fileExists bool
|
||||
streamsXML *nmapResult
|
||||
|
||||
expectedStreams []Stream
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
expectedStreams: []Stream{validStream1, validStream2},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream1.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream1.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream1.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream2.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream2.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream2.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fileExists: true,
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: false,
|
||||
},
|
||||
// Invalid speed
|
||||
{
|
||||
expectedStreams: []Stream{},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream1.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream1.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream1.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream2.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream2.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream2.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fileExists: true,
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: INSANE + 1,
|
||||
enableLogs: false,
|
||||
|
||||
expectedErrMsg: "invalid nmap speed value",
|
||||
},
|
||||
// File exists
|
||||
// Two valid streams, no error
|
||||
{
|
||||
expectedStreams: []Stream{validStream1, validStream2},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream1.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream1.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream1.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: validStream2.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: validStream2.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: validStream2.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fileExists: true,
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: false,
|
||||
},
|
||||
// File exists
|
||||
// Two invalid streams, no error
|
||||
{
|
||||
fileExists: true,
|
||||
expectedStreams: []Stream{invalidStreamNoPort, invalidStreamNoAddress},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: invalidStreamNoAddress.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: invalidStreamNoAddress.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: invalidStreamNoAddress.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: invalidStreamNoPort.Address,
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: invalidStreamNoPort.Port,
|
||||
State: state{
|
||||
State: "open",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: invalidStreamNoPort.Device,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: false,
|
||||
},
|
||||
// File does not exist, error
|
||||
{
|
||||
fileExists: false,
|
||||
expectedErrMsg: "could not read nmap result file",
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: false,
|
||||
},
|
||||
// No valid streams found
|
||||
{
|
||||
fileExists: true,
|
||||
expectedStreams: []Stream{},
|
||||
streamsXML: &nmapResult{
|
||||
Hosts: []host{
|
||||
host{
|
||||
Address: address{
|
||||
Addr: "Camera with closed ports",
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
Ports: ports{
|
||||
Ports: []port{
|
||||
port{
|
||||
PortID: 0,
|
||||
State: state{
|
||||
State: "closed",
|
||||
},
|
||||
Service: service{
|
||||
Name: "rtsp",
|
||||
Product: "Camera without closed ports",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
host{
|
||||
Address: address{
|
||||
Addr: "Camera with closed ports",
|
||||
AddrType: "ipv4",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: false,
|
||||
},
|
||||
// XML Unmarshal error
|
||||
{
|
||||
fileExists: true,
|
||||
expectedStreams: []Stream{},
|
||||
expectedErrMsg: "expected element type <nmaprun> but have <failure>",
|
||||
targets: "localhost",
|
||||
ports: "554",
|
||||
resultFilePath: "/tmp/results.xml",
|
||||
nmapSpeed: PARANOIAC,
|
||||
enableLogs: false,
|
||||
},
|
||||
}
|
||||
for i, vector := range vectors {
|
||||
filePath := "/tmp/cameradar_test_discover_" + fmt.Sprint(i) + ".xml"
|
||||
|
||||
// create file
|
||||
if vector.fileExists {
|
||||
_, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("could not create xml file for Discover: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// marshal and write
|
||||
if vector.streamsXML != nil {
|
||||
streams, err := xml.Marshal(vector.streamsXML)
|
||||
if err != nil {
|
||||
fmt.Printf("invalid streams for Discover: %v. iteration: %d. streams: %v\n", err, i, vector.streamsXML)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filePath, streams, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write xml file for Discover: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
err := ioutil.WriteFile(filePath, []byte("<?xml version=\"1.0\" encoding=\"UTF-8\"?><failure>"), 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write xml file for Discover: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results, err := Discover(vector.targets, vector.ports, filePath, vector.nmapSpeed, vector.enableLogs)
|
||||
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success in Discover test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error in Discover test, iteration %d: %v\n", i, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, stream := range vector.expectedStreams {
|
||||
foundStream := false
|
||||
for _, result := range results {
|
||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||
foundStream = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
assert.Equal(t, len(vector.expectedStreams), len(results), "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmrdr
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestReplace(t *testing.T) {
|
||||
validStream1 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
validStream2 := Stream{
|
||||
Device: "fakeDevice",
|
||||
Address: "differentFakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
invalidStreamNoPort := Stream{
|
||||
Device: "invalidDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 0,
|
||||
}
|
||||
|
||||
invalidStreamNoPortModified := Stream{
|
||||
Device: "updatedDevice",
|
||||
Address: "fakeAddress",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
streams []Stream
|
||||
newStream Stream
|
||||
|
||||
expectedStreams []Stream
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
streams: []Stream{validStream1, validStream2, invalidStreamNoPort},
|
||||
newStream: invalidStreamNoPortModified,
|
||||
|
||||
expectedStreams: []Stream{validStream1, validStream2, invalidStreamNoPortModified},
|
||||
},
|
||||
}
|
||||
for _, vector := range vectors {
|
||||
streams := replace(vector.streams, vector.newStream)
|
||||
|
||||
for _, stream := range vector.streams {
|
||||
foundStream := false
|
||||
for _, result := range streams {
|
||||
if result.Address == stream.Address && result.Device == stream.Device && result.Port == stream.Port {
|
||||
foundStream = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundStream, "wrong streams parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCameraRTSPURL(t *testing.T) {
|
||||
validStream := Stream{
|
||||
Address: "1.2.3.4",
|
||||
Username: "ullaakut",
|
||||
Password: "ba69897483886f0d2b0afb6345b76c0c",
|
||||
Route: "cameradar.sdp",
|
||||
Port: 1337,
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
stream Stream
|
||||
|
||||
expectedRTSPURL string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
stream: validStream,
|
||||
|
||||
expectedRTSPURL: "rtsp://ullaakut:ba69897483886f0d2b0afb6345b76c0c@1.2.3.4:1337/cameradar.sdp",
|
||||
},
|
||||
}
|
||||
for _, vector := range vectors {
|
||||
output := GetCameraRTSPURL(vector.stream)
|
||||
assert.Equal(t, vector.expectedRTSPURL, output, "wrong RTSP URL generated")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCameraAdminPanelURL(t *testing.T) {
|
||||
validStream := Stream{
|
||||
Address: "1.2.3.4",
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
stream Stream
|
||||
|
||||
expectedRTSPURL string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
stream: validStream,
|
||||
|
||||
expectedRTSPURL: "http://1.2.3.4/",
|
||||
},
|
||||
}
|
||||
for _, vector := range vectors {
|
||||
output := GetCameraAdminPanelURL(vector.stream)
|
||||
assert.Equal(t, vector.expectedRTSPURL, output, "wrong Admin Panel URL generated")
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -28,7 +28,7 @@ func LoadCredentials(path string) (Credentials, error) {
|
||||
// Open & Read XML file
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return creds, errors.Wrap(err, "Could not read credentials dictionary file at "+path+":")
|
||||
return creds, errors.Wrap(err, "could not read credentials dictionary file at "+path+":")
|
||||
}
|
||||
|
||||
// Unmarshal content of JSON file into data structure
|
||||
|
||||
+182
@@ -0,0 +1,182 @@
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package cmrdr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLoadCredentials(t *testing.T) {
|
||||
credentialsJSONString := []byte("{\"usernames\":[\"admin\",\"root\"],\"passwords\":[\"12345\",\"root\"]}")
|
||||
validCredentials := Credentials{
|
||||
Usernames: []string{"admin", "root"},
|
||||
Passwords: []string{"12345", "root"},
|
||||
}
|
||||
|
||||
vectors := []struct {
|
||||
input []byte
|
||||
fileExists bool
|
||||
|
||||
expectedOutput Credentials
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
fileExists: true,
|
||||
input: credentialsJSONString,
|
||||
expectedOutput: validCredentials,
|
||||
},
|
||||
// File does not exist
|
||||
{
|
||||
fileExists: false,
|
||||
input: credentialsJSONString,
|
||||
expectedErrMsg: "could not read credentials dictionary file at",
|
||||
},
|
||||
// Invalid format
|
||||
{
|
||||
fileExists: true,
|
||||
input: []byte("not json"),
|
||||
expectedErrMsg: "invalid character",
|
||||
},
|
||||
// No streams in dictionary
|
||||
{
|
||||
fileExists: true,
|
||||
input: []byte("{\"invalid\":\"json\"}"),
|
||||
},
|
||||
}
|
||||
for i, vector := range vectors {
|
||||
filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml"
|
||||
// create file
|
||||
if vector.fileExists {
|
||||
_, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filePath, vector.input, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
result, err := LoadCredentials(filePath)
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success in LoadCredentials test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error in LoadCredentials test, iteration %d: %v\n", i, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, expectedUsername := range vector.expectedOutput.Usernames {
|
||||
foundUsername := false
|
||||
for _, username := range result.Usernames {
|
||||
if username == expectedUsername {
|
||||
foundUsername = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundUsername, "wrong usernames parsed")
|
||||
}
|
||||
for _, expectedPassword := range vector.expectedOutput.Passwords {
|
||||
foundPassword := false
|
||||
for _, password := range result.Passwords {
|
||||
if password == expectedPassword {
|
||||
foundPassword = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundPassword, "wrong passwords parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadRoutes(t *testing.T) {
|
||||
routesJSONString := []byte("admin\nroot")
|
||||
validRoutes := Routes{"admin", "root"}
|
||||
|
||||
vectors := []struct {
|
||||
input []byte
|
||||
fileExists bool
|
||||
|
||||
expectedOutput Routes
|
||||
expectedErrMsg string
|
||||
}{
|
||||
// Valid baseline
|
||||
{
|
||||
fileExists: true,
|
||||
input: routesJSONString,
|
||||
expectedOutput: validRoutes,
|
||||
},
|
||||
// File does not exist
|
||||
{
|
||||
fileExists: false,
|
||||
input: routesJSONString,
|
||||
expectedErrMsg: "no such file or directory",
|
||||
},
|
||||
// No streams in dictionary
|
||||
{
|
||||
fileExists: true,
|
||||
input: []byte(""),
|
||||
},
|
||||
}
|
||||
for i, vector := range vectors {
|
||||
filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml"
|
||||
// create file
|
||||
if vector.fileExists {
|
||||
_, err := os.Create(filePath)
|
||||
if err != nil {
|
||||
fmt.Printf("could not create xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(filePath, vector.input, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("could not write xml file for LoadRoutes: %v. iteration: %d. file path: %s\n", err, i, filePath)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
result, err := LoadRoutes(filePath)
|
||||
if len(vector.expectedErrMsg) > 0 {
|
||||
if err == nil {
|
||||
fmt.Printf("unexpected success in LoadRoutes test, iteration %d. expected error: %s\n", i, vector.expectedErrMsg)
|
||||
os.Exit(1)
|
||||
}
|
||||
assert.Contains(t, err.Error(), vector.expectedErrMsg, "wrong error message")
|
||||
} else {
|
||||
if err != nil {
|
||||
fmt.Printf("unexpected error in LoadRoutes test, iteration %d: %v\n", i, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
for _, expectedRoute := range vector.expectedOutput {
|
||||
foundRoute := false
|
||||
for _, route := range result {
|
||||
if route == expectedRoute {
|
||||
foundRoute = true
|
||||
}
|
||||
}
|
||||
assert.Equal(t, true, foundRoute, "wrong routes parsed")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user