<commit_before>package gremlin.scala

import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph
import org.scalatest.WordSpec
import org.scalatest.Matchers
import shapeless.test.illTyped

case class CCSimple(s: String, i: Int)

case class MyValueClass(value: Int) extends AnyVal
case class CCWithValueClass(s: String, i: MyValueClass)
case class CCWithOptionValueClass(s: String, i: Option[MyValueClass])

case class CCWithOption(i: Int, s: Option[String])

case class CCWithOptionId(s: String, @id id: Option[Int])

case class CCWithLabel(
  s: String,
  l: Long,
  o: Option[String],
  seq: Seq[String],
  map: Map[String, String],
  nested: NestedClass
)

@label("the_label")
case class CCWithLabelAndId(
  s: String,
  @id id: Int,
  l: Long,
  o: Option[String],
  seq: Seq[String],
  map: Map[String, String],
  nested: NestedClass
) { def randomDef = ??? }

case class NestedClass(s: String)

class NoneCaseClass(s: String)

class MarshallableSpec extends WordSpec with Matchers {

  "marshals / unmarshals case classes" which {

    "only have simple members" in new Fixture {
      val cc = CCSimple("text", 12)
      val v = graph + cc

      val vl = graph.V(v.id).head
      vl.label shouldBe cc.getClass.getSimpleName
      vl.valueMap should contain("s" → cc.s)
      vl.valueMap should contain("i" → cc.i)
    }

    "contain options" should {
      "map `Some[A]` to `A`" in new Fixture {
        val ccWithOptionSome = CCWithOption(Int.MaxValue, Some("optional value"))
        val v = graph + ccWithOptionSome
        v.toCC[CCWithOption] shouldBe ccWithOptionSome

        val vl = graph.V(v.id).head
        vl.value[String]("s") shouldBe ccWithOptionSome.s.get
      }

      "map `None` to `null`" in new Fixture {
        val ccWithOptionNone = CCWithOption(Int.MaxValue, None)
        val v = graph + ccWithOptionNone
        v.toCC[CCWithOption] shouldBe ccWithOptionNone

        val vl = graph.V(v.id).head
        vl.keys should not contain "s"  //None should be mapped to `null`
      }

      // Background: if we marshal Option types, the graph db needs to understand scala.Option,
      // which wouldn't make any sense. So we rather translate it to `null` if it's `None`.
      // https://github.com/mpollmeier/gremlin-scala/issues/98
    }

    "contain value classes" should {
      "unwrap a plain value class" in new Fixture {
        val cc = CCWithValueClass("some text", MyValueClass(42))
        val v = graph + cc

        val vl = graph.V(v.id).head
        vl.label shouldBe cc.getClass.getSimpleName
        vl.valueMap should contain("s" → cc.s)
        vl.valueMap should contain("i" → cc.i.value)
        vl.toCC[CCWithValueClass] shouldBe cc
      }

      "unwrap an optional value class" in new Fixture {
        val cc = CCWithOptionValueClass("some text", Some(MyValueClass(42)))
        val v = graph + cc

        val vl = graph.V(v.id).head
        vl.label shouldBe cc.getClass.getSimpleName
        vl.valueMap should contain("s" → cc.s)
        vl.valueMap should contain("i" → cc.i.get.value)
        vl.toCC[CCWithOptionValueClass] shouldBe cc
      }

      // TODO: handle None case
    }

    "define their custom marshaller" in new Fixture {
      val ccWithOptionNone = CCWithOption(Int.MaxValue, None)

      val marshaller = new Marshallable[CCWithOption] {<commit_msg>add test for None case of marshalling optional value classes<commit_after>package gremlin.scala

import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph
import org.scalatest.WordSpec
import org.scalatest.Matchers
import shapeless.test.illTyped

case class CCSimple(s: String, i: Int)

case class MyValueClass(value: Int) extends AnyVal
case class CCWithValueClass(s: String, i: MyValueClass)
case class CCWithOptionValueClass(s: String, i: Option[MyValueClass])

case class CCWithOption(i: Int, s: Option[String])

case class CCWithOptionId(s: String, @id id: Option[Int])

case class CCWithLabel(
  s: String,
  l: Long,
  o: Option[String],
  seq: Seq[String],
  map: Map[String, String],
  nested: NestedClass
)

@label("the_label")
case class CCWithLabelAndId(
  s: String,
  @id id: Int,
  l: Long,
  o: Option[String],
  seq: Seq[String],
  map: Map[String, String],
  nested: NestedClass
) { def randomDef = ??? }

case class NestedClass(s: String)

class NoneCaseClass(s: String)

class MarshallableSpec extends WordSpec with Matchers {

  "marshals / unmarshals case classes" which {

    "only have simple members" in new Fixture {
      val cc = CCSimple("text", 12)
      val v = graph + cc

      val vl = graph.V(v.id).head
      vl.label shouldBe cc.getClass.getSimpleName
      vl.valueMap should contain("s" → cc.s)
      vl.valueMap should contain("i" → cc.i)
    }

    "contain options" should {
      "map `Some[A]` to `A`" in new Fixture {
        val ccWithOptionSome = CCWithOption(Int.MaxValue, Some("optional value"))
        val v = graph + ccWithOptionSome
        v.toCC[CCWithOption] shouldBe ccWithOptionSome

        val vl = graph.V(v.id).head
        vl.value[String]("s") shouldBe ccWithOptionSome.s.get
      }

      "map `None` to `null`" in new Fixture {
        val ccWithOptionNone = CCWithOption(Int.MaxValue, None)
        val v = graph + ccWithOptionNone
        v.toCC[CCWithOption] shouldBe ccWithOptionNone

        val vl = graph.V(v.id).head
        vl.keys should not contain "s"  //None should be mapped to `null`
      }

      // Background: if we marshal Option types, the graph db needs to understand scala.Option,
      // which wouldn't make any sense. So we rather translate it to `null` if it's `None`.
      // https://github.com/mpollmeier/gremlin-scala/issues/98
    }

    "contain value classes" should {
      "unwrap a plain value class" in new Fixture {
        val cc = CCWithValueClass("some text", MyValueClass(42))
        val v = graph + cc

        val vl = graph.V(v.id).head
        vl.label shouldBe cc.getClass.getSimpleName
        vl.valueMap should contain("s" → cc.s)
        vl.valueMap should contain("i" → cc.i.value)
        vl.toCC[CCWithValueClass] shouldBe cc
      }

      "unwrap an optional value class" in new Fixture {
        val cc = CCWithOptionValueClass("some text", Some(MyValueClass(42)))
        val v = graph + cc

        val vl = graph.V(v.id).head
        vl.label shouldBe cc.getClass.getSimpleName
        vl.valueMap should contain("s" → cc.s)
        vl.valueMap should contain("i" → cc.i.get.value)
        vl.toCC[CCWithOptionValueClass] shouldBe cc
      }

      "handle None value class" in new Fixture {
        val cc = CCWithOptionValueClass("some text", None)
        val v = graph + cc

        val vl = graph.V(v.id).head
        vl.label shouldBe cc.getClass.getSimpleName
        vl.valueMap should contain("s" → cc.s)
        vl.valueMap.keySet should not contain("i")
        vl.toCC[CCWithOptionValueClass] shouldBe cc
      }
    }

    "define their custom marshaller" in new Fixture {
      val ccWithOptionNone = CCWithOption(Int.MaxValue, None)

      val marshaller = new Marshallable[CCWithOption] {
{'fraction_non_alphanumeric': 0.1051444679351656, 'fraction_numerical': 0.002255109231853418, 'mean_word_length': 2.9182771949199338, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2560827', 'n_tokens_mistral': 2326, 'n_tokens_neox': 2283, 'n_words': 675}