|
17 | 17 | ConstantRateLimiter,
|
18 | 18 | DBMAsyncJob,
|
19 | 19 | RateLimitingTTLCache,
|
| 20 | + TagManager, |
| 21 | + TagType, |
20 | 22 | default_json_event_encoding,
|
21 | 23 | get_agent_host_tags,
|
22 | 24 | obfuscate_sql_with_metadata,
|
@@ -388,3 +390,211 @@ def test_tracked_query(aggregator):
|
388 | 390 | aggregator.assert_metric(
|
389 | 391 | "dd.testcheck.operation.time", tags=["test:tag", "operation:test_query"], count=1, value=1000.0
|
390 | 392 | )
|
| 393 | + |
| 394 | + |
| 395 | +class TestTagManager: |
| 396 | + def test_init(self): |
| 397 | + """Test initialization of TagManager""" |
| 398 | + tag_manager = TagManager() |
| 399 | + assert tag_manager._tags == {} |
| 400 | + assert tag_manager._cached_tag_list is None |
| 401 | + assert tag_manager._keyless == TagType.KEYLESS |
| 402 | + |
| 403 | + @pytest.mark.parametrize( |
| 404 | + 'key,value,expected_tags', |
| 405 | + [ |
| 406 | + ('test_key', 'test_value', {'test_key': ['test_value']}), |
| 407 | + (None, 'test_value', {TagType.KEYLESS: ['test_value']}), |
| 408 | + ], |
| 409 | + ) |
| 410 | + def test_set_tag(self, key, value, expected_tags): |
| 411 | + """Test setting tags with various combinations of key and value""" |
| 412 | + tag_manager = TagManager() |
| 413 | + tag_manager.set_tag(key, value) |
| 414 | + assert tag_manager._tags == expected_tags |
| 415 | + assert tag_manager._cached_tag_list is None |
| 416 | + |
| 417 | + def test_set_tag_existing_key_append(self): |
| 418 | + """Test appending a value to an existing key""" |
| 419 | + tag_manager = TagManager() |
| 420 | + tag_manager.set_tag('test_key', 'value1') |
| 421 | + tag_manager.set_tag('test_key', 'value2') |
| 422 | + assert tag_manager._tags == {'test_key': ['value1', 'value2']} |
| 423 | + assert tag_manager._cached_tag_list is None |
| 424 | + |
| 425 | + def test_set_tag_existing_key_replace(self): |
| 426 | + """Test replacing values for an existing key""" |
| 427 | + tag_manager = TagManager() |
| 428 | + tag_manager.set_tag('test_key', 'value1') |
| 429 | + tag_manager.set_tag('test_key', 'value2', replace=True) |
| 430 | + assert tag_manager._tags == {'test_key': ['value2']} |
| 431 | + assert tag_manager._cached_tag_list is None |
| 432 | + |
| 433 | + def test_set_tag_duplicate_value(self): |
| 434 | + """Test setting a duplicate value for a key""" |
| 435 | + tag_manager = TagManager() |
| 436 | + tag_manager.set_tag('test_key', 'test_value') |
| 437 | + tag_manager.set_tag('test_key', 'test_value') |
| 438 | + assert tag_manager._tags == {'test_key': ['test_value']} |
| 439 | + assert tag_manager._cached_tag_list is None |
| 440 | + |
| 441 | + @pytest.mark.parametrize( |
| 442 | + 'key,values,delete_key,delete_value,expected_tags,description', |
| 443 | + [ |
| 444 | + ( |
| 445 | + 'test_key', |
| 446 | + ['value1', 'value2'], |
| 447 | + 'test_key', |
| 448 | + 'value1', |
| 449 | + {'test_key': ['value2']}, |
| 450 | + 'deleting specific value', |
| 451 | + ), |
| 452 | + ('test_key', ['value1', 'value2'], 'test_key', None, {}, 'deleting all values for key'), |
| 453 | + ( |
| 454 | + None, |
| 455 | + ['value1', 'value2'], |
| 456 | + None, |
| 457 | + 'value1', |
| 458 | + {TagType.KEYLESS: ['value2']}, |
| 459 | + 'deleting specific keyless value', |
| 460 | + ), |
| 461 | + (None, ['value1', 'value2'], None, None, {}, 'deleting all keyless values'), |
| 462 | + ], |
| 463 | + ) |
| 464 | + def test_delete_tag(self, key, values, delete_key, delete_value, expected_tags, description): |
| 465 | + """Test various tag deletion scenarios""" |
| 466 | + tag_manager = TagManager() |
| 467 | + # Set up initial tags |
| 468 | + for value in values: |
| 469 | + tag_manager.set_tag(key, value) |
| 470 | + |
| 471 | + # Generate initial cache |
| 472 | + tag_manager.get_tags() |
| 473 | + assert tag_manager._cached_tag_list is not None |
| 474 | + |
| 475 | + # Perform deletion |
| 476 | + assert tag_manager.delete_tag(delete_key, delete_value) |
| 477 | + |
| 478 | + # Verify cache is invalidated |
| 479 | + assert tag_manager._cached_tag_list is None |
| 480 | + |
| 481 | + # Verify internal state |
| 482 | + assert tag_manager._tags == expected_tags |
| 483 | + |
| 484 | + @pytest.mark.parametrize( |
| 485 | + 'initial_tags,tag_list,replace,expected_tags,description', |
| 486 | + [ |
| 487 | + ( |
| 488 | + [], |
| 489 | + ['key1:value1', 'key2:value2', 'value3'], |
| 490 | + False, |
| 491 | + ['key1:value1', 'key2:value2', 'value3'], |
| 492 | + 'setting new tags', |
| 493 | + ), |
| 494 | + ( |
| 495 | + ['key1:old_value', 'key2:old_value', 'keyless1'], |
| 496 | + ['key1:new_value', 'key3:new_value', 'keyless2'], |
| 497 | + True, |
| 498 | + ['key1:new_value', 'key3:new_value', 'keyless2'], |
| 499 | + 'replacing all existing tags with new ones', |
| 500 | + ), |
| 501 | + ( |
| 502 | + ['key1:old_value'], |
| 503 | + ['key1:new_value'], |
| 504 | + False, |
| 505 | + ['key1:new_value', 'key1:old_value'], |
| 506 | + 'appending to existing tags', |
| 507 | + ), |
| 508 | + ([], ['key1:value1', 'key1:value1'], False, ['key1:value1'], 'setting duplicate values'), |
| 509 | + ( |
| 510 | + [], |
| 511 | + ['key1:value1', 'value2', 'key2:value3'], |
| 512 | + False, |
| 513 | + ['key1:value1', 'key2:value3', 'value2'], |
| 514 | + 'setting mixed format tags', |
| 515 | + ), |
| 516 | + ], |
| 517 | + ) |
| 518 | + def test_set_tags_from_list(self, initial_tags, tag_list, replace, expected_tags, description): |
| 519 | + """Test various tag list setting scenarios""" |
| 520 | + tag_manager = TagManager() |
| 521 | + # Set up initial tags if any |
| 522 | + for tag in initial_tags: |
| 523 | + if ':' in tag: |
| 524 | + key, value = tag.split(':', 1) |
| 525 | + tag_manager.set_tag(key, value) |
| 526 | + else: |
| 527 | + tag_manager.set_tag(None, tag) |
| 528 | + |
| 529 | + # Generate cache to ensure it exists |
| 530 | + _ = tag_manager.get_tags() |
| 531 | + assert tag_manager._cached_tag_list is not None |
| 532 | + |
| 533 | + tag_manager.set_tags_from_list(tag_list, replace=replace) |
| 534 | + |
| 535 | + # Verify cache was invalidated when needed |
| 536 | + if replace: |
| 537 | + assert tag_manager._cached_tag_list is None |
| 538 | + |
| 539 | + # Verify final state |
| 540 | + assert sorted(tag_manager.get_tags()) == sorted(expected_tags) |
| 541 | + |
| 542 | + def test_get_tags_empty(self): |
| 543 | + tag_manager = TagManager() |
| 544 | + assert tag_manager.get_tags() == [] |
| 545 | + |
| 546 | + @pytest.mark.parametrize( |
| 547 | + 'regular_tags,expected_tags', |
| 548 | + [ |
| 549 | + ({'key1': ['value1']}, ['key1:value1']), |
| 550 | + ({'key1': ['value1', 'value2']}, ['key1:value1', 'key1:value2']), |
| 551 | + ], |
| 552 | + ) |
| 553 | + def test_get_tags(self, regular_tags, expected_tags): |
| 554 | + """Test getting tags with various combinations of regular tags""" |
| 555 | + tag_manager = TagManager() |
| 556 | + |
| 557 | + # Set up regular tags |
| 558 | + for key, values in regular_tags.items(): |
| 559 | + for value in values: |
| 560 | + tag_manager.set_tag(key, value) |
| 561 | + |
| 562 | + # Verify tags |
| 563 | + assert sorted(tag_manager.get_tags()) == sorted(expected_tags) |
| 564 | + |
| 565 | + def test_cache_management(self): |
| 566 | + """Test that tag caches are properly managed""" |
| 567 | + tag_manager = TagManager() |
| 568 | + |
| 569 | + # Set initial tags |
| 570 | + tag_manager.set_tag('regular_key', 'regular_value') |
| 571 | + |
| 572 | + # First call should generate cache |
| 573 | + _ = tag_manager.get_tags() |
| 574 | + assert tag_manager._cached_tag_list is not None |
| 575 | + |
| 576 | + # Modify regular tags |
| 577 | + tag_manager.set_tag('regular_key2', 'regular_value2') |
| 578 | + assert tag_manager._cached_tag_list is None |
| 579 | + |
| 580 | + # Verify all tags are included |
| 581 | + second_result = tag_manager.get_tags() |
| 582 | + assert sorted(second_result) == sorted(['regular_key:regular_value', 'regular_key2:regular_value2']) |
| 583 | + |
| 584 | + def test_get_tags_mutation(self): |
| 585 | + """Test that modifying the returned list from get_tags() does not affect the internal cache""" |
| 586 | + tag_manager = TagManager() |
| 587 | + tag_manager.set_tag('test_key', 'test_value') |
| 588 | + |
| 589 | + # Get the tags list |
| 590 | + tags = tag_manager.get_tags() |
| 591 | + assert tags == ['test_key:test_value'] |
| 592 | + |
| 593 | + # Modify the returned list |
| 594 | + tags.append('modified_tag') |
| 595 | + tags[0] = 'modified_existing_tag' |
| 596 | + |
| 597 | + # Get tags again - should be unchanged |
| 598 | + new_tags = tag_manager.get_tags() |
| 599 | + assert new_tags == ['test_key:test_value'] |
| 600 | + assert new_tags != tags # The lists should be different objects |
0 commit comments