Skip to content

Commit 34f039c

Browse files
committed
test: add tests for logfmt parser
1 parent d5227e1 commit 34f039c

File tree

1 file changed

+139
-2
lines changed

1 file changed

+139
-2
lines changed

tests/test_formatter.py

Lines changed: 139 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
import pytest
1111

12-
from logfmter.formatter import Logfmter
12+
from logfmter.formatter import Logfmter, parse_logfmt
1313

1414
STRING_ESCAPE_RULES = [
1515
# If the string contains a space, then it must be quoted.
@@ -26,6 +26,7 @@
2626
("\t", '"\\t"'),
2727
# All other control chars must be escaped and quoted
2828
("\x07", r'"\u0007"'),
29+
("\x7f", r'"\u007f"'),
2930
(
3031
"".join(chr(c) for c in range(0x20) if chr(c) not in "\t\n\r"),
3132
r'"\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u000b\u000c\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"',
@@ -375,7 +376,15 @@ def test_format_nested_keys(record, expected):
375376
@pytest.mark.external
376377
@pytest.mark.parametrize(
377378
"value",
378-
[x[0] for x in TYPE_CONVERSION_RULES + STRING_ESCAPE_RULES],
379+
[x[0] for x in TYPE_CONVERSION_RULES + STRING_ESCAPE_RULES if x[0] != "\x7f"]
380+
+ [
381+
pytest.param(
382+
"\x7f",
383+
marks=pytest.mark.xfail(
384+
reason="https://github.com/go-logfmt/logfmt/pull/17"
385+
),
386+
)
387+
],
379388
)
380389
def test_external_tools_compatibility(value):
381390
"""
@@ -538,3 +547,131 @@ def test_ignored_keys_nested(record):
538547
Logfmter(keys=["at", "foo"], ignored_keys=["foo.key1"]).format(record)
539548
== "at=INFO msg=alpha foo.key2=val2"
540549
)
550+
551+
552+
@pytest.mark.parametrize(
553+
"value",
554+
[
555+
{"at": "INFO"},
556+
{"levelname": "INFO", "a": "1"},
557+
{"at": "INFO", "msg": "test", "a": "1"},
558+
{"at": "INFO", "msg": "="},
559+
{"at": "INFO", "msg": "alpha", "exc_info": 'exc\n"info"\ntb'},
560+
{"a": " "},
561+
{"_": " "},
562+
{"a": '"'},
563+
# {"a": "\\"},
564+
# {"a": "\\", "b": "c"},
565+
{"a": "\n"},
566+
{"a": "'foo'"},
567+
{"a": "", "b": "c"},
568+
{"a": "1", "b": "2"},
569+
{"a": "1.2", "b": "2"},
570+
{"a": "1.2.3", "b": "0.2", "c": "1.0", "d": ".", "e": "1..2"},
571+
{"foo.bar": "baz", "foo.blah": "blub"},
572+
],
573+
)
574+
def test_parse_logfmt_round_trip(value):
575+
assert parse_logfmt(Logfmter.format_params(value)) == value
576+
577+
578+
@pytest.mark.parametrize(
579+
"value",
580+
[
581+
{"a": 1},
582+
{"a": 1, "b": 2},
583+
{"a": 1.2, "b": 2},
584+
{"a": "1.2.3", "b": 0.2, "c": 1.0, "d": ".", "e": "1..2"},
585+
],
586+
)
587+
def test_parse_logfmt_numeric_round_trip(value):
588+
assert parse_logfmt(Logfmter.format_params(value), convert_numeric=True) == value
589+
590+
591+
@pytest.mark.parametrize(
592+
"value,expected,kwargs",
593+
[
594+
("at=INFO", {"at": "INFO"}, {}),
595+
('at="INFO"', {"at": "INFO"}, {"reverse": False}),
596+
(
597+
"at=INFO a=1",
598+
{"levelname": "INFO", "a": "1"},
599+
{"aliases": {"at": "levelname"}, "reverse": False},
600+
),
601+
("at=INFO msg=test a=1", {"at": "INFO", "msg": "test", "a": "1"}, {}),
602+
('at=INFO msg="="', {"at": "INFO", "msg": "="}, {}),
603+
(
604+
"at=INFO first_name=josh",
605+
{"at": "INFO", "first name": "josh"},
606+
{"aliases": {"first_name": "first name"}, "reverse": False},
607+
),
608+
(
609+
r'at=INFO msg=alpha exc_info="exc\n\"info\"\ntb"',
610+
{"at": "INFO", "msg": "alpha", "exc_info": 'exc\n"info"\ntb'},
611+
{},
612+
),
613+
('a=" "', {"a": " "}, {}),
614+
('_=" "', {"_": " "}, {}),
615+
('a="\\""', {"a": '"'}, {}),
616+
(r'a="\\"', {"a": "\\"}, {"reverse": False}),
617+
(r'a="\n"', {"a": "\n"}, {}),
618+
(r"a='foo'", {"a": "'foo'"}, {}),
619+
(r"a=\n", {"a": "n"}, {"reverse": False}),
620+
("a= b=c", {"a": "", "b": "c"}, {}),
621+
("a b=c", {"a": "", "b": "c"}, {"reverse": False}),
622+
("a=1", {"a": 1}, {"convert_numeric": True}),
623+
("a=1 b=2", {"a": "1", "b": "2"}, {}),
624+
("a=1 b=2", {"a": 1, "b": 2}, {"convert_numeric": True}),
625+
("a=1.2 b=2", {"a": "1.2", "b": "2"}, {}),
626+
("a=1.2 b=2", {"a": 1.2, "b": 2}, {"convert_numeric": True}),
627+
(
628+
"a=1.2.3 b=.2 c=1. d=. e=1..2",
629+
{"a": "1.2.3", "b": 0.2, "c": 1.0, "d": ".", "e": "1..2"},
630+
{"convert_numeric": True, "reverse": False},
631+
),
632+
("foo.bar=baz foo.blah=blub", {"foo.bar": "baz", "foo.blah": "blub"}, {}),
633+
("\\n=foo", {"n": "foo"}, {"reverse": False}),
634+
("a=' '", {"'": "", "a": "'"}, {"reverse": False}),
635+
pytest.param(
636+
"a=\n", {"a": "\n"}, {"reverse": False}, marks=pytest.mark.xfail()
637+
),
638+
pytest.param(
639+
"a=\\", {"a": "\\"}, {"reverse": False}, marks=pytest.mark.xfail()
640+
),
641+
pytest.param(
642+
"a=\\ b=c",
643+
{"a": "\\", "b": "c"},
644+
{"reverse": False},
645+
marks=pytest.mark.xfail(),
646+
),
647+
],
648+
)
649+
def test_parse_logfmt(value, kwargs, expected):
650+
reverse = kwargs.pop("reverse", True)
651+
assert parse_logfmt(value, **kwargs) == expected
652+
if reverse:
653+
assert Logfmter.format_params(expected) == value
654+
655+
656+
@pytest.mark.parametrize(
657+
"value",
658+
[
659+
"a = b",
660+
],
661+
)
662+
def test_parse_invalid_logfmt(value):
663+
with pytest.raises(ValueError):
664+
print(parse_logfmt(value))
665+
exe = (
666+
shutil.which("golang-logfmt-echo")
667+
or os.getcwd() + "/external/golang-logfmt-echo/golang-logfmt-echo"
668+
)
669+
670+
with pytest.raises(subprocess.CalledProcessError):
671+
subprocess.run(
672+
[exe],
673+
check=True,
674+
input=value,
675+
capture_output=True,
676+
text=True,
677+
)

0 commit comments

Comments
 (0)