from __future__ import annotations from metadata import Column from row_decoder import CSVRowDecoder, build_ordered_row_decoder, build_ordered_row_decoder_into, decode_ordered_row def test_build_ordered_row_decoder_matches_ordered_decoder_for_mixed_types() -> None: columns = ( Column("id", "BIGINT"), Column("name", "VARCHAR(64)"), Column("amount", "DECIMAL(10,5)"), Column("created_at", "TIMESTAMP(5)"), Column("payload", "JSON"), Column("flag", "BIT", precision=1), Column("bitmap", "BIT", precision=8), Column("created_on", "DATE"), ) raw_values = [ "1", "Alice", "13.14520", "2023-11-16 04:34:56.12346", '{"type":"signup"}', "1", "7", "", ] decoder = build_ordered_row_decoder(columns, null_marker=r"\N", binary_encoding="base64") assert decoder(raw_values) == decode_ordered_row( raw_values, columns, null_marker=r"\N", binary_encoding="base64", ) assert "bitmap" not in decoder(raw_values) def test_build_ordered_row_decoder_preserves_empty_text_and_set_semantics() -> None: columns = ( Column("name", "VARCHAR(64)"), Column("note", "TEXT"), Column("tags", "SET"), Column("created_at", "DATETIME"), Column("embedding", "VECTOR(3)"), ) decoder = build_ordered_row_decoder(columns, null_marker=r"\N", binary_encoding="base64") assert decoder(["", r"\N", "", "", ""]) == { "name": "", "note": None, "tags": [], "created_at": None, "embedding": None, } def test_build_ordered_row_decoder_preserves_decimal_string_without_validation() -> None: decoder = build_ordered_row_decoder( (Column("amount", "DECIMAL(10,2)"),), null_marker=r"\N", binary_encoding="base64", ) assert decoder(["not-a-decimal"]) == {"amount": "not-a-decimal"} def test_build_ordered_row_decoder_into_reuses_target_dict() -> None: columns = ( Column("id", "BIGINT"), Column("name", "VARCHAR(64)"), Column("created_at", "DATETIME"), ) decode_into = build_ordered_row_decoder_into(columns, null_marker=r"\N", binary_encoding="base64") target: dict[str, object] = {} decode_into(["1", "Alice", "2024-01-02 03:04:05"], target) assert target == { "id": 1, "name": "Alice", "created_at": "2024-01-02 03:04:05", } decode_into(["2", r"\N", ""], target) assert target == { "id": 2, "name": None, "created_at": None, } def test_csv_row_decoder_decode_into_supports_input_offset() -> None: columns = ( Column("id", "BIGINT"), Column("name", "VARCHAR(64)"), ) decoder = CSVRowDecoder.new(columns, null_marker=r"\N", binary_encoding="base64", input_offset=4) target: dict[str, object] = {} decoder.decode_into(["I", "users", "test", "123", "7", "Alice"], target) assert target == { "id": 7, "name": "Alice", } def test_csv_row_decoder_groups_required_scalar_columns() -> None: columns = ( Column("id", "BIGINT", nullable=False), Column("amount", "DECIMAL(15,2)", nullable=False), Column("ship_date", "DATE", nullable=False), Column("comment", "VARCHAR(44)", nullable=False), Column("optional_note", "VARCHAR(44)"), ) decoder = CSVRowDecoder.new(columns, null_marker=r"\N", binary_encoding="base64") target: dict[str, object] = {} assert decoder.required_int_columns == ((0, "id"),) assert decoder.required_raw_columns == ( (1, "amount"), (2, "ship_date"), (3, "comment"), ) assert len(decoder.column_decoders) == 1 decoder.decode_into(["7", "13.45", "1998-01-01", "fast path", r"\N"], target) assert target == { "id": 7, "amount": "13.45", "ship_date": "1998-01-01", "comment": "fast path", "optional_note": None, } def test_csv_row_decoder_groups_required_complex_columns() -> None: columns = ( Column("active", "BOOLEAN", nullable=False), Column("flag", "BIT", nullable=False, precision=1), Column("tiny_flag", "TINYINT", nullable=False, precision=1), Column("created_at", "TIMESTAMP", nullable=False), Column("created_pk", "TIMESTAMP", nullable=False, primary_key=True), Column("payload", "JSON", nullable=False), Column("tags", "SET", nullable=False), Column("blob", "VARBINARY", nullable=False), ) decoder = CSVRowDecoder.new(columns, null_marker=r"\N", binary_encoding="base64") target: dict[str, object] = {} assert decoder.required_bool_columns == ((0, "active"),) assert decoder.required_one_bit_bool_columns == ((1, "flag"), (2, "tiny_flag")) assert decoder.required_timestamp_columns == ((3, "created_at", False), (4, "created_pk", True)) assert decoder.required_json_columns == ((5, "payload"),) assert decoder.required_set_columns == ((6, "tags"),) assert len(decoder.required_binary_columns) == 1 assert decoder.column_decoders == () decoder.decode_into( [ "true", "1", "0", "2026-06-04 12:34:56.123456", "0000-00-00 00:00:00", '{"type":"signup"}', "a,b", "aGVsbG8=", ], target, ) assert target == { "active": True, "flag": True, "tiny_flag": False, "created_at": "2026-06-04T12:34:56.123456Z", "created_pk": "1970-01-01T00:00:00Z", "payload": {"type": "signup"}, "tags": ["a", "b"], "blob": b"hello", }