| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- import struct
- import time
- import uuid
- from unittest import mock
-
- import pytest
- from hypothesis import given
- from hypothesis import strategies as st
-
- from libs.uuid_utils import _create_uuidv7_bytes, uuidv7, uuidv7_boundary, uuidv7_timestamp
-
-
- # Tests for private helper function _create_uuidv7_bytes
- def test_create_uuidv7_bytes_basic_structure():
- """Test basic byte structure creation."""
- timestamp_ms = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
- random_bytes = b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x11\x22"
-
- result = _create_uuidv7_bytes(timestamp_ms, random_bytes)
-
- # Should be exactly 16 bytes
- assert len(result) == 16
- assert isinstance(result, bytes)
-
- # Create UUID from bytes to verify it's valid
- uuid_obj = uuid.UUID(bytes=result)
- assert uuid_obj.version == 7
-
-
- def test_create_uuidv7_bytes_timestamp_encoding():
- """Test timestamp is correctly encoded in first 48 bits."""
- timestamp_ms = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
- random_bytes = b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
-
- result = _create_uuidv7_bytes(timestamp_ms, random_bytes)
-
- # Extract timestamp from first 6 bytes
- timestamp_bytes = b"\x00\x00" + result[0:6]
- extracted_timestamp = struct.unpack(">Q", timestamp_bytes)[0]
-
- assert extracted_timestamp == timestamp_ms
-
-
- def test_create_uuidv7_bytes_version_bits():
- """Test version bits are set to 7."""
- timestamp_ms = 1609459200000
- random_bytes = b"\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00" # Set first 2 bytes to all 1s
-
- result = _create_uuidv7_bytes(timestamp_ms, random_bytes)
-
- # Extract version from bytes 6-7
- version_and_rand_a = struct.unpack(">H", result[6:8])[0]
- version = (version_and_rand_a >> 12) & 0x0F
-
- assert version == 7
-
-
- def test_create_uuidv7_bytes_variant_bits():
- """Test variant bits are set correctly."""
- timestamp_ms = 1609459200000
- random_bytes = b"\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00" # Set byte 8 to all 1s
-
- result = _create_uuidv7_bytes(timestamp_ms, random_bytes)
-
- # Check variant bits in byte 8 (should be 10xxxxxx)
- variant_byte = result[8]
- variant_bits = (variant_byte >> 6) & 0b11
-
- assert variant_bits == 0b10 # Should be binary 10
-
-
- def test_create_uuidv7_bytes_random_data():
- """Test random bytes are placed correctly."""
- timestamp_ms = 1609459200000
- random_bytes = b"\x12\x34\x56\x78\x9a\xbc\xde\xf0\x11\x22"
-
- result = _create_uuidv7_bytes(timestamp_ms, random_bytes)
-
- # Check random data A (12 bits from bytes 6-7, excluding version)
- version_and_rand_a = struct.unpack(">H", result[6:8])[0]
- rand_a = version_and_rand_a & 0x0FFF
- expected_rand_a = struct.unpack(">H", random_bytes[0:2])[0] & 0x0FFF
- assert rand_a == expected_rand_a
-
- # Check random data B (bytes 8-15, with variant bits preserved)
- # Byte 8 should have variant bits set but preserve lower 6 bits
- expected_byte_8 = (random_bytes[2] & 0x3F) | 0x80
- assert result[8] == expected_byte_8
-
- # Bytes 9-15 should match random_bytes[3:10]
- assert result[9:16] == random_bytes[3:10]
-
-
- def test_create_uuidv7_bytes_zero_random():
- """Test with zero random bytes (boundary case)."""
- timestamp_ms = 1609459200000
- zero_random_bytes = b"\x00" * 10
-
- result = _create_uuidv7_bytes(timestamp_ms, zero_random_bytes)
-
- # Should still be valid UUIDv7
- uuid_obj = uuid.UUID(bytes=result)
- assert uuid_obj.version == 7
-
- # Version bits should be 0x7000
- version_and_rand_a = struct.unpack(">H", result[6:8])[0]
- assert version_and_rand_a == 0x7000
-
- # Variant byte should be 0x80 (variant bits + zero random bits)
- assert result[8] == 0x80
-
- # Remaining bytes should be zero
- assert result[9:16] == b"\x00" * 7
-
-
- def test_uuidv7_basic_generation():
- """Test basic UUID generation produces valid UUIDv7."""
- result = uuidv7()
-
- # Should be a UUID object
- assert isinstance(result, uuid.UUID)
-
- # Should be version 7
- assert result.version == 7
-
- # Should have correct variant (RFC 4122 variant)
- # Variant bits should be 10xxxxxx (0x80-0xBF range)
- variant_byte = result.bytes[8]
- assert (variant_byte >> 6) == 0b10
-
-
- def test_uuidv7_with_custom_timestamp():
- """Test UUID generation with custom timestamp."""
- custom_timestamp = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
- result = uuidv7(custom_timestamp)
-
- assert isinstance(result, uuid.UUID)
- assert result.version == 7
-
- # Extract and verify timestamp
- extracted_timestamp = uuidv7_timestamp(result)
- assert isinstance(extracted_timestamp, int)
- assert extracted_timestamp == custom_timestamp # Exact match for integer milliseconds
-
-
- def test_uuidv7_with_none_timestamp(monkeypatch):
- """Test UUID generation with None timestamp uses current time."""
- mock_time = 1609459200
- mock_time_func = mock.Mock(return_value=mock_time)
- monkeypatch.setattr("time.time", mock_time_func)
- result = uuidv7(None)
-
- assert isinstance(result, uuid.UUID)
- assert result.version == 7
-
- # Should use the mocked current time (converted to milliseconds)
- assert mock_time_func.called
- extracted_timestamp = uuidv7_timestamp(result)
- assert extracted_timestamp == mock_time * 1000 # 1609459200.0 * 1000
-
-
- def test_uuidv7_time_ordering():
- """Test that sequential UUIDs have increasing timestamps."""
- # Generate UUIDs with incrementing timestamps (in milliseconds)
- timestamp1 = 1609459200000 # 2021-01-01 00:00:00 UTC
- timestamp2 = 1609459201000 # 2021-01-01 00:00:01 UTC
- timestamp3 = 1609459202000 # 2021-01-01 00:00:02 UTC
-
- uuid1 = uuidv7(timestamp1)
- uuid2 = uuidv7(timestamp2)
- uuid3 = uuidv7(timestamp3)
-
- # Extract timestamps
- ts1 = uuidv7_timestamp(uuid1)
- ts2 = uuidv7_timestamp(uuid2)
- ts3 = uuidv7_timestamp(uuid3)
-
- # Should be in ascending order
- assert ts1 < ts2 < ts3
-
- # UUIDs should be lexicographically ordered by their string representation
- # due to time-ordering property of UUIDv7
- uuid_strings = [str(uuid1), str(uuid2), str(uuid3)]
- assert uuid_strings == sorted(uuid_strings)
-
-
- def test_uuidv7_uniqueness():
- """Test that multiple calls generate different UUIDs."""
- # Generate multiple UUIDs with the same timestamp (in milliseconds)
- timestamp = 1609459200000
- uuids = [uuidv7(timestamp) for _ in range(100)]
-
- # All should be unique despite same timestamp (due to random bits)
- assert len(set(uuids)) == 100
-
- # All should have the same extracted timestamp
- for uuid_obj in uuids:
- extracted_ts = uuidv7_timestamp(uuid_obj)
- assert extracted_ts == timestamp
-
-
- def test_uuidv7_timestamp_error_handling_wrong_version():
- """Test error handling for non-UUIDv7 inputs."""
-
- uuid_v4 = uuid.uuid4()
- with pytest.raises(ValueError) as exc_ctx:
- uuidv7_timestamp(uuid_v4)
- assert "Expected UUIDv7 (version 7)" in str(exc_ctx.value)
- assert f"got version {uuid_v4.version}" in str(exc_ctx.value)
-
-
- @given(st.integers(max_value=2**48 - 1, min_value=0))
- def test_uuidv7_timestamp_round_trip(timestamp_ms):
- # Generate UUID with timestamp
- uuid_obj = uuidv7(timestamp_ms)
-
- # Extract timestamp back
- extracted_timestamp = uuidv7_timestamp(uuid_obj)
-
- # Should match exactly for integer millisecond timestamps
- assert extracted_timestamp == timestamp_ms
-
-
- def test_uuidv7_timestamp_edge_cases():
- """Test timestamp extraction with edge case values."""
- # Test with very small timestamp
- small_timestamp = 1 # 1ms after epoch
- uuid_small = uuidv7(small_timestamp)
- extracted_small = uuidv7_timestamp(uuid_small)
- assert extracted_small == small_timestamp
-
- # Test with large timestamp (year 2038+)
- large_timestamp = 2147483647000 # 2038-01-19 03:14:07 UTC in milliseconds
- uuid_large = uuidv7(large_timestamp)
- extracted_large = uuidv7_timestamp(uuid_large)
- assert extracted_large == large_timestamp
-
-
- def test_uuidv7_boundary_basic_generation():
- """Test basic boundary UUID generation with a known timestamp."""
- timestamp = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
- result = uuidv7_boundary(timestamp)
-
- # Should be a UUID object
- assert isinstance(result, uuid.UUID)
-
- # Should be version 7
- assert result.version == 7
-
- # Should have correct variant (RFC 4122 variant)
- # Variant bits should be 10xxxxxx (0x80-0xBF range)
- variant_byte = result.bytes[8]
- assert (variant_byte >> 6) == 0b10
-
-
- def test_uuidv7_boundary_timestamp_extraction():
- """Test that boundary UUID timestamp can be extracted correctly."""
- timestamp = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
- boundary_uuid = uuidv7_boundary(timestamp)
-
- # Extract timestamp using existing function
- extracted_timestamp = uuidv7_timestamp(boundary_uuid)
-
- # Should match exactly
- assert extracted_timestamp == timestamp
-
-
- def test_uuidv7_boundary_deterministic():
- """Test that boundary UUIDs are deterministic for same timestamp."""
- timestamp = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
-
- # Generate multiple boundary UUIDs with same timestamp
- uuid1 = uuidv7_boundary(timestamp)
- uuid2 = uuidv7_boundary(timestamp)
- uuid3 = uuidv7_boundary(timestamp)
-
- # Should all be identical
- assert uuid1 == uuid2 == uuid3
- assert str(uuid1) == str(uuid2) == str(uuid3)
-
-
- def test_uuidv7_boundary_is_minimum():
- """Test that boundary UUID is lexicographically smaller than regular UUIDs."""
- timestamp = 1609459200000 # 2021-01-01 00:00:00 UTC in milliseconds
-
- # Generate boundary UUID
- boundary_uuid = uuidv7_boundary(timestamp)
-
- # Generate multiple regular UUIDs with same timestamp
- regular_uuids = [uuidv7(timestamp) for _ in range(50)]
-
- # Boundary UUID should be lexicographically smaller than all regular UUIDs
- boundary_str = str(boundary_uuid)
- for regular_uuid in regular_uuids:
- regular_str = str(regular_uuid)
- assert boundary_str < regular_str, f"Boundary {boundary_str} should be < regular {regular_str}"
-
- # Also test with bytes comparison
- boundary_bytes = boundary_uuid.bytes
- for regular_uuid in regular_uuids:
- regular_bytes = regular_uuid.bytes
- assert boundary_bytes < regular_bytes
-
-
- def test_uuidv7_boundary_different_timestamps():
- """Test that boundary UUIDs with different timestamps are ordered correctly."""
- timestamp1 = 1609459200000 # 2021-01-01 00:00:00 UTC
- timestamp2 = 1609459201000 # 2021-01-01 00:00:01 UTC
- timestamp3 = 1609459202000 # 2021-01-01 00:00:02 UTC
-
- uuid1 = uuidv7_boundary(timestamp1)
- uuid2 = uuidv7_boundary(timestamp2)
- uuid3 = uuidv7_boundary(timestamp3)
-
- # Extract timestamps to verify
- ts1 = uuidv7_timestamp(uuid1)
- ts2 = uuidv7_timestamp(uuid2)
- ts3 = uuidv7_timestamp(uuid3)
-
- # Should be in ascending order
- assert ts1 < ts2 < ts3
-
- # UUIDs should be lexicographically ordered
- uuid_strings = [str(uuid1), str(uuid2), str(uuid3)]
- assert uuid_strings == sorted(uuid_strings)
-
- # Bytes should also be ordered
- assert uuid1.bytes < uuid2.bytes < uuid3.bytes
-
-
- def test_uuidv7_boundary_edge_cases():
- """Test boundary UUID generation with edge case timestamp values."""
- # Test with timestamp 0 (Unix epoch)
- epoch_uuid = uuidv7_boundary(0)
- assert isinstance(epoch_uuid, uuid.UUID)
- assert epoch_uuid.version == 7
- assert uuidv7_timestamp(epoch_uuid) == 0
-
- # Test with very large timestamp values
- large_timestamp = 2147483647000 # 2038-01-19 03:14:07 UTC in milliseconds
- large_uuid = uuidv7_boundary(large_timestamp)
- assert isinstance(large_uuid, uuid.UUID)
- assert large_uuid.version == 7
- assert uuidv7_timestamp(large_uuid) == large_timestamp
-
- # Test with current time
- current_time = int(time.time() * 1000)
- current_uuid = uuidv7_boundary(current_time)
- assert isinstance(current_uuid, uuid.UUID)
- assert current_uuid.version == 7
- assert uuidv7_timestamp(current_uuid) == current_time
|