"""Tests for the SecureBuffer mlock'd, no-core-dump, wipe-on-close buffer. CPython-only: we rely on reference counting to free memoryviews as soon as they go out of scope (a `del` or function return), so no release() calls or view context managers are needed. Live exported views would make mmap.close() fail with BufferError, so view names are del'd before the owning SecureBuffer is closed. """ import pytest from quantumpipe.chab3 import decrypt, decrypt_into, encrypt, encrypt_into from quantumpipe.securebuf import SecureBuffer, wipe def test_buffer_protocol_read_write(): with SecureBuffer(32) as sb: assert len(sb) == 32 mv = memoryview(sb) assert len(mv) == 32 assert not mv.readonly mv[:4] = b"abcd" assert mv[:4] == b"abcd" del mv assert sb.closed def test_not_convertible_to_bytes(): with SecureBuffer(16) as sb, pytest.raises(TypeError, match="cannot be converted"): bytes(sb) def test_wipe_utility_fills_ff(): sb = SecureBuffer(16) sb[:] = b"A" * 16 wipe(sb) mv = memoryview(sb) assert mv.tobytes() == b"\xff" * 16 del mv sb.close() def test_close_is_idempotent(): sb = SecureBuffer(16) sb.close() assert sb.closed sb.close() # second close is a no-op assert sb.closed def test_context_manager_closes(): with SecureBuffer(16) as sb: pass assert sb.closed def test_context_manager_closes_on_exception(): with pytest.raises(RuntimeError), SecureBuffer(16) as sb: raise RuntimeError("boom") assert sb.closed def test_del_closes(): import gc sb = SecureBuffer(16) ref = sb._mmap del sb gc.collect() assert ref.closed def test_roundtrip_through_chab3(): """SecureBuffer works as key input and as an `into` target.""" with SecureBuffer(32) as key, SecureBuffer(64) as box, SecureBuffer(16) as pt: key[:] = bytes(range(32)) box_view = encrypt_into(box, b"hello", key) pt_view = decrypt_into(pt, box_view, key) assert pt_view == b"hello" del box_view, pt_view def test_key_material_needs_no_bytes_copy(): """chab3 accepts the SecureBuffer directly -- no bytes() conversion.""" with SecureBuffer(32) as key: key[:] = b"\x07" * 32 ct = encrypt(b"secret", key) assert decrypt(ct, key) == b"secret" def test_getitem_returns_memoryview(): with SecureBuffer(16) as sb: sb[:4][:] = b"abcd" view = sb[2:8] assert isinstance(view, memoryview) assert view.tobytes() == b"cd\x00\x00\x00\x00" view[:2] = b"XY" xy = sb[2:4] assert xy.tobytes() == b"XY" assert sb[0] == ord("a") del view, xy