github.com/everdrone/grab@v0.1.7-0.20230416223925-40674b995521/internal/utils/parse_urls_test.go (about) 1 package utils 2 3 import ( 4 "os" 5 "path/filepath" 6 "reflect" 7 "testing" 8 9 tu "github.com/everdrone/grab/testutils" 10 "github.com/hashicorp/hcl/v2" 11 "golang.org/x/exp/slices" 12 ) 13 14 func TestIsValidURL(t *testing.T) { 15 tests := []struct { 16 URL string 17 Want bool 18 Name string 19 }{ 20 {URL: "", Want: false, Name: "empty"}, 21 {URL: "/foo/bar", Want: false, Name: "unix absolute path"}, 22 {URL: "://foo/bar", Want: false, Name: "no scheme"}, 23 {URL: "https://foo/bar", Want: true, Name: "no dot com ssl"}, 24 {URL: "tcp://foo/bar", Want: true, Name: "tcp no dot com"}, 25 {URL: "https://foo.com/bar", Want: true, Name: "valid ssl"}, 26 {URL: "1http://anything.com/fails", Want: false, Name: "invalid scheme"}, 27 {URL: "tcp://foo.com/bar", Want: true, Name: "valid tcp"}, 28 {URL: "c:\\windows\\bad", Want: false, Name: "windows absolute"}, 29 {URL: "\\unix\\good", Want: false, Name: "windows absolute without drive"}, 30 {URL: "unix\\good", Want: false, Name: "windows relative"}, 31 {URL: "foo/bar", Want: false, Name: "unix relative"}, 32 {URL: "../foo/bar", Want: false, Name: "unix relative parent"}, 33 {URL: "~/foo/bar", Want: false, Name: "unix home directory"}, 34 } 35 36 for _, tt := range tests { 37 t.Run(tt.Name, func(t *testing.T) { 38 _, got := IsValidURL(tt.URL) 39 if got != tt.Want { 40 t.Errorf("got: %v, want: %v", got, tt.Want) 41 } 42 }) 43 } 44 } 45 46 func TestParseURLList(t *testing.T) { 47 tests := []struct { 48 Name string 49 Input string 50 Filename string 51 Want []string 52 HasErrors bool 53 WantDiags hcl.Diagnostics 54 }{ 55 { 56 Name: "empty", 57 Input: "", 58 Want: []string{}, 59 HasErrors: false, 60 WantDiags: nil, 61 }, 62 { 63 Name: "all comments", 64 Input: `// https://example.com 65 # https://example.com 66 ;https://example.com 67 68 # this is ignored as well 69 `, 70 Want: []string{}, 71 HasErrors: false, 72 WantDiags: nil, 73 }, 74 { 75 Name: "only one", 76 Input: `// https://example.com 77 https://example.com 78 ;https://example.com 79 80 # this is ignored as well 81 `, 82 Want: []string{ 83 "https://example.com", 84 }, 85 HasErrors: false, 86 WantDiags: nil, 87 }, 88 { 89 Name: "removes fragment", 90 Input: `https://example.com/foo#bar`, 91 Want: []string{ 92 "https://example.com/foo", 93 }, 94 HasErrors: false, 95 WantDiags: nil, 96 }, 97 { 98 Name: "invalid url", 99 Input: `not-an-url lol`, 100 Want: nil, 101 HasErrors: true, 102 WantDiags: hcl.Diagnostics{ 103 &hcl.Diagnostic{ 104 Severity: hcl.DiagError, 105 Summary: "Invalid URL", 106 Detail: "The string 'not-an-url lol' is not a valid url.", 107 Subject: &hcl.Range{ 108 Filename: "list.txt", 109 Start: hcl.Pos{Line: 1, Column: 1}, 110 End: hcl.Pos{Line: 1, Column: len("not-an-url lol") + 1}, 111 }, 112 }, 113 }, 114 }, 115 } 116 117 for _, test := range tests { 118 t.Run(test.Name, func(t *testing.T) { 119 urls, diags := ParseURLList(test.Input, "list.txt") 120 121 if diags.HasErrors() != test.HasErrors { 122 t.Errorf("got: %v, want: %v", diags.HasErrors(), test.HasErrors) 123 } 124 125 if !diags.HasErrors() && !reflect.DeepEqual(urls, test.Want) { 126 t.Errorf("got: %v, want: %v", urls, test.Want) 127 } 128 129 if !diags.HasErrors() && !reflect.DeepEqual(diags, test.WantDiags) { 130 t.Errorf("got: %v, want: %v", diags, test.WantDiags) 131 } 132 }) 133 } 134 } 135 136 func TestGetURLsFromArgs(t *testing.T) { 137 initialWd, _ := os.Getwd() 138 defer func() { 139 _ = os.Chdir(initialWd) 140 }() 141 142 root := tu.GetOSRoot() 143 Fs, Io, Wd = tu.SetupMemMapFs(root) 144 145 Fs.MkdirAll(filepath.Join(root, "other", "directory"), os.ModePerm) 146 Fs.MkdirAll(filepath.Join(root, "tmp"), os.ModePerm) 147 Io.WriteFile(Fs, filepath.Join(root, "restricted__r.txt"), []byte("not readable"), os.ModePerm) 148 Io.WriteFile(Fs, filepath.Join(root, "tmp", "list.ini"), []byte(`// https://example.com 149 https://example.com 150 https://more.com?foo=bar#baz 151 ;https://example.com 152 153 # this is ignored as well 154 `), os.ModePerm) 155 Io.WriteFile(Fs, filepath.Join(root, "tmp", "invalid.ini"), []byte(`// https://example.com 156 \x000 157 ;https://example.com 158 159 # this is ignored as well 160 `), os.ModePerm) 161 162 tests := []struct { 163 Name string 164 Args []string 165 Want []string 166 WantDiags hcl.Diagnostics 167 }{ 168 { 169 Name: "empty", 170 Args: []string{}, 171 Want: []string{}, 172 WantDiags: nil, 173 }, 174 { 175 Name: "file with comments", 176 Args: []string{filepath.Join(root, "tmp", "list.ini")}, 177 Want: []string{ 178 "https://example.com", 179 "https://more.com?foo=bar", 180 }, 181 WantDiags: nil, 182 }, 183 { 184 Name: "relative file with comments", 185 Args: []string{filepath.Join("tmp", "list.ini")}, 186 Want: []string{ 187 "https://example.com", 188 "https://more.com?foo=bar", 189 }, 190 WantDiags: nil, 191 }, 192 { 193 Name: "one url", 194 Args: []string{"https://example.com"}, 195 Want: []string{ 196 "https://example.com", 197 }, 198 WantDiags: nil, 199 }, 200 { 201 Name: "multiple urls", 202 Args: []string{"https://example.com", "http://aws.com"}, 203 Want: []string{ 204 "https://example.com", 205 "http://aws.com", 206 }, 207 WantDiags: nil, 208 }, 209 { 210 Name: "invalid url", 211 Args: []string{"no-scheme.org", "https://aws.com"}, 212 Want: []string{}, 213 WantDiags: hcl.Diagnostics{ 214 &hcl.Diagnostic{ 215 Severity: hcl.DiagError, 216 Summary: "Invalid argument", 217 Detail: "The argument 'no-scheme.org' is not a valid url, nor a file.", 218 }, 219 }, 220 }, 221 { 222 Name: "file does not exist", 223 Args: []string{"file_does_not_exist.txt"}, 224 Want: []string{}, 225 WantDiags: hcl.Diagnostics{ 226 &hcl.Diagnostic{ 227 Severity: hcl.DiagError, 228 Summary: "Invalid argument", 229 Detail: "The argument 'file_does_not_exist.txt' is not a valid url, nor a file.", 230 }, 231 }, 232 }, 233 { 234 Name: "file not readable", 235 Args: []string{"restricted__r.txt"}, 236 Want: []string{}, 237 WantDiags: hcl.Diagnostics{ 238 &hcl.Diagnostic{ 239 Severity: hcl.DiagError, 240 Summary: "Could not read file", 241 Detail: "Could not read file '" + filepath.Join(root, "restricted__r.txt") + "'.", 242 }, 243 }, 244 }, 245 { 246 Name: "url and relative file", 247 Args: []string{"https://aws.com", filepath.Join("tmp", "list.ini")}, 248 Want: []string{ 249 "https://aws.com", 250 "https://example.com", 251 "https://more.com?foo=bar", 252 }, 253 WantDiags: nil, 254 }, 255 { 256 Name: "url and invalid url in file", 257 Args: []string{"https://aws.com", filepath.Join("tmp", "invalid.ini")}, 258 Want: []string{}, 259 WantDiags: hcl.Diagnostics{ 260 &hcl.Diagnostic{ 261 Severity: hcl.DiagError, 262 Summary: "Invalid URL", 263 Detail: "The string '\\x000' is not a valid url.", 264 Subject: &hcl.Range{ 265 Filename: filepath.Join(root, "tmp", "invalid.ini"), 266 Start: hcl.Pos{Line: 2, Column: 1}, 267 End: hcl.Pos{Line: 2, Column: len("\\x000") + 1}, 268 }, 269 }, 270 }, 271 }, 272 } 273 274 for _, test := range tests { 275 t.Run(test.Name, func(tc *testing.T) { 276 Wd = filepath.Join(root) 277 278 got, diags := GetURLsFromArgs(test.Args) 279 280 if !slices.Equal(got, test.Want) { 281 tc.Errorf("got: %v, want: %v", got, test.Want) 282 } 283 284 if !reflect.DeepEqual(diags, test.WantDiags) { 285 tc.Errorf("got: %v, want: %v", diags, test.WantDiags) 286 } 287 }) 288 } 289 }