github.com/influxdata/influxdb/v2@v2.7.6/annotation_test.go (about) 1 package influxdb 2 3 import ( 4 "encoding/json" 5 "strings" 6 "testing" 7 "time" 8 9 "github.com/influxdata/influxdb/v2/kit/platform" 10 "github.com/influxdata/influxdb/v2/kit/platform/errors" 11 "github.com/stretchr/testify/require" 12 ) 13 14 var ( 15 testTime time.Time = time.Now() 16 testTime2 time.Time = testTime.Add(time.Minute) 17 18 annID, _ = platform.IDFromString("2345678901234567") 19 ) 20 21 func nowFunc() time.Time { 22 return testTime 23 } 24 25 func TestAnnotationCreate(t *testing.T) { 26 type tst struct { 27 name string 28 input AnnotationCreate 29 expected AnnotationCreate 30 err *errors.Error 31 } 32 33 tests := []tst{ 34 { 35 name: "minimum valid create request", 36 input: AnnotationCreate{ 37 Summary: "this is a default annotation", 38 }, 39 expected: AnnotationCreate{ 40 StreamTag: "default", 41 Summary: "this is a default annotation", 42 EndTime: &testTime, 43 StartTime: &testTime, 44 }, 45 }, 46 { 47 name: "full valid create request", 48 input: AnnotationCreate{ 49 StreamTag: "other", 50 Summary: "this is another annotation", 51 Message: "This is a much longer description or message to add to the annotation summary", 52 Stickers: map[string]string{"product": "cloud"}, 53 EndTime: &testTime2, 54 StartTime: &testTime, 55 }, 56 expected: AnnotationCreate{ 57 StreamTag: "other", 58 Summary: "this is another annotation", 59 Message: "This is a much longer description or message to add to the annotation summary", 60 Stickers: map[string]string{"product": "cloud"}, 61 EndTime: &testTime2, 62 StartTime: &testTime, 63 }, 64 }, 65 { 66 name: "empty create request", 67 input: AnnotationCreate{}, 68 err: errEmptySummary, 69 }, 70 { 71 name: "end time before start create request", 72 input: AnnotationCreate{ 73 Summary: "this is a default annotation", 74 EndTime: &testTime, 75 StartTime: &testTime2, 76 }, 77 err: errReversedTimes, 78 }, 79 { 80 name: "default end time before start create request", 81 input: AnnotationCreate{ 82 Summary: "this is a default annotation", 83 StartTime: &testTime2, 84 }, 85 err: errReversedTimes, 86 }, 87 { 88 name: "summary too long", 89 input: AnnotationCreate{ 90 Summary: strings.Repeat("a", 256), 91 }, 92 err: errSummaryTooLong, 93 }, 94 { 95 name: "message too long", 96 input: AnnotationCreate{ 97 Summary: "longTom", 98 Message: strings.Repeat("a", 4097), 99 }, 100 err: errMsgTooLong, 101 }, 102 { 103 name: "stream tag too long", 104 input: AnnotationCreate{ 105 Summary: "longTom", 106 StreamTag: strings.Repeat("a", 256), 107 }, 108 err: errStreamTagTooLong, 109 }, 110 { 111 name: "sticker key too long", 112 input: AnnotationCreate{ 113 Summary: "longTom", 114 Stickers: map[string]string{strings.Repeat("1", 256): "val"}, 115 }, 116 err: errStickerTooLong, 117 }, 118 { 119 name: "sticker val too long", 120 input: AnnotationCreate{ 121 Summary: "longTom", 122 Stickers: map[string]string{"key": strings.Repeat("1", 256)}, 123 }, 124 err: errStickerTooLong, 125 }, 126 } 127 128 for _, test := range tests { 129 t.Run(test.name, func(t *testing.T) { 130 err := test.input.Validate(nowFunc) 131 if test.err != nil { 132 require.Equal(t, test.err, err) 133 return 134 } 135 136 require.NoError(t, err) 137 require.Equal(t, test.expected, test.input) 138 }) 139 } 140 } 141 142 func TestDeleteFilter(t *testing.T) { 143 type tst struct { 144 name string 145 input AnnotationDeleteFilter 146 expected AnnotationDeleteFilter 147 err *errors.Error 148 } 149 150 tests := []tst{ 151 { 152 name: "minimum valid delete", 153 input: AnnotationDeleteFilter{ 154 StreamTag: "default", 155 EndTime: &testTime, 156 StartTime: &testTime, 157 }, 158 expected: AnnotationDeleteFilter{ 159 StreamTag: "default", 160 EndTime: &testTime, 161 StartTime: &testTime, 162 }, 163 }, 164 { 165 name: "full valid delete", 166 input: AnnotationDeleteFilter{ 167 StreamTag: "default", 168 Stickers: map[string]string{"product": "oss"}, 169 EndTime: &testTime, 170 StartTime: &testTime, 171 }, 172 expected: AnnotationDeleteFilter{ 173 StreamTag: "default", 174 Stickers: map[string]string{"product": "oss"}, 175 EndTime: &testTime, 176 StartTime: &testTime, 177 }, 178 }, 179 { 180 name: "missing stream tag", 181 input: AnnotationDeleteFilter{ 182 Stickers: map[string]string{"product": "oss"}, 183 EndTime: &testTime, 184 StartTime: &testTime, 185 }, 186 err: errMissingStreamTagOrId, 187 }, 188 { 189 name: "missing start time", 190 input: AnnotationDeleteFilter{ 191 StreamTag: "default", 192 Stickers: map[string]string{"product": "oss"}, 193 EndTime: &testTime, 194 }, 195 err: errMissingStartTime, 196 }, 197 { 198 name: "missing end time", 199 input: AnnotationDeleteFilter{ 200 StreamTag: "default", 201 Stickers: map[string]string{"product": "oss"}, 202 StartTime: &testTime, 203 }, 204 err: errMissingEndTime, 205 }, 206 { 207 name: "end time before start create request", 208 input: AnnotationDeleteFilter{ 209 StreamTag: "default", 210 Stickers: map[string]string{"product": "oss"}, 211 EndTime: &testTime, 212 StartTime: &testTime2, 213 }, 214 err: errReversedTimes, 215 }, 216 } 217 218 for _, test := range tests { 219 t.Run(test.name, func(t *testing.T) { 220 err := test.input.Validate() 221 if test.err != nil { 222 require.Equal(t, test.err, err) 223 return 224 } 225 226 require.NoError(t, err) 227 require.Equal(t, test.expected, test.input) 228 }) 229 } 230 } 231 232 func TestAnnotationListFilter(t *testing.T) { 233 type tst struct { 234 name string 235 input AnnotationListFilter 236 expected AnnotationListFilter 237 checkValue bool 238 err *errors.Error 239 } 240 241 tests := []tst{ 242 { 243 name: "minimum valid", 244 input: AnnotationListFilter{ 245 BasicFilter: BasicFilter{ 246 EndTime: &testTime, 247 StartTime: &testTime, 248 }, 249 }, 250 expected: AnnotationListFilter{ 251 BasicFilter: BasicFilter{ 252 EndTime: &testTime, 253 StartTime: &testTime, 254 }, 255 }, 256 }, 257 { 258 name: "empty valid", 259 input: AnnotationListFilter{}, 260 expected: AnnotationListFilter{ 261 BasicFilter: BasicFilter{ 262 EndTime: &testTime, 263 StartTime: &testTime, 264 }, 265 }, 266 checkValue: true, 267 }, 268 { 269 name: "invalid due to reversed times", 270 input: AnnotationListFilter{ 271 BasicFilter: BasicFilter{ 272 EndTime: &testTime, 273 StartTime: &testTime2, 274 }, 275 }, 276 err: errReversedTimes, 277 }, 278 } 279 280 for _, test := range tests { 281 t.Run(test.name, func(t *testing.T) { 282 err := test.input.Validate(nowFunc) 283 if test.err != nil { 284 require.Equal(t, test.err, err) 285 return 286 } 287 288 require.NoError(t, err) 289 if test.checkValue { 290 require.Equal(t, *test.expected.BasicFilter.StartTime, *test.expected.BasicFilter.EndTime) 291 } else { 292 require.Equal(t, test.expected, test.input) 293 } 294 }) 295 } 296 } 297 298 func TestStreamListFilter(t *testing.T) { 299 type tst struct { 300 name string 301 input StreamListFilter 302 expected StreamListFilter 303 checkValue bool 304 err *errors.Error 305 } 306 307 tests := []tst{ 308 { 309 name: "minimum valid", 310 input: StreamListFilter{ 311 BasicFilter: BasicFilter{ 312 EndTime: &testTime, 313 StartTime: &testTime, 314 }, 315 }, 316 expected: StreamListFilter{ 317 BasicFilter: BasicFilter{ 318 EndTime: &testTime, 319 StartTime: &testTime, 320 }, 321 }, 322 }, 323 { 324 name: "empty valid", 325 input: StreamListFilter{}, 326 expected: StreamListFilter{ 327 BasicFilter: BasicFilter{ 328 EndTime: &testTime, 329 StartTime: &testTime, 330 }, 331 }, 332 checkValue: true, 333 }, 334 { 335 name: "invalid due to reversed times", 336 input: StreamListFilter{ 337 BasicFilter: BasicFilter{ 338 EndTime: &testTime, 339 StartTime: &testTime2, 340 }, 341 }, 342 err: errReversedTimes, 343 }, 344 } 345 346 for _, test := range tests { 347 t.Run(test.name, func(t *testing.T) { 348 err := test.input.Validate(nowFunc) 349 if test.err != nil { 350 require.Equal(t, test.err, err) 351 return 352 } 353 354 require.NoError(t, err) 355 if test.checkValue { 356 require.Equal(t, *test.expected.BasicFilter.StartTime, *test.expected.BasicFilter.EndTime) 357 } else { 358 require.Equal(t, test.expected, test.input) 359 } 360 }) 361 } 362 } 363 364 func TestStreamIsValid(t *testing.T) { 365 type tst struct { 366 name string 367 input Stream 368 err *errors.Error 369 } 370 371 tests := []tst{ 372 { 373 name: "minimum valid", 374 input: Stream{ 375 Name: "default", 376 }, 377 }, 378 { 379 name: "empty valid", 380 input: Stream{}, 381 }, 382 { 383 name: "invalid name too long", 384 input: Stream{ 385 Name: strings.Repeat("a", 512), 386 }, 387 err: errStreamNameTooLong, 388 }, 389 { 390 name: "invalid description too long", 391 input: Stream{ 392 Name: "longTom", 393 Description: strings.Repeat("a", 2048), 394 }, 395 err: errStreamDescTooLong, 396 }, 397 } 398 399 for _, test := range tests { 400 t.Run(test.name, func(t *testing.T) { 401 if test.err != nil { 402 require.Equal(t, test.err, test.input.Validate(false)) 403 } else { 404 require.NoError(t, test.input.Validate(false)) 405 } 406 }) 407 } 408 } 409 410 func TestBasicStreamIsValid(t *testing.T) { 411 type tst struct { 412 name string 413 input BasicStream 414 expected bool 415 } 416 417 tests := []tst{ 418 { 419 name: "minimum valid", 420 input: BasicStream{ 421 Names: []string{"default"}, 422 }, 423 expected: true, 424 }, 425 { 426 name: "invalid", 427 input: BasicStream{}, 428 expected: false, 429 }, 430 { 431 name: "empty name", 432 input: BasicStream{Names: []string{""}}, 433 expected: false, 434 }, 435 } 436 437 for _, test := range tests { 438 t.Run(test.name, func(t *testing.T) { 439 require.Equal(t, test.expected, test.input.IsValid()) 440 }) 441 } 442 } 443 444 func TestMashallReadAnnotations(t *testing.T) { 445 ra := ReadAnnotations{ 446 "default": []ReadAnnotation{ 447 { 448 ID: *annID, 449 Summary: "this is one annotation", 450 Stickers: map[string]string{"env": "testing"}, 451 StartTime: testTime.Format(time.RFC3339Nano), 452 EndTime: testTime2.Format(time.RFC3339Nano), 453 }, 454 { 455 ID: *annID, 456 Summary: "this is another annotation", 457 Stickers: map[string]string{"env": "testing"}, 458 StartTime: testTime.Format(time.RFC3339Nano), 459 EndTime: testTime.Format(time.RFC3339Nano), 460 }, 461 }, 462 "testing": []ReadAnnotation{ 463 { 464 ID: *annID, 465 Summary: "this is yet another annotation", 466 Stickers: map[string]string{"env": "testing"}, 467 StartTime: testTime.Format(time.RFC3339Nano), 468 EndTime: testTime.Format(time.RFC3339Nano), 469 }, 470 }, 471 } 472 473 b, err := json.Marshal(ra) 474 require.NoError(t, err) 475 require.Greater(t, len(b), 0) 476 } 477 478 func TestSetStickerIncludes(t *testing.T) { 479 type tst struct { 480 name string 481 input map[string][]string 482 expected AnnotationStickers 483 } 484 485 tests := []tst{ 486 { 487 name: "with stickerIncludes", 488 input: map[string][]string{ 489 "stickerIncludes[product]": {"oss"}, 490 "stickerIncludes[author]": {"russ"}, 491 "streams": {"default", "blogs"}, 492 }, 493 expected: map[string]string{ 494 "product": "oss", 495 "author": "russ", 496 }, 497 }, 498 { 499 name: "no sticker includes", 500 input: map[string][]string{ 501 "startTime": {"2021-01-13T22%3A17%3A37.953Z"}, 502 "endTime": {"2021-01-13T22%3A17%3A37.953Z"}, 503 "streams": {"default", "blogs"}, 504 }, 505 expected: map[string]string{}, 506 }, 507 } 508 509 for _, test := range tests { 510 t.Run(test.name, func(t *testing.T) { 511 f := AnnotationListFilter{} 512 f.SetStickerIncludes(test.input) 513 require.Equal(t, test.expected, f.StickerIncludes) 514 }) 515 } 516 } 517 518 func TestSetStickers(t *testing.T) { 519 type tst struct { 520 name string 521 input map[string][]string 522 expected map[string]string 523 } 524 525 tests := []tst{ 526 { 527 name: "with stickers", 528 input: map[string][]string{ 529 "stickers[product]": {"oss"}, 530 "stickers[author]": {"russ"}, 531 "streams": {"default", "blogs"}, 532 }, 533 expected: map[string]string{ 534 "product": "oss", 535 "author": "russ", 536 }, 537 }, 538 { 539 name: "no stickers", 540 input: map[string][]string{ 541 "startTime": {"2021-01-13T22%3A17%3A37.953Z"}, 542 "endTime": {"2021-01-13T22%3A17%3A37.953Z"}, 543 "streams": {"default", "blogs"}, 544 }, 545 expected: map[string]string{}, 546 }, 547 } 548 549 for _, test := range tests { 550 t.Run(test.name, func(t *testing.T) { 551 f := AnnotationDeleteFilter{} 552 f.SetStickers(test.input) 553 require.Equal(t, test.expected, f.Stickers) 554 }) 555 } 556 } 557 558 func TestStickerSliceToMap(t *testing.T) { 559 t.Parallel() 560 561 tests := []struct { 562 name string 563 stickers []string 564 want map[string]string 565 wantErr error 566 }{ 567 { 568 "good stickers", 569 []string{"good1=val1", "good2=val2"}, 570 map[string]string{"good1": "val1", "good2": "val2"}, 571 nil, 572 }, 573 { 574 "bad stickers", 575 []string{"this is an invalid sticker", "shouldbe=likethis"}, 576 nil, 577 invalidStickerError("this is an invalid sticker"), 578 }, 579 { 580 "no stickers", 581 []string{}, 582 map[string]string{}, 583 nil, 584 }, 585 } 586 587 for _, tt := range tests { 588 got, err := stickerSliceToMap(tt.stickers) 589 require.Equal(t, tt.want, got) 590 require.Equal(t, tt.wantErr, err) 591 } 592 }