mirror of
				https://github.com/coredns/coredns.git
				synced 2025-11-04 03:03:14 -05:00 
			
		
		
		
	
		
			
	
	
		
			292 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			292 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 
								 | 
							
								package middleware
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								import (
							 | 
						||
| 
								 | 
							
									"fmt"
							 | 
						||
| 
								 | 
							
									"runtime"
							 | 
						||
| 
								 | 
							
									"strings"
							 | 
						||
| 
								 | 
							
									"testing"
							 | 
						||
| 
								 | 
							
								)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func TestParseUnixCommand(t *testing.T) {
							 | 
						||
| 
								 | 
							
									tests := []struct {
							 | 
						||
| 
								 | 
							
										input    string
							 | 
						||
| 
								 | 
							
										expected []string
							 | 
						||
| 
								 | 
							
									}{
							 | 
						||
| 
								 | 
							
										// 0 - emtpy command
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    ``,
							 | 
						||
| 
								 | 
							
											expected: []string{},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 1 - command without arguments
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    `command`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 2 - command with single argument
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    `command arg1`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command`, `arg1`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 3 - command with multiple arguments
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    `command arg1 arg2`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command`, `arg1`, `arg2`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 4 - command with single argument with space character - in quotes
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    `command "arg1 arg1"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command`, `arg1 arg1`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 5 - command with multiple spaces and tab character
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    "command arg1    arg2\targ3",
							 | 
						||
| 
								 | 
							
											expected: []string{`command`, `arg1`, `arg2`, `arg3`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 6 - command with single argument with space character - escaped with backspace
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    `command arg1\ arg2`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command`, `arg1 arg2`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 7 - single quotes should escape special chars
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:    `command 'arg1\ arg2'`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command`, `arg1\ arg2`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for i, test := range tests {
							 | 
						||
| 
								 | 
							
										errorPrefix := fmt.Sprintf("Test [%d]: ", i)
							 | 
						||
| 
								 | 
							
										errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
							 | 
						||
| 
								 | 
							
										actual, _ := parseUnixCommand(test.input)
							 | 
						||
| 
								 | 
							
										if len(actual) != len(test.expected) {
							 | 
						||
| 
								 | 
							
											t.Errorf(errorPrefix+"Expected %d parts, got %d: %#v."+errorSuffix, len(test.expected), len(actual), actual)
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for j := 0; j < len(actual); j++ {
							 | 
						||
| 
								 | 
							
											if expectedPart, actualPart := test.expected[j], actual[j]; expectedPart != actualPart {
							 | 
						||
| 
								 | 
							
												t.Errorf(errorPrefix+"Expected: %v Actual: %v (index %d)."+errorSuffix, expectedPart, actualPart, j)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func TestParseWindowsCommand(t *testing.T) {
							 | 
						||
| 
								 | 
							
									tests := []struct {
							 | 
						||
| 
								 | 
							
										input    string
							 | 
						||
| 
								 | 
							
										expected []string
							 | 
						||
| 
								 | 
							
									}{
							 | 
						||
| 
								 | 
							
										{ // 0 - empty command - do not fail
							 | 
						||
| 
								 | 
							
											input:    ``,
							 | 
						||
| 
								 | 
							
											expected: []string{},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 1 - cmd without args
							 | 
						||
| 
								 | 
							
											input:    `cmd`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 2 - multiple args
							 | 
						||
| 
								 | 
							
											input:    `cmd arg1 arg2`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`, `arg1`, `arg2`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 3 - multiple args with space
							 | 
						||
| 
								 | 
							
											input:    `cmd "combined arg" arg2`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`, `combined arg`, `arg2`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 4 - path without spaces
							 | 
						||
| 
								 | 
							
											input:    `mkdir C:\Windows\foo\bar`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `C:\Windows\foo\bar`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 5 - command with space in quotes
							 | 
						||
| 
								 | 
							
											input:    `"command here"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`command here`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 6 - argument with escaped quotes (two quotes)
							 | 
						||
| 
								 | 
							
											input:    `cmd ""arg""`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`, `"arg"`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 7 - argument with escaped quotes (backslash)
							 | 
						||
| 
								 | 
							
											input:    `cmd \"arg\"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`, `"arg"`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 8 - two quotes (escaped) inside an inQuote element
							 | 
						||
| 
								 | 
							
											input:    `cmd "a ""quoted value"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`, `a "quoted value`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// TODO - see how many quotes are dislayed if we use "", """, """""""
							 | 
						||
| 
								 | 
							
										{ // 9 - two quotes outside an inQuote element
							 | 
						||
| 
								 | 
							
											input:    `cmd a ""quoted value`,
							 | 
						||
| 
								 | 
							
											expected: []string{`cmd`, `a`, `"quoted`, `value`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 10 - path with space in quotes
							 | 
						||
| 
								 | 
							
											input:    `mkdir "C:\directory name\foobar"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `C:\directory name\foobar`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 11 - space without quotes
							 | 
						||
| 
								 | 
							
											input:    `mkdir C:\ space`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `C:\`, `space`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 12 - space in quotes
							 | 
						||
| 
								 | 
							
											input:    `mkdir "C:\ space"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `C:\ space`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 13 - UNC
							 | 
						||
| 
								 | 
							
											input:    `mkdir \\?\C:\Users`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `\\?\C:\Users`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 14 - UNC with space
							 | 
						||
| 
								 | 
							
											input:    `mkdir "\\?\C:\Program Files"`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `\\?\C:\Program Files`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										{ // 15 - unclosed quotes - treat as if the path ends with quote
							 | 
						||
| 
								 | 
							
											input:    `mkdir "c:\Program files`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `c:\Program files`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										{ // 16 - quotes used inside the argument
							 | 
						||
| 
								 | 
							
											input:    `mkdir "c:\P"rogra"m f"iles`,
							 | 
						||
| 
								 | 
							
											expected: []string{`mkdir`, `c:\Program files`},
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for i, test := range tests {
							 | 
						||
| 
								 | 
							
										errorPrefix := fmt.Sprintf("Test [%d]: ", i)
							 | 
						||
| 
								 | 
							
										errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										actual := parseWindowsCommand(test.input)
							 | 
						||
| 
								 | 
							
										if len(actual) != len(test.expected) {
							 | 
						||
| 
								 | 
							
											t.Errorf(errorPrefix+"Expected %d parts, got %d: %#v."+errorSuffix, len(test.expected), len(actual), actual)
							 | 
						||
| 
								 | 
							
											continue
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										for j := 0; j < len(actual); j++ {
							 | 
						||
| 
								 | 
							
											if expectedPart, actualPart := test.expected[j], actual[j]; expectedPart != actualPart {
							 | 
						||
| 
								 | 
							
												t.Errorf(errorPrefix+"Expected: %v Actual: %v (index %d)."+errorSuffix, expectedPart, actualPart, j)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func TestSplitCommandAndArgs(t *testing.T) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// force linux parsing. It's more robust and covers error cases
							 | 
						||
| 
								 | 
							
									runtimeGoos = "linux"
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										runtimeGoos = runtime.GOOS
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var parseErrorContent = "error parsing command:"
							 | 
						||
| 
								 | 
							
									var noCommandErrContent = "no command contained in"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									tests := []struct {
							 | 
						||
| 
								 | 
							
										input              string
							 | 
						||
| 
								 | 
							
										expectedCommand    string
							 | 
						||
| 
								 | 
							
										expectedArgs       []string
							 | 
						||
| 
								 | 
							
										expectedErrContent string
							 | 
						||
| 
								 | 
							
									}{
							 | 
						||
| 
								 | 
							
										// 0 - emtpy command
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:              ``,
							 | 
						||
| 
								 | 
							
											expectedCommand:    ``,
							 | 
						||
| 
								 | 
							
											expectedArgs:       nil,
							 | 
						||
| 
								 | 
							
											expectedErrContent: noCommandErrContent,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 1 - command without arguments
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:              `command`,
							 | 
						||
| 
								 | 
							
											expectedCommand:    `command`,
							 | 
						||
| 
								 | 
							
											expectedArgs:       nil,
							 | 
						||
| 
								 | 
							
											expectedErrContent: ``,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 2 - command with single argument
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:              `command arg1`,
							 | 
						||
| 
								 | 
							
											expectedCommand:    `command`,
							 | 
						||
| 
								 | 
							
											expectedArgs:       []string{`arg1`},
							 | 
						||
| 
								 | 
							
											expectedErrContent: ``,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 3 - command with multiple arguments
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:              `command arg1 arg2`,
							 | 
						||
| 
								 | 
							
											expectedCommand:    `command`,
							 | 
						||
| 
								 | 
							
											expectedArgs:       []string{`arg1`, `arg2`},
							 | 
						||
| 
								 | 
							
											expectedErrContent: ``,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 4 - command with unclosed quotes
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:              `command "arg1 arg2`,
							 | 
						||
| 
								 | 
							
											expectedCommand:    "",
							 | 
						||
| 
								 | 
							
											expectedArgs:       nil,
							 | 
						||
| 
								 | 
							
											expectedErrContent: parseErrorContent,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
										// 5 - command with unclosed quotes
							 | 
						||
| 
								 | 
							
										{
							 | 
						||
| 
								 | 
							
											input:              `command 'arg1 arg2"`,
							 | 
						||
| 
								 | 
							
											expectedCommand:    "",
							 | 
						||
| 
								 | 
							
											expectedArgs:       nil,
							 | 
						||
| 
								 | 
							
											expectedErrContent: parseErrorContent,
							 | 
						||
| 
								 | 
							
										},
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for i, test := range tests {
							 | 
						||
| 
								 | 
							
										errorPrefix := fmt.Sprintf("Test [%d]: ", i)
							 | 
						||
| 
								 | 
							
										errorSuffix := fmt.Sprintf(" Command to parse: [%s]", test.input)
							 | 
						||
| 
								 | 
							
										actualCommand, actualArgs, actualErr := SplitCommandAndArgs(test.input)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// test if error matches expectation
							 | 
						||
| 
								 | 
							
										if test.expectedErrContent != "" {
							 | 
						||
| 
								 | 
							
											if actualErr == nil {
							 | 
						||
| 
								 | 
							
												t.Errorf(errorPrefix+"Expected error with content [%s], found no error."+errorSuffix, test.expectedErrContent)
							 | 
						||
| 
								 | 
							
											} else if !strings.Contains(actualErr.Error(), test.expectedErrContent) {
							 | 
						||
| 
								 | 
							
												t.Errorf(errorPrefix+"Expected error with content [%s], found [%v]."+errorSuffix, test.expectedErrContent, actualErr)
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										} else if actualErr != nil {
							 | 
						||
| 
								 | 
							
											t.Errorf(errorPrefix+"Expected no error, found [%v]."+errorSuffix, actualErr)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// test if command matches
							 | 
						||
| 
								 | 
							
										if test.expectedCommand != actualCommand {
							 | 
						||
| 
								 | 
							
											t.Errorf(errorPrefix+"Expected command: [%s], actual: [%s]."+errorSuffix, test.expectedCommand, actualCommand)
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// test if arguments match
							 | 
						||
| 
								 | 
							
										if len(test.expectedArgs) != len(actualArgs) {
							 | 
						||
| 
								 | 
							
											t.Errorf(errorPrefix+"Wrong number of arguments! Expected [%v], actual [%v]."+errorSuffix, test.expectedArgs, actualArgs)
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
											// test args only if the count matches.
							 | 
						||
| 
								 | 
							
											for j, actualArg := range actualArgs {
							 | 
						||
| 
								 | 
							
												expectedArg := test.expectedArgs[j]
							 | 
						||
| 
								 | 
							
												if actualArg != expectedArg {
							 | 
						||
| 
								 | 
							
													t.Errorf(errorPrefix+"Argument at position [%d] differ! Expected [%s], actual [%s]"+errorSuffix, j, expectedArg, actualArg)
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								func ExampleSplitCommandAndArgs() {
							 | 
						||
| 
								 | 
							
									var commandLine string
							 | 
						||
| 
								 | 
							
									var command string
							 | 
						||
| 
								 | 
							
									var args []string
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// just for the test - change GOOS and reset it at the end of the test
							 | 
						||
| 
								 | 
							
									runtimeGoos = "windows"
							 | 
						||
| 
								 | 
							
									defer func() {
							 | 
						||
| 
								 | 
							
										runtimeGoos = runtime.GOOS
							 | 
						||
| 
								 | 
							
									}()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									commandLine = `mkdir /P "C:\Program Files"`
							 | 
						||
| 
								 | 
							
									command, args, _ = SplitCommandAndArgs(commandLine)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									fmt.Printf("Windows: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// set GOOS to linux
							 | 
						||
| 
								 | 
							
									runtimeGoos = "linux"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									commandLine = `mkdir -p /path/with\ space`
							 | 
						||
| 
								 | 
							
									command, args, _ = SplitCommandAndArgs(commandLine)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									fmt.Printf("Linux: %s: %s [%s]\n", commandLine, command, strings.Join(args, ","))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									// Output:
							 | 
						||
| 
								 | 
							
									// Windows: mkdir /P "C:\Program Files": mkdir [/P,C:\Program Files]
							 | 
						||
| 
								 | 
							
									// Linux: mkdir -p /path/with\ space: mkdir [-p,/path/with space]
							 | 
						||
| 
								 | 
							
								}
							 |