From 722dcec32611c6fbe35826bbb58e9aeee0b59148 Mon Sep 17 00:00:00 2001 From: "tomasz.kowalczewski" Date: Mon, 21 Jun 2021 22:16:15 +0200 Subject: [PATCH] Re #53: Added tests and fixed bug in default value handling. --- .../pl/tkowalcz/tjahzi/LabelSerializers.java | 1 + .../thjazi/protobuf/ProtobufDeserializer.java | 7 +- .../tkowalcz/tjahzi/LabelSerializerTest.java | 164 ++++++++++++++++++ .../tkowalcz/tjahzi/LabelSerializersTest.java | 55 ++++++ .../log4j2/labels/LabelPrinterFactory.java | 2 +- .../tjahzi/log4j2/labels/Literal.java | 2 +- .../tjahzi/log4j2/labels/MDCLookup.java | 4 +- .../labels/LabelPrinterFactoryTest.java | 3 +- .../tjahzi/log4j2/labels/LiteralTest.java | 23 +++ .../tjahzi/log4j2/labels/MDCLookupTest.java | 48 +++++ 10 files changed, 301 insertions(+), 8 deletions(-) create mode 100644 core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java create mode 100644 core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializersTest.java create mode 100644 log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LiteralTest.java create mode 100644 log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookupTest.java diff --git a/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializers.java b/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializers.java index d8e57ea..e691e19 100644 --- a/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializers.java +++ b/core/src/main/java/pl/tkowalcz/tjahzi/LabelSerializers.java @@ -4,6 +4,7 @@ public class LabelSerializers { private static final ThreadLocal THREAD_LOCAL = ThreadLocal.withInitial(LabelSerializer::new); + public static LabelSerializer threadLocal() { LabelSerializer result = THREAD_LOCAL.get(); result.clear(); diff --git a/core/src/test/java/pl/tkowalcz/thjazi/protobuf/ProtobufDeserializer.java b/core/src/test/java/pl/tkowalcz/thjazi/protobuf/ProtobufDeserializer.java index e390fc2..6c4fb18 100644 --- a/core/src/test/java/pl/tkowalcz/thjazi/protobuf/ProtobufDeserializer.java +++ b/core/src/test/java/pl/tkowalcz/thjazi/protobuf/ProtobufDeserializer.java @@ -29,9 +29,10 @@ public class ProtobufDeserializer { ); LabelSerializer labelSerializer = LabelSerializers.threadLocal(); - labelSerializer.appendLabel("faaaoo", "baqwefewr"); - labelSerializer.appendLabel("a232aa", "bbbrgwew"); - labelSerializer.appendLabel("LEVEL", "DEBUG"); + labelSerializer + .appendLabel("faaaoo", "baqwefewr") + .appendLabel("a232aa", "bbbrgwew") + .appendLabel("LEVEL", "DEBUG"); LogBufferSerializer serializer = new LogBufferSerializer(logBuffer.buffer()); serializer.writeTo( diff --git a/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java b/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java new file mode 100644 index 0000000..60f8e50 --- /dev/null +++ b/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializerTest.java @@ -0,0 +1,164 @@ +package pl.tkowalcz.tjahzi; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("ConstantConditions") +class LabelSerializerTest { + + @Test + void shouldAppendLabelAndCountIt() { + // Given + LabelSerializer serializer = new LabelSerializer(); + + // When + serializer.appendLabel("Foo", "Bar"); + + // Then + assertThat(serializer.getLabelsCount()).isEqualTo(1); + assertThat(serializer.sizeBytes()).isEqualTo(14); + assertThat(serializer.toString()).isEqualTo("FooBar"); + } + + @Test + void shouldAppendLabelsAndCountThem() { + // Given + LabelSerializer serializer = new LabelSerializer(); + + // When + serializer.appendLabel("Foo", "Bar"); + serializer.appendLabel("Key", "Value"); + serializer.appendLabel("Kang", "Kodos"); + + // Then + assertThat(serializer.getLabelsCount()).isEqualTo(3); + assertThat(serializer.sizeBytes()).isEqualTo(47); + assertThat(serializer.toString()).isEqualTo("FooBarKeyValueKangKodos"); + } + + @Test + void shouldAppendMultipartLabelAndCountItAsOne() { + // Given + LabelSerializer serializer = new LabelSerializer(); + + // When + serializer.appendLabelName("Aliens"); + serializer.startAppendingLabelValue(); + serializer.appendPartialLabelValue("Kang"); + serializer.appendPartialLabelValue("Kodos"); + serializer.appendPartialLabelValue("Johnson"); + serializer.finishAppendingLabelValue(); + + // Then + assertThat(serializer.getLabelsCount()).isEqualTo(1); + assertThat(serializer.sizeBytes()).isEqualTo(30); + assertThat(serializer.toString()).isEqualTo("AliensKangKodosJohnson"); + } + + @Test + void shouldAppendMultipartLabelsMixedWithRegular() { + // Given + LabelSerializer serializer = new LabelSerializer(); + + // When + serializer.appendLabel("Foo", "Bar"); + + serializer.appendLabelName("Aliens"); + serializer.startAppendingLabelValue(); + serializer.appendPartialLabelValue("Kang"); + serializer.appendPartialLabelValue("Kodos"); + serializer.appendPartialLabelValue("Johnson"); + serializer.finishAppendingLabelValue(); + + serializer.appendLabelName("Omicronians"); + serializer.startAppendingLabelValue(); + serializer.appendPartialLabelValue("Lrrr"); + serializer.appendPartialLabelValue("RULER OF THE PLANET OMICRON PERSEI EIGHT"); + serializer.finishAppendingLabelValue(); + + serializer.appendLabel("Popplers", "Problem"); + + // Then + assertThat(serializer.getLabelsCount()).isEqualTo(4); + assertThat(serializer.sizeBytes()).isEqualTo(130); + assertThat(serializer.toString()).isEqualTo("FooBarAliensKangKodosJohnsonOmicroniansLrrrRULER OF THE PLANET OMICRON PERSEI EIGHTPopplersProblem"); + } + + @Test + void shouldClearSerializerContent() { + // Given + LabelSerializer serializer = new LabelSerializer(); + serializer.appendLabel("Foo", "Bar"); + + // When + serializer.clear(); + + // Then + assertThat(serializer.getLabelsCount()).isZero(); + assertThat(serializer.sizeBytes()).isEqualTo(0); + assertThat(serializer.toString()).isEqualTo(""); + } + + @Test + void shouldNotEqualToNull() { + // Given + LabelSerializer serializer = new LabelSerializer() + .appendLabel("foo", "bar"); + LabelSerializer other = null; + + // When + boolean actual = serializer.equals(other); + + // Then + assertThat(actual).isFalse(); + } + + @Test + void shouldNotEqualToOtherBufferOfEqualLength() { + // Given + LabelSerializer serializer = new LabelSerializer() + .appendLabel("foo", "bar"); + LabelSerializer other = new LabelSerializer() + .appendLabel("bar", "foo"); + + // When + boolean actual = serializer.equals(other); + + // Then + assertThat(actual).isFalse(); + } + + @Test + void shouldNotEqualToOtherBufferThatPreviouslyContainedSameContents() { + // Given + LabelSerializer serializer = new LabelSerializer() + .appendLabel("foo", "bar"); + + LabelSerializer other = new LabelSerializer() + .appendLabel("foo", "bar"); + + // When + serializer.clear(); + boolean actual = serializer.equals(other); + + // Then + assertThat(actual).isFalse(); + } + + @Test + void shouldEqualToOtherBufferContainingSameData() { + // Given + LabelSerializer serializer = new LabelSerializer() + .appendLabel("foo", "bar"); + + LabelSerializer other = new LabelSerializer() + .appendLabel("foo", "bar"); + + // When + boolean actual = serializer.equals(other); + + // Then + assertThat(actual).isTrue(); + } +} diff --git a/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializersTest.java b/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializersTest.java new file mode 100644 index 0000000..85a8888 --- /dev/null +++ b/core/src/test/java/pl/tkowalcz/tjahzi/LabelSerializersTest.java @@ -0,0 +1,55 @@ +package pl.tkowalcz.tjahzi; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.atomic.AtomicReference; + +import static org.assertj.core.api.Assertions.assertThat; + +class LabelSerializersTest { + + @Test + void shouldClearBufferBeforeUse() { + // Given + LabelSerializer labelSerializer = LabelSerializers.threadLocal(); + labelSerializer.appendLabel("key", "abc"); + + // When + LabelSerializer actual = LabelSerializers.threadLocal(); + + // Then + assertThat(actual.getLabelsCount()).isZero(); + } + + @Test + void shouldReturnSameBufferInsideSameThread() { + // When + LabelSerializer labelSerializer1 = LabelSerializers.threadLocal(); + LabelSerializer labelSerializer2 = LabelSerializers.threadLocal(); + + // Then + assertThat(labelSerializer1).isSameAs(labelSerializer2); + } + + @Test + void shouldReturnDifferentSerializerInsideDifferentThreads() throws InterruptedException { + // Given + AtomicReference captor1 = new AtomicReference<>(); + AtomicReference captor2 = new AtomicReference<>(); + + // When + Thread thread1 = new Thread(() -> captor1.set(LabelSerializers.threadLocal())); + thread1.start(); + thread1.join(); + + Thread thread2 = new Thread(() -> captor2.set(LabelSerializers.threadLocal())); + thread2.start(); + thread2.join(); + + // Then + assertThat(captor1.get()).isNotNull(); + assertThat(captor2.get()).isNotNull(); + + assertThat(captor1.get()).isNotSameAs(captor2.get()); + } +} diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactory.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactory.java index 0dbd83f..68dd20c 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactory.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactory.java @@ -7,7 +7,7 @@ import java.util.regex.Pattern; public class LabelPrinterFactory { - private static final Pattern CONTEXT_PATTERN = Pattern.compile("\\$\\{ctx:([^}:]+)(:-[^}]+)?}"); + private static final Pattern CONTEXT_PATTERN = Pattern.compile("\\$\\{ctx:([^}:]+)(?::-([^}]+))?}"); public static LabelPrinter parse(Label label) { return parse(label.getValue()); diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java index 14da8f6..d2fb396 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/Literal.java @@ -17,7 +17,7 @@ public class Literal implements LabelPrinter { return true; } - public static LabelPrinter of(String string) { + public static Literal of(String string) { return new Literal(string); } diff --git a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java index f8421c6..9983256 100644 --- a/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java +++ b/log4j2-appender/src/main/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookup.java @@ -14,8 +14,8 @@ public class MDCLookup implements LabelPrinter { this.defaultValue = defaultValue == null ? "" : defaultValue; } - public static LabelPrinter of(String group, String defaultValue) { - return new MDCLookup(group, defaultValue); + public static MDCLookup of(String variableName, String defaultValue) { + return new MDCLookup(variableName, defaultValue); } @Override diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactoryTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactoryTest.java index f4ea0af..032a526 100644 --- a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactoryTest.java +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LabelPrinterFactoryTest.java @@ -21,7 +21,8 @@ class LabelPrinterFactoryTest { Arguments.of("Should handle no substitution", "Macaroon ice cream gummies donut jujubes", "Macaroon ice cream gummies donut jujubes"), Arguments.of("Should handle one substitution", "fafaewff ${ctx:tid} eawfawefweafwe", "fafaewff req-43tnfwenb eawfawefweafwe"), Arguments.of("Should handle substitution with no matching MDC", "fafaewff ${ctx:tenant} eawfawefweafwe", "fafaewff eawfawefweafwe"), - Arguments.of("Should handle many substitutions", "fooba${ctx:tenant}r${ctx:baz}_42", "foobarbaz_42") + Arguments.of("Should handle many substitutions", "fooba${ctx:tenant}r${ctx:baz}_42", "foobarbaz_42"), + Arguments.of("Should use default values if no matching MDC", "tid-${ctx:request-id:-empty}_${ctx:tid}", "tid-empty_req-43tnfwenb") ); } diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LiteralTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LiteralTest.java new file mode 100644 index 0000000..6c1c360 --- /dev/null +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/LiteralTest.java @@ -0,0 +1,23 @@ +package pl.tkowalcz.tjahzi.log4j2.labels; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LiteralTest { + + @Test + void shouldAppendItsContents() { + // Given + String expected = "constant"; + StringBuilder actual = new StringBuilder(); + + Literal literal = Literal.of(expected); + + // When + literal.append(null, actual::append); + + // Then + assertThat(actual.toString()).isEqualTo(expected); + } +} diff --git a/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookupTest.java b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookupTest.java new file mode 100644 index 0000000..8d49791 --- /dev/null +++ b/log4j2-appender/src/test/java/pl/tkowalcz/tjahzi/log4j2/labels/MDCLookupTest.java @@ -0,0 +1,48 @@ +package pl.tkowalcz.tjahzi.log4j2.labels; + +import org.apache.logging.log4j.core.impl.JdkMapAdapterStringMap; +import org.apache.logging.log4j.core.impl.MutableLogEvent; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Map; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +class MDCLookupTest { + + private static Stream shouldLookUpItsValueInContext() { + return Stream.of( + Arguments.of("Should replace with context value", Map.of("tenant", "Kang"), "David", "Kang"), + Arguments.of("Should replace with context value (default is null)", Map.of("tenant", "Kang"), null, "Kang"), + Arguments.of("Should replace with context value even if it is an empty string", Map.of("tenant", ""), "David", ""), + Arguments.of("Should replace with context value even if it is an empty string (default is null)", Map.of("tenant", ""), null, ""), + Arguments.of("Should replace with default value", Map.of(), "David", "David"), + Arguments.of("Should replace with default value even if it si null", Map.of(), null, "") + ); + } + + @ParameterizedTest(name = "{0}") + @MethodSource + void shouldLookUpItsValueInContext( + String displayName, + Map mdcMap, + String defaultValue, + String expected + ) { + // Given + MutableLogEvent event = new MutableLogEvent(); + event.setContextData(new JdkMapAdapterStringMap(mdcMap)); + + MDCLookup lookup = MDCLookup.of("tenant", defaultValue); + StringBuilder actual = new StringBuilder(); + + // When + lookup.append(event, actual::append); + + // Then + assertThat(actual.toString()).isEqualTo(expected); + } +}