github.com/GuanceCloud/cliutils@v1.1.21/point/check_test.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the MIT License. 3 // This product includes software developed at Guance Cloud (https://www.guance.com/). 4 // Copyright 2021-present Guance, Inc. 5 6 package point 7 8 import ( 9 "fmt" 10 "math" 11 "sort" 12 "testing" 13 T "testing" 14 15 "github.com/GuanceCloud/cliutils" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestCheckMeasurement(t *testing.T) { 21 cases := []struct { 22 name, 23 measurement, 24 expect string 25 opts []Option 26 }{ 27 { 28 name: "n-len", 29 measurement: "abc-def", 30 opts: []Option{ 31 WithMaxMeasurementLen(3), 32 }, 33 expect: "abc", 34 }, 35 36 { 37 name: "no-limit", 38 measurement: "abc-def", 39 expect: "abc-def", 40 }, 41 42 { 43 name: "empty-measurement", 44 measurement: "", 45 expect: DefaultMeasurementName, 46 }, 47 48 { 49 name: "empty-measurement-trim", 50 measurement: "", 51 opts: []Option{ 52 WithMaxMeasurementLen(3), 53 }, 54 expect: DefaultMeasurementName[:3], 55 }, 56 57 { 58 name: "test-utf8-measurement", 59 measurement: "δΈζπ", 60 expect: "δΈζπ", 61 }, 62 63 { 64 name: "test-utf8-measurement-trim", 65 measurement: "δΈζπ", 66 opts: []Option{ 67 WithMaxMeasurementLen(3), 68 }, 69 expect: string([]byte("δΈζπ")[:3]), 70 }, 71 } 72 73 for _, tc := range cases { 74 t.Run(tc.name, func(t *T.T) { 75 cfg := GetCfg() 76 defer PutCfg(cfg) 77 for _, opt := range tc.opts { 78 opt(cfg) 79 } 80 81 c := checker{cfg: cfg} 82 m := c.checkMeasurement(tc.measurement) 83 assert.Equal(t, tc.expect, m) 84 }) 85 } 86 } 87 88 func TestCheckPoints(t *T.T) { 89 t.Run("string", func(t *T.T) { 90 var kvs KVs 91 kvs = kvs.Add("f1", 1.23, false, false) 92 kvs = kvs.Add("str", "hello", false, false) 93 kvs = kvs.Add("u64", uint64(math.MaxUint64), false, false) 94 95 pt := NewPointV2("m1", kvs, WithPrecheck(false)) 96 pts := CheckPoints([]*Point{pt}, WithStrField(false)) 97 assert.Len(t, pts, 1) 98 assert.Nil(t, pts[0].Get("str")) 99 assert.Equal(t, 1.23, pts[0].Get("f1")) 100 }) 101 102 t.Run("u64", func(t *T.T) { 103 var kvs KVs 104 kvs = kvs.Add("f1", 1.23, false, false) 105 kvs = kvs.Add("str", "hello", false, false) 106 kvs = kvs.Add("u64", uint64(math.MaxUint64), false, false) 107 108 pt := NewPointV2("m1", kvs, WithPrecheck(false)) 109 pts := CheckPoints([]*Point{pt}, WithU64Field(false)) 110 assert.Len(t, pts, 1) 111 assert.Nil(t, pts[0].Get("u64")) 112 assert.Equal(t, "hello", pts[0].Get("str")) 113 }) 114 115 t.Run("dot-in-key", func(t *T.T) { 116 var kvs KVs 117 kvs = kvs.Add("f.1", 1.23, false, false) 118 kvs = kvs.Add("u64", uint64(math.MaxUint64), false, false) 119 120 pt := NewPointV2("m1", kvs, WithPrecheck(false)) 121 122 pts := CheckPoints([]*Point{pt}, WithDotInKey(false)) 123 assert.Len(t, pts, 1) 124 assert.Equal(t, uint64(math.MaxUint64), pts[0].Get("u64")) 125 assert.Equal(t, 1.23, pts[0].Get("f_1")) 126 assert.Len(t, pts[0].Warns(), 1) 127 128 t.Logf("point: %s", pts[0].Pretty()) 129 }) 130 } 131 132 func TestCheckTags(t *T.T) { 133 cases := []struct { 134 name string 135 t map[string]string 136 expect KVs 137 warns int 138 opts []Option 139 }{ 140 { 141 name: "disable-tag", 142 t: map[string]string{ 143 "t1": "123456", 144 "t2": "23456", 145 }, 146 opts: []Option{ 147 WithDisabledKeys(NewTagKey(`t1`, "")), 148 }, 149 warns: 1, 150 expect: NewTags( 151 map[string]string{ 152 "t2": "23456", 153 }), 154 }, 155 156 // { TODO 157 // name: `exceed-tag-kv-compose`, 158 // t: map[string]string{ 159 // "t1": "12345", 160 // "t2": "abcde", 161 // }, 162 // opts: []Option{ 163 // WithMaxKVComposeLen(10), 164 // WithTime(time.Unix(0, 123)), 165 // }, 166 167 // warns: 1, 168 // expect: NewTags(map[string]string{ 169 // "t1": "12345", 170 // }), 171 // }, 172 173 { 174 name: `tag-kv-compose-limit-0`, 175 t: map[string]string{ 176 "t1": "12345", 177 "t2": "abcde", 178 }, 179 opts: []Option{ 180 WithMaxMeasurementLen(0), // do nothing 181 }, 182 183 expect: NewTags(map[string]string{ 184 "t1": "12345", 185 "t2": "abcde", 186 }), 187 }, 188 } 189 190 for _, tc := range cases { 191 t.Run(tc.name, func(t *T.T) { 192 cfg := GetCfg() 193 defer PutCfg(cfg) 194 195 for _, opt := range tc.opts { 196 opt(cfg) 197 } 198 199 c := checker{cfg: cfg} 200 kvs := c.checkKVs(NewTags(tc.t)) 201 202 assert.Equal(t, tc.warns, len(c.warns), "got warns: %v, kvs: %s", c.warns, kvs.Pretty()) 203 204 eopt := eqopt{} 205 if tc.expect != nil { 206 eq, r := eopt.kvsEq(tc.expect, kvs) 207 assert.True(t, eq, "reason: %s", r) 208 } 209 }) 210 } 211 212 t.Run("key-updated-but-conflict", func(t *T.T) { 213 /////////////////////// 214 // dot in tag key 215 var kvs KVs 216 kvs = kvs.AddV2("f.1", "some string", false, WithKVTagSet(true)) 217 kvs = kvs.AddV2("f_1", 1.23, false) 218 219 pt := NewPointV2("m", kvs, WithDotInKey(false)) 220 221 assert.Lenf(t, pt.pt.Fields, 1, "pt: %s", pt.Pretty()) 222 // drop tag 223 assert.Len(t, pt.pt.Fields, 1) 224 assert.Equal(t, 1.23, pt.Get(`f_1`).(float64)) 225 t.Logf("pt: %s", pt.Pretty()) 226 227 /////////////////////// 228 // too long tag key 229 kvs = kvs[:0] 230 kvs = kvs.AddV2("f111", "some string", false, WithKVTagSet(true)) 231 kvs = kvs.AddV2("f1", 1.23, false) 232 pt = NewPointV2("m", kvs, WithMaxTagKeyLen(2)) 233 234 assert.Len(t, pt.pt.Fields, 1) 235 // drop tag 236 assert.Equal(t, 1.23, pt.Get(`f1`).(float64)) 237 t.Logf("pt: %s", pt.Pretty()) 238 239 /////////////////////// 240 // too long field key 241 kvs = kvs[:0] 242 kvs = kvs.AddV2("f1", 1.23, false) 243 kvs = kvs.AddV2("f111", "some string", false) 244 pt = NewPointV2("m", kvs, WithMaxFieldKeyLen(2)) 245 246 assert.Len(t, pt.pt.Fields, 1) 247 // drop field 248 assert.Equal(t, 1.23, pt.Get(`f1`).(float64)) 249 t.Logf("pt: %s", pt.Pretty()) 250 251 /////////////////////// 252 // conflict on updated-key 253 kvs = kvs[:0] 254 kvs = kvs.AddV2("f.1", 1.23, false) // f.1 => f_1 255 kvs = kvs.AddV2("f_111", "some string", false) // f_111 => f_1: conflict 256 pt = NewPointV2("m", kvs, WithMaxFieldKeyLen(3), WithDotInKey(false)) 257 258 assert.Len(t, pt.pt.Fields, 1) 259 // drop field 260 assert.Equal(t, 1.23, pt.Get(`f_1`).(float64)) 261 t.Logf("pt: %s", pt.Pretty()) 262 }) 263 } 264 265 func TestCheckFields(t *T.T) { 266 cases := []struct { 267 name string 268 f map[string]interface{} 269 expect map[string]interface{} 270 warns int 271 opts []Option 272 }{ 273 { 274 name: "exceed-max-field-len", 275 f: map[string]interface{}{ 276 "f1": "123456", 277 }, 278 opts: []Option{WithMaxFieldValLen(1)}, 279 warns: 1, 280 expect: map[string]interface{}{ 281 "f1": "1", 282 }, 283 }, 284 285 { 286 name: "exceed-max-field-count", 287 f: map[string]interface{}{ 288 "f1": "aaaaaa1", 289 "f2": "aaaaaa2", 290 "f3": "aaaaaa3", 291 "f4": "aaaaaa4", 292 "f5": "aaaaaa5", 293 "f6": "aaaaaa6", 294 "f7": "aaaaaa7", 295 "f8": "aaaaaa8", 296 "f9": "aaaaaa9", 297 "f0": "aaaaaa0", 298 }, 299 opts: []Option{WithMaxFields(1), WithKeySorted(true)}, 300 warns: 1, 301 expect: map[string]interface{}{ 302 "f0": "aaaaaa0", 303 }, 304 }, 305 306 { 307 name: "exceed-max-field-key-len", 308 f: map[string]interface{}{ 309 "a1": "123456", 310 "b": "abc123", 311 }, 312 opts: []Option{WithMaxFieldKeyLen(1)}, 313 warns: 1, 314 expect: map[string]interface{}{ 315 "a": "123456", // key truncated 316 "b": "abc123", 317 }, 318 }, 319 320 { 321 name: "drop-metric-string-field", 322 f: map[string]interface{}{ 323 "a": 123456, 324 "b": "abc123", // dropped 325 }, 326 opts: []Option{WithStrField(false)}, 327 warns: 1, 328 expect: map[string]interface{}{ 329 "a": int64(123456), 330 }, 331 }, 332 333 { 334 name: "invalid-field-type", 335 f: map[string]interface{}{ 336 "b": struct{}{}, 337 }, 338 warns: 1, 339 }, 340 341 { 342 name: "nil-field", 343 f: map[string]interface{}{ 344 "a": nil, // set value to nil 345 "b": 123, 346 "c": struct{}{}, // ignored 347 }, 348 warns: 2, 349 expect: map[string]interface{}{ 350 "b": int64(123), 351 "a": nil, 352 "c": nil, 353 }, 354 }, 355 356 { 357 name: "exceed-max-int64-under-influxdb1.x", 358 f: map[string]interface{}{ 359 "b": uint64(math.MaxInt64) + 1, // exceed max-int64 360 }, 361 opts: DefaultMetricOptionsForInflux1X(), 362 warns: 1, 363 }, 364 365 { 366 name: "exceed-max-int64", 367 f: map[string]interface{}{ 368 "a": uint64(math.MaxInt64) + 1, // exceed max-int64, drop the key under non-strict mode 369 "b": "abc", 370 }, 371 372 expect: map[string]interface{}{ 373 "a": uint64(math.MaxInt64) + 1, 374 "b": "abc", 375 }, 376 }, 377 378 { 379 name: "small-uint64", 380 f: map[string]interface{}{ 381 "a": uint64(12345), 382 }, 383 expect: map[string]interface{}{ 384 "a": uint64(12345), 385 }, 386 }, 387 388 { 389 name: "no-field", 390 expect: nil, 391 warns: 0, 392 }, 393 394 { 395 name: "dot-in-key", 396 f: map[string]interface{}{ 397 "a.b": 12345, 398 "c": "12345", 399 }, 400 opts: []Option{WithDotInKey(false)}, 401 warns: 1, 402 expect: map[string]interface{}{ 403 "a_b": int64(12345), 404 "c": "12345", 405 }, 406 }, 407 408 { 409 name: "disabled-field", 410 f: map[string]interface{}{ 411 "a": 12345, 412 "b": "12345", 413 }, 414 warns: 1, 415 opts: []Option{WithDisabledKeys(NewKey("a", I))}, 416 expect: map[string]interface{}{ 417 "b": "12345", 418 }, 419 }, 420 421 { 422 name: "valid-fields", 423 f: map[string]interface{}{ 424 "small-uint64": uint64(12345), 425 "int8": int8(1), 426 "int": int(1), 427 "int16": int16(12345), 428 "int32": int32(1234567), 429 "int64": int64(123456789), 430 "uint8": uint8(1), 431 "uint": uint(1), 432 "uint16": uint16(12345), 433 "uint32": uint32(1234567), 434 "uint64": uint64(12345678), 435 "float32": float32(1.234), 436 "float64": float64(1.234), 437 "str": "abc", 438 }, 439 440 expect: map[string]interface{}{ 441 "small-uint64": uint64(12345), 442 "int8": int64(1), 443 "int": int64(1), 444 "int16": int64(12345), 445 "int32": int64(1234567), 446 "int64": int64(123456789), 447 "uint": uint64(1), 448 "uint8": uint64(1), 449 "uint16": uint64(12345), 450 "uint32": uint64(1234567), 451 "uint64": uint64(12345678), 452 "float32": float32(1.234), 453 "float64": float64(1.234), 454 "str": "abc", 455 }, 456 }, 457 } 458 459 for _, tc := range cases { 460 t.Run(tc.name, func(t *T.T) { 461 cfg := GetCfg() 462 defer PutCfg(cfg) 463 464 for _, opt := range tc.opts { 465 opt(cfg) 466 } 467 468 t.Logf("cfg: %+#v", cfg) 469 470 c := checker{cfg: cfg} 471 472 kvs := NewKVs(tc.f) 473 expect := NewKVs(tc.expect) 474 475 if cfg.keySorted { 476 sort.Sort(kvs) 477 sort.Sort(expect) 478 } 479 480 kvs = c.checkKVs(kvs) 481 require.Equal(t, tc.warns, len(c.warns), "got pt %s", kvs.Pretty()) 482 483 eopt := eqopt{} 484 if tc.expect != nil { 485 eq, _ := eopt.kvsEq(expect, kvs) 486 assert.True(t, eq, "expect:\n%s\ngot:\n%s", expect.Pretty(), kvs.Pretty()) 487 } 488 }) 489 } 490 } 491 492 func TestAdjustKV(t *T.T) { 493 cases := []struct { 494 name, x, y string 495 }{ 496 { 497 name: "x-with-trailling-backslash", 498 x: "x\\", 499 y: "x", 500 }, 501 502 { 503 name: "x-with-line-break", 504 x: ` 505 x 506 def`, 507 y: " x def", 508 }, 509 } 510 511 for _, tc := range cases { 512 t.Run(tc.name, func(t *T.T) { 513 assert.Equal(t, tc.y, adjustKV(tc.x)) 514 }) 515 } 516 } 517 518 func TestRequiredKV(t *T.T) { 519 t.Run(`add`, func(t *T.T) { 520 pt := NewPointV2(`abc`, NewKVs(map[string]any{"f1": 123}), 521 WithRequiredKeys(NewKey(`rk`, I, 1024))) 522 assert.Equal(t, int64(1024), pt.Get(`rk`)) 523 }) 524 } 525 526 func BenchmarkCheck(b *T.B) { 527 __shortKey := cliutils.CreateRandomString(10) 528 __shortVal := cliutils.CreateRandomString(128) 529 530 cases := []struct { 531 name string 532 m string 533 t map[string]string 534 f map[string]interface{} 535 opts []Option 536 }{ 537 { 538 name: "3-tags-4-field", 539 m: "not-set", 540 t: map[string]string{ 541 __shortKey: __shortVal, 542 }, 543 f: map[string]interface{}{ 544 "f1": 123, 545 "f2": 123.0, 546 "f3": __shortVal, 547 "f4": false, 548 }, 549 }, 550 551 { 552 name: "3-tags-4-field-on-string-metric", 553 m: "not-set", 554 t: map[string]string{ 555 __shortKey: __shortVal, 556 }, 557 f: map[string]interface{}{ 558 "f1": 123, 559 "f2": 123.0, 560 "f3": __shortVal, 561 "f4": false, 562 }, 563 opts: DefaultMetricOptions(), 564 }, 565 566 { 567 name: "3-tags-4-field-on-disabled-tag-and-field", 568 m: "not-set", 569 t: map[string]string{ 570 __shortKey: __shortVal, 571 "source": "should-be-dropped", 572 }, 573 f: map[string]interface{}{ 574 "f1": 123, 575 "f2": 123.0, 576 "f3": __shortVal, 577 "f4": false, 578 "source": "should-be-dropped", 579 }, 580 opts: DefaultLoggingOptions(), 581 }, 582 583 { 584 name: "100-tags-300-field-on-warnning-tags-fields", 585 m: "not-set", 586 t: func() map[string]string { 587 x := map[string]string{} 588 for i := 0; i < 100; i++ { 589 switch i % 3 { 590 case 0: // normal 591 x[fmt.Sprintf("%s-%d", __shortKey, i)] = cliutils.CreateRandomString(32) 592 case 1: // key contains `\n' 593 x[fmt.Sprintf("%s-\n%d", __shortKey, i)] = cliutils.CreateRandomString(32) 594 case 2: // key suffix with `\' 595 x[fmt.Sprintf("%s-%d\\", __shortKey, i)] = cliutils.CreateRandomString(32) 596 } 597 } 598 return x 599 }(), 600 f: func() map[string]interface{} { 601 x := map[string]interface{}{} 602 for i := 0; i < 100; i++ { 603 switch i % 3 { 604 case 0: // exceed max int64 605 x[fmt.Sprintf("%s-%d", __shortKey, i)] = uint64(math.MaxInt64) + 1 606 case 1: // exceed max field value length 607 x[fmt.Sprintf("%s-%d", __shortKey, i)] = cliutils.CreateRandomString(1024 + 1) 608 case 2: // nil 609 x[fmt.Sprintf("%s-%d", __shortKey, i)] = nil 610 } 611 } 612 return x 613 }(), 614 opts: []Option{ 615 WithMaxFieldValLen(1024), 616 WithMaxFields(299), // < 300 617 }, 618 }, 619 } 620 621 for _, tc := range cases { 622 pt, err := NewPoint(tc.m, tc.t, tc.f, tc.opts...) 623 assert.NoError(b, err) 624 625 cfg := GetCfg() 626 defer PutCfg(cfg) 627 628 for _, opt := range tc.opts { 629 opt(cfg) 630 } 631 c := checker{cfg: cfg} 632 633 b.ResetTimer() 634 b.Run(tc.name, func(b *T.B) { 635 for i := 0; i < b.N; i++ { 636 c.check(pt) 637 } 638 }) 639 } 640 } 641 642 func BenchmarkCheckPoints(b *T.B) { 643 b.Run("check-rand-pts", func(b *T.B) { 644 r := NewRander() 645 pts := r.Rand(1000) 646 647 b.ResetTimer() 648 for i := 0; i < b.N; i++ { 649 CheckPoints(pts) 650 } 651 }) 652 653 b.Run("check-pts-without-str-field", func(b *T.B) { 654 r := NewRander() 655 pts := r.Rand(1000) 656 657 b.ResetTimer() 658 for i := 0; i < b.N; i++ { 659 CheckPoints(pts, WithStrField(false)) 660 } 661 }) 662 663 b.Run("check-pts-without-u64-field", func(b *T.B) { 664 r := NewRander() 665 pts := r.Rand(1000) 666 667 b.ResetTimer() 668 for i := 0; i < b.N; i++ { 669 CheckPoints(pts, WithU64Field(false)) 670 } 671 }) 672 }