"""Tests for base64url module.""" import binascii import pytest import base64url class TestEnc: """Tests for enc() function.""" def test_empty(self): assert base64url.enc(b"") == "" def test_single_byte(self): assert base64url.enc(b"\x00") == "AA" def test_no_padding_needed(self): # 3 bytes encodes to 4 chars, no padding assert base64url.enc(b"abc") == "YWJj" def test_one_padding_stripped(self): # 2 bytes encodes to 3 chars + 1 padding assert base64url.enc(b"ab") == "YWI" assert "=" not in base64url.enc(b"ab") def test_two_padding_stripped(self): # 1 byte encodes to 2 chars + 2 padding assert base64url.enc(b"a") == "YQ" assert "=" not in base64url.enc(b"a") def test_urlsafe_chars(self): # Bytes that produce + and / in standard Base64 should use - and _ # 0xfb -> standard: +, urlsafe: - # 0xff -> standard: /, urlsafe: _ data = b"\xfb\xff\xfe" result = base64url.enc(data) assert "+" not in result assert "/" not in result assert "-" in result or "_" in result def test_all_bytes(self): # Verify all byte values encode/decode correctly data = bytes(range(256)) encoded = base64url.enc(data) assert "+" not in encoded assert "/" not in encoded assert "=" not in encoded class TestDec: """Tests for dec() function.""" def test_empty(self): assert base64url.dec("") == b"" def test_single_byte(self): assert base64url.dec("AA") == b"\x00" def test_without_padding(self): assert base64url.dec("YWJj") == b"abc" assert base64url.dec("YWI") == b"ab" assert base64url.dec("YQ") == b"a" def test_with_padding(self): # Padding should be optional assert base64url.dec("YWI=") == b"ab" assert base64url.dec("YQ==") == b"a" def test_urlsafe_chars(self): # Should accept - instead of +, _ instead of / assert base64url.dec("--__") == base64url.dec("++//") def test_standard_base64_chars(self): # Should also accept standard Base64 with + and / assert base64url.dec("++//") == b"\xfb\xef\xff" def test_mixed_chars(self): # Mix of standard and urlsafe should work assert base64url.dec("+-/_") is not None def test_invalid_char_raises(self): # Invalid characters should raise binascii.Error with pytest.raises(binascii.Error): base64url.dec("!!!!") def test_invalid_char_in_middle_raises(self): with pytest.raises(binascii.Error): base64url.dec("YW!j") def test_whitespace_raises(self): # Whitespace should not be silently ignored with pytest.raises(binascii.Error): base64url.dec("YW Jj") def test_newline_raises(self): with pytest.raises(binascii.Error): base64url.dec("YWJj\n") class TestRoundtrip: """Tests for enc/dec roundtrip.""" def test_empty_roundtrip(self): assert base64url.dec(base64url.enc(b"")) == b"" def test_hello_roundtrip(self): assert base64url.dec(base64url.enc(b"hello")) == b"hello" def test_binary_roundtrip(self): data = bytes(range(256)) assert base64url.dec(base64url.enc(data)) == data def test_various_lengths(self): # Test lengths 0-20 to cover all padding scenarios for i in range(21): data = bytes(range(i)) assert base64url.dec(base64url.enc(data)) == data def test_random_bytes(self): import os for _ in range(10): data = os.urandom(100) assert base64url.dec(base64url.enc(data)) == data class TestEncLines: """Tests for enc_lines() function.""" def test_empty(self): assert base64url.enc_lines(b"") == "" def test_short_no_wrap(self): # Short data fits on one line result = base64url.enc_lines(b"hello", length=76) assert "\n" not in result assert result == "aGVsbG8" def test_wrapping(self): # 60 bytes -> 80 base64 chars, should wrap at 76 data = b"x" * 60 result = base64url.enc_lines(data, length=76) lines = result.split("\n") assert len(lines) == 2 assert len(lines[0]) == 76 assert len(lines[1]) == 4 def test_custom_line_length(self): data = b"hello world" result = base64url.enc_lines(data, length=4) lines = result.split("\n") assert all(len(line) <= 4 for line in lines) def test_exact_multiple(self): # 6 bytes -> 8 base64 chars, exactly 2 lines of 4 data = b"abcdef" result = base64url.enc_lines(data, length=4) lines = result.split("\n") assert len(lines) == 2 assert all(len(line) == 4 for line in lines) class TestDecLines: """Tests for dec_lines() function.""" def test_empty(self): assert base64url.dec_lines("") == b"" def test_single_line(self): assert base64url.dec_lines("aGVsbG8") == b"hello" def test_multiline(self): multiline = "aGVs\nbG8" assert base64url.dec_lines(multiline) == b"hello" def test_spaces(self): spaced = "aGVs bG8" assert base64url.dec_lines(spaced) == b"hello" def test_tabs(self): tabbed = "aGVs\tbG8" assert base64url.dec_lines(tabbed) == b"hello" def test_mixed_whitespace(self): mixed = " aGVs\n\t bG8 \n" assert base64url.dec_lines(mixed) == b"hello" def test_crlf(self): crlf = "aGVs\r\nbG8" assert base64url.dec_lines(crlf) == b"hello" class TestLinesRoundtrip: """Tests for enc_lines/dec_lines roundtrip.""" def test_roundtrip(self): data = bytes(range(256)) assert base64url.dec_lines(base64url.enc_lines(data)) == data def test_roundtrip_various_line_lengths(self): data = b"hello world" * 10 for line_length in [4, 16, 64, 76, 100]: encoded = base64url.enc_lines(data, length=line_length) assert base64url.dec_lines(encoded) == data