def test_parse_valid_frames():
    # let's construct a very simple frame, with Opcode.TEXT, of length=1
    # so:
    # 1   0 0 0   0001 | 1 000001 |
    # -   -----   ----   - ------
    # |     |       |    |   +----- Payload length
    # |     |       |    +--------- Masking bit
    # |     |       +-------------- Opcode
    # |     +---------------------- RSV{1,2,3}
    # +---------------------------- FIN
    #
    # 00000000 00000000 00000000 00000000 | 01000001
    # -----------------------------------   --------
    #                   |                      +---- Payload (letter A)
    #                   +--------------------------- Mask
    # the above frame yields 7 bytes:

    data = b'\x81\x81\x00\x00\x00\x00A'
    parser = FrameParser(parse_headers=False, validate=False)
    parsed = list(parser.feed(data))

    assert len(parsed) == 1
    assert parsed[0].opcode == Opcode.TEXT
    assert len(parsed[0]) == 1
    assert parsed[0].payload == b'A'
def test_payload_without_masking_key_set():
    data = b'\x81\x01A'
    parser = FrameParser(parse_headers=False, validate=False)
    parsed = list(parser.feed(data))

    assert len(parsed) == 1
    assert parsed[0].opcode == Opcode.TEXT
    assert parsed[0].payload == b'A'
def test_payload_with_headers():
    data = b'Connection:Keep-Alive\r\nUser-Agent:Test\r\n\r\n\x81\x81\x00\x00\x00\x00A'  # noqa
    parser = FrameParser(validate=False)
    parsed = list(parser.feed(data))

    assert len(parsed) == 2
    assert parsed[0] == b'Connection:Keep-Alive\r\nUser-Agent:Test\r\n\r\n'
    assert parsed[1].opcode == Opcode.TEXT
    assert len(parsed[1]) == 1
    assert parsed[1].payload == b'A'
def test_too_large_payload():
    # here we construct the header with 2**63 which is above the limit allowed
    # by the spec.
    data = b'\x81\xff\x80\x00\x00\x00\x00\x00\x00\x00'
    #              ^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #              |     +----------------  1 << 63, encoded as uint64_t
    #              +----------------------  1 << 7 | 127, then turned into hex
    # the payload above is missing the mas bytes, but we don't have to worry
    # about that because the parser will discard the length anyway
    with pytest.raises(PayloadTooLarge):
        parser = FrameParser(parse_headers=False, validate=False)
        list(parser.feed(data))
def test_frame_with_length_gt_2__16():
    # please note that we don't actually *have to* construct a payload with
    # length greater than 2**16, we simply need to encode the length as
    # uint64_t for the code to work.
    data = b'\x81\xff\x00\x00\x00\x00\x00\x00\x00\x7e\x00\x00\x00\x00'
    #              ^   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    #              |     +----------------  126, encoded as uint64_t
    #              +----------------------  1 << 7 | 127, then turned into hex
    # we also append the actual payload
    data += b'\x41' * 126
    parser = FrameParser(parse_headers=False, validate=False)
    parsed = list(parser.feed(data))
    assert len(parsed) == 1
    assert parsed[0].opcode == Opcode.TEXT
    assert len(parsed[0]) == 126
    assert parsed[0].payload == b'A' * 126
def test_frame_with_length_gt_125():
    # the frame will start exactly the same
    data = b'\x81\xfe\x00\x7e\x00\x00\x00\x00'
    #              ^   ^^^^^
    #              |     +--  126, encoded as uint16_t
    #              +--------  1 << 7 | 126, then turned into hex
    # we also append the actual payload
    data += b'\x41' * 126

    parser = FrameParser(parse_headers=False, validate=False)
    parsed = list(parser.feed(data))

    assert len(parsed) == 1
    assert parsed[0].opcode == Opcode.TEXT
    assert len(parsed[0]) == 126
    assert parsed[0].payload == b'A' * 126
def test_default_constructor():
    # not really profound, but nevertheless .
    parser = FrameParser()
    str(parser)
    assert isinstance(parser, FrameParser)