packages feed

hydra-0.15.0: src/main/haskell/Hydra/Sources/Avro/Testing.hs

-- | Test case types for the bidirectional Avro coder

module Hydra.Sources.Avro.Testing where

import           Hydra.Kernel
import           Hydra.Dsl.Annotations (doc)
import           Hydra.Dsl.Bootstrap
import           Hydra.Dsl.Types                 ((>:))
import qualified Hydra.Dsl.Types                 as T
import qualified Hydra.Sources.Kernel.Types.Core as Core
import qualified Hydra.Sources.Json.Model        as JsonModel
import qualified Hydra.Sources.Avro.Schema   as AvroSchema


ns :: Namespace
ns = Namespace "hydra.avro.testing"

define :: String -> Type -> Binding
define = defineType ns

avro :: String -> Type
avro = typeref AvroSchema.ns

json :: String -> Type
json = typeref JsonModel.ns

local :: String -> Type
local = typeref ns

module_ :: Module
module_ = Module {
            moduleNamespace = ns,
            moduleDefinitions = (map toTypeDef definitions),
            moduleTermDependencies = [],
            moduleTypeDependencies = [Core.ns, AvroSchema.ns, JsonModel.ns],
            moduleDescription = Just "Test case types for the bidirectional Avro coder"}
  where
    definitions = [
      typeLevelForwardTestCase,
      typeLevelReverseTestCase,
      typeLevelRoundTripAvroTestCase,
      typeLevelRoundTripHydraTestCase,
      termLevelForwardTestCase,
      termLevelReverseTestCase,
      termLevelRoundTripJsonTestCase,
      termLevelRoundTripTermTestCase,
      unionTestCase,
      nameMappingTestCase,
      lossinessTestCase,
      schemaSerializationTestCase,
      avroTestCase]

-- | Category 1: Type-level forward (Avro Schema -> Hydra Type)
typeLevelForwardTestCase :: Binding
typeLevelForwardTestCase = define "TypeLevelForwardTestCase" $
  doc "A test case which maps an Avro schema to a Hydra type and compares the result" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "schema">:
      doc "The input Avro schema"
      (avro "Schema"),
    "type">:
      doc "The expected Hydra type"
      Core.type_]

-- | Category 2: Type-level reverse (Hydra Type -> Avro Schema)
typeLevelReverseTestCase :: Binding
typeLevelReverseTestCase = define "TypeLevelReverseTestCase" $
  doc "A test case which maps a Hydra type to an Avro schema and compares the result" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "type">:
      doc "The input Hydra type"
      Core.type_,
    "schema">:
      doc "The expected Avro schema"
      (avro "Schema")]

-- | Category 3: Type-level round-trip starting from Avro (Avro -> Hydra -> Avro)
typeLevelRoundTripAvroTestCase :: Binding
typeLevelRoundTripAvroTestCase = define "TypeLevelRoundTripAvroTestCase" $
  doc "A test case which maps an Avro schema to a Hydra type and back, verifying the result" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "schema">:
      doc "The original Avro schema"
      (avro "Schema"),
    "expectedSchema">:
      doc "The expected Avro schema after the round-trip, which may differ from the original due to normalization or information loss"
      (avro "Schema")]

-- | Category 4: Type-level round-trip starting from Hydra (Hydra -> Avro -> Hydra)
typeLevelRoundTripHydraTestCase :: Binding
typeLevelRoundTripHydraTestCase = define "TypeLevelRoundTripHydraTestCase" $
  doc "A test case which maps a Hydra type to an Avro schema and back, verifying the result" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "type">:
      doc "The original Hydra type"
      Core.type_,
    "expectedType">:
      doc "The expected Hydra type after the round-trip, which may differ from the original due to normalization or information loss"
      Core.type_]

-- | Category 5: Term-level forward (JSON -> Hydra Term)
termLevelForwardTestCase :: Binding
termLevelForwardTestCase = define "TermLevelForwardTestCase" $
  doc "A test case which encodes a JSON value as a Hydra term using an Avro schema" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "schema">:
      doc "The Avro schema describing the JSON value"
      (avro "Schema"),
    "json">:
      doc "The input JSON value"
      (json "Value"),
    "term">:
      doc "The expected Hydra term"
      Core.term]

-- | Category 6: Term-level reverse (Hydra Term -> JSON)
termLevelReverseTestCase :: Binding
termLevelReverseTestCase = define "TermLevelReverseTestCase" $
  doc "A test case which decodes a Hydra term to a JSON value using an Avro schema" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "schema">:
      doc "The Avro schema describing the expected JSON value"
      (avro "Schema"),
    "term">:
      doc "The input Hydra term"
      Core.term,
    "json">:
      doc "The expected JSON value"
      (json "Value")]

-- | Category 7: Term-level round-trip starting from JSON (JSON -> Term -> JSON)
termLevelRoundTripJsonTestCase :: Binding
termLevelRoundTripJsonTestCase = define "TermLevelRoundTripJsonTestCase" $
  doc "A test case which encodes a JSON value as a Hydra term and decodes it back, verifying the result" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "schema">:
      doc "The Avro schema describing the JSON value"
      (avro "Schema"),
    "json">:
      doc "The original JSON value"
      (json "Value"),
    "expectedJson">:
      doc "The expected JSON value after the round-trip, which may differ from the original due to normalization"
      (json "Value")]

