github.com/GoogleCloudPlatform/testgrid@v0.0.174/util/links_test.go (about) 1 /* 2 Copyright 2023 The TestGrid Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package util 18 19 import ( 20 "testing" 21 22 configpb "github.com/GoogleCloudPlatform/testgrid/pb/config" 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/go-cmp/cmp/cmpopts" 25 ) 26 27 func TestTokens(t *testing.T) { 28 cases := []struct { 29 name string 30 template *configpb.LinkTemplate 31 want []string 32 }{ 33 { 34 name: "nil", 35 template: nil, 36 want: nil, 37 }, 38 { 39 name: "empty", 40 template: &configpb.LinkTemplate{}, 41 want: nil, 42 }, 43 { 44 name: "basically works", 45 template: &configpb.LinkTemplate{ 46 Url: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-name>", 47 Options: []*configpb.LinkOptionsTemplate{ 48 { 49 Key: "prefix", 50 Value: "<gcs-prefix>", 51 }, 52 { 53 Key: "build", 54 Value: "<build-id>", 55 }, 56 { 57 Key: "prop", 58 Value: "<my-prop>", 59 }, 60 { 61 Key: "foo", 62 Value: "<custom-0>", 63 }, 64 }, 65 }, 66 want: []string{WorkflowName, WorkflowID, TestID, TestName, GcsPrefix, BuildID, "<my-prop>", "<custom-0>"}, 67 }, 68 { 69 name: "duplicate tokens", 70 template: &configpb.LinkTemplate{ 71 Url: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-id>", 72 Options: []*configpb.LinkOptionsTemplate{ 73 { 74 Key: "workflow", 75 Value: "<workflow-name>", 76 }, 77 }, 78 }, 79 want: []string{WorkflowName, WorkflowID, TestID}, 80 }, 81 { 82 name: "encode", 83 template: &configpb.LinkTemplate{ 84 Url: "https://test.com/<encode:<workflow-name>>", 85 Options: []*configpb.LinkOptionsTemplate{ 86 { 87 Key: "workflow-id", 88 Value: "<encode:<workflow-id>>", 89 }, 90 }, 91 }, 92 want: []string{WorkflowName, WorkflowID}, 93 }, 94 { 95 name: "invalid", 96 template: &configpb.LinkTemplate{ 97 Url: "http://test.com/<oh-no", 98 }, 99 want: nil, 100 }, 101 } 102 103 for _, tc := range cases { 104 t.Run(tc.name, func(t *testing.T) { 105 got := Tokens(tc.template) 106 if diff := cmp.Diff(tc.want, got, cmpopts.SortSlices(func(a, b string) bool { return a < b })); diff != "" { 107 t.Errorf("Tokens(%v) differed (-want, +got): %s", tc.template, diff) 108 } 109 }) 110 } 111 } 112 113 func TestExpandTemplate(t *testing.T) { 114 cases := []struct { 115 name string 116 template *configpb.LinkTemplate 117 parameters map[string]string 118 err bool 119 want string 120 }{ 121 { 122 name: "nil", 123 template: nil, 124 parameters: nil, 125 want: "", 126 }, 127 { 128 name: "nil parameters", 129 template: &configpb.LinkTemplate{ 130 Url: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-name>", 131 Options: []*configpb.LinkOptionsTemplate{ 132 { 133 Key: "prefix", 134 Value: "<gcs-prefix>", 135 }, 136 { 137 Key: "build", 138 Value: "<build-id>", 139 }, 140 { 141 Key: "prop", 142 Value: "<my-prop>", 143 }, 144 { 145 Key: "foo", 146 Value: "<custom-0>", 147 }, 148 }, 149 }, 150 parameters: nil, 151 want: "https://test.com/%3Cworkflow-name%3E/%3Cworkflow-id%3E/%3Ctest-id%3E/%3Ctest-name%3E?build=%3Cbuild-id%3E&foo=%3Ccustom-0%3E&prefix=%3Cgcs-prefix%3E&prop=%3Cmy-prop%3E", 152 }, 153 { 154 name: "empty", 155 template: &configpb.LinkTemplate{}, 156 parameters: map[string]string{}, 157 want: "", 158 }, 159 { 160 name: "basically works", 161 template: &configpb.LinkTemplate{ 162 Url: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-name>", 163 Options: []*configpb.LinkOptionsTemplate{ 164 { 165 Key: "prefix", 166 Value: "<gcs-prefix>", 167 }, 168 { 169 Key: "build", 170 Value: "<build-id>", 171 }, 172 { 173 Key: "prop", 174 Value: "<my-prop>", 175 }, 176 { 177 Key: "foo", 178 Value: "<custom-0>", 179 }, 180 }, 181 }, 182 parameters: map[string]string{ 183 WorkflowName: "//my-workflow", 184 WorkflowID: "workflow-id-1", 185 TestID: "test-id-1", 186 TestName: "//path/to:my-test", 187 GcsPrefix: "my-bucket/has/results", 188 BuildID: "build-1", 189 "<my-prop>": "foo", 190 "<custom-0>": "bar", 191 }, 192 want: "https://test.com///my-workflow/workflow-id-1/test-id-1///path/to:my-test?build=build-1&foo=bar&prefix=my-bucket%2Fhas%2Fresults&prop=foo", 193 }, 194 { 195 name: "invalid url", 196 template: &configpb.LinkTemplate{ 197 Url: "https://test.com%+", 198 Options: []*configpb.LinkOptionsTemplate{ 199 { 200 Key: "prop", 201 Value: "%+<my-prop>", 202 }, 203 }, 204 }, 205 parameters: map[string]string{ 206 "<my-prop>": "foo", 207 }, 208 err: true, 209 }, 210 { 211 name: "duplicate tokens", 212 template: &configpb.LinkTemplate{ 213 Url: "https://test.com/<test-id>/<test-id>", 214 Options: []*configpb.LinkOptionsTemplate{ 215 { 216 Key: "workflow", 217 Value: "<workflow-name>", 218 }, 219 { 220 Key: "workflow-name", 221 Value: "<workflow-name>", 222 }, 223 }, 224 }, 225 parameters: map[string]string{ 226 WorkflowName: "//my-workflow", 227 WorkflowID: "workflow-id-1", 228 TestID: "test-id-1", 229 TestName: "//path/to:my-test", 230 GcsPrefix: "my-bucket/has/results", 231 BuildID: "build-1", 232 "<my-prop>": "foo", 233 }, 234 want: "https://test.com/test-id-1/test-id-1?workflow=%2F%2Fmy-workflow&workflow-name=%2F%2Fmy-workflow", 235 }, 236 { 237 name: "encode", 238 template: &configpb.LinkTemplate{ 239 Url: "https://test.com/<encode:<workflow-name>>", 240 Options: []*configpb.LinkOptionsTemplate{ 241 { 242 Key: "workflow", 243 Value: "<encode:<workflow-name>>", 244 }, 245 }, 246 }, 247 parameters: map[string]string{ 248 WorkflowName: "//my-workfl@w", 249 }, 250 want: "https://test.com/%2F%2Fmy-workfl@w?workflow=%2F%2Fmy-workfl%40w", 251 }, 252 { 253 name: "invalid", 254 template: &configpb.LinkTemplate{ 255 Url: "http://test.com/<workflow-name", 256 }, 257 parameters: map[string]string{ 258 WorkflowName: "//my-workflow", 259 }, 260 want: "http://test.com/%3Cworkflow-name", 261 }, 262 } 263 264 for _, tc := range cases { 265 t.Run(tc.name, func(t *testing.T) { 266 got, err := ExpandTemplate(tc.template, tc.parameters) 267 if tc.err && err == nil { 268 t.Errorf("ExpandTemplate() did not error as expected.") 269 } else if !tc.err && err != nil { 270 t.Errorf("ExpandTemplate() errored unexpectedly: %v", err) 271 } 272 if diff := cmp.Diff(tc.want, got); diff != "" { 273 t.Errorf("ExpandTemplate() differed (-want, +got): %s", diff) 274 } 275 }) 276 } 277 } 278 279 func TestExpandTemplateString(t *testing.T) { 280 cases := []struct { 281 name string 282 templateStr string 283 parameters map[string]string 284 isQuery bool 285 want string 286 }{ 287 { 288 name: "empty", 289 templateStr: "", 290 parameters: nil, 291 want: "", 292 }, 293 { 294 name: "nil parameters", 295 templateStr: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-name>", 296 parameters: nil, 297 want: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-name>", 298 }, 299 { 300 name: "basically works", 301 templateStr: "https://test.com/<workflow-name>/<workflow-id>/<test-id>/<test-name>/<my-prop>/<custom-0>", 302 parameters: map[string]string{ 303 WorkflowName: "//my-workflow", 304 WorkflowID: "workflow-id-1", 305 TestID: "test-id-1", 306 TestName: "//path/to:my-test", 307 "<my-prop>": "foo", 308 "<custom-0>": "magic", 309 }, 310 want: "https://test.com///my-workflow/workflow-id-1/test-id-1///path/to:my-test/foo/magic", 311 }, 312 { 313 name: "duplicate tokens", 314 templateStr: "https://test.com/<test-id>/<test-id>", 315 parameters: map[string]string{ 316 TestID: "test-id-1", 317 }, 318 want: "https://test.com/test-id-1/test-id-1", 319 }, 320 { 321 name: "encode", 322 templateStr: "https://test.com/<encode:<workflow-name>>", 323 parameters: map[string]string{ 324 WorkflowName: "//my-workfl@w", 325 }, 326 want: "https://test.com/%2F%2Fmy-workfl@w", 327 }, 328 { 329 name: "encode query", 330 templateStr: "<encode:<workflow-name>>", 331 parameters: map[string]string{ 332 WorkflowName: "//my-workfl@w", 333 }, 334 isQuery: true, 335 want: "//my-workfl@w", 336 }, 337 { 338 name: "invalid", 339 templateStr: "http://test.com/<workflow-name", 340 parameters: map[string]string{ 341 WorkflowName: "//my-workflow", 342 }, 343 want: "http://test.com/<workflow-name", 344 }, 345 } 346 347 for _, tc := range cases { 348 t.Run(tc.name, func(t *testing.T) { 349 got := expandTemplateString(tc.templateStr, tc.parameters, tc.isQuery) 350 if diff := cmp.Diff(tc.want, got); diff != "" { 351 t.Errorf("expandTemplateString() differed (-want, +got): %s", diff) 352 } 353 }) 354 } 355 }