diff --git a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/EtlPipelineSpec.scala b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/EtlPipelineSpec.scala index 2d8527e2f..1b052c1e0 100644 --- a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/EtlPipelineSpec.scala +++ b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/EtlPipelineSpec.scala @@ -22,6 +22,7 @@ import com.snowplowanalytics.iglu.client.resolver.registries.Registry import com.snowplowanalytics.iglu.client.validator.CirceValidator import com.snowplowanalytics.snowplow.badrows.Processor +import com.snowplowanalytics.snowplow.badrows.BadRow import org.apache.thrift.TSerializer @@ -44,6 +45,8 @@ class EtlPipelineSpec extends Specification with ValidatedMatchers { def is = s2""" EtlPipeline should always produce either bad or good row for each event of the payload $e1 Processing of events with malformed query string should be supported $e2 + Processing of invalid CollectorPayload (CPFormatViolation bad row) should be supported $e3 + Absence of CollectorPayload (None) should be supported $e4 """ val adapterRegistry = new AdapterRegistry() @@ -88,6 +91,32 @@ class EtlPipelineSpec extends Specification with ValidatedMatchers { case res => ko(s"[$res] doesn't contain one enriched event") } } + + def e3 = { + val invalidCollectorPayload = ThriftLoader.toCollectorPayload(Array(1.toByte), processor) + EtlPipeline.processEvents[Id]( + adapterRegistry, + enrichmentReg, + client, + processor, + dateTime, + invalidCollectorPayload + ) must be like { + case Validated.Invalid(_: BadRow.CPFormatViolation) :: Nil => ok + } + } + + def e4 = { + val collectorPayload: Option[CollectorPayload] = None + EtlPipeline.processEvents[Id]( + adapterRegistry, + enrichmentReg, + client, + processor, + dateTime, + collectorPayload.validNel[BadRow] + ) must beEqualTo(Nil) + } } object EtlPipelineSpec { diff --git a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/enrichments/EnrichmentManagerSpec.scala b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/enrichments/EnrichmentManagerSpec.scala index eb152f316..630d19bc7 100644 --- a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/enrichments/EnrichmentManagerSpec.scala +++ b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/enrichments/EnrichmentManagerSpec.scala @@ -345,6 +345,21 @@ class EnrichmentManagerSpec extends Specification with EitherMatchers { EnrichmentManager.getIabContext(input, iabEnrichment) must beRight.like { case ctx => ctx must beSome } } } + + "getCollectorVersionSet" should { + "return an enrichment failure if v_collector is null or empty" >> { + val input = new EnrichedEvent() + EnrichmentManager.getCollectorVersionSet(input) must beLeft.like { case _: FailureDetails.EnrichmentFailure => ok } + input.v_collector = "" + EnrichmentManager.getCollectorVersionSet(input) must beLeft.like { case _: FailureDetails.EnrichmentFailure => ok } + } + + "return Unit if v_collector is set" >> { + val input = new EnrichedEvent() + input.v_collector = "v42" + EnrichmentManager.getCollectorVersionSet(input) must beRight(()) + } + } } object EnrichmentManagerSpec { diff --git a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/JsonUtilsSpec.scala b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/JsonUtilsSpec.scala index 5586b55b8..a3e020e18 100644 --- a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/JsonUtilsSpec.scala +++ b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/JsonUtilsSpec.scala @@ -15,12 +15,19 @@ package utils import org.specs2.Specification +import org.joda.time.format.DateTimeFormat + import io.circe.Json +import cats.data.NonEmptyList + class JsonUtilsSpec extends Specification { def is = s2""" toJson can deal with non-null String $e1 toJson can deal with null String $e2 + toJson can deal with booleans $e3 + toJson can deal with integers $e4 + toJson can deal with dates $e5 """ def e1 = { @@ -36,4 +43,52 @@ class JsonUtilsSpec extends Specification { JsonUtils.toJson(key, value, Nil, Nil, None) must beEqualTo((key, Json.Null)) } + + def e3 = { + val key = "field" + + val truE = "true" + val exp1 = JsonUtils.toJson(key, truE, List(key), Nil, None) must + beEqualTo(key -> Json.True) + + val falsE = "false" + val exp2 = JsonUtils.toJson(key, falsE, List(key), Nil, None) must + beEqualTo(key -> Json.False) + + val foo = "foo" + val exp3 = JsonUtils.toJson(key, foo, List(key), Nil, None) must + beEqualTo(key -> Json.fromString(foo)) + + exp1 and exp2 and exp3 + } + + def e4 = { + val key = "field" + + val number = 123 + val exp1 = JsonUtils.toJson(key, number.toString(), Nil, List(key), None) must + beEqualTo(key -> Json.fromBigInt(number)) + + val notNumber = "abc" + val exp2 = JsonUtils.toJson(key, notNumber, Nil, List(key), None) must + beEqualTo(key -> Json.fromString(notNumber)) + + exp1 and exp2 + } + + def e5 = { + val key = "field" + + val formatter = DateTimeFormat.forPattern("yyyy-MM-dd") + val malformedDate = "2020-09-02" + val correctDate = "2020-09-02T22:00:00.000Z" + + val exp1 = JsonUtils.toJson(key, malformedDate, Nil, Nil, Some(NonEmptyList.one(key) -> formatter)) must + be !== (key -> Json.fromString(malformedDate)) + + val exp2 = JsonUtils.toJson(key, correctDate, Nil, Nil, Some(NonEmptyList.one(key) -> formatter)) must + beEqualTo(key -> Json.fromString(correctDate)) + + exp1 and exp2 + } } diff --git a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/conversionUtilsSpecs.scala b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/conversionUtilsSpecs.scala index be3ea91db..5cf98ed60 100644 --- a/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/conversionUtilsSpecs.scala +++ b/modules/common/src/test/scala/com.snowplowanalytics.snowplow.enrich.common/utils/conversionUtilsSpecs.scala @@ -15,6 +15,7 @@ package utils import java.net.{Inet6Address, InetAddress, URI} import java.nio.ByteBuffer +import java.nio.charset.StandardCharsets import cats.syntax.either._ import cats.syntax.option._ @@ -275,6 +276,49 @@ class ValidateUuidSpec extends Specification with DataTables with ScalaCheck { } } +class ValidateIntegerSpec extends Specification { + def is = s2""" + validateInteger should return the original string if it contains an integer $e1 + validateInteger should return an enrichment failure for a string not containing a valid integer $e2 + """ + + val FieldName = "integer" + + def e1 = ConversionUtils.validateInteger(FieldName, "123") must beRight("123") + + def e2 = { + val str = "abc" + ConversionUtils.validateInteger(FieldName, str) must beLeft( + FailureDetails.EnrichmentFailure( + None, + FailureDetails.EnrichmentFailureMessage.InputData( + FieldName, + Some(str), + "not a valid integer" + ) + ) + ) + } +} + +class DecodeStringSpec extends Specification { + def is = s2""" + decodeString should decode a correctly URL-encoded string $e1 + decodeString should fail decoding a string not correctly URL-encoded $e2 + """ + + val utf8 = StandardCharsets.UTF_8 + + def e1 = { + val clear = "12 ++---=&&3abc%%%34%2234%$#@%^PLLPbgfxbf$#%$@#@^" + val encoded = ConversionUtils.encodeString(utf8.toString(), clear) + ConversionUtils.decodeString(utf8, encoded) must beRight(clear) + } + + def e2 = + ConversionUtils.decodeString(utf8, "%%23") must beLeft +} + class StringToDoubleLikeSpec extends Specification with DataTables { def is = s2""" stringToDoublelike should fail if the supplied String is not parseable as a number $e1