-- | Category 8: Term-level round-trip starting from a term (Term -> JSON -> Term)
termLevelRoundTripTermTestCase :: Binding
termLevelRoundTripTermTestCase = define "TermLevelRoundTripTermTestCase" $
  doc "A test case which decodes a Hydra term to JSON and encodes it back, verifying the result" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "type">:
      doc "The Hydra type of the term"
      Core.type_,
    "term">:
      doc "The original Hydra term"
      Core.term,
    "expectedTerm">:
      doc "The expected Hydra term after the round-trip, which may differ from the original due to normalization"
      Core.term]

-- | Category 9: Union-specific tests
unionTestCase :: Binding
unionTestCase = define "UnionTestCase" $
  doc "A test case for union type encoding and decoding, covering the various strategies for representing unions in Avro" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "hydraType">:
      doc "The Hydra union type"
      Core.type_,
    "avroSchema">:
      doc "The expected Avro schema (enum for all-unit unions, record with optional fields for general unions)"
      (avro "Schema"),
    "termPairs">:
      doc "Pairs of corresponding Hydra terms and JSON values for this union"
      (T.list (T.pair Core.term (json "Value")))]

-- | Category 10: Name mapping tests
nameMappingTestCase :: Binding
nameMappingTestCase = define "NameMappingTestCase" $
  doc "A test case for bidirectional name mapping between Hydra and Avro" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "hydraName">:
      doc "The Hydra name"
      Core.name,
    "avroName">:
      doc "The expected Avro qualified name (dotted string)"
      T.string,
    "avroNamespace">:
      doc "The expected Avro namespace, if any"
      (T.optional T.string)]

-- | Category 11: Lossiness and annotation tests
lossinessTestCase :: Binding
lossinessTestCase = define "LossinessTestCase" $
  doc "A test case which verifies that lossy conversions stash original information in annotations" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "originalSchema">:
      doc "The original Avro schema containing information that may be lost"
      (avro "Schema"),
    "hydraType">:
      doc "The resulting Hydra type, which should carry annotations for any lost information"
      Core.type_,
    "recoveredSchema">:
      doc "The Avro schema recovered from the annotated Hydra type"
      (avro "Schema"),
    "isLossy">:
      doc "Whether the adapter reports this conversion as lossy"
      T.boolean]

-- | Category 12: Schema serialization (Avro Schema <-> JSON)
schemaSerializationTestCase :: Binding
schemaSerializationTestCase = define "SchemaSerializationTestCase" $
  doc "A test case for Avro schema serialization to and from JSON" $
  T.record [
    "description">:
      doc "A human-readable description of what this test case covers"
      T.string,
    "schema">:
      doc "The Avro schema"
      (avro "Schema"),
    "json">:
      doc "The expected JSON representation of the schema"
      (json "Value")]

-- | The union of all Avro test case types
avroTestCase :: Binding
avroTestCase = define "AvroTestCase" $
  doc "A test case for the bidirectional Avro coder" $
  T.union [
    "typeLevelForward">:
      doc "Type-level forward mapping (Avro Schema -> Hydra Type)"
      (local "TypeLevelForwardTestCase"),
    "typeLevelReverse">:
      doc "Type-level reverse mapping (Hydra Type -> Avro Schema)"
      (local "TypeLevelReverseTestCase"),
    "typeLevelRoundTripAvro">:
      doc "Type-level round-trip starting from Avro (Avro -> Hydra -> Avro)"
      (local "TypeLevelRoundTripAvroTestCase"),
    "typeLevelRoundTripHydra">:
      doc "Type-level round-trip starting from Hydra (Hydra -> Avro -> Hydra)"
      (local "TypeLevelRoundTripHydraTestCase"),
    "termLevelForward">:
      doc "Term-level forward mapping (JSON -> Hydra Term)"
      (local "TermLevelForwardTestCase"),
    "termLevelReverse">:
      doc "Term-level reverse mapping (Hydra Term -> JSON)"
      (local "TermLevelReverseTestCase"),
    "termLevelRoundTripJson">:
      doc "Term-level round-trip starting from JSON (JSON -> Term -> JSON)"
      (local "TermLevelRoundTripJsonTestCase"),
    "termLevelRoundTripTerm">:
      doc "Term-level round-trip starting from a term (Term -> JSON -> Term)"
      (local "TermLevelRoundTripTermTestCase"),
    "union">:
      doc "Union-specific encoding and decoding"
      (local "UnionTestCase"),
    "nameMapping">:
      doc "Name mapping between Hydra and Avro"
      (local "NameMappingTestCase"),
    "lossiness">:
      doc "Lossy conversion with annotation stashing"
      (local "LossinessTestCase"),
    "schemaSerialization">:
      doc "Schema serialization to and from JSON"
      (local "SchemaSerializationTestCase")]