package cameradar import ( "bytes" "errors" "fmt" "io/ioutil" "os" "testing" "github.com/ullaakut/disgo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) // Setup Mock type mockedFS struct { osFS fileExists bool openError bool fileMock *fileMock fileSize int64 } // fileMock mocks a file type fileMock struct { mock.Mock readError bool bytes.Buffer } type mockedFileInfo struct { os.FileInfo } func (m mockedFileInfo) Size() int64 { return 1 } func (m mockedFS) Stat(name string) (os.FileInfo, error) { if !m.fileExists { return nil, os.ErrNotExist } return mockedFileInfo{}, nil } func (m mockedFS) Open(name string) (file, error) { if m.openError { return nil, os.ErrNotExist } return m.fileMock, nil } func (m *fileMock) Read(p []byte) (n int, err error) { if m.readError { return 0, os.ErrNotExist } return m.Buffer.Read(p) } func (m *fileMock) ReadAt(p []byte, off int64) (n int, err error) { return 1, nil } func (m *fileMock) Seek(offset int64, whence int) (int64, error) { return offset, nil } func (m *fileMock) Stat() (os.FileInfo, error) { return mockedFileInfo{}, nil } // Close mock func (m *fileMock) Close() error { args := m.Called() return args.Error(0) } // Sync mock func (m *fileMock) Sync() error { args := m.Called() return args.Error(0) } func TestLoadCredentials(t *testing.T) { credentialsJSONString := []byte("{\"usernames\":[\"admin\",\"root\"],\"passwords\":[\"12345\",\"root\"]}") validCredentials := Credentials{ Usernames: []string{"admin", "root"}, Passwords: []string{"12345", "root"}, } tests := []struct { description string input []byte fileExists bool expectedCredentials Credentials expectedErr error }{ { description: "Valid baseline", fileExists: true, input: credentialsJSONString, expectedCredentials: validCredentials, }, { description: "File does not exist", fileExists: false, input: credentialsJSONString, expectedErr: errors.New("could not read credentials dictionary file at \"/tmp/cameradar_test_load_credentials_1.xml\": open /tmp/cameradar_test_load_credentials_1.xml: no such file or directory"), }, { description: "Invalid format", fileExists: true, input: []byte("not json"), expectedErr: errors.New("unable to unmarshal dictionary contents: invalid character 'o' in literal null (expecting 'u')"), }, { description: "No streams in dictionary", fileExists: true, input: []byte("{\"invalid\":\"json\"}"), }, } for i, test := range tests { t.Run(test.description, func(t *testing.T) { filePath := "/tmp/cameradar_test_load_credentials_" + fmt.Sprint(i) + ".xml" // create file. if test.fileExists { _, err := os.Create(filePath) if err != nil { t.Fatalf("could not create xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath) } err = ioutil.WriteFile(filePath, test.input, 0644) if err != nil { t.Fatalf("could not write xml file for LoadCredentials: %v. iteration: %d. file path: %s\n", err, i, filePath) } } scanner := &Scanner{ term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), credentialDictionaryPath: filePath, } err := scanner.LoadCredentials() assert.Equal(t, test.expectedErr, err) assert.Len(t, scanner.credentials.Usernames, len(test.expectedCredentials.Usernames)) for _, expectedUsername := range test.expectedCredentials.Usernames { assert.Contains(t, scanner.credentials.Usernames, expectedUsername) } assert.Len(t, scanner.credentials.Passwords, len(test.expectedCredentials.Passwords)) for _, expectedPassword := range test.expectedCredentials.Passwords { assert.Contains(t, scanner.credentials.Passwords, expectedPassword) } }) } } func TestLoadRoutes(t *testing.T) { routesJSONString := []byte("admin\nroot") validRoutes := Routes{"admin", "root"} tests := []struct { description string input []byte fileExists bool expectedRoutes Routes expectedErr error }{ { description: "Valid baseline", fileExists: true, input: routesJSONString, expectedRoutes: validRoutes, }, { description: "File does not exist", fileExists: false, input: routesJSONString, expectedErr: errors.New("unable to open dictionary: open /tmp/cameradar_test_load_routes_1.xml: no such file or directory"), }, { description: "No streams in dictionary", fileExists: true, input: []byte(""), }, } for i, test := range tests { t.Run(test.description, func(t *testing.T) { filePath := "/tmp/cameradar_test_load_routes_" + fmt.Sprint(i) + ".xml" // Create file. if test.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, test.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) } } scanner := &Scanner{ term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), routeDictionaryPath: filePath, } err := scanner.LoadRoutes() assert.Equal(t, test.expectedErr, err) assert.Len(t, scanner.routes, len(test.expectedRoutes)) for _, expectedRoute := range test.expectedRoutes { assert.Contains(t, scanner.routes, expectedRoute) } }) } } func TestParseCredentialsFromString(t *testing.T) { defaultCredentials := Credentials{ Usernames: []string{ "", "admin", "Admin", "Administrator", "root", "supervisor", "ubnt", "service", "Dinion", "administrator", "admin1", }, Passwords: []string{ "", "admin", "9999", "123456", "pass", "camera", "1234", "12345", "fliradmin", "system", "jvc", "meinsm", "root", "4321", "111111", "1111111", "password", "ikwd", "supervisor", "ubnt", "wbox123", "service", }, } tests := []struct { str string expectedCredentials Credentials }{ { str: "{\"usernames\":[\"\",\"admin\",\"Admin\",\"Administrator\",\"root\",\"supervisor\",\"ubnt\",\"service\",\"Dinion\",\"administrator\",\"admin1\"],\"passwords\":[\"\",\"admin\",\"9999\",\"123456\",\"pass\",\"camera\",\"1234\",\"12345\",\"fliradmin\",\"system\",\"jvc\",\"meinsm\",\"root\",\"4321\",\"111111\",\"1111111\",\"password\",\"ikwd\",\"supervisor\",\"ubnt\",\"wbox123\",\"service\"]}", expectedCredentials: defaultCredentials, }, { str: "{}", expectedCredentials: Credentials{}, }, { str: "{\"invalid_field\":42}", expectedCredentials: Credentials{}, }, { str: "not json", expectedCredentials: Credentials{}, }, } for _, test := range tests { parsedCredentials, _ := ParseCredentialsFromString(test.str) assert.Equal(t, test.expectedCredentials, parsedCredentials) } } func TestParseRoutesFromString(t *testing.T) { tests := []struct { str string expectedRoutes Routes }{ { str: "a\nb\nc", expectedRoutes: []string{"a", "b", "c"}, }, { str: "a", expectedRoutes: []string{"a"}, }, { str: "", expectedRoutes: []string{""}, }, } for _, test := range tests { assert.Equal(t, test.expectedRoutes, ParseRoutesFromString(test.str)) } } func TestLoadTargets(t *testing.T) { oldFS := fs mfs := &mockedFS{} fs = mfs defer func() { fs = oldFS }() tests := []struct { description string targets []string fileExists bool openError bool readError bool expectedTargets []string expectedError error }{ { description: "not a file", targets: []string{"0.0.0.0"}, fileExists: false, expectedTargets: []string{"0.0.0.0"}, expectedError: nil, }, { description: "not file targets", targets: []string{"0.0.0.0", "1.2.3.4/24"}, expectedTargets: []string{"0.0.0.0", "1.2.3.4/24"}, expectedError: nil, }, { description: "file contains targets", targets: []string{"test_does_not_really_exist"}, fileExists: true, expectedTargets: []string{"0.0.0.0", "localhost", "192.17.0.0/16", "192.168.1.140-255", "192.168.2-3.0-255"}, expectedError: nil, }, { description: "open error", targets: []string{"test_does_not_really_exist"}, fileExists: true, openError: true, expectedTargets: []string{"test_does_not_really_exist"}, expectedError: errors.New("unable to open targets file \"test_does_not_really_exist\": file does not exist"), }, { description: "read error", targets: []string{"test_does_not_really_exist"}, fileExists: true, readError: true, expectedTargets: []string{"test_does_not_really_exist"}, expectedError: errors.New("unable to read targets file \"test_does_not_really_exist\": file does not exist"), }, } for _, test := range tests { t.Run(test.description, func(t *testing.T) { mfs.fileExists = test.fileExists mfs.openError = test.openError mfs.fileMock = &fileMock{ readError: test.readError, } mfs.fileMock.On("Close").Return(nil) mfs.fileMock.WriteString("0.0.0.0\nlocalhost\n192.17.0.0/16\n192.168.1.140-255\n192.168.2-3.0-255") scanner := &Scanner{ term: disgo.NewTerminal(disgo.WithDefaultOutput(ioutil.Discard)), targets: test.targets, } err := scanner.LoadTargets() assert.Equal(t, test.expectedTargets, scanner.targets) assert.Equal(t, test.expectedError, err) }) } } // This is completely useless and just lets me // not look at these two red lines on the coverage // any longer. func TestFS(t *testing.T) { fs := osFS{} fs.Open("test") fs.Stat("test") }