|
25 | 25 | BSONMaxKey, |
26 | 26 | BSONMinKey, |
27 | 27 | BSONObjectId, |
| 28 | + BSONTimestamp, |
28 | 29 | _BSONType, |
29 | 30 | ) |
30 | 31 |
|
@@ -340,3 +341,83 @@ def test_bson_binary_copy(): |
340 | 341 | def test_bson_binary_pickle(): |
341 | 342 | val = BSONBinary(b"hello", subtype=5) |
342 | 343 | assert pickle.loads(pickle.dumps(val)) == val |
| 344 | + |
| 345 | + |
| 346 | +def test_bson_timestamp_valid(): |
| 347 | + ts = BSONTimestamp(1700000000, 42) |
| 348 | + assert ts.seconds == 1700000000 |
| 349 | + assert ts.increment == 42 |
| 350 | + assert ts._to_map_value() == { |
| 351 | + "__request_timestamp__": { |
| 352 | + "seconds": 1700000000, |
| 353 | + "increment": 42, |
| 354 | + } |
| 355 | + } |
| 356 | + assert repr(ts) == "BSONTimestamp(seconds=1700000000, increment=42)" |
| 357 | + |
| 358 | + |
| 359 | +def test_bson_timestamp_signed_int32_conversion(): |
| 360 | + # Negative signed 32-bit int inputs convert to unsigned uint32 per spec |
| 361 | + ts1 = BSONTimestamp(-1, -1) |
| 362 | + assert ts1.seconds == 4294967295 |
| 363 | + assert ts1.increment == 4294967295 |
| 364 | + |
| 365 | + ts2 = BSONTimestamp(-2147483648, -2147483648) |
| 366 | + assert ts2.seconds == 2147483648 |
| 367 | + assert ts2.increment == 2147483648 |
| 368 | + |
| 369 | + |
| 370 | +def test_bson_timestamp_boundaries(): |
| 371 | + ts_min = BSONTimestamp(0, 0) |
| 372 | + ts_max = BSONTimestamp(4294967295, 4294967295) |
| 373 | + assert ts_min.seconds == 0 |
| 374 | + assert ts_min.increment == 0 |
| 375 | + assert ts_max.seconds == 4294967295 |
| 376 | + assert ts_max.increment == 4294967295 |
| 377 | + |
| 378 | + |
| 379 | +@pytest.mark.parametrize( |
| 380 | + "sec_input, inc_input, exc_type, match_msg", |
| 381 | + [ |
| 382 | + (-2147483649, 0, ValueError, "must be between"), |
| 383 | + (0, -2147483649, ValueError, "must be between"), |
| 384 | + (4294967296, 0, ValueError, "must be between"), |
| 385 | + (0, 4294967296, ValueError, "must be between"), |
| 386 | + (True, 0, TypeError, "seconds must be an int"), |
| 387 | + (0, False, TypeError, "increment must be an int"), |
| 388 | + ("1700000000", 0, TypeError, "seconds must be an int"), |
| 389 | + (0, 1.5, TypeError, "increment must be an int"), |
| 390 | + ], |
| 391 | +) |
| 392 | +def test_bson_timestamp_invalid_inputs(sec_input, inc_input, exc_type, match_msg): |
| 393 | + with pytest.raises(exc_type, match=match_msg): |
| 394 | + BSONTimestamp(sec_input, inc_input) |
| 395 | + |
| 396 | + |
| 397 | +def test_bson_timestamp_equality(): |
| 398 | + ts1 = BSONTimestamp(100, 1) |
| 399 | + ts2 = BSONTimestamp(100, 1) |
| 400 | + ts3 = BSONTimestamp(100, 2) |
| 401 | + ts4 = BSONTimestamp(200, 1) |
| 402 | + assert ts1 == ts2 |
| 403 | + assert ts1 != ts3 |
| 404 | + assert ts1 != ts4 |
| 405 | + assert ts1 != 100 |
| 406 | + |
| 407 | + |
| 408 | +def test_bson_timestamp_hash_and_dict_key(): |
| 409 | + ts1 = BSONTimestamp(100, 1) |
| 410 | + ts2 = BSONTimestamp(100, 1) |
| 411 | + assert hash(ts1) == hash(ts2) |
| 412 | + assert len({ts1, ts2}) == 1 |
| 413 | + |
| 414 | + |
| 415 | +def test_bson_timestamp_copy(): |
| 416 | + ts = BSONTimestamp(100, 1) |
| 417 | + assert copy.copy(ts) == ts |
| 418 | + assert copy.deepcopy(ts) == ts |
| 419 | + |
| 420 | + |
| 421 | +def test_bson_timestamp_pickle(): |
| 422 | + ts = BSONTimestamp(100, 1) |
| 423 | + assert pickle.loads(pickle.dumps(ts)) == ts |
0 commit comments