[
  {
    "path": ".credo.exs",
    "content": "# This file contains the configuration for Credo.\n#\n# If you find anything wrong or unclear in this file, please report an\n# issue on GitHub: https://github.com/rrrene/credo/issues\n%{\n  #\n  # You can have as many configs as you like in the `configs:` field.\n  configs: [\n    %{\n      #\n      # Run any config using `mix credo -C <name>`. If no config name is given\n      # \"default\" is used.\n      name: \"default\",\n      #\n      # these are the files included in the analysis\n      files: %{\n        #\n        # you can give explicit globs or simply directories\n        # in the latter case `**/*.{ex,exs}` will be used\n        included: [\"lib/\", \"src/\", \"web/\", \"apps/\"],\n        excluded: []\n      },\n      #\n      # The `checks:` field contains all the checks that are run. You can\n      # customize the parameters of any given check by adding a second element\n      # to the tuple.\n      #\n      # There are two ways of deactivating a check:\n      # 1. deleting the check from this list\n      # 2. putting `false` as second element (to quickly \"comment it out\"):\n      #\n      #      {Credo.Check.Consistency.ExceptionNames, false}\n      #\n      checks: [\n        {Credo.Check.Consistency.ExceptionNames},\n        {Credo.Check.Consistency.LineEndings, false},\n        {Credo.Check.Consistency.MultiAliasImportRequireUse, false},\n        {Credo.Check.Consistency.ParameterPatternMatching},\n        {Credo.Check.Consistency.SpaceAroundOperators},\n        {Credo.Check.Consistency.SpaceInParentheses, false},\n        {Credo.Check.Consistency.TabsOrSpaces},\n\n        # For some checks, like AliasUsage, you can only customize the priority\n        # Priority values are: `low, normal, high, higher`\n        {Credo.Check.Design.AliasUsage, priority: :low},\n        # For others you can set parameters\n        {Credo.Check.Design.DuplicatedCode, mass_threshold: 16, nodes_threshold: 2},\n        {Credo.Check.Design.TagTODO, false},\n        {Credo.Check.Design.TagFIXME, false},\n        {Credo.Check.Readability.FunctionNames},\n        {Credo.Check.Readability.LargeNumbers},\n        {Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80},\n        {Credo.Check.Readability.ModuleAttributeNames},\n        {Credo.Check.Readability.ModuleDoc},\n        {Credo.Check.Readability.ModuleNames},\n        {Credo.Check.Readability.ParenthesesInCondition},\n        {Credo.Check.Readability.PredicateFunctionNames},\n        {Credo.Check.Readability.PreferImplicitTry},\n        {Credo.Check.Readability.RedundantBlankLines},\n        {Credo.Check.Readability.Specs, false},\n        {Credo.Check.Readability.StringSigils},\n        {Credo.Check.Readability.TrailingBlankLine},\n        {Credo.Check.Readability.TrailingWhiteSpace},\n        {Credo.Check.Readability.VariableNames},\n        {Credo.Check.Refactor.DoubleBooleanNegation},\n        {Credo.Check.Refactor.ABCSize, max_size: 50},\n        {Credo.Check.Refactor.CaseTrivialMatches, false},\n        {Credo.Check.Refactor.CondStatements},\n        {Credo.Check.Refactor.CyclomaticComplexity},\n        {Credo.Check.Refactor.FunctionArity},\n        {Credo.Check.Refactor.MatchInCondition},\n        {Credo.Check.Refactor.NegatedConditionsInUnless},\n        {Credo.Check.Refactor.NegatedConditionsWithElse},\n        {Credo.Check.Refactor.Nesting},\n        {Credo.Check.Refactor.PipeChainStart},\n        {Credo.Check.Refactor.CyclomaticComplexity},\n        {Credo.Check.Refactor.NegatedConditionsInUnless},\n        {Credo.Check.Refactor.NegatedConditionsWithElse},\n        {Credo.Check.Refactor.Nesting},\n        {Credo.Check.Refactor.UnlessWithElse},\n        {Credo.Check.Refactor.VariableRebinding},\n        {Credo.Check.Warning.BoolOperationOnSameValues},\n        {Credo.Check.Warning.IExPry},\n        {Credo.Check.Warning.IoInspect},\n        {Credo.Check.Warning.OperationOnSameValues},\n        {Credo.Check.Warning.OperationWithConstantResult},\n        {Credo.Check.Warning.UnusedEnumOperation},\n        {Credo.Check.Warning.UnusedFileOperation},\n        {Credo.Check.Warning.UnusedKeywordOperation},\n        {Credo.Check.Warning.UnusedListOperation},\n        {Credo.Check.Warning.UnusedPathOperation},\n        {Credo.Check.Warning.UnusedRegexOperation},\n        {Credo.Check.Warning.UnusedStringOperation},\n        {Credo.Check.Warning.UnusedTupleOperation}\n      ]\n    }\n  ]\n}\n"
  },
  {
    "path": ".dialyzer_ignore.exs",
    "content": "[\n  ~r/__impl__.*does\\ not\\ exist\\./\n]\n"
  },
  {
    "path": ".formatter.exs",
    "content": "# Used by \"mix format\" and to export configuration.\nexport_locals_without_parens = [\n  plug: 1,\n  plug: 2,\n  adapter: 1,\n  adapter: 2\n]\n\n[\n  inputs: [\n    \"lib/**/*.{ex,exs}\",\n    \"test/**/*.{ex,exs}\",\n    \"mix.exs\"\n  ],\n  locals_without_parens: export_locals_without_parens,\n  export: [locals_without_parens: export_locals_without_parens]\n]\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n"
  },
  {
    "path": ".gitignore",
    "content": "### Elixir template\n# The directory Mix will write compiled artifacts to.\n/_build\n\n# If you run \"mix test --cover\", coverage assets end up here.\n/cover\n\n# The directory Mix downloads your dependencies sources to.\n/deps\n\n# Where 3rd-party dependencies like ExDoc output generated docs.\n/doc\n\n# If the VM crashes, it generates a dump, let's ignore it too.\nerl_crash.dump\n\n# Also ignore archive artifacts (built via \"mix archive.build\").\n*.ez\n\n/bench/snapshots\n/bench/graphs\n/cover\n\n### Vim template\n[._]*.s[a-w][a-z]\n[._]s[a-w][a-z]\n*.un~\nSession.vim\n.netrwhist\n*~\n\n### SublimeText template\n# cache files for sublime text\n*.tmlanguage.cache\n*.tmPreferences.cache\n*.stTheme.cache\n\n# workspace files are user-specific\n*.sublime-workspace\n*.sublime-project\n\n# project files should be checked into the repository, unless a significant\n# proportion of contributors will probably not be using SublimeText\n# *.sublime-project\n\n# sftp configuration file\nsftp-config.json\n\n### OSX template\n.DS_Store\n.AppleDouble\n.LSOverride\n\n# Icon must end with two \\r\nIcon\n\n# Thumbnails\n._*\n\n# Files that might appear in the root of a volume\n.DocumentRevisions-V100\n.fseventsd\n.Spotlight-V100\n.TemporaryItems\n.Trashes\n.VolumeIcon.icns\n\n# Directories potentially created on remote AFP share\n.AppleDB\n.AppleDesktop\nNetwork Trash Folder\nTemporary Items\n.apdisk\n\n### Tags template\n# Ignore tags created by etags, ctags, gtags (GNU global) and cscope\nTAGS\n!TAGS/\ntags\n!tags/\ngtags.files\nGTAGS\nGRTAGS\nGPATH\ncscope.files\ncscope.out\ncscope.in.out\ncscope.po.out\n/tmp\n\n### JetBrains template\n# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio\n\n## Directory-based project format:\n.idea/\n\n## File-based project format:\n*.ipr\n*.iws\n\n## Plugin-specific files:\n\n# IntelliJ\n/out/\n\n# mpeltonen/sbt-idea plugin\n.idea_modules/\n\n# JIRA plugin\natlassian-ide-plugin.xml\n\n.tags\n.tags_sorted_by_file\n\n### Erlang template\n.eunit\n*.plt\nebin\nrel/example_project\n.concrete/DEV_MODE\n\nlogs/\n.vscode/\n.elixir_ls/\n\n# py related\n*~\n*.py[co]\n__pycache__\n.pytest_cache\n*.egg-info"
  },
  {
    "path": ".iex.exs",
    "content": "try do\n  Code.eval_file(\".iex.exs\", \"~\")\nrescue\n  Code.LoadError -> :rescued\nend\n\nalias Bolt.Sips.{Utils, Protocol, Router, ConnectionSupervisor, Response}\nalias Bolt.Sips\n\nApplication.put_env(:tzdata, :autoupdate, :disabled)\n\n# default port considered to be: 7687\ntest_config = [\n  # url: 'localhost',\n  url: \"bolt://localhost\",\n  basic_auth: [username: \"neo4j\", password: \"test\"],\n  pool_size: 5,\n  max_overflow: 1,\n  # retry the request, in case of error - in the example below the retry will\n  # linearly increase the delay from 150ms following a Fibonacci pattern,\n  # cap the delay at 15 seconds (the value defined by the default `:timeout`\n  # parameter) and giving up after 3 attempts\n  retry_linear_backoff: [delay: 150, factor: 2, tries: 3],\n  read: [pool_size: 5, pool_overflow: 0],\n  write: [pool_size: 1, pool_overflow: 0]\n]\n\nMix.shell().info([\n  :green,\n  \"\"\"\n  Optional, if needed for development (Sips is the alias for Bolt.Sips):\n    {:ok, _neo} = Sips.start_link(url: \"bolt://neo4j:test@localhost\")\n    conn = Sips.conn()\n  Examples:\n    Sips.query!(conn, \"UNWIND range(1, 10) AS n RETURN n\")\n    Sips.query!(conn, \"RETURN 1 as n\")\n  --- ✄  -------------------------------------------------\n\n  \"\"\"\n])\n"
  },
  {
    "path": ".markdownlint.json",
    "content": "{\n    \"MD013\": false,\n    \"MD030\": false\n}"
  },
  {
    "path": ".prettierrc.yaml",
    "content": "# .prettierrc or .prettierrc.yaml\ntrailingComma: \"es5\"\ntabWidth: 2\nsemi: false\nsingleQuote: true\nMD013: false\nMD030: false\n"
  },
  {
    "path": ".tool-versions",
    "content": "erlang 24.1.7\nelixir 1.13.0-otp-24\nnodejs 12.6.0\n#python 3.7.3\npython 3.7.3 2.7.16\n\nruby 2.7.5\nlua 5.3.5\nterraform 0.15.4\ndirenv 2.20.0\n"
  },
  {
    "path": ".travis.yml",
    "content": "sudo: required\nservices: docker\nlanguage: elixir\nmatrix:\n  include:\n    - elixir: 1.7.4\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.2.14\n        - BOLT_V1_EXCLUDED=false\n        - BOLT_V2_EXCLUDED=true\n        - BOLT_V3_EXCLUDED=true\n    - elixir: 1.7.4\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.2.14\n        - BOLT_V1_EXCLUDED=false\n        - BOLT_V2_EXCLUDED=true\n        - BOLT_V3_EXCLUDED=true\n    - elixir: 1.7.4\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.4.17\n        - BOLT_V1_EXCLUDED=false\n        - BOLT_V2_EXCLUDED=false\n        - BOLT_V3_EXCLUDED=true\n    - elixir: 1.7.4\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.5.14\n        - BOLT_V1_EXCLUDED=true\n        - BOLT_V2_EXCLUDED=false\n        - BOLT_V3_EXCLUDED=false\n    - elixir: 1.8.2\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.2.14\n        - BOLT_V1_EXCLUDED=false\n        - BOLT_V2_EXCLUDED=true\n        - BOLT_V3_EXCLUDED=true\n    - elixir: 1.8.2\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.2.14\n        - BOLT_V1_EXCLUDED=false\n        - BOLT_V2_EXCLUDED=true\n        - BOLT_V3_EXCLUDED=true\n    - elixir: 1.8.2\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.4.17\n        - BOLT_V1_EXCLUDED=false\n        - BOLT_V2_EXCLUDED=false\n        - BOLT_V3_EXCLUDED=true\n    - elixir: 1.8.2\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=3.5.14\n        - BOLT_V1_EXCLUDED=true\n        - BOLT_V2_EXCLUDED=false\n        - BOLT_V3_EXCLUDED=false\n    - elixir: 1.8.2\n      otp_release: 21.2\n      env:\n        - NEO4J_VERSION=4.2.1\n        - BOLT_V1_EXCLUDED=true\n        - BOLT_V2_EXCLUDED=false\n        - BOLT_V3_EXCLUDED=false\n  exclude:\n    - elixir: 1.8\n      otp_release: 19.3\n    - elixir: 1.8\n      otp_release: 18.3\n    - elixir: 1.7\n      otp_release: 18.3\n    - elixir: 1.6\n      otp_release: 18.3\n    - elixir: 1.5\n      otp_release: 21.2\n    - elixir: 1.4\n      otp_release: 21.2\n    - elixir: 1.3\n      otp_release: 21.2\n    - elixir: 1.3\n      otp_release: 20.3\n    - elixir: 1.2\n      otp_release: 21.2\n    - elixir: 1.2\n      otp_release: 20.3\n\naddons:\n  apt:\n    sources:\n      - ubuntu-toolchain-r-test\n    packages:\n      - g++-6\n      - ninja-build\ncache:\n  directories:\n    - $HOME/cmake\n\nenv:\n  global:\n    - ELIXIR_ERL_OPTIONS=\"+T 9\"\n    - PATH=$HOME/cmake/bin:$PATH\n    - CXX=g++-6\n    - CC=gcc-6\n\nbranches:\n  only:\n    - master\nbefore_install:\n  - if [ ! -d \"$HOME/cmake/bin\" ]; then wget --no-check-certificate https://cmake.org/files/v3.5/cmake-3.5.2-Linux-x86_64.sh && sh cmake-3.5.2-Linux-x86_64.sh --prefix=$HOME/cmake --exclude-subdir; fi\n  - docker run --name neo4j -d -p 7687:7687 -e 'NEO4J_AUTH=neo4j/test' neo4j:$NEO4J_VERSION\n  - docker logs -f neo4j | sed /Bolt\\ enabled/q\nscript:\n  - mix test --exclude routing --exclude bolt_v1:$BOLT_V1_EXCLUDED --exclude bolt_v2:$BOLT_V2_EXCLUDED --exclude bolt_v3:$BOLT_V3_EXCLUDED\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# Changelog\n\n## 2.1.0\n\nThank you https://github.com/zediogoviana, for the following improvements:\n\n- Add configurable SSL options\n- Fix local connection\n- Keep the :ssl keyword to manage the options also\n\nDependencies updated, paving the road to switching to latest Elixir/Erlang combo.\n\n## 2.0.11\n\n- Issue #100: Timeout set in config in now used by queries\n- DBConnection, bump dependencies\n\n## 2.0.10\n\n- Fix temporal types usage: microseconds are not fully available\n- Review to pass test on Neo4j 4 :\n  - test and doctests to use new parameter syntax (using {} is deprecated in Neo4j 4)\n  - `toUpper` instead of `upper`\n\n## 2.0.9\n\n- fix: (Bolt.Sips.Exception) unable to encode value: -128, see: https://boltprotocol.org/v1/#ints, for details. Closes #93 Thank you, @kalamarski-marcin\n\n## 2.0.8\n\n- Fix Response.profile not being properly filled. Closes #91\n\n## 2.0.7\n\n- sometimes the server version is missing the patch number, and the router couldn't return the proper version. Thank you @barry-w-hill, for finding this bug and reporting it!\n- remove the `basic_auth` when using the `&Bolt.Sips.info/0` function. Thanks @dominique-vassard, for suggestion. Closes: #89\n\n## 2.0.6\n\n- Fix 'unused alias' compilation warnings\n- Fix Bolt.Sips.Response type: `stats` was a `list` instead of `list|map`\n- Add typespec for Bolt.Sips.Types: Node, Relationship and UnboundRelationship\n\n## 2.0.5\n\n- fix #83. More details in commit: https://github.com/florinpatrascu/bolt_sips/commit/ebe17e62ab1d823e301b11d99d532663b0b25135 Thank you @kristofka!\n\n## 2.0.4\n\n- feature: support connection options in queries PR #82. Many thanks @tcrossland, for this contribution!\n  This PR adds support for passing options through to DBConnection.execute/4\n- fix some broken links, in the docs; closes #76\n- update some dependencies, including the DBConnection package.\n- squashing some compile warnings; to be continued /attn: @team ;)\n- please use Elixir 1.9 or 1.10, for test and development - where possible.\n\n## 2.0.3\n\n- refactoring the internals for achieving a better performance, while improving the code readability and extensibility - many thanks to @kristofka and @dominique-vassard. You guys are awesome!\n- fix: Consistent bad connection state after malformed query [...] issue #78\n\n## === 2.0.2 ===\n\n- The 2.0, stable release. Thank you all for your feedback and for contributing to making this driver better. w⦿‿⦿t!\n- fix: Simple Query taking too much time to process #73\n\n## 2.0.0-rc.2\n\n- swapping the assets around, for better organizing the docs\n\n## 2.0.0-rc.1\n\n- more documentation\n- fix the TravisCi build\n- min versions\n  erlang 21.2\n  elixir 1.7\n\n## === 2.0.0-rc ===\n\n## What's New?\n\n### `bolt+routing://` is now supported\n\nRead more what this schema is, as defined by the [Neo4j team](https://neo4j.com/developer/kb/how-neo4j-browser-bolt-routing/)\n\n### Role-based connections\n\nUntil this version, Bolt.Sips was used for connecting to a single Neo4j server, aka: the \"direct\" mode. Basically you configure the driver with a url to a Neo4j server and Bolt.Sips will use that to attach itself to it, using a single configuration, remaining attached to that server until it is restarted (or reconfigured). In direct mode, bolt_sips \"knows\" only one server.\n\nStarting with this version you can have as many distinct connection configurations, each of them dedicated to different Neo4j servers, as/if needed. We call these connections: \"role-based connections\". For example, when you'll connect to a Neo4j cluster using the new protocol, i.e. by using a configuration like this:\n\n    config :bolt_sips, Bolt,\n      # default port considered to be: 7687\n      url: \"bolt+routing://localhost\",\n      basic_auth: [username: \"neo4j\", password: \"test\"],\n      pool_size: 10\n\nBolt.Sips will automatically create three pools of size 10, with the following **reserved** names: `:read`, `:write` and `:route`. Now you can specify what type of connection you want to use, by its name (role). For example:\n\n    wconn = Bolt.Sips.conn(:write)\n    ... = Bolt.Sips.query!(wconn, \"CREATE (a:Person {name:'Bob'})\")\n\n    rconn = Bolt.Sips.conn(:read)\n    ... = Bolt.Sips.query!(rconn, \"MATCH (a:Person {name: 'Bob'}) RETURN a.name AS name\")\n\nThe roles above: `:read`, `:write` and `:route`, are reserved. Please do not name custom connections using the same names (atoms). And as you just realized, yes: now you can create as many Bolt.Sips **direct** \"driver instances\" as you want, or as many as your app/hardware supports.\n\nPlease see the documentation for much more details.\n\n### Main breaking changes introduced in version 2.x\n\n- the `hostname` config parameter is a string; used to be a charlist\n- the `url` config parameter must start with a valid schema i.e. `bolt`, `bolt+routing` or `neo4j`.\n  Examples:\n\n      url: \"bolt://localhost\"\n      url: \"bolt+routing://neo4j:password@neo01.graph.example.com:123456?policy=europe\"\n\n- Bolt.Sips.Query, will return a Bolt.Sips.Response now; it used to be a simple data structure.\n\n## === 1.5 ===\n\n## 1.5.1\n\n- add a test alias for running the tests compatible with the most recent Neo4j server while\n  disabling the older/legacy ones\n- cleanup some warning about unused aliases\n\n## 1.5.0\n\n- Bolt V3 support\n- Decompose tests by bolt version\n- Important note about transaction\n\n## 1.4.0\n\n- Encoding / Decoding types is now at the lowest possible level\n- Decompose encoders / decoders by bolt version\n- Expose only public API in docs\n\n## 1.3.0\n\n- 1.3.0 stable release. Many thanks to Dominique VASSARD, for his awesome contributions.\n\n## 1.3.0-rc2\n\n- Fix some typos\n- add json encoding capability\n\n## 1.2.2-rc2\n\n- Bug fix: Nanoseconds formating was erroneous. Example: 54 nanoseconds was formated to \"PT0.54S\" instead of \"PT0.000000054S\"\n- Bug fix: Large amount of nanoseconds (>= 1_000_000) wasn't treated and lead to Neo4j errors. Now large amount of nanoseconds are converted in seconds, with the remainder in nanoseconds.\n\n## 1.2.1-rc2\n\n- Bug fix: If a property contains a speciifc types (date, datetime, point, etc.), it wasn't decoded. see: https://github.com/florinpatrascu/bolt_sips/issues/55\n\n## 1.2.0-rc2\n\n- support for the spatial and temporal types.\n\n## 1.1.0-rc2\n\n- removed the `boltex` dependency and added all its \"low-level\" code to `internals`.\n\n## 1.0.0-rc2\n\n### Breaking changes introduced in version 1.x\n\n- non-closure based transactions are not supported anymore. This is a change introduced in DBConnection 2.x. `Bolt.Sips` version tagged `v0.5.10` is the last version supporting open transactions.\n- the support for ETLS was dropped. It was mostly used for development or hand-crafted deployments\n\nThis version is using the official [DBConnection 2.0.0-rc2](https://hex.pm/packages/db_connection/2.0.0-rc.0), from [hex.pm](https://hex.pm)\n\n## 0.5.10\n\n- update the links referencing the Bolt protocol documentation (types, etc)\n\n## 0.5.9\n\n- upgrade dependencies\n- trading carefully around the new db_connection, as we're chasing the code from `master` currently, and there more changes in the pipe to come for the both projects; db_connection, and this one, respectively.\n\n## 0.5.8\n\n- dealing with negative integers see issue #42, for more details\n\n## 0.5.7\n\n- elixir 1.6 and code formatting, of course :)\n- minor test updates\n- update dependencies\n- pending code for the newest `db_connection` (currently using db_connection from the master branch)\n\n## 0.5.5\n\n- using the [DBConnection](https://hexdocs.pm/db_connection/DBConnection.html), thanks to the work done by Dmitriy Nesteryuk.\n\n## 0.4.11\n\n- using Elixir 1.5\n- not using the ConCache anymore. I initially intended to use its support throughout the driver, but it is not needed.\n- README updated with a short snippet from a Phoenix web app demo, showing how to start Bolt.Sips, as a worker\n- dependencies update\n- minor code cleanup, to prep the code for receiving HA and Bolt routing capabilities\n\n## v0.3.5\n\n- better error messages; issue #33\n- not retrying a connection when the server is not available/started\n- incorrect number of retries, performed by the driver in case of errors; was one extra\n\n## v0.3.4\n\n- dependencies update, minor code cleanup, listening to Credo :) and finally using a Markdown linter\n\n## v0.3.3\n\n- Add link to travis build; #31 by vic\n\n## v0.3.2\n\n- Use the project's own configuration file when executing the `bolt.cypher` mix task. Fixes issue #20\n\n## v0.3.1 Breaking changes\n\n- rollback/refactor to optionally allow external configuration options to be defined at runtime. You must start the Bolt.Sips manually, when needed, i.e. `Bolt.Sips.start_link(url: \"localhost\")`, or by changing your app's mix config file, i.e.\n\n```elixir\ndef application do\n  [applications: [:logger, :bolt_sips],\n   mod: {Bolt.Sips.Application, []}]\nend\n```\n\nYou can also specify custom configuration settings in you app's mix config file. These may overwrite your config file:\n\n```elixir\ndef application do\n  [extra_applications: [:logger], mod:\n    {Bolt.Sips.Application, [url: 'localhost', pool_size: 15]}\n  ]\nend\n```\n\n- code cleanup\n\n## v0.2.6 (2017-04-21)\n\n- cleanup, and minor dependencies update\n\n## v0.2.5 (2017-03-22)\n\n- split multi-line Cypher statements containing semicolons only if the `;` character is at the end of the line, followed by \\r\\n on Windows and \\n on Unix like system, otherwise it may break the Cypher statement when the semicolon appears somewhere else\n\n## v0.2.4 (2017-02-26)\n\n- add the fuzzyurl to the list of apps, for project using Elixir < 1.4 (thank you, @dnesteryuk!)\n\n## v0.2.3 (2017-02-26)\n\n- improved connection handling\n\n## v0.2.2 (2017-02-24)\n\n- PR #18; Bring up `:boltex` and `:retry` in `applications`, for Elixir < 1.4 (from: @wli0503, thank you!)\n- PR #19; test for error message on invalid parameter types (from: @vic, thank you!).\n\n## v0.2.1 (2017-02-20)\n\n- stop retrying a request if the failure is an internal one (driver, or driver dependencies related).\n- update the Boltex driver containing two important bug fixes: one where Boltex will fail when receiving too much data (florinpatrascu/bolt_sips/issues/16) and the other one, an improvement, make Boltex.Error.get_id/1 more resilient for new transports (details here: mschae/boltex/issues/14)\n- changed the pool strategy to :fifo, and its timeout to :infinity, and let the (:gen_server) call timeout expire according to the user's :timeout configuration parameter\n- added a test unit provided by @adri (thank you), for executing a Cypher query, with large set of parameters\n\n## v0.2.0 Breaking changes\n\n- Elixir 1.4 is now required.\n- Using Boltex 0.2.0\n- bugfix: invalid Cypher statements will now be properly handled when the request is retried automatically\n\n## v0.1.11\n\n- With a larger amount of parameters it seems like generating chunks isn't working correctly. This is a patch imported from Boltex, see: https://github.com/mschae/boltex/issues/13, for more info\n\n## v0.1.10 (2017-02-11)\n\n- accept Map and Struct for query parameters, transparently. Thank you [@wli0503], for the PR.\n\n## v0.1.9 (2017-01-27)\n\nSome of the users are encountering difficulties when trying to compile bolt_sips on Windows. This release is addressing their concern.\n\n`Bolt.Sips` will use the optional System variable: `BOLT_WITH_ETLS`, for depending on the [ETLS](https://hex.pm/packages/etls) package. If that variable is not defined, then `Bolt.Sips` will use the standard Erlang [`:ssl` module](http://erlang.org/doc/man/ssl.html), for the SSL/TLS protocol; the default behavior, starting with this version.\n\nTherefore, if you want the **much** faster ssl/tls support offered by ETLS, then use this: `export BOLT_WITH_ETLS=true` on Linux/OSX, for example. Then:\n\n```elixir\n mix deps.get\n mix test\n```\n\nand so on.\n\n(Don't forget to `mix deps.unlock --all`, if you plan to plan to further debugging/developing w/ or w/o the `BOLT_WITH_ETLS` support)\n\nMany thanks to: [Ben Wilson](https://elixir-lang.slack.com/team/benwilson512), for advices.\n\n## v0.1.8 (2017-01-07)\n\n- using Elixir 1.4\n- add more details to the README, about the components required to build ETLS, the TCP/TLS layer\n- added newer Elixirs to the Travis CI configuration file\n- minor code cleanups\n\n## v0.1.7 (2017-01-02)\n\n- Connection code refactored for capturing the errors when the remote server is not responding on the first request, or if the driver is misconfigured i.e. wrong port number, bad hostname ...\n- updated the test configuration file with detailed info about the newly introduced option: `:retry_linear_backoff`, mostly as a reminder\n\n## v0.1.6 (2017-01-01)\n\n- we're already using configurable timeouts, when executing requests from the connection pool. But with Bolt, the initial handshake sequence (happening before sending any commands to the server) is represented by two important calls, executed in sequence: `handshake` and `init`, and they must both succeed, before sending any (Cypher) requests. You can see the details in the [Bolt protocol](http://boltprotocol.org/v1/#handshake) specs. This sequence is also sensitive to latencies, such as: network latencies, busy servers, etc., and because of that we're introducing a simple support for retrying the handshake (and the subsequent requests) with a linear backoff, and try the handshake sequence (or the request) a couple of times before giving up - all these as part of the exiting pool management, of course. This retry is configurable via a new configuration parameter, the: `:retry_linear_backoff`, respectively. For example:\n\n```elixir\nconfig :bolt_sips, Bolt,\n  url: \"bolt://Bilbo:Baggins@hobby-hobbits.dbs.graphenedb.com:24786\",\n  ssl: true,\n  timeout: 15_000,\n  retry_linear_backoff: [delay: 150, factor: 2, tries: 3]\n```\n\nIn the example above the retry will linearly increase the delay from 150ms following a Fibonacci pattern, cap the delay at 15 seconds (the value defined by the `:timeout` parameter) and giving up after 3 attempts. The same retry mechanism (and configuration parameters) is also honored when we send requests to the neo4j server.\n\n## v0.1.5 (2016-12-30)\n\n- as requested by many users, this version is introducing the optional `url` configuration parameter. If present, it will be used for extracting the host name, the port and the authentication details. Please see the README, for a couple of examples. For brevity:\n\n```elixir\nconfig :bolt_sips, Bolt,\n  url: 'bolt://demo:demo@hobby-wowsoeasy.dbs.graphenedb.com:24786',\n  ssl: true\n```\n\n## v0.1.4 (Merry Christmas)\n\n- add support for connecting to Neo4j servers on encrypted sockets. Currently only TLSv1.2 is supported, using the default [BoringSSL](https://boringssl.googlesource.com/boringssl/) cipher; via [:etls](https://github.com/kzemek/etls). To connect securely to a remote Neo4j server, such as the ones provided by graphenedb.com, modify your Bolt.Sips config file like this (example):\n\n```elixir\nconfig :bolt_sips, Bolt,\n  hostname: 'bolt://hobby-blah.dbs.graphenedb.com',\n  basic_auth: [username: \"wow\", password: \"of_course_this_is_the_password\"],\n  port: 24786,\n  pool_size: 5,\n  ssl: true,\n  max_overflow: 1\n```\n\nObserve the new flag: `ssl: true`\n\nPlease note this is work in progress\n\n## v0.1.2 (2016-11-06)\n\n- integrate the Boltex code from https://github.com/mschae/boltex, and let the Bolt.Sips wrapper to manage the connectivity, using a simple Poolboy implementation for connection pooling\n\n## v0.1.1 (2016-09-09)\n\n- a temporary solution for dealing with negative values while extracting a graph walk-through from a Path. Dealing with this in Boltex instead, but this fix should work for now.\n\n## v0.1.0 (2016-08-31)\n\nFirst release!\n"
  },
  {
    "path": "ISSUE_TEMPLATE.md",
    "content": "# Precheck\n\n* For bugs, do a quick search and make sure the bug has not yet been reported.\n* Finally, be nice and have fun!\n\n## Environment\n\n* Elixir version (elixir -v):\n* Neo4j and version (Neo4j 3.5.3, etc.):\n* Connection scheme (`bolt://`, `bolt+routing://` or `neo4j://`):\n* Bolt.Sips version (mix deps):\n* Operating system:\n\n## Current behavior\n\nInclude code samples, errors and stacktraces if appropriate.\n\n## Expected behavior\n"
  },
  {
    "path": "LICENSE",
    "content": "                              Apache License\n                        Version 2.0, January 2004\n                     http://www.apache.org/licenses/\n\nTERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n1. Definitions.\n\n   \"License\" shall mean the terms and conditions for use, reproduction,\n   and distribution as defined by Sections 1 through 9 of this document.\n\n   \"Licensor\" shall mean the copyright owner or entity authorized by\n   the copyright owner that is granting the License.\n\n   \"Legal Entity\" shall mean the union of the acting entity and all\n   other entities that control, are controlled by, or are under common\n   control with that entity. For the purposes of this definition,\n   \"control\" means (i) the power, direct or indirect, to cause the\n   direction or management of such entity, whether by contract or\n   otherwise, or (ii) ownership of fifty percent (50%) or more of the\n   outstanding shares, or (iii) beneficial ownership of such entity.\n\n   \"You\" (or \"Your\") shall mean an individual or Legal Entity\n   exercising permissions granted by this License.\n\n   \"Source\" form shall mean the preferred form for making modifications,\n   including but not limited to software source code, documentation\n   source, and configuration files.\n\n   \"Object\" form shall mean any form resulting from mechanical\n   transformation or translation of a Source form, including but\n   not limited to compiled object code, generated documentation,\n   and conversions to other media types.\n\n   \"Work\" shall mean the work of authorship, whether in Source or\n   Object form, made available under the License, as indicated by a\n   copyright notice that is included in or attached to the work\n   (an example is provided in the Appendix below).\n\n   \"Derivative Works\" shall mean any work, whether in Source or Object\n   form, that is based on (or derived from) the Work and for which the\n   editorial revisions, annotations, elaborations, or other modifications\n   represent, as a whole, an original work of authorship. For the purposes\n   of this License, Derivative Works shall not include works that remain\n   separable from, or merely link (or bind by name) to the interfaces of,\n   the Work and Derivative Works thereof.\n\n   \"Contribution\" shall mean any work of authorship, including\n   the original version of the Work and any modifications or additions\n   to that Work or Derivative Works thereof, that is intentionally\n   submitted to Licensor for inclusion in the Work by the copyright owner\n   or by an individual or Legal Entity authorized to submit on behalf of\n   the copyright owner. For the purposes of this definition, \"submitted\"\n   means any form of electronic, verbal, or written communication sent\n   to the Licensor or its representatives, including but not limited to\n   communication on electronic mailing lists, source code control systems,\n   and issue tracking systems that are managed by, or on behalf of, the\n   Licensor for the purpose of discussing and improving the Work, but\n   excluding communication that is conspicuously marked or otherwise\n   designated in writing by the copyright owner as \"Not a Contribution.\"\n\n   \"Contributor\" shall mean Licensor and any individual or Legal Entity\n   on behalf of whom a Contribution has been received by Licensor and\n   subsequently incorporated within the Work.\n\n2. Grant of Copyright License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   copyright license to reproduce, prepare Derivative Works of,\n   publicly display, publicly perform, sublicense, and distribute the\n   Work and such Derivative Works in Source or Object form.\n\n3. Grant of Patent License. Subject to the terms and conditions of\n   this License, each Contributor hereby grants to You a perpetual,\n   worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n   (except as stated in this section) patent license to make, have made,\n   use, offer to sell, sell, import, and otherwise transfer the Work,\n   where such license applies only to those patent claims licensable\n   by such Contributor that are necessarily infringed by their\n   Contribution(s) alone or by combination of their Contribution(s)\n   with the Work to which such Contribution(s) was submitted. If You\n   institute patent litigation against any entity (including a\n   cross-claim or counterclaim in a lawsuit) alleging that the Work\n   or a Contribution incorporated within the Work constitutes direct\n   or contributory patent infringement, then any patent licenses\n   granted to You under this License for that Work shall terminate\n   as of the date such litigation is filed.\n\n4. Redistribution. You may reproduce and distribute copies of the\n   Work or Derivative Works thereof in any medium, with or without\n   modifications, and in Source or Object form, provided that You\n   meet the following conditions:\n\n   (a) You must give any other recipients of the Work or\n       Derivative Works a copy of this License; and\n\n   (b) You must cause any modified files to carry prominent notices\n       stating that You changed the files; and\n\n   (c) You must retain, in the Source form of any Derivative Works\n       that You distribute, all copyright, patent, trademark, and\n       attribution notices from the Source form of the Work,\n       excluding those notices that do not pertain to any part of\n       the Derivative Works; and\n\n   (d) If the Work includes a \"NOTICE\" text file as part of its\n       distribution, then any Derivative Works that You distribute must\n       include a readable copy of the attribution notices contained\n       within such NOTICE file, excluding those notices that do not\n       pertain to any part of the Derivative Works, in at least one\n       of the following places: within a NOTICE text file distributed\n       as part of the Derivative Works; within the Source form or\n       documentation, if provided along with the Derivative Works; or,\n       within a display generated by the Derivative Works, if and\n       wherever such third-party notices normally appear. The contents\n       of the NOTICE file are for informational purposes only and\n       do not modify the License. You may add Your own attribution\n       notices within Derivative Works that You distribute, alongside\n       or as an addendum to the NOTICE text from the Work, provided\n       that such additional attribution notices cannot be construed\n       as modifying the License.\n\n   You may add Your own copyright statement to Your modifications and\n   may provide additional or different license terms and conditions\n   for use, reproduction, or distribution of Your modifications, or\n   for any such Derivative Works as a whole, provided Your use,\n   reproduction, and distribution of the Work otherwise complies with\n   the conditions stated in this License.\n\n5. Submission of Contributions. Unless You explicitly state otherwise,\n   any Contribution intentionally submitted for inclusion in the Work\n   by You to the Licensor shall be under the terms and conditions of\n   this License, without any additional terms or conditions.\n   Notwithstanding the above, nothing herein shall supersede or modify\n   the terms of any separate license agreement you may have executed\n   with Licensor regarding such Contributions.\n\n6. Trademarks. This License does not grant permission to use the trade\n   names, trademarks, service marks, or product names of the Licensor,\n   except as required for reasonable and customary use in describing the\n   origin of the Work and reproducing the content of the NOTICE file.\n\n7. Disclaimer of Warranty. Unless required by applicable law or\n   agreed to in writing, Licensor provides the Work (and each\n   Contributor provides its Contributions) on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n   implied, including, without limitation, any warranties or conditions\n   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n   PARTICULAR PURPOSE. You are solely responsible for determining the\n   appropriateness of using or redistributing the Work and assume any\n   risks associated with Your exercise of permissions under this License.\n\n8. Limitation of Liability. In no event and under no legal theory,\n   whether in tort (including negligence), contract, or otherwise,\n   unless required by applicable law (such as deliberate and grossly\n   negligent acts) or agreed to in writing, shall any Contributor be\n   liable to You for damages, including any direct, indirect, special,\n   incidental, or consequential damages of any character arising as a\n   result of this License or out of the use or inability to use the\n   Work (including but not limited to damages for loss of goodwill,\n   work stoppage, computer failure or malfunction, or any and all\n   other commercial damages or losses), even if such Contributor\n   has been advised of the possibility of such damages.\n\n9. Accepting Warranty or Additional Liability. While redistributing\n   the Work or Derivative Works thereof, You may choose to offer,\n   and charge a fee for, acceptance of support, warranty, indemnity,\n   or other liability obligations and/or rights consistent with this\n   License. However, in accepting such obligations, You may act only\n   on Your own behalf and on Your sole responsibility, not on behalf\n   of any other Contributor, and only if You agree to indemnify,\n   defend, and hold each Contributor harmless for any liability\n   incurred by, or claims asserted against, such Contributor by reason\n   of your accepting any such warranty or additional liability.\n\nEND OF TERMS AND CONDITIONS\n\nAPPENDIX: How to apply the Apache License to your work.\n\n   To apply the Apache License to your work, attach the following\n   boilerplate notice, with the fields enclosed by brackets \"{}\"\n   replaced with your own identifying information. (Don't include\n   the brackets!)  The text should be enclosed in the appropriate\n   comment syntax for the file format. We also recommend that a\n   file or class name and description of purpose be included on the\n   same \"printed page\" as the copyright notice for easier\n   identification within third-party archives.\n\nCopyright {yyyy} {name of copyright owner}\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<img src=\"assets/logo_transparent.png\" alt=\"logo\" width=\"240\"/>\n\n# Neo4j driver for Elixir.\n\n[![Build Status](https://travis-ci.org/florinpatrascu/bolt_sips.svg?branch=master)](https://travis-ci.org/florinpatrascu/bolt_sips)\n[![Hex.pm](https://img.shields.io/hexpm/dt/bolt_sips.svg?maxAge=2592000)](https://hex.pm/packages/bolt_sips)\n[![Hexdocs.pm](https://img.shields.io/badge/api-hexdocs-brightgreen.svg)](https://hexdocs.pm/bolt_sips)\n\n`Bolt.Sips` is an Elixir driver for [Neo4j](https://neo4j.com/developer/graph-database/), providing many useful features:\n\n- using the Bolt protocol, the Elixir implementation - the Neo4j's newest network protocol, designed for high-performance; latest Bolt versions, are supported.\n- Can connect to a standalone Neo4j server (`:direct` mode) or to a Neo4j causal cluster, using the `bolt+routing` or the newer `neo4j` schemes; connecting in `:routing` mode.\n- Provides the user with the ability to create and manage distinct ad-hoc `role-based` connections to one or more Neo4j servers/databases\n- Supports transactions, simple and complex Cypher queries with or w/o parameters\n- Multi-tenancy\n- Supports Neo4j versions: 3.0.x/3.1.x/3.2.x/3.4.x/3.5.x/4.0.x/4.1.x/4.2.x\n\nNotes:\n\n- Regarding Neo4j 4, stream capabilities are not yet supported.\n- If you're seeking a substitute driver, here's a compilation of repositories:\n  - https://github.com/sagastume/boltx\n\n## Table of Contents\n\n- [Installation](#installation)\n  - [Getting Started](docs/getting-started.md#starting-the-driver)\n  - [Basic usage](docs/getting-started.md#usage)\n  - [Configuration](docs/features/configuration.md)\n    - [Direct mode](docs/features/configuration.md#direct-mode)\n    - [Routing](docs/features/configuration.md#routing-mode)\n    - [Role-based connections](docs/features/configuration.md#role-based-connections)\n    - [Multi tenancy](docs/features/configuration.md#multi-tenancy)\n- [Using Cypher](docs/features/using-cypher.md)\n- [Temporal and spatial types](docs/features/using-temporal-and-spatial-types.md)\n- [Transactions](docs/features/about-transactions.md)\n- [Encoding](docs/features/about-encoding.md)\n- [Routing, in detail](docs/features/routing.md)\n- [Multi tenancy, in detail](docs/features/multi-tenancy.md)\n- [Using Bolt.Sips with Phoenix](docs/features/using-with-phoenix.md)\n- [More examples](docs/examples/readme.md)\n\n### Installation\n\n[Available in Hex](https://hex.pm/packages/bolt_sips), the package can be added to your list of dependencies, in the: `mix.exs`:\n\n```elixir\ndef deps do\n  [{:bolt_sips, \"~> 2.0\"}]\nend\n```\n\n### Basic usage\n\nProvided you have access to a running Neo4j server, and a project where you just added the `:bolt_sips` dependency, run an `iex` session inside the project's folder, and once inside the shell, follow this simple step-by-step example.\n\nStart an iex session:\n\n```elixir\nErlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]\nInteractive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)\n\niex> {:ok, _neo} = Bolt.Sips.start_link(url: \"bolt://neo4j:test@localhost\")\n{:ok, #PID<0.237.0>}\n\niex> conn = Bolt.Sips.conn()\n#PID<0.242.0>\n\niex> Bolt.Sips.query!(conn, \"return 1 as n\") |>\n...> Bolt.Sips.Response.first()\n%{\"n\" => 1}\n```\n\nPlease see the docs for more examples and details about this driver.\n\n### Testing\n\nYou'll need a running Neo4j server, for running the tests. Please verify that you do not store critical data on this server, as its contents will be wiped clean when the tests are running.\n\nIf you have docker available on your system, you can start an instance before running the test suite:\n\n```shell\ndocker run --rm -p 7687:7687 -e 'NEO4J_AUTH=neo4j/test' neo4j:3.0.6\n```\n\nNeo4j versions used for test: 3.0, 3.1, 3.4, 3.5\n\n```shell\nmix test\n```\n\nFor the stubs using [boltkit](https://github.com/neo4j-drivers/boltkit/), you will have to install Python 3.7 and run: `pip install boltkit`. After this you can run any tests tagged with `:boltkit`. Example:\n\n```shell\nmix test test/boltkit_test.exs --include boltkit\n```\n\nor:\n\n```shell\nmix test --only boltkit\n```\n\n### Special thanks\n\n- Michael Schaefermeyer (@mschae), for the initial version of the Bolt protocol in Elixir: [mschae/boltex](https://github.com/mschae/boltex)\n\n### Contributors\n\nAs reported by Github: [contributions to master, excluding merge commits](https://github.com/florinpatrascu/bolt_sips/graphs/contributors)\n\n### Contributing\n\n- [Fork it](https://github.com/florinpatrascu/bolt_sips/fork)\n- Create your feature branch (`git checkout -b my-new-feature`)\n- Test (`mix test`)\n- Commit your changes (`git commit -am 'Add some feature'`)\n- Push to the branch (`git push origin my-new-feature`)\n- Create new Pull Request\n\n### License\n\n```txt\nCopyright 2016-2020 the original author or authors\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n"
  },
  {
    "path": "benchees/conn_to_local_bench.exs",
    "content": "# benchmark the time it takes to open connections to a local neo4j server.\n# Misc...\n#\n# What I want:\n#\n# - I want to measure the time it takes to open a connection and run\n#   a simple query\n# - I want to demonstrate how saving and reusing a connection (where\n#   applicable) takes less time compared with a similar code where\n#   I'm creating a new connection for every query\n#\n# Sample from a quick run:\n#\n#    $ mix run benchees/conn_to_local_bench.exs\n#\n#    Operating System: macOS\n#    CPU Information: Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz\n#    Number of Available Cores: 8\n#    Available memory: 17.179869184 GB\n#    Elixir 1.5.0\n#    Erlang 20.0\n#    Benchmark suite executing with the following configuration:\n#    warmup: 2.00 s\n#    time: 1.00 s\n#    parallel: 1\n#    inputs: none specified\n#    Estimated total run time: 6.00 s\n#\n#    Benchmarking  new conn...\n#    Benchmarking same conn...\n#\n#    Name                ips        average  deviation         median\n#    same conn        1.46 K        0.68 ms    ±20.73%        0.64 ms\n#     new conn        0.47 K        2.15 ms    ±67.17%        1.98 ms\n#\n#    Comparison:\n#    same conn        1.46 K\n#     new conn        0.47 K - 3.14x slower\n\n{:ok, _pid} = Bolt.Sips.start_link(url: \"localhost\")\n\nsimple_cypher = \"\"\"\n  MATCH (p:Person)-[r:WROTE]->(b:Book {title: 'The Name of the Wind'})\n  RETURN p\n\"\"\"\n\nquery = fn (conn, cypher) ->\n  Bolt.Sips.Query.query(conn, cypher)\nend\n\nconn = Bolt.Sips.conn()\n\nBenchee.run(\n  %{\n    \"same conn\" => fn -> query.(conn, simple_cypher) end,\n    \" new conn\" => fn -> query.(Bolt.Sips.conn(), simple_cypher) end\n  }, time: 1)\n"
  },
  {
    "path": "config/config.exs",
    "content": "# This file is responsible for configuring your application\n# and its dependencies with the aid of the Mix.Config module.\nimport Config\n\n# This configuration is loaded before any dependency and is restricted\n# to this project. If another project depends on this project, this\n# file won't be loaded nor affect the parent project. For this reason,\n# if you want to provide default values for your application for\n# 3rd-party users, it should be done in your \"mix.exs\" file.\n\n# You can configure for your application as:\n#\n#     config :bolt_sips, key: :value\n#\n# And access this configuration in your application as:\n#\n#     Application.get_env(:bolt_sips, :key)\n#\n# Or configure a 3rd-party app:\n#\n#     config :logger, level: :info\n#\n\n# It is also possible to import configuration files, relative to this\n# directory. For example, you can emulate configuration per environment\n# by uncommenting the line below and defining dev.exs, test.exs and such.\n# Configuration from the imported file will override the ones defined\n# here (which is why it is important to import them last).\n#\nimport_config \"#{Mix.env()}.exs\"\n"
  },
  {
    "path": "config/dev.exs",
    "content": "import Config\n\nconfig :mix_test_watch,\n  clear: true\n\nlevel =\n  if System.get_env(\"DEBUG\") do\n    :debug\n  else\n    :info\n  end\n\nconfig :bolt_sips,\n  log: false,\n  log_hex: false\n\nconfig :logger, :console,\n  level: level,\n  format: \"$date $time [$level] $metadata$message\\n\"\n\nconfig :tzdata, :autoupdate, :disabled\nconfig :elixir, :time_zone_database, Tzdata.TimeZoneDatabase\n\nconfig :eye_drops,\n  tasks: [\n    %{\n      id: :docs,\n      name: \"docs\",\n      run_on_start: true,\n      cmd: \"mix docs\",\n      paths: [\"lib/*\", \"README.md\", \"examples/*\", \"mix.exs\"]\n    }\n  ]\n"
  },
  {
    "path": "config/test.exs",
    "content": "import Config\n\nconfig :bolt_sips, Bolt,\n  # default port considered to be: 7687\n  url: \"bolt://localhost\",\n  basic_auth: [username: \"neo4j\", password: \"test\"],\n  pool_size: 10,\n  max_overflow: 2,\n  queue_interval: 500,\n  queue_target: 1500,\n  prefix: :default\n\n\nlevel =\n  if System.get_env(\"DEBUG\") do\n    :debug\n  else\n    :info\n  end\n\nconfig :bolt_sips,\n  log: true,\n  log_hex: false\n\nconfig :logger, :console,\n  level: level,\n  format: \"$date $time [$level] $metadata$message\\n\"\n\nconfig :mix_test_watch,\n  clear: true\n\nconfig :tzdata, :autoupdate, :disabled\nconfig :elixir, :time_zone_database, Tzdata.TimeZoneDatabase\nconfig :porcelain, driver: Porcelain.Driver.Basic\n"
  },
  {
    "path": "docker-compose.yml",
    "content": "version: \"3.0\"\n\nnetworks:\n  lan:\n\nservices:\n  core1:\n    container_name: core1\n    image: neo4j:3.5.3-enterprise\n    networks:\n      - lan\n    ports:\n      - 7474:7474\n      - 6477:6477\n      - 7687:7687\n    environment:\n      - NEO4J_AUTH=neo4j/test\n      - NEO4J_dbms_mode=CORE\n      - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes\n      - NEO4J_causalClustering_expectedCoreClusterSize=3\n      - NEO4J_causalClustering_initialDiscoveryMembers=core1:5000,core2:5000,core3:5000\n      - NEO4J_dbms_connector_http_listen__address=:7474\n      - NEO4J_dbms_connector_https_listen__address=:6477\n      - NEO4J_dbms_connector_bolt_listen__address=:7687\n      - NEO4J_dbms_memory_heap_initial__size=300m\n      - NEO4J_dbms_memory_heap_max__size=300m\n      - NEO4J_dbms_logs_query_enabled=true\n      - NEO4J_dbms_logs_query_page__logging__enabled=false\n      - NEO4J_dbms_logs_query_parameter__logging__enabled=true\n      - NEO4J_dbms_logs_query_threshold=0\n\n  core2:\n    container_name: core2\n    image: neo4j:3.5.3-enterprise\n    networks:\n      - lan\n    ports:\n      - 7475:7475\n      - 6478:6478\n      - 7688:7688\n    environment:\n      - NEO4J_AUTH=neo4j/test\n      - NEO4J_dbms_mode=CORE\n      - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes\n      - NEO4J_causalClustering_expectedCoreClusterSize=3\n      - NEO4J_causalClustering_initialDiscoveryMembers=core1:5000,core2:5000,core3:5000\n      - NEO4J_dbms_connector_http_listen__address=:7475\n      - NEO4J_dbms_connector_https_listen__address=:6478\n      - NEO4J_dbms_connector_bolt_listen__address=:7688\n      - NEO4J_dbms_memory_heap_initial__size=300m\n      - NEO4J_dbms_memory_heap_max__size=300m\n      - NEO4J_dbms_logs_query_enabled=true\n      - NEO4J_dbms_logs_query_page__logging__enabled=false\n      - NEO4J_dbms_logs_query_parameter__logging__enabled=true\n      - NEO4J_dbms_logs_query_threshold=0\n\n  core3:\n    container_name: core3\n    image: neo4j:3.5.3-enterprise\n    networks:\n      - lan\n    ports:\n      - 7476:7476\n      - 6479:6479\n      - 7689:7689\n    environment:\n      - NEO4J_AUTH=neo4j/test\n      - NEO4J_dbms_mode=CORE\n      - NEO4J_ACCEPT_LICENSE_AGREEMENT=yes\n      - NEO4J_causalClustering_expectedCoreClusterSize=3\n      - NEO4J_causalClustering_initialDiscoveryMembers=core1:5000,core2:5000,core3:5000\n      - NEO4J_dbms_connector_http_listen__address=:7476\n      - NEO4J_dbms_connector_https_listen__address=:6479\n      - NEO4J_dbms_connector_bolt_listen__address=:7689\n      - NEO4J_dbms_memory_heap_initial__size=300m\n      - NEO4J_dbms_memory_heap_max__size=300m\n      - NEO4J_dbms_logs_query_enabled=true\n      - NEO4J_dbms_logs_query_page__logging__enabled=false\n      - NEO4J_dbms_logs_query_parameter__logging__enabled=true\n      - NEO4J_dbms_logs_query_threshold=0\n"
  },
  {
    "path": "docs/examples/readme.md",
    "content": ""
  },
  {
    "path": "docs/features/about-encoding.md",
    "content": "# About encoding\n\nBolt.Sips provides support for encoding your query result in different formats.\nFor now, only JSON is supported.\n\nThere is two way of encoding data to json:\n\n- By using the helpers provided by the module `Bolt.Sips.ResponseEncoder`\n- Using your usual JSON encoding library. `Bolt.Sips` have implementation for: Jason and Poison. With this the query results can be automatically encoded by one of the libraries available: Jason or Poison. No further work is required when using a framework like: Phoenix, for example.\n\nA few examples around the encoding suport:\n\n```elixir\niex> query_result = [\n   %{\n     \"t\" => %Bolt.Sips.Types.Node{\n       id: 26,\n       labels: [\"Test\"],\n       properties: %{\n         \"created_at\" => \"2019-08-03T12:34:56+01:00\",\n         \"name\" => \"A test node\",\n         \"uid\" => 12345\n       }\n     }\n   }\n ]\n\n# Using Bolt.Sips.ResponseEncoder\n iex> Bolt.Sips.ResponseEncoder.encode(query_result, :json)\n{:ok,\n \"[{\\\"t\\\":{\\\"id\\\":26,\\\"labels\\\":[\\\"Test\\\"],\\\"properties\\\":{\\\"created_at\\\":\\\"2019-08-03T12:34:56+01:00\\\",\\\"name\\\":\\\"A test node\\\",\\\"uid\\\":12345}}}]\"}\niex(11)> Bolt.Sips.ResponseEncoder.encode!(query_result, :json)\n\"[{\\\"t\\\":{\\\"id\\\":26,\\\"labels\\\":[\\\"Test\\\"],\\\"properties\\\":{\\\"created_at\\\":\\\"2019-08-03T12:34:56+01:00\\\",\\\"name\\\":\\\"A test node\\\",\\\"uid\\\":12345}}}]\"\n\n# Using Jason\niex(14)> Jason.encode!(query_result)\n\"[{\\\"t\\\":{\\\"id\\\":26,\\\"labels\\\":[\\\"Test\\\"],\\\"properties\\\":{\\\"created_at\\\":\\\"2019-08-03T12:34:56+01:00\\\",\\\"name\\\":\\\"A test node\\\",\\\"uid\\\":12345}}}]\"\n\n# Using Poison\niex(13)> Poison.encode!(query_result)\n\"[{\\\"t\\\":{\\\"properties\\\":{\\\"uid\\\":12345,\\\"name\\\":\\\"A test node\\\",\\\"created_at\\\":\\\"2019-08-03T12:34:56+01:00\\\"},\\\"labels\\\":[\\\"Test\\\"],\\\"id\\\":26}}]\"\n```\n\nBoth solutions rely on protocols, then they can be easily overridden if needed.\nMore info in the modules `Bolt.Sips.ResponseEncoder.Json`, `Bolt.Sips.ResponseEncoder.Json.Jason`, `Bolt.Sips.ResponseEncoder.Json.Poison`\n"
  },
  {
    "path": "docs/features/about-transactions.md",
    "content": "# About transactions\n\nTransaction management in Neo4j 3.5+ differs from what it was in prior versions.\nThe cypher keyword `BEGIN`, `COMMIT` and `ROLLBACK` are no longer available.\n\nIn order to have a query that runs fine in all versions, you should use the following pattern:\n\n```elixir\n# Commit is performed automatically if everythings went fine\nconn = Bolt.Sips.conn()\nBolt.Sips.transaction(conn, fn conn ->\n  result = Bolt.Sips.query!(conn, \"CREATE (m:Movie {title: \"Matrix\"}) RETURN m\")\nend)\n\n# Rollback is performed automatically in case of error\nBolt.Sips.transaction(conn, fn conn ->\n  result =Bolt.Sips.query!(conn, \"Invalid query\")\nend)\n\n# Rollback can stil be forced\nBolt.Sips.transaction(conn, fn conn ->\n  result = Bolt.Sips.query!(conn, \"CREATE (m:Movie {title: \"Matrix\"}) RETURN m\")\n  Bolt.Sips.rollback(conn, :dont_save)\nend)\n```\n"
  },
  {
    "path": "docs/features/configuration.md",
    "content": "# Configuration\n\nBolt.Sips can be configured using the well known Mix config files, or by using simple keyword lists.\n\nThis is the most basic configuration:\n\n```elixir\nconfig :bolt_sips, Bolt,\n  url: \"bolt://localhost:7687\"\n```\n\nIt tells Bolt.Sips your Neo4j server is available locally, and it listens on port 7687, expecting bolt commands.\n\nThese are the values you can configure, and their default values:\n\n- `:url`- a full url to pointing to a running Neo4j server. Please remember you must specify the scheme used to connect to the server. Valid schemes:`bolt`,`bolt+routing`and`neo4j` - the last two being used for connecting to a Neo4j causal cluster.\n- `:pool_size` - the size of the connection pool. Default: 15\n- `:timeout` - a connection timeout value defined in milliseconds. Default: 15_000\n- `:ssl`-`true`, if the connection must be encrypted. If the user wants to specify custom ssl options, just pass a keyword list with the options. More info [here](https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/ssl) Default:`false`\n- `:prefix`- used for differentiating between multiple connections available in the same app. Default:`:default`\n\n## Examples of configurations\n\nConnecting to remote (hosted) Neo4j servers, such as the ones available (also for free) at [Neo4j/Sandbox](https://neo4j.com/sandbox-v2/):\n\n```elixir\nconfig :bolt_sips, Bolt,\n  url: \"bolt://<ip_address>:<bolt_port>\",\n  basic_auth: [username: \"neo4j\", password: \"#######\"]\n  ssl: true # or for example `[verify: :verify_none]` if custom ssl options\n```\n\n## Direct mode\n\nUntil this version, `Bolt.Sips` was used for connecting to a single Neo4j server from the moment the hosting app started, until the hosting app was terminated/restarted. This is known as the: `direct` mode. In `direct` mode, the `Bolt.Sips` driver has one configuration describing the connection to a single Neo4j server.\n\nSince this connection mode is well known to our users, we'll not spend time on talking about it. It is sufficient to say that in direct mode, you have one configurable pool of connections, and the settings governing them i.e. timeout,  size, etc., are all about this single connection.\n\nBecause starting with version 2.0 `Bolt.Sips` is supporting a new type of connectivity: `routing`, for connecting to multiple servers or to a Neo4j causal cluster, you must specify the `scheme` in the `url` parameter, of your configuration. Example, for configuring a connection in `direct` mode:\n\n    url: \"bolt://localhost:7687\"\n\nWe'll spend more ink on talking about the `routing` mode, next.\n\n## Routing mode\n\nWith the 2.0 version, `Bolt.Sips` is implementing the ability to connect your app to a Neo4j causal cluster. You can read more about this, here: [Neo4j Causal Clustering](https://neo4j.com/docs/operations-manual/current/clustering/introduction/)\n\nThe features of using a causal cluster, in Neo4j's own words:\n\n> Neo4j’s Causal Clustering provides three main features:\n>\n> - Safety: Core Servers provide a fault tolerant platform for transaction processing which will remain available while a simple majority of those Core Servers are functioning.\n> - Scale: Read Replicas provide a massively scalable platform for graph queries that enables very large graph workloads to be executed in a widely distributed topology.\n> - Causal consistency: when invoked, a client application is guaranteed to read at least its own writes.\n\nTo configure `Bolt.Sips` for connecting to a Neo4j Causal Cluster, you only need the specify the appropriate scheme, in the `url` configuration parameter:\n\n    url: \"bolt+routing://localhost:7687\"\n\nor:\n\n    url: \"neo4j://localhost:7687\"\n\nPrefer the latter, since `bolt+routing` appears to be soon deprecated, by Neo4j. We'll use `neo4j://` throughout the docs for referring to the `routing` mode, for brevity. Read more about `routing`, [here](routing.md).\n\n## Role based connections\n\nWhen we implemented the routing mode, we realized we could extend this ability to letting you define any number of connections, identified by a role name of your choice. For example, say your default configuration for `Bolt.Sips` looks like this:\n\n```elixir\nconfig :bolt_sips, Bolt,\n  url: \"bolt://localhost:7687\",\n  basic_auth: [username: \"neo4j\", password: \"test\"],\n  pool_size: 10,\n  max_overflow: 2,\n```\n\n`Bolt.Sips` will load it by default, when your application starts. And with a configuration like that, the default mode, you will continue to obtain connections using the default `Bolt.Sips.conn()` function.\n\nHowever, if you require to have different connections, say: to a different Neo4j server that has some specific role, you could add a new configuration, for example:\n\n```elixir\nconfig :bolt_sips, :hidden_gems,\n  url: \"bolt://localhost:1234\",\n  pool_size: 50,\n  role: :hidden_gems\n```\n\nYou'd have to load this config separately, after the starting the `Bolt.Sips`driver. Like this:\n\n```elixir\niex> Bolt.Sips.start_link(Application.get_env(:bolt_sips, :hidden_gems))\n{:ok, #PID<0.266.0>}\n```\n\nand the you can use connections from this new configuration, as easy as this:\n\n```elixir\niex> conn = Bolt.Sips.conn(:hidden_gems)\n#PID<0.324.0>\n```\n\nwhile for obtaing the connections from your default configuration, is business as usual:\n\n```elixir\niex> conn = Bolt.Sips.conn()\n#PID<0.309.0>\n```\n\nThe new connection pool is supervised by the main `Bolt.Sips.ConnectionSupervisor`, you don't have to do anythings special for that.\n\n![](assets/role_based_connections.png?raw=true)\n\nIn the final release, we'll add a friendlier api for adding role-based connections. More details about role-based-connections, [here](role-based-connections.md)\n\n## Multi tenancy\n\nAnother important feature of the 2.0 version, is: **multi-tenancy**.\n\nStarting with this version, your app can connect to any number of Neo4j servers, in `direct` mode or `routing`.\n\nWe introduced a new configurable parameter, named: `prefix`.\n\nAt this time, the only way to configure the driver for multi-tenancy, is programmatically, not via the configuration file. Example:\n\n```elixir\nmy_secret_cluster_config [\n    url: \"neo4j://localhost:9001\",\n    basic_auth: [username: \"neo4j\", password: \"test\"],\n    pool_size: 10,\n    max_overflow: 2,\n    queue_interval: 500,\n    queue_target: 1500,\n    prefix: :secret_cluster\n  ]\n\n{:ok, _pid} = Bolt.Sips.start_link(@routing_connection_config)\nconn = Bolt.Sips.conn(:write, prefix: :secret_cluster)\n```\n\nAnd you can start as many connections as needed, for as long as the `:prefix` has different names. These connections can be used for connecting to the same or different Neo4j servers.\n\nMore details about multi-tenancy, [here](multi-tenancy.md)\n"
  },
  {
    "path": "docs/features/multi-tenancy.md",
    "content": "# Multi tenancy\n\nVery similar to the role-based connections, with multi-tenancy you will be able to connect to servers where the type of the server (role) is defined by the server itself, such as the Neo4j causal cluster. This setting is sill in its infancy, it works, but you'll have to be careful when using it.\n\nFor differentiating about Neo4j tenants, we introduced a new configurations parameter, named: `:prefix`. Example:\n\n```elixir\nmonster_cluster_conf = [\n  url: \"neo4j://localhost\",\n  basic_auth: [username: \"neo4j\", password: \"password\"],\n  pool_size: 50,\n  prefix: :monster_cluster\n\nbaby_monster_cluster_conf = [\n  url: \"neo4j://raspberry_π\",\n  basic_auth: [username: \"πs\", password: \"4VR\"],\n  pool_size: 50,\n  prefix: :baby_monster_cluster\n```\n\nIn the example above we defined two different connections, each of them pointing to different Neo4j clusters. As you know now, every cluster will have role-specific connections as defined by the routers, in those clusters. The connection roles will be: `:write`, `:read` and `:route`. To specify what connection you want and on what server, you will use the `:prefix` optional parameter of the new `Bolt.Sips.conn/2` method. Example:\n\n```elixir\nBolt.Sips.conn(:read, prefix: :monster_cluster)\n  |> Bolt.Sips.query!(\"MATCH (n) RETURN n.name AS name\")\n```\n\nor:\n\n```elixir\nBolt.Sips.conn(:read, prefix: :baby_monster_cluster)\n  |> Bolt.Sips.query!(\"MATCH (n) RETURN n.name AS name\")\n```\n\n(wip)\n"
  },
  {
    "path": "docs/features/role-based-connections.md",
    "content": "# Role-based connections\n\nStarting with the 2.0 version, you can have distinct configurations that you can use in your app, concurrently. These configurations can connect to connect in `:direct` mode to different Neo4j servers, or the same but with different credentials, pool sizes, etc.\n\n> This is not recommended for connecting to a causal cluster; the `:routing` mode, respectively.\n\nTo differentiate between multiple `:direct` configurations, you'll use a new parameter: `:role`. Let's see a some code examples, for brevity.\n\n```elixir\nfrontend_config = [\n    url: \"bolt://localhost\",\n    basic_auth: [username: \"neo4j\", password: \"test\"],\n    pool_size: 10,\n    max_overflow: 2,\n    role: :frontend\n  ]\n\nbackend_config = [\n    url: \"bolt://not_my_localhost:12345\",\n    basic_auth: [username: \"xxxxx\", password: \"yyyyy\"],\n    pool_size: 10,\n    max_overflow: 2,\n    role: :backend\n  ]\n\n\n{:ok, _pid} = Bolt.Sips.start_link(frontend_config)\n{:ok, _pid} = Bolt.Sips.start_link(backend_config)\n\n:frontend = Bolt.Sips.conn(:frontend)\n:backend = Bolt.Sips.conn(:backend)\n\n%Response{results: [%{\"n\" => 1}]} = Bolt.Sips.query!(:frontend, \"RETURN 1 as n\")\n%Response{results: [%{\"n\" => 1}]} = Bolt.Sips.query!(:backend, \"RETURN 1 as n\")\n\n```\n\nThe last two Cypher queries above will be executed on two different servers. And yes you can run them concurrently since their respective pools will not compete for the same resources.\n\nIf you desire to terminate a role-based connection, you can easily do so. Just like this: `:ok = Bolt.Sips.terminate_connections(:backend)`.\n"
  },
  {
    "path": "docs/features/routing.md",
    "content": "# Routing\n\nWhen connecting to a Neo4j cluster, `Bolt.Sips` will create 3 distinct connection pools, each of them dedicated to one of the following connection types (**connection roles**):\n\n- `:route` - used for getting information from the Neo4j router, such as: routing details about which server is handling what type of role: read/write, and more.\n- `:read` - used for read-only connections\n- `:write` - used for write-only connections.\n\nHaving the `Bolt.Sips` configured in `routing` mode, will enforce your code to clarify what type of connections you want, type you **must** specify when requesting a `Bolt.Sips` connection. Example:\n\n```elixir\nrconn = Bolt.Sips.conn(:read)\nwconn = Bolt.Sips.conn(:write)\nrouter_conn = Bolt.Sips.conn(:route)\n```\n\nWithout being explicit about the connection type, you will receive errors, in case you'll attempt to execute a query that will say: create new nodes, on a server having the role: `read` or `route`. This is the only rule you must observe, when using the `Bolt.Sips` driver with a causal cluster.\n\n## Routing walk-through\n\nLet's walk-through a simple experiment with using `Bolt.Sips` in routing mode and a Neo4j cluster.\n\nIf you don't have a local server, or a remote Neo4j cluster available for your tests, you can easily setup your own local playground. All you need is Docker.\n\nWe'll use a [docker-compose.yml](../../docker-compose.yml) file that you can find in the `Bolt.Sips` main source repo.\n\nIf you have:\n\n- [Docker](<https://en.wikipedia.org/wiki/Docker_(software)>) installed, and running. You can get Docker from here: https://docs.docker.com/installation/\n- and a simple Elixir project having the `:bolt_sips` driver installer, as a dependency\n\n### Start the Neo4j cluster\n\nIn a folder where you have the `docker-compose.yml` file, start a new shell session and run the following command:\n\n    docker-compose up\n\nIf this is the first time you run this command, or use Neo4j as a Docker image, then based on the quality of your Internet connection, you'll wait a few seconds while Docker downloads a Neo4j Enterprise image. You'll see something like this:\n\n```sh\nCreating network \"neo4j_lan\" with the default driver\nPulling core1 (neo4j:3.5.3-enterprise)...\n3.5.3-enterprise: Pulling from library/neo4j\ne7c96db7181b: Pull complete\nf910a506b6cb: Pull complete\nb6abafe80f63: Pull complete\nb95a7fd32595: Pull complete\n6c09128ad074: Pull complete\n648805e5f471: Pull complete\ne2790f69a70d: Pull complete\nCreating core2 ... done\nCreating core3 ... done\nCreating core1 ... done\nAttaching to core3, core1, core2\ncore3    | Changed password for user 'neo4j'.\ncore1    | Changed password for user 'neo4j'.\ncore2    | Changed password for user 'neo4j'.\ncore3    | Active database: graph.db\ncore3    | Directories in use:\ncore3    |   home:         /var/lib/neo4j\ncore3    |   config:       /var/lib/neo4j/conf\n...\n```\n\nand towards the end of the starting sequence, this:\n\n```sh\ncore2    | 2019-06-17 12:37:59.078+0000 INFO  Remote interface available at http://localhost:7475/\ncore3    | 2019-06-17 12:37:59.165+0000 INFO  Remote interface available at http://localhost:7476/\n```\n\nCheck to see if you can connect to your local Neo4j cluster, as simple as pointing your Internet browser to this url: `http://localhost:7474`, and if everything was executed successfully, you'll be seeing the familiar Neo4j web interface.\n\nNow let's play with the `Bolt.Sips`driver and our local Neo4j cluster.\n\nChange your elixir test project configuration and modify the `config/config.exs` file like this (excerpt):\n\n```elixir\nuse Mix.Config\n\nconfig :bolt_sips, Bolt,\n  # bolt+routing will be deprecated?!\n  # url: \"bolt+routing://localhost:7687\",\n  url: \"neo4j://localhost:7687\",\n  basic_auth: [username: \"neo4j\", password: \"test\"],\n  pool_size: 10\n```\n\nthen start a IEx shell session, from the projects'r main folder: `iex -S mix`. While inside the IEx session, let's see if our configuration is sound?\n\n```elixir\niex> Bolt.Sips.info()\n%{\n  default: %{\n    connections: %{\n      read: %{\"localhost:7688\" => 0, \"localhost:7689\" => 0},\n      route: %{\n        \"localhost:7687\" => 0,\n        \"localhost:7688\" => 0,\n        \"localhost:7689\" => 0\n      },\n      write: %{\"localhost:7687\" => 0},\n      routing_query: %{...},\n      ttl: 300,\n      updated_at: 1560775628\n    },\n    user_options: [\n      url: \"neo4j://localhost:7687\",\n      pool_size: 10,\n      ....\n    ]\n  }\n}\n```\n\nif you see the response above, it means your settings are ready. Without going into much details about the data structure above, the routing details are these:\n\n```elixir\n  read: %{\"localhost:7688\" => 0, \"localhost:7689\" => 0},\n  write: %{\"localhost:7687\" => 0},\n  route: %{\n    \"localhost:7687\" => 0,\n    \"localhost:7688\" => 0,\n    \"localhost:7689\" => 0\n  ttl: 300,\n  updated_at: ...\n```\n\nAccording to the routing information returned by our cluster, we have:\n\n- two nodes accepting `:read` commands: `localhost:7688` and`localhost:7689`\n- three nodes capabale of responding with routing specific details: `localhost:7687` `localhost:7688` and `localhost:7689`\n- one node accepting `:write` commands; the `localhost:7687`, respectively.\n\nBut don't worry about the gory details, we got you covered :)\n\nLet's run some Cypher queries.\n\n```elixir\niex> alias Bolt.Sips.Response\niex> alias Bolt.Sips, as: Neo\n\n# obtaining a read(only) connection:\niex> rconn = Neo.conn(:read)\n#PID<0.324.0>\n\n# checking if there are any Person nodes \"named\": Bob?\niex> %Response{results: r} = Neo.query!(rconn, \"MATCH (p:Person{name: 'Bob'}) RETURN p\")\n%Bolt.Sips.Response{\n  bookmark: \"neo4j:bookmark:v1:tx2\",\n  fields: [\"p\"],\n  notifications: [],\n  plan: nil,\n  profile: nil,\n  records: [],\n  results: [],\n  stats: [],\n  type: \"r\"\n}\n\n# r is [], meaning: our query found none. So let's create one.\n# First we obtain a connection suitable for `write` operations:\niex> wconn = Neo.conn(:write)\n#PID<0.384.0>\n\n# and now we can use it for creating a new node:\n\niex> %Response{results: r} = Neo.query!(wconn, \"CREATE (p:Person{name:'Bob'})\")\n%Bolt.Sips.Response{\n  ...\n  stats: %{\"labels-added\" => 1, \"nodes-created\" => 1, \"properties-set\" => 1},\n  type: \"w\"\n}\n\n# our node was created and has one property set,  w⦿‿⦿t!\n# but can we find it? Rerun the previous query using the `read` connection:\n\niex> Neo.query!(rconn, \"MATCH (p:Person{name: 'Bob'}) RETURN p\") |> Response.first()\n%{\n  \"p\" => %Bolt.Sips.Types.Node{\n    id: 20,\n    labels: [\"Person\"],\n    properties: %{\"name\" => \"Bob\"}\n  }\n}\n\n# and yessss, our new Person node is in the cluster!\n# Do you need its json form, instead? Easy:\niex> Neo.query!(rconn, \"MATCH (p:Person{name: 'Bob'}) RETURN p\") |>\n...> Response.first() |>\n...> Bolt.Sips.ResponseEncoder.encode!(:json)\n\"{\\\"p\\\":{\\\"id\\\":20,\\\"labels\\\":[\\\"Person\\\"],\\\"properties\\\":{\\\"name\\\":\\\"Bob\\\"}}}\"\n\n```\n\nBut what happens if we try to create a new Person, using our `read` connection?\n\n```elixir\niex> Neo.query!(rconn, \"CREATE (p:Person{name:'Alice'})\")\n** (Bolt.Sips.Exception) ... No write operations are allowed directly on this database. Writes must pass through the leader. The role of this server is: FOLLOWER\n```\n\nNeo4j will promptly let us know we can't use that connection for write operations. This is the main difference that you must consider when coding.\n\nSame command executed on the proper (write) connection, will be successful:\n\n```elixir\niex> Neo.query!(wconn, \"CREATE (p:Person{name:'Alice'})\")\n%Bolt.Sips.Response{\n  ...\n  stats: %{\"labels-added\" => 1, \"nodes-created\" => 1, \"properties-set\" => 1},\n  type: \"w\"\n}\n```\n"
  },
  {
    "path": "docs/features/using-cypher.md",
    "content": "# Using Bolt.Sips to query the Neo4j server\n\nLet's talk about the basics of querying a Neo4j server, using `Bolt.Sips`, and a few methods you could use for using the data returned by the server, using the `Bolt.Sips.Response`. You can learn so much more from the official docs, available at [Neo4j](https://neo4j.com/developer/graph-database/), you should start from there, if you want to get a deep understanding about the Neo4j graph database and its query language: Cypher.\n\n## What you need?\n\n- a [Neo4j](https://neo4j.com/download/) server running locally and available at this `url`: `bolt://neo4j:test@localhost`\n- a mix project with `:bolt_sips` available\n\n\n## Simple queries, using Cypher\n\nWith the above prerequisites, let's drop into the IEx shell and start experimenting.\n\n```sh\ncd my_neo4j\niex -S mix\nErlang/OTP 21 [erts-10.2.3] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]\n\nInteractive Elixir (1.8.1) - press Ctrl+C to exit (type h() ENTER for help)\n\niex>\n```\n\nFirst we need to start the driver with a minimalist configuration (unless it is already started by your project?):\n\n```elixir\niex> {:ok, _neo} = Bolt.Sips.start_link(url: \"bolt://neo4j:test@localhost\")\n{:ok, #PID<0.243.0>}\niex>\n\n```\n\nPresuming your database is empty, you can still test your setup by running a simple Cypher query:\n\n```elixir\niex> conn = Bolt.Sips.conn()\n#PID<0.248.0>\niex> Bolt.Sips.query!(conn, \"RETURN 1 as n\") |>\n...> Bolt.Sips.Response.first()\n%{\"n\" => 1}\n```\n\nand we obtained our first response from the server: `%{\"n\" => 1}`, w⦿‿⦿t!! Now let's try some more complicated Cypher queries. We'll use examples that you may want to paste them in your `.exs/.ex` files rather than into the IEx shell, for readability.\n\nWhile most of the Cypher querier fit on a simple row, and they look compact, you might encounter situations where you need to send multiple queries in a single trip, to the server. `Bolt.Sips` allows you do that.\n\nLet's initialize our **test** database with some data.\n\n```elixir\ncypher = \"\"\"\n  CREATE (BoltSips:BoltSips {title:'Elixir sipping from Neo4j, using Bolt', released:2016, license:'MIT', bolt_sips: true})\n  CREATE (TNOTW:Book {title:'The Name of the Wind', released:2007, genre:'fantasy', bolt_sips: true})\n  CREATE (Patrick:Person {name:'Patrick Rothfuss', bolt_sips: true})\n  CREATE (Kvothe:Person {name:'Kote', bolt_sips: true})\n  CREATE (Denna:Person {name:'Denna', bolt_sips: true})\n  CREATE (Chandrian:Deamon {name:'Chandrian', bolt_sips: true})\n\n  CREATE\n    (Kvothe)-[:ACTED_IN {roles:['sword fighter', 'magician', 'musician']}]->(TNOTW),\n    (Denna)-[:ACTED_IN {roles:['many talents']}]->(TNOTW),\n    (Chandrian)-[:ACTED_IN {roles:['killer']}]->(TNOTW),\n    (Patrick)-[:WROTE]->(TNOTW)\n\"\"\"\n\n{:ok, response} =\n  Bolt.Sips.conn()\n  |> Bolt.Sips.query(cypher)\n```\n\nAccording to the response from the server, this is what we did:\n\n```elixir\niex> response\n%Bolt.Sips.Response{\n  results: [],\n  stats: %{\n    \"labels-added\" => 6,\n    \"nodes-created\" => 6,\n    \"properties-set\" => 19,\n    \"relationships-created\" => 4\n  },\n  type: \"w\"\n}\n```\n\nwe have 6 new Nodes, 6 new labels and 4 new relationships.\n\nAt any time, if you want to clean up the data we're creating, you can use this query:\n\n`MATCH (n {bolt_sips: true}) OPTIONAL MATCH (n)-[r]-() DELETE n,r`\n\nObserve we're adding a `bolt_sips` property to the Nodes we're adding, so that it's easier to refer them in our tests.\n\nLet's see how many nodes of \"type\" (`label`, according to Cypher's official terminology) `Person` having the property `bolt_sips` true, we have in our database:\n\n```elixir\niex> query = \"\"\"\n...>   MATCH (n:Person {bolt_sips: true})\n...>   RETURN n.name AS Name\n...>   ORDER BY Name DESC\n...>   LIMIT 5\n...>  \"\"\"\n\niex> %Bolt.Sips.Response{} = response = Bolt.Sips.query!(conn, query)\n%Bolt.Sips.Response{\n  bookmark: \"neo4j:bookmark:v1:tx21613\",\n  fields: [\"Name\"],\n  notifications: [],\n  plan: nil,\n  profile: nil,\n  records: [[\"Patrick Rothfuss\"], [\"Kote\"], [\"Denna\"]],\n  results: [\n    %{\"Name\" => \"Patrick Rothfuss\"},\n    %{\"Name\" => \"Kote\"},\n    %{\"Name\" => \"Denna\"}\n  ],\n  stats: [],\n  type: \"r\"\n}\n```\n\nWe have 3 of them, and we're only showing the `name` property! Above you see the full `Bolt.Sips.Response` returned by our driver based on the raw data returned by the Neo4j server. The `:results` key, contains the aggregated response you will use most of the time, and for that the `Bolt.Sips.Response` module has some useful helpers, for example:\n\n```elixir\niex> response |>\n...> Bolt.Sips.Response.first()\n%{\"Name\" => \"Patrick Rothfuss\"}\n```\n\nand much more. Check the `Bolt.Sips.Response`'s own docs, for more.\n"
  },
  {
    "path": "docs/features/using-temporal-and-spatial-types.md",
    "content": "# Using temporal and spatial types\n\nTemporal and spatial types are supported since Neo4J 3.4.\nYou can used the elixir structs: Time, NaiveDateTime, DateTime,\nas well as the Bolt Sips structs: DateTimeWithTZOffset, TimeWithTZOffset, Duration, Point.\n\n```elixir\n$ MIX_ENV=test iex -S mix\nErlang/OTP 21 [erts-10.0.5] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [hipe]\n\nInteractive Elixir (1.7.3) - press Ctrl+C to exit (type h() ENTER for help)\niex> alias Bolt.Sips.Types.{Duration, DateTimeWithTZOffset, Point, TimeWithTZOffset}\n[Bolt.Sips.Types.Duration, Bolt.Sips.Types.DateTimeWithTZOffset,\n Bolt.Sips.Types.Point, Bolt.Sips.Types.TimeWithTZOffset]\n\niex> alias Bolt.Sips.TypesHelper\nBolt.Sips.TypesHelper\n\niex> {:ok, pid} = Bolt.Sips.start_link(url: \"localhost\", basic_auth: [username: \"neo4j\", password: \"test\"])\n{:ok, #PID<0.236.0>}\n\niex> conn = Bolt.Sips.conn\n:bolt_sips_pool\n\n# Date without timezone with Date\niex(8)> Bolt.Sips.query!(conn, \"RETURN date($d) AS d\", %{d: ~D[2019-02-04]})\n[%{\"d\" => ~D[2019-02-04]}]\n\n# Time without timezone with Time\niex> Bolt.Sips.query!(conn, \"RETURN localtime($t) AS t\", %{t: ~T[13:26:08.543440]})\n[%{\"t\" => ~T[13:26:08.543440]}]\n\n# Datetime without timezone with Naive DateTime\niex> Bolt.Sips.query!(conn, \"RETURN localdatetime($ldt) AS ldt\", %{ldt: ~N[2016-05-24 13:26:08.543]})\n[%{\"ldt\" => ~N[2016-05-24 13:26:08.543]}]\n\n# Datetime with timezone ID with DateTime (through Calendar)\niex> date_time_with_tz_id = TypesHelper.datetime_with_micro(~N[2016-05-24 13:26:08.543], \"Europe/Paris\")\n#DateTime<2016-05-24 13:26:08.543+02:00 CEST Europe/Paris>\niex> Bolt.Sips.query!(conn, \"RETURN datetime($dt) AS dt\", %{dt: date_time_with_tz_id})\n[%{\"dt\" => #DateTime<2016-05-24 13:26:08.543+02:00 CEST Europe/Paris>}]\n\n# Datetime with timezone offset (seconds) with DateTimeWithTZOffset\niex(17)> date_time_with_tz = DateTimeWithTZOffset.create(~N[2016-05-24 13:26:08.543], 7200)\n%Bolt.Sips.Types.DateTimeWithTZOffset{\n  naive_datetime: ~N[2016-05-24 13:26:08.543],\n  timezone_offset: 7200\n}\niex(18)> Bolt.Sips.query!(conn, \"RETURN datetime($dt) AS dt\", %{dt: date_time_with_tz})\n[\n  %{\n    \"dt\" => %Bolt.Sips.Types.DateTimeWithTZOffset{\n      naive_datetime: ~N[2016-05-24 13:26:08.543],\n      timezone_offset: 7200\n    }\n  }\n]\n\n\n# Datetime with timezone offset (seconds) with TimeWithTZOffset\niex> time_with_tz = TimeWithTZOffset.create(~T[12:45:30.250000], 3600)\n%Bolt.Sips.Types.TimeWithTZOffset{\n  time: ~T[12:45:30.250000],\n  timezone_offset: 3600\n}\niex> Bolt.Sips.query!(conn, \"RETURN time($t) AS t\", %{t: time_with_tz})\n[\n  %{\n    \"t\" => %Bolt.Sips.Types.TimeWithTZOffset{\n      time: ~T[12:45:30.250000],\n      timezone_offset: 3600\n    }\n  }\n]\n\n# Cartesian 2D point with Point\niex> point_cartesian_2D = Point.create(:cartesian, 50, 60.5)\n%Bolt.Sips.Types.Point{\n  crs: \"cartesian\",\n  height: nil,\n  latitude: nil,\n  longitude: nil,\n  srid: 7203,\n  x: 50.0,\n  y: 60.5,\n  z: nil\n}\niex> Bolt.Sips.query!(conn, \"RETURN point($pt) AS pt\", %{pt: point_cartesian_2D})\n[\n  %{\n    \"pt\" => %Bolt.Sips.Types.Point{\n      crs: \"cartesian\",\n      height: nil,\n      latitude: nil,\n      longitude: nil,\n      srid: 7203,\n      x: 50.0,\n      y: 60.5,\n      z: nil\n    }\n  }\n]\n\n# Geographic 2D point with Point\niex> point_geo_2D = Point.create(:wgs_84, 50, 60.5)\n%Bolt.Sips.Types.Point{\n  crs: \"wgs-84\",\n  height: nil,\n  latitude: 60.5,\n  longitude: 50.0,\n  srid: 4326,\n  x: 50.0,\n  y: 60.5,\n  z: nil\n}\niex> Bolt.Sips.query!(conn, \"RETURN point($pt) AS pt\", %{pt: point_geo_2D})\n[\n  %{\n    \"pt\" => %Bolt.Sips.Types.Point{\n      crs: \"wgs-84\",\n      height: nil,\n      latitude: 60.5,\n      longitude: 50.0,\n      srid: 4326,\n      x: 50.0,\n      y: 60.5,\n      z: nil\n    }\n  }\n]\n\n# Cartesian 3D point with Point\niex> point_cartesian_3D = Point.create(:cartesian, 50, 60.5, 12.34)\n%Bolt.Sips.Types.Point{\n  crs: \"cartesian-3d\",\n  height: nil,\n  latitude: nil,\n  longitude: nil,\n  srid: 9157,\n  x: 50.0,\n  y: 60.5,\n  z: 12.34\n}\niex> Bolt.Sips.query!(conn, \"RETURN point($pt) AS pt\", %{pt: point_cartesian_3D})\n[\n  %{\n    \"pt\" => %Bolt.Sips.Types.Point{\n      crs: \"cartesian-3d\",\n      height: nil,\n      latitude: nil,\n      longitude: nil,\n      srid: 9157,\n      x: 50.0,\n      y: 60.5,\n      z: 12.34\n    }\n  }\n]\n\n# Geographic 2D point with Point\niex> point_geo_3D = Point.create(:wgs_84, 50, 60.5, 12.34)\n%Bolt.Sips.Types.Point{\n  crs: \"wgs-84-3d\",\n  height: 12.34,\n  latitude: 60.5,\n  longitude: 50.0,\n  srid: 4979,\n  x: 50.0,\n  y: 60.5,\n  z: 12.34\n}\niex> Bolt.Sips.query!(conn, \"RETURN point($pt) AS pt\", %{pt: point_geo_2D})\n[\n  %{\n    \"pt\" => %Bolt.Sips.Types.Point{\n      crs: \"wgs-84\",\n      height: nil,\n      latitude: 60.5,\n      longitude: 50.0,\n      srid: 4326,\n      x: 50.0,\n      y: 60.5,\n      z: nil\n    }\n  }\n]\n```\n"
  },
  {
    "path": "docs/features/using-with-phoenix.md",
    "content": "# Using Bolt.Sips with Phoenix, or similar\n\nDon't forget to start the `Bolt.Sips` driver in your supervision tree. Example:\n\n```elixir\ndefmodule MoviesElixirPhoenix do\n  use Application\n\n  # See https://hexdocs.pm/elixir/Application.html\n  # for more information on OTP Applications\n  def start(_type, _args) do\n    # Define workers and child supervisors to be supervised\n    children = [\n      # Start the endpoint when the application starts\n      {Bolt.Sips, Application.get_env(:bolt_sips, Bolt)},\n      %{\n        id: MoviesElixirPhoenix.Endpoint,\n        start: {MoviesElixirPhoenix.Endpoint, :start_link, []}\n      }\n    ]\n\n    # See https://hexdocs.pm/elixir/Supervisor.html\n    # for other strategies and supported options\n    opts = [strategy: :one_for_one, name: MoviesElixirPhoenix.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\n\n  # Tell Phoenix to update the endpoint configuration\n  # whenever the application is updated.\n  def config_change(changed, _new, removed) do\n    MoviesElixirPhoenix.Endpoint.config_change(changed, removed)\n    :ok\n  end\nend\n```\n\nThe code above was extracted from [the Neo4j Movies Demo](https://github.com/florinpatrascu/bolt_movies_elixir_phoenix), a Phoenix web application using this driver and the well known [Dataset - Movie Database](https://neo4j.com/developer/movie-database/).\n\nNote: as explained below, you don't need to convert your query result before having it  encoded in JSON. BoltSips provides Jason and Poison implementation to tackle this problem automatically.\n"
  },
  {
    "path": "docs/getting-started.md",
    "content": "# Getting Started\n\nLet's start by creating a simple Elixir project, as a playground for our tests.\n\n```sh\nmix new neo4j_demo --sup --app n4d --module N4D\ncd neo4j_demo\n```\n\nOpen the `mix.exs` and add the bolt_sips dependency.\n\n```elixir\ndefmodule N4D.MixProject do\n  use Mix.Project\n\n  def project do\n    [\n      app: :n4d,\n      version: \"0.1.0\",\n      elixir: \"~> 1.8\",\n      start_permanent: Mix.env() == :prod,\n      deps: deps()\n    ]\n  end\n\n  # Run \"mix help compile.app\" to learn about applications.\n  def application do\n    [\n      extra_applications: [:logger],\n      mod: {N4D.Application, []}\n    ]\n  end\n\n  # Run \"mix help deps\" to learn about dependencies.\n  defp deps do\n    [\n      {:bolt_sips, \"~> 2.0.0-rc\"},\n      {:jason, \"~> 1.1\"}\n    ]\n  end\nend\n```\n\nwe added the [jason](https://hex.pm/packages/jason) library too, for converting the server responses to json. And then run:\n\n```sh\nmix do deps.get, compile\n```\n\n## Starting the driver\n\nAnd our simple project is ready for us to start experimenting with it.\n\nLet's first configure the connection to a running Neo4j server. We presume a standalone community edition server is started and available on the `localhost` interface, and having its Bolt port open at: `7687`. For simplicity edit the `config/config.exs`, and modify it to look like this:\n\n```elixir\nuse Mix.Config\n\nconfig :bolt_sips, Bolt,\n  url: \"bolt://localhost:7687\",\n  basic_auth: [username: \"neo4j\", password: \"test\"],\n  pool_size: 10\n\n```\n\nWith the project configured to connect to a Neo4j server, in direct mode, we can add `Bolt.Sips` to the app's main supervision tree, and let the OTP manage it.\n\n```elixir\n# lib/n4_d/application.ex\n\ndefmodule N4D.Application do\n  @moduledoc false\n\n  use Application\n\n  def start(_type, _args) do\n    children = [\n      {Bolt.Sips, Application.get_env(:bolt_sips, Bolt)}\n    ]\n\n    opts = [strategy: :one_for_one, name: N4D.Supervisor]\n    Supervisor.start_link(children, opts)\n  end\nend\n```\n\nThere are a couple of different other ways to start the driver but let's keep it simple for now.\n\nThe easiest way to start playing with the driver, in the current configuration, is to drop into the IEx shell and run simple Cypher commands through it.\n\n```sh\ncd neo4j_demo\niex -S mix\n```\n\n## Usage\n\nA few examples:\n\n```elixir\niex> alias Bolt.Sips, as: Neo\niex> alias Bolt.Sips.Response\n\n# check the driver is up and running:\n\niex> Neo.info()\n%{\n  default: %{\n    connections: %{direct: %{\"localhost:7687\" => 0}, routing_query: nil},\n    user_options: [\n      socket: Bolt.Sips.Socket,\n      port: 7687,\n      url: \"bolt://localhost:7687\",\n      # ...\n      basic_auth: [username: \"neo4j\", password: \"test\"],\n      pool_size: 10\n    ]\n  }\n}\n\n# in direct mode, our current configuration, all the operations such as: read/write or\n# delete, are sent to the Neo4j server using a common connection (pool).\n# Let's obtain a connection:\n\niex> conn = Neo.conn()\n#PID<0.308.0>\n\n# a few examples:\n\niex> response = Neo.query!(conn, \"CREATE (p:Person)-[:LIKES]->(t:Technology)\")\n%Response{\n  bookmark: nil,\n  fields: [],\n  notifications: [],\n  plan: nil,\n  profile: nil,\n  records: [],\n  results: [],\n  stats: %{\n    \"labels-added\" => 2,\n    \"nodes-created\" => 2,\n    \"relationships-created\" => 1\n  },\n  type: \"w\"\n}\n\n# query with undirected relationship unless sure of direction\n%Bolt.Sips.Response{results: results} = response =  Neo.query!(conn, \"MATCH (p:Person)-[:LIKES]-(t:Technology) RETURN p\")\n\n# where `results` contain:\n[%{\"p\" => %Bolt.Sips.Types.Node{id: 355, labels: [\"Person\"], properties: %{}}}]\n\n# and we can also encode them to json, as simple as this:\n\niex> Jason.encode!(results)\n\"[{\\\"p\\\":{\\\"id\\\":355,\\\"labels\\\":[\\\"Person\\\"],\\\"properties\\\":{}}}]\"\n\n# of course you can do more:\n\niex> Bolt.Sips.query!(Bolt.Sips.conn(), \"RETURN [10,11,21] AS arr\", %{}, timeout: 19_000) |>\n...> Enum.reduce(0, &(Enum.sum(&1[\"arr\"]) + &2))\n42\n\n# see more examples and the tests, for getting familiar with what is possible.\n\n# Enjoy!\n```\n\nFollow this link: [Cypher Basics](https://neo4j.com/developer/cypher-query-language/), for a gentle introduction to Cypher; Neo4j's query language. Throughout the code snippets we are often using examples copied from the original documentation published by Neo4j, so that you can feel comfortable with them.\n"
  },
  {
    "path": "lib/bolt_sips/application.ex",
    "content": "defmodule Bolt.Sips.Application do\n  @moduledoc false\n\n  use Application\n\n  alias Bolt.Sips\n\n  def start(_, start_args) do\n    Sips.start_link(start_args)\n  end\n\n  def stop(_state) do\n    :ok\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/enumerable_response.ex",
    "content": "defimpl Enumerable, for: Bolt.Sips.Response do\n  alias Bolt.Sips.Response\n\n  def count(%Response{results: nil}), do: {:ok, 0}\n  def count(%Response{results: []}), do: {:ok, 0}\n  def count(%Response{results: results}), do: {:ok, length(results)}\n\n  def member?(%Response{fields: fields}, field), do: {:ok, Enum.member?(fields, field)}\n  def slice(_response), do: {:error, __MODULE__}\n\n  def reduce(%Response{results: []}, acc, _fun), do: acc\n\n  def reduce(%Response{results: results}, acc, fun) when is_list(results),\n    do: reduce_list(results, acc, fun)\n\n  defp reduce_list(_, {:halt, acc}, _fun), do: {:halted, acc}\n  defp reduce_list(list, {:suspend, acc}, fun), do: {:suspended, acc, &reduce_list(list, &1, fun)}\n  defp reduce_list([], {:cont, acc}, _fun), do: {:done, acc}\n  defp reduce_list([h | t], {:cont, acc}, fun), do: reduce_list(t, fun.(h, acc), fun)\n\n  @doc false\n  def slice(%Response{results: []}, _start, _count), do: []\n  def slice(_response, _start, 0), do: []\n  def slice(%Response{results: [head | tail]}, 0, count), do: [head | slice(tail, 0, count - 1)]\n  def slice(%Response{results: [_head | tail]}, start, count), do: slice(tail, start - 1, count)\nend\n"
  },
  {
    "path": "lib/bolt_sips/error.ex",
    "content": "defmodule Bolt.Sips.Error do\n  @moduledoc \"\"\"\n  represents an error message\n  \"\"\"\n  alias __MODULE__\n  @type t :: %__MODULE__{}\n\n  defstruct [:code, :message]\n\n  def new(%Bolt.Sips.Internals.Error{\n        code: code,\n        connection_id: cid,\n        function: f,\n        message: message,\n        type: t\n      }) do\n    {:error,\n     %Error{\n       code: code,\n       message:\n         \"Details: #{message}; connection_id: #{inspect(cid)}, function: #{inspect(f)}, type: #{\n           inspect(t)\n         }\"\n     }}\n  end\n\n  def new({:ignored, f} = _r), do: new({:error, f})\n\n  def new({:failure, %{\"code\" => code, \"message\" => message}} = _r) do\n    {:error, %Error{code: code, message: message}}\n  end\n\n  def new(r), do: r\nend\n"
  },
  {
    "path": "lib/bolt_sips/exception.ex",
    "content": "defmodule Bolt.Sips.Exception do\n  @moduledoc \"\"\"\n  This module defines a `Bolt.Sips.Exception` structure containing two fields:\n\n  * `code` - the error code\n  * `message` - the error details\n  \"\"\"\n  @type t :: %Bolt.Sips.Exception{}\n\n  defexception [:code, :message]\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol.ex",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocol do\n  @moduledoc false\n  # A library that handles Bolt Protocol (v1 and v2).\n  # Note that for now, only Neo4j implements Bolt v2.\n\n  # It handles all the protocol specific steps (i.e.\n  # handshake, init) as well as sending and receiving messages and wrapping\n  # them in chunks.\n\n  # It abstracts transportation, expecting the transport layer to define\n  # `send/2` and `recv/3` analogous to `:gen_tcp`.\n\n  # ## Logging configuration\n  # Logging can be enable / disable via config files (e.g, `config/config.exs`).\n  #   - `:log`: (bool) wether Bolt.Sips.Internals. should produce logs or not. Defaults to `false`\n  #   - `:log_hex`: (bool) wether Bolt.Sips.Internals. should produce logs hexadecimal counterparts. While this may be interesting,\n  #   note that all the hexadecimal data will be written and this can be very long, and thus can seriously impact performances. Defaults to `false`\n\n  # For example, configuration to see the logs and their hexadecimal counterparts:\n  # ```\n  #   config :Bolt.Sips.Internals.,\n  #     log: true,\n  #     log_hex: true\n  # ```\n  #   # #### Examples of logging (without log_hex)\n\n  #     iex> Bolt.Sips.Internals.test('localhost', 7687, \"RETURN 1 as num\", %{}, {\"neo4j\", \"password\"})\n  #     C: HANDSHAKE ~ \"<<0x60, 0x60, 0xB0, 0x17>> [2, 1, 0, 0]\"\n  #     S: HANDSHAKE ~ 2\n  #     C: INIT ~ [\"BoltSips/1.1.0.rc2\", %{credentials: \"password\", principal: \"neo4j\", scheme: \"basic\"}]\n  #     S: SUCCESS ~ %{\"server\" => \"Neo4j/3.4.1\"}\n  #     C: RUN ~ [\"RETURN 1 as num\", %{}]\n  #     S: SUCCESS ~ %{\"fields\" => [\"num\"], \"result_available_after\" => 1}\n  #     C: PULL_ALL ~ []\n  #     S: RECORD ~ [1]\n  #     S: SUCCESS ~ %{\"result_consumed_after\" => 0, \"type\" => \"r\"}\n  #     [\n  #       success: %{\"fields\" => [\"num\"], \"result_available_after\" => 1},\n  #       record: [1],\n  #       success: %{\"result_consumed_after\" => 0, \"type\" => \"r\"}\n  #     ]\n\n  # #### Examples of logging (with log_hex)\n\n  #     iex> Bolt.Sips.Internals.test('localhost', 7687, \"RETURN 1 as num\", %{}, {\"neo4j\", \"password\"})\n  #     13:32:23.882 [debug] C: HANDSHAKE ~ \"<<0x60, 0x60, 0xB0, 0x17>> [2, 1, 0, 0]\"\n  #     S: HANDSHAKE ~ <<0x0, 0x0, 0x0, 0x2>>\n  #     S: HANDSHAKE ~ 2\n  #     C: INIT ~ [\"BoltSips/1.1.0.rc2\", %{credentials: \"password\", principal: \"neo4j\", scheme: \"basic\"}]\n  #     C: INIT ~ <<0x0, 0x42, 0xB2, 0x1, 0x8C, 0x42, 0x6F, 0x6C, 0x74, 0x65, 0x78, 0x2F, 0x30, 0x2E, 0x35, 0x2E, 0x30, 0xA3, 0x8B, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x61, 0x6C, 0x73, 0x88, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x89, 0x70, 0x72, 0x69, 0x6E, 0x63, 0x69, 0x70, 0x61, 0x6C, 0x85, 0x6E, 0x65, 0x6F, 0x34, 0x6A, 0x86, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x65, 0x85, 0x62, 0x61, 0x73, 0x69, 0x63, 0x0, 0x0>>\n  #     S: SUCCESS ~ <<0xA1, 0x86, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x8B, 0x4E, 0x65, 0x6F, 0x34, 0x6A, 0x2F, 0x33, 0x2E, 0x34, 0x2E, 0x31>>\n  #     S: SUCCESS ~ %{\"server\" => \"Neo4j/3.4.1\"}\n  #     C: RUN ~ [\"RETURN 1 as num\", %{}]\n  #     C: RUN ~ <<0x0, 0x13, 0xB2, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31, 0x20, 0x61, 0x73, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0x0, 0x0>>\n  #     S: SUCCESS ~ <<0xA2, 0xD0, 0x16, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x5F, 0x61, 0x76, 0x61, 0x69, 0x6C, 0x61, 0x62, 0x6C, 0x65, 0x5F, 0x61, 0x66, 0x74, 0x65, 0x72, 0x1, 0x86, 0x66, 0x69, 0x65, 0x6C, 0x64, 0x73, 0x91, 0x83, 0x6E, 0x75, 0x6D>>\n  #     S: SUCCESS ~ %{\"fields\" => [\"num\"], \"result_available_after\" => 1}\n  #     C: PULL_ALL ~ []\n  #     C: PULL_ALL ~ <<0x0, 0x2, 0xB0, 0x3F, 0x0, 0x0>>\n  #     S: RECORD ~ <<0x91, 0x1>>\n  #     S: RECORD ~ [1]\n  #     S: SUCCESS ~ <<0xA2, 0xD0, 0x15, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74, 0x5F, 0x63, 0x6F, 0x6E, 0x73, 0x75, 0x6D, 0x65, 0x64, 0x5F, 0x61, 0x66, 0x74, 0x65, 0x72, 0x0, 0x84, 0x74, 0x79, 0x70, 0x65, 0x81, 0x72>>\n  #     S: SUCCESS ~ %{\"result_consumed_after\" => 0, \"type\" => \"r\"}\n  #     [\n  #       success: %{\"fields\" => [\"num\"], \"result_available_after\" => 1},\n  #       record: [1],\n  #       success: %{\"result_consumed_after\" => 0, \"type\" => \"r\"}\n  #     ]\n\n  # ## Shared options\n\n  # Functions that allow for options accept these default options:\n\n  #   * `recv_timeout`: The timeout for receiving a response from the Neo4J s\n  #     server (default: #{@recv_timeout})\n\n  alias Bolt.Sips.Metadata\n  alias Bolt.Sips.Internals.BoltProtocolV1\n  alias Bolt.Sips.Internals.BoltProtocolV3\n\n  defdelegate handshake(transport, port, options \\\\ []), to: BoltProtocolV1\n  defdelegate init(transport, port, version, auth \\\\ {}, options \\\\ []), to: BoltProtocolV1\n  defdelegate hello(transport, port, version, auth \\\\ {}, options \\\\ []), to: BoltProtocolV3\n  defdelegate goodbye(transport, port, version), to: BoltProtocolV3\n\n  defdelegate ack_failure(transport, port, bolt_version, options \\\\ []), to: BoltProtocolV1\n  defdelegate reset(transport, port, bolt_version, options \\\\ []), to: BoltProtocolV1\n  defdelegate discard_all(transport, port, bolt_version, options \\\\ []), to: BoltProtocolV1\n\n  defdelegate begin(transport, port, bolt_version, metadata \\\\ %Metadata{}, options \\\\ []),\n    to: BoltProtocolV3\n\n  defdelegate commit(transport, port, bolt_version, options \\\\ []), to: BoltProtocolV3\n  defdelegate rollback(transport, port, bolt_version, options \\\\ []), to: BoltProtocolV3\n\n  defdelegate pull_all(transport, port, bolt_version, options \\\\ []), to: BoltProtocolV1\n\n  @doc \"\"\"\n  run for all Bolt version, but call differs.\n  For Bolt <= 2, use: run_statement(transport, port, bolt_version, statement, params, options)\n  For Bolt >=3: run_statement(transport, port, bolt_version, statement, params, metadata, options)\n\n  Note that Bolt V2 calls works with Bolt V3, but it is preferrable to update them.\n  \"\"\"\n  @spec run(\n          atom(),\n          port(),\n          integer(),\n          String.t(),\n          map(),\n          nil | Keyword.t() | Bolt.Sips.Metadata.t(),\n          nil | Keyword.t()\n        ) ::\n          {:ok, tuple()}\n          | Bolt.Sips.Internals.Error.t()\n  def run(\n        transport,\n        port,\n        bolt_version,\n        statement,\n        params \\\\ %{},\n        options_or_metadata \\\\ [],\n        options \\\\ []\n      )\n\n  def run(transport, port, bolt_version, statement, params, options_or_metadata, _)\n      when bolt_version <= 2 do\n    BoltProtocolV1.run(\n      transport,\n      port,\n      bolt_version,\n      statement,\n      params,\n      options_or_metadata || []\n    )\n  end\n\n  def run(transport, port, bolt_version, statement, params, metadata, options)\n      when bolt_version >= 2 do\n    metadata =\n      case metadata do\n        [] -> %{}\n        metadata -> metadata\n      end\n\n    {metadata, options} = manage_metadata_and_options(metadata, options)\n\n    BoltProtocolV3.run(transport, port, bolt_version, statement, params, metadata, options)\n  end\n\n  defp manage_metadata_and_options([], options) do\n    {:ok, empty_metadata} = Metadata.new(%{})\n    {empty_metadata, options}\n  end\n\n  defp manage_metadata_and_options([_ | _] = metadata, options) do\n    {:ok, empty_metadata} = Metadata.new(%{})\n    {empty_metadata, metadata ++ options}\n  end\n\n  defp manage_metadata_and_options(metadata, options) do\n    {metadata, options}\n  end\n\n  @doc \"\"\"\n  run_statement for all Bolt version, but call differs.\n  For Bolt <= 2, use: run_statement(transport, port, bolt_version, statement, params, options)\n  For Bolt >=3: run_statement(transport, port, bolt_version, statement, params, metadata, options)\n\n  Note that Bolt V2 calls works with Bolt V3, but it is preferrable to update them.\n  \"\"\"\n  @spec run_statement(\n          atom(),\n          port(),\n          integer(),\n          String.t(),\n          map(),\n          nil | Keyword.t() | Bolt.Sips.Metadata.t(),\n          nil | Keyword.t()\n        ) ::\n          list()\n          | Bolt.Sips.Internals.Error.t()\n  def run_statement(\n        transport,\n        port,\n        bolt_version,\n        statement,\n        params \\\\ %{},\n        options_v2_or_metadata_v3 \\\\ [],\n        options_v3 \\\\ []\n      )\n\n  def run_statement(transport, port, bolt_version, statement, params, options_or_metadata, _)\n      when bolt_version <= 2 do\n    BoltProtocolV1.run_statement(\n      transport,\n      port,\n      bolt_version,\n      statement,\n      params,\n      options_or_metadata || []\n    )\n  end\n\n  def run_statement(transport, port, bolt_version, statement, params, metadata, options)\n      when bolt_version >= 2 do\n    metadata =\n      case metadata do\n        [] -> %{}\n        metadata -> metadata\n      end\n\n    BoltProtocolV3.run_statement(\n      transport,\n      port,\n      bolt_version,\n      statement,\n      params,\n      metadata,\n      options\n    )\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_helper.ex",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolHelper do\n  @moduledoc false\n\n  alias Bolt.Sips.Internals.PackStream.Message\n  alias Bolt.Sips.Internals.Error\n\n  @recv_timeout :infinity #10_000\n  @zero_chunk <<0x00, 0x00>>\n  @summary ~w(success ignored failure)a\n\n  @doc \"\"\"\n  Sends a message using the Bolt protocol and PackStream encoding.\n\n  Message have to be in the form of {message_type, [data]}.\n  \"\"\"\n  @spec send_message(atom(), port(), integer(), Bolt.Sips.Internals.PackStream.Message.raw()) ::\n          :ok | {:error, any()}\n  def send_message(transport, port, bolt_version, message) do\n    message\n    |> Message.encode(bolt_version)\n    |> (fn data -> transport.send(port, data) end).()\n  end\n\n  @doc \"\"\"\n  Receives data.\n\n  This function is supposed to be called after a request to the server has been\n  made. It receives data chunks, mends them (if they were split between frames)\n  and decodes them using PackStream.\n\n  When just a single message is received (i.e. to acknowledge a command), this\n  function returns a tuple with two items, the first being the signature and the\n  second being the message(s) itself. If a list of messages is received it will\n  return a list of the former.\n\n  The same goes for the messages: If there was a single data point in a message\n  said data point will be returned by itself. If there were multiple data\n  points, the list will be returned.\n\n  The signature is represented as one of the following:\n\n  * `:success`\n  * `:record`\n  * `:ignored`\n  * `:failure`\n\n  ## Options\n\n  See \"Shared options\" in the documentation of this module.\n  \"\"\"\n  @spec receive_data(atom(), port(), integer(), Keyword.t(), list()) ::\n          {atom(), Bolt.Sips.Internals.PackStream.value()}\n          | {:error, any()}\n          | Bolt.Sips.Internals.Error.t()\n  def receive_data(transport, port, bolt_version, options \\\\ [], previous \\\\ []) do\n    with {:ok, data} <- do_receive_data(transport, port, options) do\n      case Message.decode(data, bolt_version) do\n        {:record, _} = data ->\n          receive_data(transport, port, bolt_version, options, [data | previous])\n\n        {status, _} = data when status in @summary and previous == [] ->\n          data\n\n        {status, _} = data when status in @summary ->\n          Enum.reverse([data | previous])\n\n        other ->\n          {:error, Error.exception(other, port, :receive_data)}\n      end\n    else\n      other ->\n        # Should be the line below to have a cleaner typespec\n        # Keep the old return value to not break usage\n        # {:error, Error.exception(other, port, :receive_data)}\n        Error.exception(other, port, :receive_data)\n    end\n  end\n\n  @spec do_receive_data(atom(), port(), Keyword.t()) :: {:ok, binary()}\n  defp do_receive_data(transport, port, options) do\n    #recv_timeout = get_recv_timeout(options)\n\n    case transport.recv(port, 2, :infinity) do\n      {:ok, <<chunk_size::16>>} ->\n        do_receive_data_(transport, port, chunk_size, options, <<>>)\n\n      other ->\n        other\n    end\n  end\n\n  @spec do_receive_data_(atom(), port(), integer(), Keyword.t(), binary()) :: {:ok, binary()}\n  defp do_receive_data_(transport, port, chunk_size, options, old_data) do\n    recv_timeout = get_recv_timeout(options)\n\n    with {:ok, data} <- transport.recv(port, chunk_size, recv_timeout),\n         {:ok, marker} <- transport.recv(port, 2, recv_timeout) do\n      case marker do\n        @zero_chunk ->\n          {:ok, <<old_data::binary, data::binary>>}\n\n        <<chunk_size::16>> ->\n          data = <<old_data::binary, data::binary>> \n          do_receive_data_(transport, port, chunk_size, options, data)\n      end\n    else\n      other ->\n        Error.exception(other, port, :recv)\n    end\n  end\n\n  @doc \"\"\"\n  Define timeout\n  \"\"\"\n  @spec get_recv_timeout(Keyword.t()) :: integer()\n  def get_recv_timeout(options) do\n    Keyword.get(options, :recv_timeout, @recv_timeout)\n  end\n\n  @doc \"\"\"\n  Deal with message without data.\n\n  ## Example\n\n      iex> BoltProtocolHelper.treat_simple_message(:reset, :gen_tcp, port, 1, [])\n      :ok\n  \"\"\"\n  @spec treat_simple_message(\n          Bolt.Sips.Internals.Message.out_signature(),\n          atom(),\n          port(),\n          integer(),\n          Keyword.t()\n        ) :: :ok | Error.t()\n  def treat_simple_message(message, transport, port, bolt_version, options) do\n    send_message(transport, port, bolt_version, {message, []})\n\n    case receive_data(transport, port, bolt_version, options) do\n      {:success, %{}} ->\n        :ok\n\n      error ->\n        Error.exception(error, port, message)\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_v1.ex",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolV1 do\n  @moduledoc false\n  alias Bolt.Sips.Internals.BoltProtocolHelper\n  alias Bolt.Sips.Internals.BoltVersionHelper\n  alias Bolt.Sips.Internals.Error\n\n  @hs_magic <<0x60, 0x60, 0xB0, 0x17>>\n\n  @doc \"\"\"\n  Initiates the handshake between the client and the server.\n\n  See [http://boltprotocol.org/v1/#handshake](http://boltprotocol.org/v1/#handshake)\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.handshake(:gen_tcp, port, [])\n      {:ok, bolt_version}\n  \"\"\"\n  @spec handshake(atom(), port(), Keyword.t()) ::\n          {:ok, integer()} | {:error, Bolt.Sips.Internals.Error.t()}\n  def handshake(transport, port, options \\\\ [recv_timeout: 15_000]) do\n    recv_timeout = BoltProtocolHelper.get_recv_timeout(options)\n    max_version = BoltVersionHelper.last()\n\n    # Define version list. Should be a 4 integer list\n    # Example: [1, 0, 0, 0]\n    versions =\n      ((max_version..0\n        |> Enum.into([])) ++ [0, 0, 0])\n      |> Enum.take(4)\n\n    Bolt.Sips.Internals.Logger.log_message(\n      :client,\n      :handshake,\n      \"#{inspect(@hs_magic, base: :hex)} #{inspect(versions)}\"\n    )\n\n    data = @hs_magic <> Enum.into(versions, <<>>, fn version_ -> <<version_::32>> end)\n    transport.send(port, data)\n\n    case transport.recv(port, 4, recv_timeout) do\n      {:ok, <<version::32>> = packet} when version <= max_version ->\n        Bolt.Sips.Internals.Logger.log_message(:server, :handshake, packet, :hex)\n        Bolt.Sips.Internals.Logger.log_message(:server, :handshake, version)\n        {:ok, version}\n\n      {:ok, other} ->\n        {:error, Error.exception(other, port, :handshake)}\n\n      other ->\n        {:error, Error.exception(other, port, :handshake)}\n    end\n  end\n\n  @doc \"\"\"\n  Initialises the connection.\n\n  Expects a transport module (i.e. `gen_tcp`) and a `Port`. Accepts\n  authorisation params in the form of {username, password}.\n\n  See [https://boltprotocol.org/v1/#message-init](https://boltprotocol.org/v1/#message-init)\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Examples\n\n      iex> Bolt.Sips.Internals.BoltProtocol.init(:gen_tcp, port, 1, {}, [])\n      {:ok, info}\n\n      iex> Bolt.Sips.Internals.BoltProtocol.init(:gen_tcp, port, 1, {\"username\", \"password\"}, [])\n      {:ok, info}\n  \"\"\"\n  @spec init(atom(), port(), integer(), tuple(), Keyword.t()) ::\n          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}\n  def init(transport, port, bolt_version, auth, options \\\\ [recv_timeout: 15_000]) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:init, [auth]})\n\n    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do\n      {:success, info} ->\n        {:ok, info}\n\n      {:failure, response} ->\n        {:error, Error.exception(response, port, :init)}\n\n      other ->\n        {:error, Error.exception(other, port, :init)}\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's RUN. It passes a statement for execution on the server.\n\n  Note that this message doesn't return the statemetn result. For this purpose, use PULL_ALL.\n\n  See [https://boltprotocol.org/v1/#message-run](https://boltprotocol.org/v1/#message-run)\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN $num AS num\", %{num: 5}, [])\n      {:ok, {:success, %{\"fields\" => [\"num\"]}}}\n  \"\"\"\n  @spec run(atom(), port(), integer(), String.t(), map(), Keyword.t()) ::\n          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}\n  def run(transport, port, bolt_version, statement, params, options) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:run, [statement, params]})\n\n    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do\n      {:success, _} = result ->\n        {:ok, result}\n\n      {:failure, response} ->\n        {:error, Error.exception(response, port, :run)}\n\n      %Error{} = error ->\n        {:error, error}\n\n      other ->\n        {:error, Error.exception(other, port, :run)}\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's PULL_ALL. It retrieves all remaining items from the active result\n  stream.\n\n  See [https://boltprotocol.org/v1/#message-run](https://boltprotocol.org/v1/#message-run)\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN $num AS num\", %{num: 5}, [])\n      {:ok, {:success, %{\"fields\" => [\"num\"]}}}\n      iex> BoltProtocolV1.pull_all(:gen_tcp, port_, 1, [])\n      {:ok,\n        [\n          record: [5],\n          success: %{\"type\" => \"r\"}\n        ]}\n  \"\"\"\n  @spec pull_all(atom(), port(), integer(), Keyword.t()) ::\n          {:ok, list()}\n          | {:failure, Bolt.Sips.Internals.Error.t()}\n          | {:failure, Bolt.Sips.Internals.Error.t()}\n  def pull_all(transport, port, bolt_version, options) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:pull_all, []})\n\n    with data <- BoltProtocolHelper.receive_data(transport, port, bolt_version, options),\n         data <- List.wrap(data),\n         {:success, _} <- List.last(data) do\n      {:ok, data}\n    else\n      {:failure, response} ->\n        {:failure, Error.exception(response, port, :pull_all)}\n\n      other ->\n        {:error, Error.exception(other, port, :pull_all)}\n    end\n  end\n\n  @doc \"\"\"\n  Runs a statement (most likely Cypher statement) and returns a list of the\n  records and a summary (Act as as a RUN + PULL_ALL).\n\n  Records are represented using PackStream's record data type. Their Elixir\n  representation is a Keyword with the indexes `:sig` and `:fields`.\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Examples\n\n      iex> Bolt.Sips.Internals.BoltProtocol.run_statement(:gen_tcp, port, 1, \"MATCH (n) RETURN n\")\n      [\n        {:success, %{\"fields\" => [\"n\"]}},\n        {:record, [sig: 1, fields: [1, \"Example\", \"Labels\", %{\"some_attribute\" => \"some_value\"}]]},\n        {:success, %{\"type\" => \"r\"}}\n      ]\n  \"\"\"\n  @spec run_statement(atom(), port(), integer(), String.t(), map(), Keyword.t()) ::\n          [\n            Bolt.Sips.Internals.PackStream.Message.decoded()\n          ]\n          | Bolt.Sips.Internals.Error.t()\n  def run_statement(transport, port, bolt_version, statement, params, options) do\n    with {:ok, run_data} <- run(transport, port, bolt_version, statement, params, options),\n         {:ok, result} <- pull_all(transport, port, bolt_version, options) do\n      [run_data | result]\n    else\n      {:error, %Error{} = error} ->\n        error\n\n      other ->\n        Error.exception(other, port, :run_statement)\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's DISCARD_ALL. It discards all remaining items from the active result\n  stream.\n\n  See [https://boltprotocol.org/v1/#message-discard-all](https://boltprotocol.org/v1/#message-discard-all)\n\n  See http://boltprotocol.org/v1/#message-ack-failure\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.discard_all(:gen_tcp, port, 1, [])\n      :ok\n  \"\"\"\n  @spec discard_all(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()\n  def discard_all(transport, port, bolt_version, options) do\n    BoltProtocolHelper.treat_simple_message(:discard_all, transport, port, bolt_version, options)\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's ACK_FAILURE. It acknowledges a failure while keeping\n  transactions alive.\n\n  See [http://boltprotocol.org/v1/#message-ack-failure](http://boltprotocol.org/v1/#message-ack-failure)\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.ack_failure(:gen_tcp, port, 1, [])\n      :ok\n  \"\"\"\n  @spec ack_failure(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()\n  def ack_failure(transport, port, bolt_version, options) do\n    BoltProtocolHelper.treat_simple_message(:ack_failure, transport, port, bolt_version, options)\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's RESET message. It resets a session to a \"clean\"\n  state.\n\n  See [http://boltprotocol.org/v1/#message-reset](http://boltprotocol.org/v1/#message-reset)\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.reset(:gen_tcp, port, 1, [])\n      :ok\n  \"\"\"\n  @spec reset(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()\n  def reset(transport, port, bolt_version, options) do\n    BoltProtocolHelper.treat_simple_message(:reset, transport, port, bolt_version, options)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_v2.ex",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolV2 do\n  @moduledoc false\n  # There's no specific messagee for Bolt V2\n  # This file exists only to fill the gap between the 2 bolt protocol versions\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_protocol_v3.ex",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolV3 do\n  alias Bolt.Sips.Internals.BoltProtocol\n  alias Bolt.Sips.Internals.BoltProtocolHelper\n  alias Bolt.Sips.Internals.Error\n\n  @doc \"\"\"\n  Implementation of Bolt's HELLO. It initialises the connection.\n\n  Expects a transport module (i.e. `gen_tcp`) and a `Port`. Accepts\n  authorisation params in the form of {username, password}.\n\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Examples\n\n      iex> Bolt.Sips.Internals.BoltProtocolV3.hello(:gen_tcp, port, 3, {}, [])\n      {:ok, info}\n\n      iex> Bolt.Sips.Internals.BoltProtocolV3.hello(:gen_tcp, port, 3, {\"username\", \"password\"}, [])\n      {:ok, info}\n  \"\"\"\n  @spec hello(atom(), port(), integer(), tuple(), Keyword.t()) ::\n          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}\n  def hello(transport, port, bolt_version, auth, options \\\\ [recv_timeout: 15_000]) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:hello, [auth]})\n\n    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do\n      {:success, info} ->\n        {:ok, info}\n\n      {:failure, response} ->\n        {:error, Error.exception(response, port, :hello)}\n\n      other ->\n        {:error, Error.exception(other, port, :hello)}\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's RUN. It closes the connection.\n\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Examples\n\n      iex> Bolt.Sips.Internals.BoltProtocolV3.goodbye(:gen_tcp, port, 3)\n      :ok\n\n      iex> Bolt.Sips.Internals.BoltProtocolV3.goodbye(:gen_tcp, port, 3)\n      :ok\n  \"\"\"\n  def goodbye(transport, port, bolt_version) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:goodbye, []})\n\n    try do\n      Port.close(port)\n      :ok\n    rescue\n      ArgumentError -> Error.exception(\"Can't close port\", port, :goodbye)\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's RUN. It passes a statement for execution on the server.\n\n  Note that this message doesn't return the statement result. For this purpose, use PULL_ALL.\n  In bolt >= 3, run has an additional paramters; metadata\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN $num AS num\", %{num: 5}, %{}, [])\n      {:ok, {:success, %{\"fields\" => [\"num\"]}}}\n  \"\"\"\n  @spec run(atom(), port(), integer(), String.t(), map(), Bolt.Sips.Metadata.t(), Keyword.t()) ::\n          {:ok, any()} | {:error, Bolt.Sips.Internals.Error.t()}\n  def run(transport, port, bolt_version, statement, params, metadata, options) do\n    BoltProtocolHelper.send_message(\n      transport,\n      port,\n      bolt_version,\n      {:run, [statement, params, metadata]}\n    )\n\n    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do\n      {:success, _} = result ->\n        {:ok, result}\n\n      {:failure, response} ->\n        {:error, Error.exception(response, port, :run)}\n\n      %Error{} = error ->\n        {:error, error}\n\n      other ->\n        {:error, Error.exception(other, port, :run)}\n    end\n  end\n\n  @doc \"\"\"\n  Runs a statement (most likely Cypher statement) and returns a list of the\n  records and a summary (Act as as a RUN + PULL_ALL).\n\n  Records are represented using PackStream's record data type. Their Elixir\n  representation is a Keyword with the indexes `:sig` and `:fields`.\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Examples\n\n      iex> Bolt.Sips.Internals.BoltProtocol.run_statement(:gen_tcp, port, 1, \"MATCH (n) RETURN n\")\n      [\n        {:success, %{\"fields\" => [\"n\"]}},\n        {:record, [sig: 1, fields: [1, \"Example\", \"Labels\", %{\"some_attribute\" => \"some_value\"}]]},\n        {:success, %{\"type\" => \"r\"}}\n      ]\n  \"\"\"\n  @spec run_statement(\n          atom(),\n          port(),\n          integer(),\n          String.t(),\n          map(),\n          Bolt.Sips.Metadata.t(),\n          Keyword.t()\n        ) ::\n          [\n            Bolt.Sips.Internals.PackStream.Message.decoded()\n          ]\n          | Bolt.Sips.Internals.Error.t()\n  def run_statement(transport, port, bolt_version, statement, params, metadata, options) do\n    with {:ok, run_data} <-\n           run(transport, port, bolt_version, statement, params, metadata, options),\n         {:ok, result} <- BoltProtocol.pull_all(transport, port, bolt_version, options) do\n      [run_data | result]\n    else\n      {:error, %Error{} = error} ->\n        error\n\n      other ->\n        Error.exception(other, port, :run_statement)\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's BEGIN. It opens a transaction.\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV3.begin(:gen_tcp, port, 3, [])\n      {:ok, metadata}\n  \"\"\"\n  @spec begin(atom(), port(), integer(), Bolt.Sips.Metadata.t() | map(), Keyword.t()) ::\n          {:ok, any()} | Bolt.Sips.Internals.Error.t()\n  def begin(transport, port, bolt_version, metadata, options) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:begin, [metadata]})\n\n    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do\n      {:success, info} ->\n        {:ok, info}\n\n      {:failure, response} ->\n        {:error, Error.exception(response, port, :begin)}\n\n      other ->\n        {:error, Error.exception(other, port, :begin)}\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's COMMIT. It commits the open transaction.\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV3.commit(:gen_tcp, port, 3, [])\n      :ok\n  \"\"\"\n  @spec commit(atom(), port(), integer(), Keyword.t()) ::\n          {:ok, any()} | Bolt.Sips.Internals.Error.t()\n  def commit(transport, port, bolt_version, options) do\n    BoltProtocolHelper.send_message(transport, port, bolt_version, {:commit, []})\n\n    case BoltProtocolHelper.receive_data(transport, port, bolt_version, options) do\n      {:success, info} ->\n        {:ok, info}\n\n      {:failure, response} ->\n        {:error, Error.exception(response, port, :commit)}\n\n      other ->\n        {:error, Error.exception(other, port, :commit)}\n    end\n  end\n\n  @doc \"\"\"\n  Implementation of Bolt's ROLLBACK. It rollbacks the open transaction.\n\n  ## Options\n\n  See \"Shared options\" in `Bolt.Sips.Internals.BoltProtocolHelper` documentation.\n\n  ## Example\n\n      iex> BoltProtocolV3.rollback(:gen_tcp, port, 3, [])\n      :ok\n  \"\"\"\n  @spec rollback(atom(), port(), integer(), Keyword.t()) :: :ok | Bolt.Sips.Internals.Error.t()\n  def rollback(transport, port, bolt_version, options) do\n    BoltProtocolHelper.treat_simple_message(:rollback, transport, port, bolt_version, options)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/bolt_version_helper.ex",
    "content": "defmodule Bolt.Sips.Internals.BoltVersionHelper do\n  @moduledoc false\n  @available_bolt_versions [1, 2, 3]\n\n  @doc \"\"\"\n  List bolt versions.\n  Only bolt version that have specific encoding functions are listed.\n\n  \"\"\"\n  @spec available_versions() :: [integer()]\n  def available_versions(), do: @available_bolt_versions\n\n  @doc \"\"\"\n  Retrieve previous valid version.\n  Return nil if there is no previous version.\n\n  ## Example\n\n      iex> Bolt.Sips.Internals.BoltVersionHelper.previous(2)\n      1\n      iex> Bolt.Sips.Internals.BoltVersionHelper.previous(1)\n      nil\n      iex> Bolt.Sips.Internals.BoltVersionHelper.previous(15)\n      3\n  \"\"\"\n  @spec previous(integer()) :: nil | integer()\n  def previous(version) do\n    @available_bolt_versions\n    |> Enum.take_while(&(&1 < version))\n    |> List.last()\n  end\n\n  @doc \"\"\"\n  Return the last available bolt version.\n\n  ## Example:\n\n      iex> Bolt.Sips.Internals.BoltVersionHelper.last()\n      3\n  \"\"\"\n  def last() do\n    List.last(@available_bolt_versions)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/error.ex",
    "content": "defmodule Bolt.Sips.Internals.Error do\n  @moduledoc false\n  defexception [:message, :code, :connection_id, :function, :type]\n\n  @type t :: %__MODULE__{\n          message: String.t(),\n          code: nil | any(),\n          connection_id: nil | integer(),\n          function: atom(),\n          type: atom()\n        }\n\n  @doc false\n  # Produce a Bolt.Sips.Internals.Error depending on the context.\n  @spec exception(any(), nil | port(), atom()) :: Bolt.Sips.Internals.Error.t()\n  def exception(%{\"message\" => message, \"code\" => code}, pid, function) do\n    %Bolt.Sips.Internals.Error{\n      message: message,\n      code: code,\n      connection_id: get_id(pid),\n      function: function,\n      type: :cypher_error\n    }\n  end\n\n  def exception({:error, :closed}, pid, function) do\n    %Bolt.Sips.Internals.Error{\n      message: \"Port #{inspect(pid)} is closed\",\n      connection_id: get_id(pid),\n      function: function,\n      type: :connection_error\n    }\n  end\n\n  def exception({:failure, %Bolt.Sips.Internals.Error{message: _message, code:  _code} = err}, _pid, _function) do\n    err\n  end\n\n  def exception(%Bolt.Sips.Internals.Error{} = err, _pid, _function), do: err\n  \n  def exception(message, pid, function) do\n    %Bolt.Sips.Internals.Error{\n      message: message_for(function, message),\n      connection_id: get_id(pid),\n      function: function,\n      type: :protocol_error\n    }\n  end\n\n  @spec message_for(nil | atom(), any()) :: String.t()\n  defp message_for(:handshake, \"HTTP\") do\n    \"\"\"\n    Handshake failed.\n    The port expected a HTTP request.\n    This happens when trying to Neo4J using the REST API Port (default: 7474)\n    instead of the Bolt Port (default: 7687).\n    \"\"\"\n  end\n\n  defp message_for(:handshake, bin) when is_binary(bin) do\n    \"\"\"\n    Handshake failed.\n    Expected 01:00:00:00 as a result, received: #{inspect(bin, base: :hex)}.\n    \"\"\"\n  end\n\n  defp message_for(:handshake, other) do\n    \"\"\"\n    Handshake failed.\n    Expected 01:00:00:00 as a result, received: #{inspect(other)}.\n    \"\"\"\n  end\n\n  defp message_for(nil, message) do\n    \"\"\"\n    Unknown failure: #{inspect(message)}\n    \"\"\"\n  end\n\n  defp message_for(_function, {:error, error}) do\n    case error |> :inet.format_error() |> to_string do\n      \"unknown POSIX error\" -> to_string(error)\n      other -> other\n    end\n  end\n\n  defp message_for(_function, {:ignored, []}) do\n    \"\"\"\n    The session is in a failed state and ignores further messages. You need to\n    `ACK_FAILURE` or `RESET` in order to send new messages.\n    \"\"\"\n  end\n\n  defp message_for(function, message) do\n    \"\"\"\n    #{function}: Unknown failure: #{inspect(message)}\n    \"\"\"\n  end\n\n  @spec get_id(any()) :: nil | integer()\n  defp get_id({:sslsocket, {:gen_tcp, port, _tls, _unused_yet}, _pid}) do\n    get_id(port)\n  end\n\n  defp get_id(port) when is_port(port) do\n    case Port.info(port, :id) do\n      {:id, id} -> id\n      nil -> nil\n    end\n  end\n\n  defp get_id(_), do: nil\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/logger.ex",
    "content": "defmodule Bolt.Sips.Internals.Logger do\n  @moduledoc false\n  # Designed to log Bolt protocol message between Client and Server.\n  #\n  # The `from` parameter must be a atom, either `:client` or `:server`\n  require Logger\n\n  @doc \"\"\"\n  Produces a formatted Log for a message\n\n  ## Example\n      iex> Logger.log_message(:client, {:init, []})\n  \"\"\"\n  def log_message(from, {type, data}) do\n    msg_type = type |> Atom.to_string() |> String.upcase()\n    do_log_message(from, fn -> \"#{msg_type} ~ #{inspect(data)}\" end)\n  end\n\n  @doc \"\"\"\n  Produces a formatted Log\n\n  ## Example\n      iex> Logger.log_message(:server, :handshake, 2)\n  \"\"\"\n  def log_message(from, type, data) do\n    if Application.get_env(:bolt_sips, :log) do\n      log_message(from, {type, data})\n    end\n  end\n\n  @doc \"\"\"\n  Produces a formatted Log for a message\n  Data will be output in hexadecimal\n\n  ## Example\n      iex> Logger.log_message(:server, :handshake, <<0x02>>)\n  \"\"\"\n  def log_message(from, type, data, :hex) do\n    if Application.get_env(:bolt_sips, :log_hex, false) do\n      msg_type = type |> Atom.to_string() |> String.upcase()\n\n      do_log_message(from, fn ->\n        \"#{msg_type} ~ #{inspect(data, base: :hex, limit: :infinity)}\"\n      end)\n    end\n  end\n\n  defp do_log_message(from, func) when is_function(func) do\n    from_txt =\n      case from do\n        :server -> \"S\"\n        :client -> \"C\"\n      end\n\n    Logger.debug(fn -> \"#{from_txt}: #{func.()}\" end)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Decoder do\n  @moduledoc false\n  _moduledoc = \"\"\"\n  This module is responsible for dispatching decoding amongst decoder depending on the\n  used bolt version.\n\n  Most of the documentation regarding Bolt binary format can be found in\n  `Bolt.Sips.Internals.PackStream.EncoderV1` and `Bolt.Sips.Internals.PackStream.EncoderV2`.\n\n  Here will be found ocumenation about data that are only availalbe for decoding::\n  - Node\n  - Relationship\n  - Unbound relationship\n  - Path\n  \"\"\"\n\n  use Bolt.Sips.Internals.PackStream.DecoderImplV1\n  use Bolt.Sips.Internals.PackStream.DecoderImplV2\n  use Bolt.Sips.Internals.PackStream.DecoderUtils\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_impl_v1.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderImplV1 do\n  alias Bolt.Sips.Types\n\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MODULE__)\n\n      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()\n\n      # Null\n      @null_marker 0xC0\n\n      # Boolean\n      @true_marker 0xC3\n      @false_marker 0xC2\n\n      # String\n      @tiny_bitstring_marker 0x8\n      @bitstring8_marker 0xD0\n      @bitstring16_marker 0xD1\n      @bitstring32_marker 0xD2\n\n      # Integer\n      @int8_marker 0xC8\n      @int16_marker 0xC9\n      @int32_marker 0xCA\n      @int64_marker 0xCB\n\n      # Float\n      @float_marker 0xC1\n\n      # List\n      @tiny_list_marker 0x9\n      @list8_marker 0xD4\n      @list16_marker 0xD5\n      @list32_marker 0xD6\n\n      # Map\n      @tiny_map_marker 0xA\n      @map8_marker 0xD8\n      @map16_marker 0xD9\n      @map32_marker 0xDA\n\n      # Structure\n      @tiny_struct_marker 0xB\n      @struct8_marker 0xDC\n      @struct16_marker 0xDD\n\n      # Node\n      @node_marker 0x4E\n\n      # Relationship\n      @relationship_marker 0x52\n\n      # Unbounded relationship\n      @unbounded_relationship_marker 0x72\n\n      # Path\n      @path_marker 0x50\n\n      @spec decode(binary() | {integer(), binary(), integer()}, integer()) ::\n              list() | {:error, :not_implemented}\n      def decode(<<@null_marker, rest::binary>>, bolt_version) when bolt_version <= @last_version do\n        [nil | decode(rest, bolt_version)]\n      end\n\n      # Boolean\n      def decode(<<@true_marker, rest::binary>>, bolt_version) when bolt_version <= @last_version do\n        [true | decode(rest, bolt_version)]\n      end\n\n      def decode(<<@false_marker, rest::binary>>, bolt_version) when bolt_version <= @last_version do\n        [false | decode(rest, bolt_version)]\n      end\n\n      # Float\n      def decode(<<@float_marker, number::float, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        [number | decode(rest, bolt_version)]\n      end\n\n      # Strings\n      def decode(<<@tiny_bitstring_marker::4, str_length::4, rest::bytes>>, bolt_version)\n          when bolt_version <= @last_version do\n        decode_string(rest, str_length, bolt_version)\n      end\n\n      def decode(<<@bitstring8_marker, str_length, rest::bytes>>, bolt_version)\n          when bolt_version <= @last_version do\n        decode_string(rest, str_length, bolt_version)\n      end\n\n      def decode(<<@bitstring16_marker, str_length::16, rest::bytes>>, bolt_version)\n          when bolt_version <= @last_version do\n        decode_string(rest, str_length, bolt_version)\n      end\n\n      def decode(<<@bitstring32_marker, str_length::32, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        decode_string(rest, str_length, bolt_version)\n      end\n\n      # Lists\n      def decode(<<@tiny_list_marker::4, list_size::4>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_list(bin, list_size, bolt_version)\n      end\n\n      def decode(<<@list8_marker, list_size::8>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_list(bin, list_size, bolt_version)\n      end\n\n      def decode(<<@list16_marker, list_size::16>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_list(bin, list_size, bolt_version)\n      end\n\n      def decode(<<@list32_marker, list_size::32>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_list(bin, list_size, bolt_version)\n      end\n\n      # Maps\n      def decode(<<@tiny_map_marker::4, entries::4>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_map(bin, entries, bolt_version)\n      end\n\n      def decode(<<@map8_marker, entries::8>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_map(bin, entries, bolt_version)\n      end\n\n      def decode(<<@map16_marker, entries::16>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_map(bin, entries, bolt_version)\n      end\n\n      def decode(<<@map32_marker, entries::32>> <> bin, bolt_version)\n          when bolt_version <= @last_version do\n        decode_map(bin, entries, bolt_version)\n      end\n\n      # Struct\n      def decode(<<@tiny_struct_marker::4, struct_size::4, sig::8>> <> struct, bolt_version)\n          when bolt_version <= @last_version do\n        decode({sig, struct, struct_size}, bolt_version)\n      end\n\n      def decode(<<@struct8_marker, struct_size::8, sig::8>> <> struct, bolt_version)\n          when bolt_version <= @last_version do\n        decode({sig, struct, struct_size}, bolt_version)\n      end\n\n      def decode(<<@struct16_marker, struct_size::16, sig::8>> <> struct, bolt_version)\n          when bolt_version <= @last_version do\n        decode({sig, struct, struct_size}, bolt_version)\n      end\n\n      ######### SPECIAL STRUCTS\n\n      # Node\n      def decode({@node_marker, struct, struct_size}, bolt_version)\n          when bolt_version <= @last_version do\n        {[id, labels, props], rest} = decode_struct(struct, struct_size, bolt_version)\n\n        node = %Types.Node{id: id, labels: labels, properties: props}\n\n        [node | rest]\n      end\n\n      # Relationship\n      def decode({@relationship_marker, struct, struct_size}, bolt_version)\n          when bolt_version <= @last_version do\n        {[id, start_node, end_node, type, props], rest} =\n          decode_struct(struct, struct_size, bolt_version)\n\n        relationship = %Types.Relationship{\n          id: id,\n          start: start_node,\n          end: end_node,\n          type: type,\n          properties: props\n        }\n\n        [relationship | rest]\n      end\n\n      # UnboundedRelationship\n      def decode({@unbounded_relationship_marker, struct, struct_size}, bolt_version)\n          when bolt_version <= @last_version do\n        {[id, type, props], rest} = decode_struct(struct, struct_size, bolt_version)\n\n        unbounded_relationship = %Types.UnboundRelationship{\n          id: id,\n          type: type,\n          properties: props\n        }\n\n        [unbounded_relationship | rest]\n      end\n\n      # Path\n      def decode({@path_marker, struct, struct_size}, bolt_version)\n          when bolt_version <= @last_version do\n        {[nodes, relationships, sequence], rest} =\n          decode_struct(struct, struct_size, bolt_version)\n\n        path = %Types.Path{\n          nodes: nodes,\n          relationships: relationships,\n          sequence: sequence\n        }\n\n        [path | rest]\n      end\n\n      # Manage the end of data\n      def decode(\"\", bolt_version) when bolt_version <= @last_version do\n        []\n      end\n\n      # Integers\n      def decode(<<@int8_marker, int::signed-integer, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        [int | decode(rest, bolt_version)]\n      end\n\n      def decode(<<@int16_marker, int::signed-integer-16, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        [int | decode(rest, bolt_version)]\n      end\n\n      def decode(<<@int32_marker, int::signed-integer-32, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        [int | decode(rest, bolt_version)]\n      end\n\n      def decode(<<@int64_marker, int::signed-integer-64, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        [int | decode(rest, bolt_version)]\n      end\n\n      def decode(<<int::signed-integer, rest::binary>>, bolt_version)\n          when bolt_version <= @last_version do\n        [int | decode(rest, bolt_version)]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_impl_v2.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderImplV2 do\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}\n\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MODULE__)\n\n      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()\n\n      # Null\n      @null_marker 0xC0\n\n      # Boolean\n      @true_marker 0xC3\n      @false_marker 0xC2\n\n      # String\n      @tiny_bitstring_marker 0x8\n      @bitstring8_marker 0xD0\n      @bitstring16_marker 0xD1\n      @bitstring32_marker 0xD2\n\n      # Integer\n      @int8_marker 0xC8\n      @int16_marker 0xC9\n      @int32_marker 0xCA\n      @int64_marker 0xCB\n\n      # Float\n      @float_marker 0xC1\n\n      # List\n      @tiny_list_marker 0x9\n      @list8_marker 0xD4\n      @list16_marker 0xD5\n      @list32_marker 0xD6\n\n      # Map\n      @tiny_map_marker 0xA\n      @map8_marker 0xD8\n      @map16_marker 0xD9\n      @map32_marker 0xDA\n\n      # Structure\n      @tiny_struct_marker 0xB\n      @struct8_marker 0xDC\n      @struct16_marker 0xDD\n\n      # Node\n      @node_marker 0x4E\n\n      # Relationship\n      @relationship_marker 0x52\n\n      # Unbounded relationship\n      @unbounded_relationship_marker 0x72\n\n      # Path\n      @path_marker 0x50\n\n      # Local Time\n      @local_time_signature 0x74\n      @local_time_struct_size 1\n\n      # Time With TZ Offset\n      @time_with_tz_signature 0x54\n      @time_with_tz_struct_size 2\n\n      # Date\n      @date_signature 0x44\n      @date_struct_size 1\n\n      # Local DateTime\n      @local_datetime_signature 0x64\n      @local_datetime_struct_size 2\n\n      # Datetime with TZ offset\n      @datetime_with_zone_offset_signature 0x46\n      @datetime_with_zone_offset_struct_size 3\n\n      # Datetime with TZ id\n      @datetime_with_zone_id_signature 0x66\n      @datetime_with_zone_id_struct_size 3\n\n      # Duration\n      @duration_signature 0x45\n      @duration_struct_size 4\n\n      # Point 2D\n      @point2d_signature 0x58\n      @point2d_struct_size 3\n\n      # Point 3D\n      @point3d_signature 0x59\n      @point3d_struct_size 4\n\n      # Local Date\n      def decode({@date_signature, struct, @date_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[date], rest} = decode_struct(struct, @date_struct_size, bolt_version)\n        [Date.add(~D[1970-01-01], date) | rest]\n      end\n\n      # Local Time\n      def decode({@local_time_signature, struct, @local_time_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[time], rest} = decode_struct(struct, @local_time_struct_size, bolt_version)\n\n        [Time.add(~T[00:00:00.000000], time, :nanosecond) | rest]\n      end\n\n      # Local DateTime\n      def decode({@local_datetime_signature, struct, @local_datetime_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[seconds, nanoseconds], rest} =\n          decode_struct(struct, @local_datetime_struct_size, bolt_version)\n\n        ndt =\n          NaiveDateTime.add(\n            ~N[1970-01-01 00:00:00.000000000],\n            seconds * 1_000_000_000 + nanoseconds,\n            :nanosecond\n          )\n\n        [ndt | rest]\n      end\n\n      # Time with Zone Offset\n      def decode({@time_with_tz_signature, struct, @time_with_tz_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[time, offset], rest} = decode_struct(struct, @time_with_tz_struct_size, bolt_version)\n\n        t = TimeWithTZOffset.create(Time.add(~T[00:00:00.000000], time, :nanosecond), offset)\n        [t | rest]\n      end\n\n      # Datetime with zone Id\n      def decode(\n            {@datetime_with_zone_id_signature, struct, @datetime_with_zone_id_struct_size},\n            bolt_version\n          )\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[seconds, nanoseconds, zone_id], rest} =\n          decode_struct(struct, @datetime_with_zone_id_struct_size, bolt_version)\n\n        naive_dt =\n          NaiveDateTime.add(\n            ~N[1970-01-01 00:00:00.000000],\n            seconds * 1_000_000_000 + nanoseconds,\n            :nanosecond\n          )\n\n        dt = Bolt.Sips.TypesHelper.datetime_with_micro(naive_dt, zone_id)\n        [dt | rest]\n      end\n\n      # Datetime with zone offset\n      def decode(\n            {@datetime_with_zone_offset_signature, struct,\n             @datetime_with_zone_offset_struct_size},\n            bolt_version\n          )\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[seconds, nanoseconds, zone_offset], rest} =\n          decode_struct(struct, @datetime_with_zone_id_struct_size, bolt_version)\n\n        naive_dt =\n          NaiveDateTime.add(\n            ~N[1970-01-01 00:00:00.000000],\n            seconds * 1_000_000_000 + nanoseconds,\n            :nanosecond\n          )\n\n        dt = DateTimeWithTZOffset.create(naive_dt, zone_offset)\n        [dt | rest]\n      end\n\n      # Duration\n      def decode({@duration_signature, struct, @duration_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[months, days, seconds, nanoseconds], rest} =\n          decode_struct(struct, @duration_struct_size, bolt_version)\n\n        duration = Duration.create(months, days, seconds, nanoseconds)\n        [duration | rest]\n      end\n\n      # Point2D\n      def decode({@point2d_signature, struct, @point2d_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[srid, x, y], rest} = decode_struct(struct, @point2d_struct_size, bolt_version)\n        point = Point.create(srid, x, y)\n\n        [point | rest]\n      end\n\n      # Point3D\n      def decode({@point3d_signature, struct, @point3d_struct_size}, bolt_version)\n          when bolt_version >= 2 and bolt_version <= @last_version do\n        {[srid, x, y, z], rest} = decode_struct(struct, @point3d_struct_size, bolt_version)\n        point = Point.create(srid, x, y, z)\n\n        [point | rest]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_utils.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderUtils do\n  alias Bolt.Sips.Internals.PackStreamError\n\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MODULE__)\n\n      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()\n\n      def decode(data, bolt_version) when is_integer(bolt_version) do\n        if bolt_version > @last_version do\n          decode(data, @last_version)\n        else\n          raise PackStreamError,\n            data: data,\n            bolt_version: bolt_version,\n            message: \"Unsupported decoder version\"\n        end\n      end\n\n      def decode(_, _) do\n        {:error, :not_implemented}\n      end\n\n      @doc \"\"\"\n      Decodes a struct\n      \"\"\"\n      @spec decode_struct(binary(), integer(), integer()) :: {list(), list()}\n      def decode_struct(struct, struct_size, bolt_version) do\n        struct\n        |> decode(bolt_version)\n        |> Enum.split(struct_size)\n      end\n\n      @spec to_map(list()) :: map()\n      defp to_map(map) do\n        map\n        |> Enum.chunk_every(2)\n        |> Enum.map(&List.to_tuple/1)\n        |> Map.new()\n      end\n\n      @spec decode_string(binary(), integer(), integer()) :: list()\n      defp decode_string(bytes, str_length, bolt_version) do\n        <<string::binary-size(str_length), rest::binary>> = bytes\n\n        [string | decode(rest, bolt_version)]\n      end\n\n      @spec decode_list(binary(), integer(), integer()) :: list()\n      defp decode_list(list, list_size, bolt_version) do\n        {list, rest} = list |> decode(bolt_version) |> Enum.split(list_size)\n        [list | rest]\n      end\n\n      @spec decode_map(binary(), integer(), integer()) :: list()\n      defp decode_map(map, entries, bolt_version) do\n        {map, rest} = map |> decode(bolt_version) |> Enum.split(entries * 2)\n\n        [to_map(map) | rest]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_v1.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderV1 do\n  @moduledoc false\n  _moduledoc = \"\"\"\n  Bolt V1 can decode:\n  - Null\n  - Boolean\n  - Integer\n  - Float\n  - String\n  - List\n  - Map\n  - Struct\n\n  Functions from this module are not meant to be used directly.\n  Use `Decoder.decode(data, bolt_version)` for all decoding purposes.\n  \"\"\"\n\n  use Bolt.Sips.Internals.PackStream.Markers\n  alias Bolt.Sips.Internals.PackStream.Decoder\n\n  @spec decode(binary() | {integer(), binary(), integer()}, integer()) ::\n          list() | {:error, :not_implemented}\n  def decode(data, bolt_version), do: Decoder.decode(data, bolt_version)\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_v2.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderV2 do\n  @moduledoc false\n  _module_doc = \"\"\"\n  Bolt V2 has specification for decoding:\n  - Temporal types:\n    - Local Date\n    - Local Time\n    - Local DateTime\n    - Time with Timezone Offset\n    - DateTime with Timezone Id\n    - DateTime with Timezone Offset\n    - Duration\n  - Spatial types:\n    - Point2D\n    - Point3D\n\n  For documentation about those typs representation in Bolt binary,\n  please see `Bolt.Sips.Internals.PackStream.EncoderV2`.\n\n  Functions from this module are not meant to be used directly.\n  Use `Decoder.decode(data, bolt_version)` for all decoding purposes.\n  \"\"\"\n\n  use Bolt.Sips.Internals.PackStream.Markers\n  alias Bolt.Sips.Internals.PackStream.Decoder\n\n  # Local Date\n  @spec decode({integer(), binary(), integer()}, integer()) :: list() | {:error, :not_implemented}\n  def decode(data, bolt_version), do: Decoder.decode(data, bolt_version)\n\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/decoder_v3.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderV3 do\n  def decode(_, _) do\n    {:error, :not_implemented}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder.ex",
    "content": "alias Bolt.Sips.Internals.PackStream\nalias Bolt.Sips.Internals.PackStream.EncoderHelper\n\ndefprotocol Bolt.Sips.Internals.PackStream.Encoder do\n  @moduledoc false\n\n  # Encodes an item to its binary PackStream Representation\n  #\n  # Implementation exists for following types:\n  #   - Integer\n  #   - Float\n  #   - List\n  #   - Map\n  #   - Struct (defined in the Bolt protocol)\n  @fallback_to_any true\n\n  @doc \"\"\"\n  Encode entity into its Bolt binary represenation depending of the used bolt version\n  \"\"\"\n\n  @spec encode(any(), integer()) :: binary()\n  def encode(entity, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: Atom do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:atom, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: BitString do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:string, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: Integer do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:integer, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: Float do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:float, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: List do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:list, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: Map do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:map, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: Time do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:local_time, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: Bolt.Sips.Types.TimeWithTZOffset do\n  def encode(data, bolt_version) do\n    EncoderHelper.call_encode(:time_with_tz, data, bolt_version)\n  end\nend\n\ndefimpl PackStream.Encoder, for: Date do\n  def encode(data, bolt_version), do: EncoderHelper.call_encode(:date, data, bolt_version)\nend\n\ndefimpl PackStream.Encoder, for: NaiveDateTime do\n  def encode(data, bolt_version) do\n    EncoderHelper.call_encode(:local_datetime, data, bolt_version)\n  end\nend\n\ndefimpl PackStream.Encoder, for: DateTime do\n  def encode(data, version) do\n    EncoderHelper.call_encode(:datetime_with_tz_id, data, version)\n  end\nend\n\ndefimpl PackStream.Encoder, for: Bolt.Sips.Types.DateTimeWithTZOffset do\n  def encode(data, version) do\n    EncoderHelper.call_encode(:datetime_with_tz_offset, data, version)\n  end\nend\n\ndefimpl PackStream.Encoder, for: Bolt.Sips.Types.Duration do\n  def encode(data, version), do: EncoderHelper.call_encode(:duration, data, version)\nend\n\ndefimpl PackStream.Encoder, for: Bolt.Sips.Types.Point do\n  def encode(data, version), do: EncoderHelper.call_encode(:point, data, version)\nend\n\ndefimpl PackStream.Encoder, for: Any do\n  @spec encode({integer(), list()} | %{:__struct__ => String.t()}, integer()) ::\n          Bolt.Sips.Internals.PackStream.value() | <<_::16, _::_*8>>\n  def encode({signature, data}, bolt_version) when is_list(data) do\n    valid_signatures =\n      PackStream.Message.Encoder.valid_signatures(bolt_version) ++\n        Bolt.Sips.Internals.PackStream.MarkersHelper.valid_signatures()\n\n    if signature in valid_signatures do\n      EncoderHelper.call_encode(:struct, {signature, data}, bolt_version)\n    else\n      raise Bolt.Sips.Internals.PackStreamError,\n        message: \"Unable to encode\",\n        data: data,\n        bolt_version: bolt_version\n    end\n  end\n\n  # Elixir structs just need to be convertedd to map befoare being encoded\n  def encode(%{__struct__: _} = data, bolt_version) do\n    map = Map.from_struct(data)\n    PackStream.Encoder.encode(map, bolt_version)\n  end\n\n  def encode(data, bolt_version) do\n    raise Bolt.Sips.Internals.PackStreamError,\n      message: \"Unable to encode\",\n      data: data,\n      bolt_version: bolt_version\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_helper.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderHelper do\n  @moduledoc false\n  alias Bolt.Sips.Internals.BoltVersionHelper\n  alias Bolt.Sips.Internals.PackStreamError\n\n  use Bolt.Sips.Internals.PackStream.V1\n  use Bolt.Sips.Internals.PackStream.V2\n  use Bolt.Sips.Internals.PackStream.Utils\n\n  @available_bolt_versions BoltVersionHelper.available_versions()\n  @last_version BoltVersionHelper.last()\n\n\n  @doc \"\"\"\n  For the given `data_type` and `bolt_version`, determine the right enconding function\n  and call it agains `data`\n  \"\"\"\n  @spec call_encode(atom(), any(), any()) :: binary() | PackStreamError.t()\n  def call_encode(data_type, data, bolt_version)\n      when is_integer(bolt_version) and bolt_version in @available_bolt_versions do\n    do_call_encode(data_type, data, bolt_version)\n  end\n\n  def call_encode(data_type, data, bolt_version) when is_integer(bolt_version) do\n    if bolt_version > @last_version do\n      call_encode(data_type, data, @last_version)\n    else\n      raise PackStreamError,\n        data_type: data_type,\n        data: data,\n        bolt_version: bolt_version,\n        message: \"Unsupported encoder version\"\n    end\n  end\n\n  def call_encode(data_type, data, bolt_version) do\n    raise PackStreamError,\n      data_type: data_type,\n      data: data,\n      bolt_version: bolt_version,\n      message: \"Unsupported encoder version\"\n  end\n\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_v1.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderV1 do\n  @moduledoc false\n  alias Bolt.Sips.Internals.PackStream.EncoderHelper\n  use Bolt.Sips.Internals.PackStream.Markers\n\n\n  @doc \"\"\"\n  Encode an atom into Bolt binary format.\n\n  Encoding:\n  `Marker`\n\n  with\n\n  | Value | Marker |\n  | ------- | -------- |\n  | nil | `0xC0` |\n  | false | `0xC2` |\n  | true | `0xC3` |\n\n  Other atoms are converted to string before encoding.\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_atom(nil, 1))\n      <<0xC0>>\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_atom(true, 1))\n      <<0xC3>>\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_atom(:guten_tag, 1))\n      <<0x89, 0x67, 0x75, 0x74, 0x65, 0x6E, 0x5F, 0x74, 0x61, 0x67>>\n  \"\"\"\n  @spec encode_atom(atom(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_atom(atom , bolt_version), do: EncoderHelper.call_encode(:atom, atom, bolt_version) \n\n  @doc \"\"\"\n  Encode a string into Bolt binary format.\n\n  Encoding:\n  `Marker` `Size` `Content`\n\n  with\n\n  | Marker | Size | Max data size |\n  |--------|------|---------------|\n  | `0x80`..`0x8F` | None (contained in marker) | 15 bytes |\n  | `0xD0` | 8-bit integer | 255 bytes |\n  | `0xD1` | 16-bit integer | 65_535 bytes |\n  | `0xD2` | 32-bit integer | 4_294_967_295 bytes |\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_string(\"guten tag\", 1))\n      <<0x89, 0x67, 0x75, 0x74, 0x65, 0x6E, 0x20, 0x74, 0x61, 0x67>>\n  \"\"\"\n  @spec encode_string(String.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_string(string, bolt_version), do: EncoderHelper.call_encode(:string, string, bolt_version)\n\n\n  @doc \"\"\"\n  Encode an integer into Bolt binary format.\n\n  Encoding:\n  `Marker` `Value`\n\n  with\n\n  |   | Marker |\n  |---|--------|\n  | tiny int | `0x2A` |\n  | int8 | `0xC8` |\n  | int16 | `0xC9` |\n  | int32 | `0xCA` |\n  | int64 | `0xCB` |\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_integer(74, 1))\n      <<0x4A>>\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_integer(-74_789, 1))\n      <<0xCA, 0xFF, 0xFE, 0xDB, 0xDB>>\n  \"\"\"\n  @spec encode_integer(integer(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_integer(integer, bolt_version), do: EncoderHelper.call_encode(:integer, integer, bolt_version)\n\n  @doc \"\"\"\n  Encode a float into Bolt binary format.\n\n  Encoding: `Marker` `8 byte Content`.\n\n  Marker: `0xC1`\n\n  Formated according to the IEEE 754 floating-point \"double format\" bit layout.\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_float(42.42, 1))\n      <<0xC1, 0x40, 0x45, 0x35, 0xC2, 0x8F, 0x5C, 0x28, 0xF6>>\n  \"\"\"\n  @spec encode_float(float(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_float(number, bolt_version), do: EncoderHelper.call_encode(:float, number, bolt_version) \n\n  @doc \"\"\"\n  Encode a list into Bolt binary format.\n\n  Encoding:\n  `Marker` `Size` `Content`\n\n  with\n\n  | Marker | Size | Max list size |\n  |--------|------|---------------|\n  | `0x90`..`0x9F` | None (contained in marker) | 15 bytes |\n  | `0xD4` | 8-bit integer | 255 items |\n  | `0xD5` | 16-bit integer | 65_535 items |\n  | `0xD6` | 32-bit integer | 4_294_967_295 items |\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_list([\"hello\", \"world\"], 1))\n      <<0x92, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x85, 0x77, 0x6F, 0x72, 0x6C, 0x64>>\n  \"\"\"\n  @spec encode_list(list(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_list(list, bolt_version), do: EncoderHelper.call_encode(:list, list, bolt_version)\n\n  @doc \"\"\"\n  Encode a map into Bolt binary format.\n\n  Note that Elixir structs are converted to map for encoding purpose.\n\n  Encoding:\n  `Marker` `Size` `Content`\n\n  with\n\n  | Marker | Size | Max map size |\n  |--------|------|---------------|\n  | `0xA0`..`0xAF` | None (contained in marker) | 15 entries |\n  | `0xD8` | 8-bit integer | 255 entries |\n  | `0xD9` | 16-bit integer | 65_535 entries |\n  | `0xDA` | 32-bit integer | 4_294_967_295 entries |\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_map(%{id: 345, value: \"hello world\"}, 1))\n      <<0xA2, 0x82, 0x69, 0x64, 0xC9, 0x1, 0x59, 0x85, 0x76, 0x61, 0x6C, 0x75,\n      0x65, 0x8B, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64>>\n  \"\"\"\n  @spec encode_map(map(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_map(map, bolt_version), do: EncoderHelper.call_encode(:map, map, bolt_version)\n\n\n  @doc \"\"\"\n  Encode a struct into Bolt binary format.\n  This concerns Bolt Structs as defined in []().\n  Elixir structs are just converted to regular maps before encoding\n\n  Encoding:\n  `Marker` `Size` `Signature` `Content`\n\n  with\n\n  | Marker | Size | Max structure size |\n  |--------|------|---------------|\n  | `0xB0`..`0xBF` | None (contained in marker) | 15 fields |\n  | `0xDC` | 8-bit integer | 255 fields |\n  | `0xDD` | 16-bit integer | 65_535 fields |\n\n  ## Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.EncoderV1\n      iex> :erlang.iolist_to_binary(EncoderV1.encode_struct({0x01, [\"two\", \"params\"]}, 1))\n      <<0xB2, 0x1, 0x83, 0x74, 0x77, 0x6F, 0x86, 0x70, 0x61, 0x72, 0x61, 0x6D, 0x73>>\n\n  \"\"\"\n  @spec encode_struct({integer(), list()}, integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_struct(struct, bolt_version) , do: EncoderHelper.call_encode(:struct, struct, bolt_version)\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_v2.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderV2 do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Markers\n  alias Bolt.Sips.Internals.PackStream.EncoderHelper\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}\n\n  @doc \"\"\"\n  Encode a Time (represented by Time) into Bolt binary format.\n  Encoded in a structure.\n\n  Signature: `0x74`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Nanoseconds_from_00:00:00`\n\n  ## Example\n\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_local_time(~T[06:54:32.453], 2))\n      <<0xB1, 0x74, 0xCB, 0x0, 0x0, 0x16, 0x9F, 0x11, 0xB9, 0xCB, 0x40>>\n  \"\"\"\n  @spec encode_local_time(Time.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_local_time(local_time, bolt_version), do: EncoderHelper.call_encode(:local_time, local_time, bolt_version)\n\n  @doc \"\"\"\n  Encode a TIME WITH TIMEZONE OFFSET (represented by TimeWithTZOffset) into Bolt binary format.\n  Encoded in a structure.\n\n  Signature: `0x54`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Nanoseconds_from_00:00:00` `Offset_in_seconds`\n\n  ## Example\n\n      iex> time_with_tz = Bolt.Sips.Types.TimeWithTZOffset.create(~T[06:54:32.453], 3600)\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_time_with_tz(time_with_tz, 2))\n      <<0xB2, 0x54, 0xCB, 0x0, 0x0, 0x16, 0x9F, 0x11, 0xB9, 0xCB, 0x40, 0xC9, 0xE, 0x10>>\n  \"\"\"\n  def encode_time_with_tz(%TimeWithTZOffset{time: time, timezone_offset: offset}, bolt_version), do: EncoderHelper.call_encode(:time_with_tz, %TimeWithTZOffset{time: time, timezone_offset: offset}, bolt_version )\n\n  @doc \"\"\"\n  Encode a DATE (represented by Date) into Bolt binary format.\n  Encoded in a structure.\n\n  Signature: `0x44`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Nb_days_since_epoch`\n\n  ## Example\n\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_date(~D[2019-04-23], 2))\n      <<0xB1, 0x44, 0xC9, 0x46, 0x59>>\n\n  \"\"\"\n  @spec encode_date(Date.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_date(date, bolt_version), do: EncoderHelper.call_encode(:date, date, bolt_version)\n\n  @doc \"\"\"\n  Encode a LOCAL DATETIME (Represented by NaiveDateTime) into Bolt binary format.\n  Encoded in a structure.\n\n  WARNING: Nanoseconds are left off as NaiveDateTime doesn't handle them.\n  A new Calendar should be implemented to manage them.\n\n  Signature: `0x64`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Nb_seconds_since_epoch` `Remainder_in_nanoseconds`\n\n  ## Example\n\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_local_datetime(~N[2019-04-23 13:45:52.678], 2))\n      <<0xB2, 0x64, 0xCA, 0x5C, 0xBF, 0x17, 0x10, 0xCA, 0x28, 0x69, 0x75, 0x80>>\n\n  \"\"\"\n  @spec encode_local_datetime(Calendar.naive_datetime(), integer()) ::\n          Bolt.Sips.Internals.PackStream.value()\n  def encode_local_datetime(local_datetime, bolt_version), do: EncoderHelper.call_encode(:local_datetime, local_datetime, bolt_version)\n\n  @doc \"\"\"\n  Encode DATETIME WITH TIMEZONE ID (represented by Calendar.DateTime) into Bolt binary format.\n  Encoded in a structure.\n\n  WARNING: Nanoseconds are left off as NaiveDateTime doesn't handle them.\n  A new Calendar should be implemented to manage them.\n\n  Signature: `0x66`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Nb_seconds_since_epoch` `Remainder_in_nanoseconds` `Zone_id`\n\n  ## Example\n\n      iex> d = Bolt.Sips.TypesHelper.datetime_with_micro(~N[2013-11-12 07:32:02.003],\n      ...> \"Europe/Berlin\")\n      #DateTime<2013-11-12 07:32:02.003+01:00 CET Europe/Berlin>\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_datetime_with_tz_id(d, 2))\n      <<0xB3, 0x66, 0xCA, 0x52, 0x81, 0xD9, 0x72, 0xCA, 0x0, 0x2D, 0xC6, 0xC0, 0x8D, 0x45, 0x75,\n      0x72, 0x6F, 0x70, 0x65, 0x2F, 0x42, 0x65, 0x72, 0x6C, 0x69, 0x6E>>\n\n  \"\"\"\n  @spec encode_datetime_with_tz_id(Calendar.datetime(), integer()) ::\n          Bolt.Sips.Internals.PackStream.value()\n  def encode_datetime_with_tz_id(datetime, bolt_version), do: EncoderHelper.call_encode(:datetime_with_tz_id, datetime, bolt_version)\n\n  @doc \"\"\"\n  Encode DATETIME WITH TIMEZONE OFFSET (represented by DateTimeWithTZOffset) into Bolt binary format.\n  Encoded in a structure.\n\n  WARNING: Nanoseconds are left off as NaiveDateTime doesn't handle them.\n  A new Calendar should be implemented to manage them.\n\n  Signature: `0x46`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Nb_seconds_since_epoch` `Remainder_in_nanoseconds` `Zone_offset`\n\n  ## Example\n\n      iex> d = Bolt.Sips.Types.DateTimeWithTZOffset.create(~N[2013-11-12 07:32:02.003], 7200)\n      %Bolt.Sips.Types.DateTimeWithTZOffset{\n              naive_datetime: ~N[2013-11-12 07:32:02.003],\n              timezone_offset: 7200\n            }\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_datetime_with_tz_offset(d, 2))\n      <<0xB3, 0x46, 0xCA, 0x52, 0x81, 0xD9, 0x72, 0xCA, 0x0, 0x2D, 0xC6, 0xC0, 0xC9, 0x1C, 0x20>>\n\n  \"\"\"\n  @spec encode_datetime_with_tz_offset(DateTimeWithTZOffset.t(), integer()) ::\n          Bolt.Sips.Internals.PackStream.value()\n  def encode_datetime_with_tz_offset(\n        %DateTimeWithTZOffset{naive_datetime: ndt, timezone_offset: tz_offset},\n        bolt_version\n  ), do: EncoderHelper.call_encode(:datetime_with_tz_offset,\n\n        %DateTimeWithTZOffset{naive_datetime: ndt, timezone_offset: tz_offset},\n        bolt_version\n  )\n\n\n  @doc \"\"\"\n  Encode DURATION (represented by Duration) into Bolt binary format.\n  Encoded in a structure.\n\n  Signature: `0x45`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `Months` `Days` `Seconds` `Nanoseconds`\n\n  ## Example\n\n      iex(60)> d = %Bolt.Sips.Types.Duration{\n      ...(60)>   years: 3,\n      ...(60)>   months: 1,\n      ...(60)>   weeks: 7,\n      ...(60)>   days: 4,\n      ...(60)>   hours: 13,\n      ...(60)>   minutes: 2,\n      ...(60)>   seconds: 21,\n      ...(60)>   nanoseconds: 554\n      ...(60)> }\n      %Bolt.Sips.Types.Duration{\n        days: 4,\n        hours: 13,\n        minutes: 2,\n        months: 1,\n        nanoseconds: 554,\n        seconds: 21,\n        weeks: 7,\n        years: 3\n      }\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_duration(d, 2))\n      <<0xB4, 0x45, 0x25, 0x35, 0xCA, 0x0, 0x0, 0xB7, 0x5D, 0xC9, 0x2, 0x2A>>\n  \"\"\"\n  @spec encode_duration(Duration.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_duration(%Duration{} = duration, bolt_version), do: EncoderHelper.call_encode(:duration, duration, bolt_version)\n\n\n  @doc \"\"\"\n  Encode POINT 2D & 3D (represented by Point) into Bolt binary format.\n  Encoded in a structure.\n\n\n  ## Point 2D\n  Signature: `0x58`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `SRID` `x_or_longitude` `y_or_latitude`\n\n  ## Example\n\n      iex> p = Bolt.Sips.Types.Point.create(:wgs_84, 65.43, 12.54)\n      %Bolt.Sips.Types.Point{\n              crs: \"wgs-84\",\n              height: nil,\n              latitude: 12.54,\n              longitude: 65.43,\n              srid: 4326,\n              x: 65.43,\n              y: 12.54,\n              z: nil\n            }\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_point(p, 2))\n      <<0xB3, 0x58, 0xC9, 0x10, 0xE6, 0xC1, 0x40, 0x50, 0x5B, 0x85, 0x1E, 0xB8, 0x51, 0xEC, 0xC1,\n      0x40, 0x29, 0x14, 0x7A, 0xE1, 0x47, 0xAE, 0x14>>\n\n  ## Point 3D\n  Signature: `0x58`\n\n  Encoding:\n  `Marker` `Size` `Signature` ` Content`\n\n  where `Content` is:\n  `SRID` `x_or_longitude` `y_or_latitude` `z_or_height`\n\n  ## Example\n\n      iex> p = Bolt.Sips.Types.Point.create(:wgs_84, 45.0003, 40.3245, 23.1)\n      %Bolt.Sips.Types.Point{\n              crs: \"wgs-84-3d\",\n              height: 23.1,\n              latitude: 40.3245,\n              longitude: 45.0003,\n              srid: 4979,\n              x: 45.0003,\n              y: 40.3245,\n              z: 23.1\n            }\n      iex> :erlang.iolist_to_binary(Bolt.Sips.Internals.PackStream.EncoderV2.encode_point(p, 2))\n      <<0xB4, 0x59, 0xC9, 0x13, 0x73, 0xC1, 0x40, 0x46, 0x80, 0x9, 0xD4, 0x95, 0x18, 0x2B, 0xC1,\n      0x40, 0x44, 0x29, 0x89, 0x37, 0x4B, 0xC6, 0xA8, 0xC1, 0x40, 0x37, 0x19, 0x99, 0x99, 0x99,\n      0x99, 0x9A>>\n\n  \"\"\"\n  @spec encode_point(Point.t(), integer()) :: Bolt.Sips.Internals.PackStream.value()\n  def encode_point(%Point{z: nil} = point, bolt_version), do: EncoderHelper.call_encode(:point, point, bolt_version) \n\n  def encode_point(%Point{} = point, bolt_version), do: EncoderHelper.call_encode(:point, point, bolt_version)\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/encoder_v3.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderV3 do\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/error.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStreamError do\n  @moduledoc false\n\n  # Represents an error when encoding data for the Bolt protocol.\n\n  defexception data_type: nil, data: nil, message: nil, bolt_version: nil\n\n  @typedoc \"\"\"\n  Send back the `item` that cannot be encoded with a `message` explaining the  reason why it\n  can't be successfully encoded.\n  \"\"\"\n  @type t :: %__MODULE__{\n          data_type: atom(),\n          data: any(),\n          message: String.t(),\n          bolt_version: integer()\n        }\n\n  def message(%{data_type: nil, data: data, message: message, bolt_version: bolt_version}) do\n    \"#{message} [bolt_version: #{inspect(bolt_version)}, data: #{inspect(data)}]\"\n  end\n\n  def message(%{data_type: data_type, data: data, message: message, bolt_version: bolt_version}) do\n    \"#{message} [bolt_version: #{inspect(bolt_version)}, data_type: #{data_type}, data: #{\n      inspect(data)\n    }]\"\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/markers.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Markers do\n  @moduledoc false\n  defmacro __using__(_opts) do\n    quote do\n      # Null\n      @null_marker 0xC0\n\n      # Boolean\n      @true_marker 0xC3\n      @false_marker 0xC2\n\n      # String\n      @tiny_bitstring_marker 0x8\n      @bitstring8_marker 0xD0\n      @bitstring16_marker 0xD1\n      @bitstring32_marker 0xD2\n\n      # Integer\n      @int8_marker 0xC8\n      @int16_marker 0xC9\n      @int32_marker 0xCA\n      @int64_marker 0xCB\n\n      # Float\n      @float_marker 0xC1\n\n      # List\n      @tiny_list_marker 0x9\n      @list8_marker 0xD4\n      @list16_marker 0xD5\n      @list32_marker 0xD6\n\n      # Map\n      @tiny_map_marker 0xA\n      @map8_marker 0xD8\n      @map16_marker 0xD9\n      @map32_marker 0xDA\n\n      # Structure\n      @tiny_struct_marker 0xB\n      @struct8_marker 0xDC\n      @struct16_marker 0xDD\n\n      # Node\n      @node_marker 0x4E\n\n      # Relationship\n      @relationship_marker 0x52\n\n      # Unbounded relationship\n      @unbounded_relationship_marker 0x72\n\n      # Path\n      @path_marker 0x50\n\n      # Local Time\n      @local_time_signature 0x74\n      @local_time_struct_size 1\n\n      # Time With TZ Offset\n      @time_with_tz_signature 0x54\n      @time_with_tz_struct_size 2\n\n      # Date\n      @date_signature 0x44\n      @date_struct_size 1\n\n      # Local DateTime\n      @local_datetime_signature 0x64\n      @local_datetime_struct_size 2\n\n      # Datetime with TZ offset\n      @datetime_with_zone_offset_signature 0x46\n      @datetime_with_zone_offset_struct_size 3\n\n      # Datetime with TZ id\n      @datetime_with_zone_id_signature 0x66\n      @datetime_with_zone_id_struct_size 3\n\n      # Duration\n      @duration_signature 0x45\n      @duration_struct_size 4\n\n      # Point 2D\n      @point2d_signature 0x58\n      @point2d_struct_size 3\n\n      # Point 3D\n      @point3d_signature 0x59\n      @point3d_struct_size 4\n    end\n  end\nend\n\ndefmodule Bolt.Sips.Internals.PackStream.MarkersHelper do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Markers\n\n  @doc \"\"\"\n  Return the list of valid signatures (for data encoding).\n  \"\"\"\n  @spec valid_signatures() :: [integer()]\n  def valid_signatures() do\n    [\n      @local_time_signature,\n      @time_with_tz_signature,\n      @date_signature,\n      @local_datetime_signature,\n      @datetime_with_zone_offset_signature,\n      @datetime_with_zone_id_signature,\n      @duration_signature,\n      @point2d_signature,\n      @point3d_signature\n    ]\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/decoder.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.Decoder do\n  @moduledoc false\n\n  @tiny_struct_marker 0xB\n\n  @success_signature 0x70\n  @failure_signature 0x7F\n  @record_signature 0x71\n  @ignored_signature 0x7E\n\n  # Decode SUCCESS message\n  @spec decode(Bolt.Sips.Internals.PackStream.Message.encoded(), integer()) ::\n          Bolt.Sips.Internals.PackStream.Message.decoded()\n  def decode(\n        <<@tiny_struct_marker::4, nb_entries::4, @success_signature, data::binary>>,\n        bolt_version\n      ) do\n    build_response(:success, data, nb_entries, bolt_version)\n  end\n\n  # Decode FAILURE message\n  def decode(\n        <<@tiny_struct_marker::4, nb_entries::4, @failure_signature, data::binary>>,\n        bolt_version\n      ) do\n    build_response(:failure, data, nb_entries, bolt_version)\n  end\n\n  # Decode RECORD message\n  def decode(\n        <<@tiny_struct_marker::4, nb_entries::4, @record_signature, data::binary>>,\n        bolt_version\n      ) do\n    build_response(:record, data, nb_entries, bolt_version)\n  end\n\n  # Decode IGNORED message\n  def decode(\n        <<@tiny_struct_marker::4, nb_entries::4, @ignored_signature, data::binary>>,\n        bolt_version\n      ) do\n    build_response(:ignored, data, nb_entries, bolt_version)\n  end\n\n  @spec build_response(\n          Bolt.Sips.Internals.PackStream.Message.in_signature(),\n          any(),\n          integer(),\n          integer()\n        ) ::\n          Bolt.Sips.Internals.PackStream.Message.decoded()\n  defp build_response(message_type, data, nb_entries, bolt_version) do\n    Bolt.Sips.Internals.Logger.log_message(:server, message_type, data, :hex)\n\n    response =\n      case Bolt.Sips.Internals.PackStream.decode(data, bolt_version) do\n        response when nb_entries == 1 ->\n          List.first(response)\n\n        responses ->\n          responses\n      end\n\n    Bolt.Sips.Internals.Logger.log_message(:server, message_type, response)\n    {message_type, response}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.Encoder do\n  @moduledoc false\n  _module_doc = \"\"\"\n  Manages the message encoding.\n\n  A mesage is a tuple formated as:\n  `{message_type, data}`\n  with:\n  - message_type: atom amongst the valid message type (:init, :discard_all, :pull_all,\n  :ack_failure, :reset, :run)\n  - data: a list of data to be used by the message\n\n  Messages are passed in one or more chunk. The structure of a chunk is as follow: `chunk_size` `data`\n  with `chunk_size` beign a 16-bit integer.\n  A message always ends with the end marker `0x00 0x00`.\n  Thus the possible typologies of messages are:\n  - One-chunk message:\n  `chunk_size` `message_data` `end_marker`\n  - multiple-chunk message:\n  `chunk_1_size` `message_data` `chunk_n_size` `message_data`...`end_marker`\n  More documentation on message transfer encoding:\n  [https://boltprotocol.org/v1/#message_transfer_encoding](https://boltprotocol.org/v1/#message_transfer_encoding)\n\n  All messages are serialized structures. See `Bolt.Sips.Internals.PackStream.EncoderV1` for\n  more information about structure encoding).\n\n  An extensive documentation on messages can be found here:\n  [https://boltprotocol.org/v1/#messages](https://boltprotocol.org/v1/#messages)\n  \"\"\"\n\n  alias Bolt.Sips.Metadata\n\n  @max_chunk_size 65_535\n  @end_marker <<0x00, 0x00>>\n\n  @ack_failure_signature 0x0E\n  @begin_signature 0x11\n  @commit_signature 0x12\n  @discard_all_signature 0x2F\n  @goodbye_signature 0x02\n  @hello_signature 0x01\n  @init_signature 0x01\n  @pull_all_signature 0x3F\n  @reset_signature 0x0F\n  @rollback_signature 0x13\n  @run_signature 0x10\n\n  # OUT Signature\n\n  # TODO improve using macros?\n  @valid_signatures [\n    @ack_failure_signature,\n    @begin_signature,\n    @commit_signature,\n    @discard_all_signature,\n    @goodbye_signature,\n    @hello_signature,\n    @pull_all_signature,\n    @reset_signature,\n    @rollback_signature,\n    @run_signature\n  ]\n\n  @valid_v1_signatures [\n    @ack_failure_signature,\n    @discard_all_signature,\n    @init_signature,\n    @pull_all_signature,\n    @reset_signature,\n    @run_signature\n  ]\n\n  @valid_message_types [\n    :ack_failure,\n    :begin,\n    :commit,\n    :discard_all,\n    :goodbye,\n    :hello,\n    :rollback,\n    :pull_all,\n    :reset,\n    :run\n  ]\n\n  @valid_v1_message_types [\n    :ack_failure,\n    :discard_all,\n    :init,\n    :pull_all,\n    :reset,\n    :run\n  ]\n\n  @last_bolt_version 3\n\n  @spec signature(Bolt.Sips.Internals.PackStream.Message.out_signature()) :: integer()\n  defp signature(:ack_failure), do: @ack_failure_signature\n  defp signature(:discard_all), do: @discard_all_signature\n  defp signature(:pull_all), do: @pull_all_signature\n  defp signature(:reset), do: @reset_signature\n  defp signature(:begin), do: @begin_signature\n  defp signature(:commit), do: @commit_signature\n  defp signature(:goodbye), do: @goodbye_signature\n  defp signature(:hello), do: @hello_signature\n  defp signature(:rollback), do: @rollback_signature\n  defp signature(:run), do: @run_signature\n  defp signature(:init), do: @init_signature\n\n  @doc \"\"\"\n  Return client name (based on bolt_sips version)\n  \"\"\"\n  def client_name() do\n    \"BoltSips/\" <> to_string(Application.spec(:bolt_sips, :vsn))\n  end\n\n  @doc \"\"\"\n  Return the valid message signatures depending on the Bolt version\n  \"\"\"\n  @spec valid_signatures(integer()) :: [integer()]\n  def valid_signatures(bolt_version) when bolt_version <= 2 do\n    @valid_v1_signatures\n  end\n\n  def valid_signatures(3) do\n    @valid_signatures\n  end\n\n  # Encode messages for bolt version 3\n\n  # Encode HELLO message without auth token\n  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::\n          Bolt.Sips.Internals.PackStream.Message.encoded()\n          | {:error, :not_implemented}\n          | {:error, :invalid_message}\n  def encode({:hello, []}, 3) do\n    encode({:hello, [{}]}, 3)\n  end\n\n  # Encode INIT message with a valid auth token.\n  # The auth token is tuple formated as: {user, password}\n  def encode({:hello, [auth]}, 3) do\n    do_encode(:hello, [auth_params(auth)], 3)\n  end\n\n  # Encode BEGIN message without metadata.\n\n  # BEGIN is used to open a transaction.\n  def encode({:begin, []}, 3) do\n    encode({:begin, [%{}]}, 3)\n  end\n\n  # Encode BEGIN message with metadata\n  def encode({:begin, [%Metadata{} = metadata]}, 3) do\n    do_encode(:begin, [Metadata.to_map(metadata)], 3)\n  end\n\n  def encode({:begin, [%{} = map]}, 3) when map_size(map) == 0 do\n    {:ok, metadata} = Metadata.new(%{})\n    encode({:begin, [metadata]}, 3)\n  end\n\n  def encode({:begin, _}, _) do\n    {:error, :invalid_data}\n  end\n\n  # Encode RUN without params nor metadata\n  def encode({:run, [statement]}, 3) do\n    do_encode(:run, [statement, %{}, %{}], 3)\n  end\n\n  # Encode RUN message with its data: statement and parameters\n  def encode({:run, [statement]}, bolt_version) when bolt_version <= 2 do\n    do_encode(:run, [statement, %{}], bolt_version)\n  end\n\n  # Encode RUN with params but without metadata\n  def encode({:run, [statement, params]}, 3) do\n    do_encode(:run, [statement, params, %{}], 3)\n  end\n\n  # Encode RUN with params and metadata\n  def encode({:run, [statement, params, %Metadata{} = metadata]}, 3) do\n    do_encode(:run, [statement, params, Metadata.to_map(metadata)], 3)\n  end\n\n  # INIT is no more a valid message in Bolt V3\n  def encode({:init, _}, 3) do\n    {:error, :invalid_message}\n  end\n\n  # Encode INIT message without auth token\n  def encode({:init, []}, bolt_version) when bolt_version <= 2 do\n    encode({:init, [{}]}, bolt_version)\n  end\n\n  # Encode INIT message with a valid auth token.\n  # The auth token is tuple formated as: {user, password}\n  def encode({:init, [auth]}, bolt_version) when bolt_version <= 2 do\n    do_encode(:init, [client_name(), auth_params_v1(auth)], bolt_version)\n  end\n\n  # Encode messages that don't need any data formating\n  def encode({message_type, data}, 3) when message_type in @valid_message_types do\n    do_encode(message_type, data, 3)\n  end\n\n  # Encode messages that don't need any data formating\n  def encode({message_type, data}, bolt_version)\n      when bolt_version <= 2 and message_type in @valid_v1_message_types do\n    do_encode(message_type, data, bolt_version)\n  end\n\n  @doc \"\"\"\n  Encode Bolt V3 messages\n\n  Not that INIT is not valid in bolt V3, it is replaced by HELLO\n\n  ## HELLO\n  Usage: intialize the session.\n\n  Signature: `0x01` (Same as INIT in previous bolt version)\n\n  Struct: `auth_parms`\n\n  with:\n\n  | data | type |\n  |-----|-----|\n  |auth_token | map: {scheme: string, principal: string, credentials: string, user_agent: string}|\n\n  Note: `user_agent` is equivalent to `client_name` in bolt previous version.\n\n  Examples (excluded from doctest because client_name changes at each bolt_sips version)\n\n      # without auth token\n      diex> :erlang.iolist_to_binary(Encoder.encode({:hello, []}, 3))\n      <<0x0, 0x1D, 0xB1, 0x1, 0xA1, 0x8A, 0x75, 0x73, 0x65, 0x72, 0x5F, 0x61, 0x67, 0x65, 0x6E,\n      0x74, 0x8E, 0x42, 0x6F, 0x6C, 0x74, 0x53, 0x69, 0x70, 0x73, 0x2F, 0x31, 0x2E, 0x34, 0x2E,\n      0x30, 0x0, 0x0>>\n\n      # with auth token\n      diex(20)> :erlang.iolist_to_binary(Encoder.encode({:hello, [{\"neo4j\", \"test\"}]}, 3))\n      <<0x0, 0x4B, 0xB1, 0x1, 0xA4, 0x8B, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x61,\n      0x6C, 0x73, 0x84, 0x74, 0x65, 0x73, 0x74, 0x89, 0x70, 0x72, 0x69, 0x6E, 0x63, 0x69, 0x70,\n      0x61, 0x6C, 0x85, 0x6E, 0x65, 0x6F, 0x34, 0x6A, 0x86, 0x73, 0x63, 0x68, 0x65, 0x6D, 0x65,\n      0x85, 0x62, 0x61, 0x73, 0x69, ...>>\n\n  ## GOODBYE\n  Usage: close the connection with the server\n\n  Signature: `0x02`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:goodbye, []}, 3))\n      <<0x0, 0x2, 0xB0, 0x2, 0x0, 0x0>>\n\n  ## BEGIN\n  Usage: Open a transaction\n\n  Signature: `0x11`\n\n  Struct: `metadata`\n\n  with:\n\n  | data | type |\n  |------|------|\n  | metadata | See Bolt.Sips.Metadata\n\n  Example\n\n      # without metadata\n      # iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      # iex> :erlang.iolist_to_binary(Encoder.encode({:begin, []}, 3))\n      # <<0x0, 0x3, 0xB1, 0x11, 0xA0, 0x0, 0x0>>\n\n      # # with metadata\n      # iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      # iex> alias Bolt.Sips.Metadata\n      # iex> {:ok, metadata} = Metadata.new(%{tx_timeout: 5000})\n      # {:ok,\n      # %Bolt.Sips.Metadata{\n      #   bookmarks: nil,\n      #   metadata: nil,\n      #   tx_timeout: 5000\n      # }}\n      # iex> :erlang.iolist_to_binary(Encoder.encode({:begin, [metadata]}, 3))\n      # <<0x0, 0x11, 0xB1, 0x11, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75,\n      # 0x74, 0xC9, 0x13, 0x88, 0x0, 0x0>>\n\n  ## COMMIT\n  Usage: commit the currently open transaction\n\n  Signature: `0x12`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:commit, []}, 3))\n      <<0x0, 0x2, 0xB0, 0x12, 0x0, 0x0>>\n\n  ## ROLLBACK\n  Usage: rollback the currently open transaction\n\n  Signature: `0x13`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:rollback, []}, 3))\n      <<0x0, 0x2, 0xB0, 0x13, 0x0, 0x0>>\n\n  ## RUN\n  Usage: pass statement for execution to the server. Same as in bolt previous version.\n  The only difference: `metadata` are passed as well since bolt v3.\n\n  Signature: `0x10`\n\n  Struct: `statement` `parameters` `metadata`\n\n  with:\n\n  | data | type |\n  |-----|-----|\n  | statement | string |\n  | parameters | map |\n  | metadata | See Bolt.Sips.Metadata\n\n  Example\n\n      # without params nor metadata\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:run, [\"RETURN 'hello' AS str\"]}, 3))\n      <<0x0, 0x1B, 0xB3, 0x10, 0xD0, 0x15, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x27, 0x68,\n      0x65, 0x6C, 0x6C, 0x6F, 0x27, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA0, 0xA0, 0x0,\n      0x0>>\n\n      # without params but with metadata\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> alias Bolt.Sips.Metadata\n      iex> {:ok, metadata} = Metadata.new(%{tx_timeout: 4500})\n      {:ok,\n      %Bolt.Sips.Metadata{\n        bookmarks: nil,\n        metadata: nil,\n        tx_timeout: 4500\n      }}\n      iex> :erlang.iolist_to_binary(Encoder.encode({:run, [\"RETURN 'hello' AS str\", %{}, metadata]}, 3))\n      <<0x0, 0x29, 0xB3, 0x10, 0xD0, 0x15, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x27, 0x68,\n      0x65, 0x6C, 0x6C, 0x6F, 0x27, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA0, 0xA1, 0x8A,\n      0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0xC9, 0x11, 0x94, 0x0, 0x0>>\n\n      # with params but without metadata\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:run, [\"RETURN $str AS str\", %{str: \"hello\"}]}, 3))\n      <<0x0, 0x22, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n      0x24, 0x73, 0x74, 0x72, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA1, 0x83,\n      0x73, 0x74, 0x72, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0xA0, 0x0, 0x0>>\n\n      # with params and metadata\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> alias Bolt.Sips.Metadata\n      iex> {:ok, metadata} = Metadata.new(%{tx_timeout: 4500})\n      {:ok,\n      %Bolt.Sips.Metadata{\n        bookmarks: nil,\n        metadata: nil,\n        tx_timeout: 4500\n      }}\n      iex> :erlang.iolist_to_binary(Encoder.encode({:run, [\"RETURN $str AS str\", %{str: \"hello\"}, metadata]}, 3))\n      <<0x0, 0x30, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n      0x24, 0x73, 0x74, 0x72, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA1, 0x83,\n      0x73, 0x74, 0x72, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0xA1, 0x8A, 0x74, 0x78,\n      0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0xC9, 0x11, 0x94, 0x0, 0x0>>\n\n   #   Encode  messages v1\n\n  # Supported messages\n\n  ## INIT\n  Usage: intialize the session.\n\n  Signature: `0x01`\n\n  Struct: `client_name` `auth_token`\n\n  with:\n\n  | data | type |\n  |-----|-----|\n  |client_name | string|\n  |auth_token | map: {scheme: string, principal: string, credentials: string}|\n\n  Examples (excluded from doctest because client_name changes at each bolt_sips version)\n\n      # without auth token\n      diex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      :erlang.iolist_to_binary(Encoder.encode({:init, []}, 1))\n      <<0x0, 0x10, 0xB2, 0x1, 0x8C, 0x42, 0x6F, 0x6C, 0x74, 0x65, 0x78, 0x2F, 0x30, 0x2E, 0x34,\n      0x2E, 0x30, 0xA0, 0x0, 0x0>>\n\n      # with auth token\n      # The auth token is tuple formated as: {user, password}\n      diex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      diex> :erlang.iolist_to_binary(Encoder.encode({:init, [{\"neo4j\", \"password\"}]}))\n      <<0x0, 0x42, 0xB2, 0x1, 0x8C, 0x42, 0x6F, 0x6C, 0x74, 0x65, 0x78, 0x2F, 0x30, 0x2E, 0x34,\n      0x2E, 0x30, 0xA3, 0x8B, 0x63, 0x72, 0x65, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x61, 0x6C, 0x73,\n      0x88, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x89, 0x70, 0x72, 0x69, 0x6E, 0x63,\n      0x69, 0x70, 0x61, 0x6C, 0x85, ...>>\n\n\n  ## RUN\n  Usage: pass statement for execution to the server.\n\n  Signature: `0x10`\n\n  Struct: `statement` `parameters`\n\n  with:\n\n  | data | type |\n  |-----|-----|\n  | statement | string |\n  | parameters | map |\n\n  Examples\n      # without parameters\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:run, [\"RETURN 1 AS num\"]}, 1))\n      <<0x0, 0x13, 0xB2, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31, 0x20, 0x41,\n      0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0x0, 0x0>>\n      # with parameters\n      iex> :erlang.iolist_to_binary(Encoder.encode({:run, [\"RETURN $num AS num\", %{num: 1}]}, 1))\n      <<0x0, 0x1C, 0xB2, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n      0x24, 0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83,\n      0x6E, 0x75, 0x6D, 0x1, 0x0, 0x0>>\n\n  ## ACK_FAILURE\n  Usage: Acknowledge a failure the server has sent.\n\n  Signature: `0x0E`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:ack_failure, []}, 1))\n      <<0x0, 0x2, 0xB0, 0xE, 0x0, 0x0>>\n\n  ## DISCARD_ALL\n  Uage: Discard all remaining items from the active result stream.\n\n  Signature: `0x2F`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:discard_all, []}, 1))\n      <<0x0, 0x2, 0xB0, 0x2F, 0x0, 0x0>>\n\n  ## PULL_ALL\n  Usage: Retrieve all remaining items from the active result stream.\n\n  Signature: `0x3F`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:pull_all, []}, 1))\n      <<0x0, 0x2, 0xB0, 0x3F, 0x0, 0x0>>\n\n  ## RESET\n  Usage: Return the current session to a \"clean\" state.\n\n  Signature: `0x0F`\n\n  Struct: no data\n\n  Example\n\n      iex> alias Bolt.Sips.Internals.PackStream.Message.Encoder\n      iex> :erlang.iolist_to_binary(Encoder.encode({:reset, []}, 1))\n      <<0x0, 0x2, 0xB0, 0xF, 0x0, 0x0>>\n\n\n  Check if the encoder for the given bolt version is capable of encoding the given message\n  If it is the case, the encoding function will be called\n  If not, fallback to previous bolt version\n\n  If encoding function is not present in any of the bolt  version, an error will be raised\n  \"\"\"\n\n  def encode(data, bolt_version)\n      when is_integer(bolt_version) and bolt_version > @last_bolt_version do\n    encode(data, @last_bolt_version)\n  end\n\n  def encode(_data, _bolt_version) do\n    {:error, :not_implemented}\n  end\n\n  defp do_encode(message_type, data, bolt_version) do\n    signature = signature(message_type)\n    encode_message(message_type, signature, data, bolt_version)\n  end\n\n  # Format the auth params for v1 to v2\n  @spec auth_params_v1({} | {String.t(), String.t()}) :: map()\n  defp auth_params_v1({}), do: %{}\n\n  defp auth_params_v1({username, password}) do\n    %{\n      scheme: \"basic\",\n      principal: username,\n      credentials: password\n    }\n  end\n\n  # Format the auth params\n  @spec auth_params({} | {String.t(), String.t()}) :: map()\n  defp auth_params({}), do: user_agent()\n\n  defp auth_params({username, password}) do\n    %{\n      scheme: \"basic\",\n      principal: username,\n      credentials: password\n    }\n    |> Map.merge(user_agent())\n  end\n\n  defp user_agent() do\n    %{user_agent: client_name()}\n  end\n\n  @doc \"\"\"\n  Perform the final message:\n  - add header\n  - manage chunk if necessary\n  - add end marker\n  \"\"\"\n  @spec encode_message(\n          Bolt.Sips.Internals.PackStream.Message.out_signature(),\n          integer(),\n          list(),\n          integer()\n        ) ::\n          [[Bolt.Sips.Internals.PackStream.Message.encoded()]]\n\n  def encode_message(message_type, signature, data, bolt_version) do\n    Bolt.Sips.Internals.Logger.log_message(:client, message_type, data)\n\n    encoded =\n      {signature, data}\n      |> Bolt.Sips.Internals.PackStream.encode(bolt_version)\n      |> generate_chunks([])\n\n    Bolt.Sips.Internals.Logger.log_message(:client, message_type, encoded, :hex)\n    encoded\n  end\n\n  @spec generate_chunks(Bolt.Sips.Internals.PackStream.value() | <<>>, list()) ::\n          [[Bolt.Sips.Internals.PackStream.Message.encoded()]]\n  defp generate_chunks(<<>>, chunks) do\n    [chunks, [@end_marker], []]\n  end\n\n  defp generate_chunks(data, chunks) do\n    data_size = :erlang.iolist_size(data)\n\n    case data_size > @max_chunk_size do\n      true ->\n        bindata = :erlang.iolist_to_binary(data)\n        <<chunk::binary-@max_chunk_size, rest::binary>> = bindata\n        new_chunk = format_chunk(chunk)\n        # [new_chunk, generate_chunks(rest,[])]\n        generate_chunks(rest, [chunks, new_chunk])\n\n      # generate_chunks(<<rest>>, [new_chunk, chunks])\n\n      _ ->\n        generate_chunks(<<>>, [chunks, format_chunk(data)])\n    end\n  end\n\n  @spec format_chunk(Bolt.Sips.Internals.PackStream.value()) ::\n          [Bolt.Sips.Internals.PackStream.Message.encoded()]\n  defp format_chunk(chunk) do\n    [<<:erlang.iolist_size(chunk)::16>>, chunk]\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder_v1.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1 do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Message.Signatures\n  alias Bolt.Sips.Internals.PackStream.Message.Encoder\n\n\n  @doc \"\"\"\n  Encode INIT message without auth token\n  \"\"\"\n  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::\n          Bolt.Sips.Internals.PackStream.Message.encoded() | {:error, :not_implemented}\n  def encode(data, bolt_version) do\n    Encoder.encode(data, bolt_version)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder_v2.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV2 do\n  def encode(_, _) do\n    {:error, :not_implemented}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/encoder_v3.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3 do\n  @moduledoc false\n  use Bolt.Sips.Internals.PackStream.Message.Signatures\n  alias Bolt.Sips.Internals.PackStream.Message.Encoder\n\n  @valid_signatures [\n    @begin_signature,\n    @commit_signature,\n    @discard_all_signature,\n    @goodbye_signature,\n    @hello_signature,\n    @pull_all_signature,\n    @reset_signature,\n    @rollback_signature,\n    @run_signature\n  ]\n\n\n  @doc \"\"\"\n  Return the valid signatures for bolt V1\n  \"\"\"\n  @spec valid_signatures() :: [integer()]\n  def valid_signatures() do\n    @valid_signatures\n  end\n\n\n  @doc \"\"\"\n  Encode HELLO message without auth token\n  \"\"\"\n  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::\n          Bolt.Sips.Internals.PackStream.Message.encoded()\n          | {:error, :not_implemented}\n          | {:error, :invalid_message}\n  def encode(data, bolt_version) do\n    Encoder.encode(data, bolt_version)\n  end\n\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message/signatures.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.Signatures do\n  @moduledoc false\n  defmacro __using__(_opts) do\n    quote do\n      # Message OUT\n      @ack_failure_signature 0x0E\n      @begin_signature 0x11\n      @commit_signature 0x12\n      @discard_all_signature 0x2F\n      @goodbye_signature 0x02\n      @hello_signature 0x01\n      @init_signature 0x01\n      @pull_all_signature 0x3F\n      @reset_signature 0x0F\n      @rollback_signature 0x13\n      @run_signature 0x10\n\n      # Message IN\n      @success_signature 0x70\n      @failure_signature 0x7F\n      @record_signature 0x71\n      @ignored_signature 0x7E\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/message.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message do\n  @moduledoc false\n\n  # Manage the message encoding and decoding.\n  #\n  # Message encoding / decoding is the first step of encoding / decoding.\n  # The next step is the message data encoding /decoding (which is handled by packstream.ex)\n\n  alias Bolt.Sips.Internals.PackStream.Message.Encoder\n  alias Bolt.Sips.Internals.PackStream.Message.Decoder\n\n  @type in_signature :: :failure | :ignored | :record | :success\n  @type out_signature ::\n          :ack_failure\n          | :begin\n          | :commit\n          | :discard_all\n          | :goodbye\n          | :hello\n          | :init\n          | :pull_all\n          | :reset\n          | :rollback\n          | :run\n  @type raw :: {out_signature, list()}\n  @type decoded :: {in_signature(), any()}\n  @type encoded :: <<_::16, _::_*8>>\n\n  @doc \"\"\"\n  Encode a message depending on its type\n  \"\"\"\n  @spec encode({Bolt.Sips.Internals.PackStream.Message.out_signature(), list()}, integer()) ::\n          Bolt.Sips.Internals.PackStream.Message.encoded()\n  def encode(message, bolt_version) do\n    Encoder.encode(message, bolt_version)\n  end\n\n  @doc \"\"\"\n  Decode a message\n  \"\"\"\n  @spec decode(Bolt.Sips.Internals.PackStream.Message.encoded(), integer()) ::\n          Bolt.Sips.Internals.PackStream.Message.decoded()\n  def decode(message, bolt_version) do\n    Decoder.decode(message, bolt_version)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/utils.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Utils do\n  alias Bolt.Sips.Internals.PackStream.Encoder\n  alias Bolt.Sips.Types.Duration\n  alias Bolt.Sips.Internals.PackStreamError\n\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MODULE__)\n\n      # catch all clause for encoding implementation\n      defp do_call_encode(data_type, data, original_version) do\n        raise PackStreamError,\n          data_type: data_type,\n          data: data,\n          bolt_version: original_version,\n          message: \"Encoding function not implemented for\"\n      end\n\n      @spec encode_list_data(list(), integer()) :: [any()]\n      defp encode_list_data(data, bolt_version) do\n        Enum.map(\n          data,\n          &Encoder.encode(&1, bolt_version)\n        )\n      end\n\n      @spec encode_kv(map(), integer()) :: binary()\n      defp encode_kv(map, bolt_version) do\n        Enum.reduce(map, <<>>, fn data, acc -> [acc, do_reduce_kv(data, bolt_version)] end)\n      end\n\n      @spec do_reduce_kv({atom(), any()}, integer()) :: [binary()]\n      defp do_reduce_kv({key, value}, bolt_version) do\n        [\n          Encoder.encode(\n            key,\n            bolt_version\n          ),\n          Encoder.encode(value, bolt_version)\n        ]\n      end\n\n      @spec day_time(Time.t()) :: integer()\n      defp day_time(time) do\n        Time.diff(time, ~T[00:00:00.000], :nanosecond)\n      end\n\n      @spec decompose_datetime(Calendar.naive_datetime()) :: [integer()]\n      defp decompose_datetime(%NaiveDateTime{} = datetime) do\n        datetime_micros = NaiveDateTime.diff(datetime, ~N[1970-01-01 00:00:00.000], :microsecond)\n\n        seconds = div(datetime_micros, 1_000_000)\n        nanoseconds = rem(datetime_micros, 1_000_000) * 1_000\n\n        [seconds, nanoseconds]\n      end\n\n      @spec compact_duration(Duration.t()) :: [integer()]\n      defp compact_duration(%Duration{} = duration) do\n        months = 12 * duration.years + duration.months\n        days = 7 * duration.weeks + duration.days\n        seconds = 3600 * duration.hours + 60 * duration.minutes + duration.seconds\n\n        [months, days, seconds, duration.nanoseconds]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/v1.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.V1 do\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MODULE__)\n\n      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()\n\n      @int8 -128..-17\n      @int16_low -32_768..-129\n      @int16_high 128..32_767\n      @int32_low -2_147_483_648..-32_769\n      @int32_high 32_768..2_147_483_647\n      @int64_low -9_223_372_036_854_775_808..-2_147_483_649\n      @int64_high 2_147_483_648..9_223_372_036_854_775_807\n      # Null\n      @null_marker 0xC0\n\n      # Boolean\n      @true_marker 0xC3\n      @false_marker 0xC2\n\n      # String\n      @tiny_bitstring_marker 0x8\n      @bitstring8_marker 0xD0\n      @bitstring16_marker 0xD1\n      @bitstring32_marker 0xD2\n\n      # Integer\n      @int8_marker 0xC8\n      @int16_marker 0xC9\n      @int32_marker 0xCA\n      @int64_marker 0xCB\n\n      # Float\n      @float_marker 0xC1\n\n      # List\n      @tiny_list_marker 0x9\n      @list8_marker 0xD4\n      @list16_marker 0xD5\n      @list32_marker 0xD6\n\n      # Map\n      @tiny_map_marker 0xA\n      @map8_marker 0xD8\n      @map16_marker 0xD9\n      @map32_marker 0xDA\n\n      # Structure\n      @tiny_struct_marker 0xB\n      @struct8_marker 0xDC\n      @struct16_marker 0xDD\n\n      @spec do_call_encode(atom(), any(), integer()) ::\n              binary() | PackStreamError.t()\n\n      # Atoms\n      defp do_call_encode(:atom, nil, bolt_version) when bolt_version <= @last_version do\n        <<@null_marker>>\n      end\n\n      defp do_call_encode(:atom, true, bolt_version) when bolt_version <= @last_version do\n        <<@true_marker>>\n      end\n\n      defp do_call_encode(:atom, false, bolt_version) when bolt_version <= @last_version do\n        <<@false_marker>>\n      end\n\n      defp do_call_encode(:atom, other, bolt_version) when bolt_version <= @last_version do\n        call_encode(:string, other |> Atom.to_string(), bolt_version)\n      end\n\n      # Strings\n      defp do_call_encode(:string, string, bolt_version)\n           when bolt_version <= @last_version and byte_size(string) <= 15 do\n        [<<@tiny_bitstring_marker::4, byte_size(string)::4>>, string]\n      end\n\n      defp do_call_encode(:string, string, bolt_version)\n           when bolt_version <= @last_version and byte_size(string) <= 255 do\n        [<<@bitstring8_marker, byte_size(string)::8>>, string]\n      end\n\n      defp do_call_encode(:string, string, bolt_version)\n           when bolt_version <= @last_version and byte_size(string) <= 65_535 do\n        [<<@bitstring16_marker, byte_size(string)::16>>, string]\n      end\n\n      defp do_call_encode(:string, string, bolt_version)\n           when bolt_version <= @last_version and byte_size(string) <= 4_294_967_295 do\n        [<<@bitstring32_marker, byte_size(string)::32>>, string]\n      end\n\n      # Integer\n      defp do_call_encode(:integer, integer, bolt_version)\n           when bolt_version <= @last_version and integer in -16..127 do\n        <<integer>>\n      end\n\n      defp do_call_encode(:integer, integer, bolt_version)\n           when bolt_version <= @last_version and integer in @int8 do\n        <<@int8_marker, integer>>\n      end\n\n      defp do_call_encode(:integer, integer, bolt_version)\n           when bolt_version <= @last_version and integer in @int16_low\n           when bolt_version <= @last_version and integer in @int16_high do\n        <<@int16_marker, integer::16>>\n      end\n\n      defp do_call_encode(:integer, integer, bolt_version)\n           when bolt_version <= @last_version and integer in @int32_low\n           when bolt_version <= @last_version and integer in @int32_high do\n        <<@int32_marker, integer::32>>\n      end\n\n      defp do_call_encode(:integer, integer, bolt_version)\n           when bolt_version <= @last_version and integer in @int64_low\n           when bolt_version <= @last_version and integer in @int64_high do\n        <<@int64_marker, integer::64>>\n      end\n\n      # Float\n\n      defp do_call_encode(:float, number, bolt_version) when bolt_version <= 3 do\n        <<@float_marker, number::float>>\n      end\n\n      # lists\n      defp do_call_encode(:list, list, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 15 do\n        [<<@tiny_list_marker::4, length(list)::4>>, encode_list_data(list, bolt_version)]\n      end\n\n      defp do_call_encode(:list, list, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 255 do\n        [<<@list8_marker, length(list)::8>>, encode_list_data(list, bolt_version)]\n      end\n\n      defp do_call_encode(:list, list, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 65_535 do\n        [<<@list16_marker, length(list)::16>>, encode_list_data(list, bolt_version)]\n      end\n\n      defp do_call_encode(:list, list, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 4_294_967_295 do\n        [<<@list32_marker, length(list)::32>>, encode_list_data(list, bolt_version)]\n      end\n\n      # maps\n      defp do_call_encode(:map, map, bolt_version)\n           when bolt_version <= @last_version and map_size(map) <= 15 do\n        [<<@tiny_map_marker::4, map_size(map)::4>>, encode_kv(map, bolt_version)]\n      end\n\n      defp do_call_encode(:map, map, bolt_version)\n           when bolt_version <= @last_version and map_size(map) <= 255 do\n        [<<@map8_marker, map_size(map)::8>>, encode_kv(map, bolt_version)]\n      end\n\n      defp do_call_encode(:map, map, bolt_version)\n           when bolt_version <= @last_version and map_size(map) <= 65_535 do\n        [<<@map16_marker, map_size(map)::16>>, encode_kv(map, bolt_version)]\n      end\n\n      defp do_call_encode(:map, map, bolt_version)\n           when bolt_version <= @last_version and map_size(map) <= 4_294_967_295 do\n        [<<@map32_marker, map_size(map)::32>>, encode_kv(map, bolt_version)]\n      end\n\n      # Structs\n      defp do_call_encode(:struct, {signature, list}, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 15 do\n        [\n          <<@tiny_struct_marker::4, length(list)::4, signature>>,\n          encode_list_data(list, bolt_version)\n        ]\n      end\n\n      defp do_call_encode(:struct, {signature, list}, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 255 do\n        [<<@struct8_marker::8, length(list)::8, signature>>, encode_list_data(list, bolt_version)]\n      end\n\n      defp do_call_encode(:struct, {signature, list}, bolt_version)\n           when bolt_version <= @last_version and length(list) <= 65_535 do\n        [\n          <<@struct16_marker::8, length(list)::16, signature>>,\n          encode_list_data(list, bolt_version)\n        ]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream/v2.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream.V2 do\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}\n  alias Bolt.Sips.Internals.PackStream.Encoder\n\n  defmacro __using__(_options) do\n    quote do\n      import unquote(__MODULE__)\n\n      @last_version Bolt.Sips.Internals.BoltVersionHelper.last()\n\n      # Local Time\n      @local_time_signature 0x74\n\n      # Time With TZ Offset\n      @time_with_tz_signature 0x54\n\n      # Date\n      @date_signature 0x44\n\n      # Local DateTime\n      @local_datetime_signature 0x64\n\n      # Datetime with TZ offset\n      @datetime_with_zone_offset_signature 0x46\n\n      # Datetime with TZ id\n      @datetime_with_zone_id_signature 0x66\n\n      # Duration\n      @duration_signature 0x45\n\n      # Point 2D\n      @point2d_signature 0x58\n\n      # Point 3D\n      @point3d_signature 0x59\n      defp do_call_encode(:local_time, local_time, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        Encoder.encode({@local_time_signature, [day_time(local_time)]}, bolt_version)\n      end\n\n      defp do_call_encode(\n             :time_with_tz,\n             %TimeWithTZOffset{time: time, timezone_offset: offset},\n             bolt_version\n           )\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        Encoder.encode({@time_with_tz_signature, [day_time(time), offset]}, bolt_version)\n      end\n\n      defp do_call_encode(:date, date, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        epoch = Date.diff(date, ~D[1970-01-01])\n\n        Encoder.encode({@date_signature, [epoch]}, bolt_version)\n      end\n\n      defp do_call_encode(:local_datetime, local_datetime, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        Encoder.encode(\n          {@local_datetime_signature, decompose_datetime(local_datetime)},\n          bolt_version\n        )\n      end\n\n      defp do_call_encode(:datetime_with_tz_id, datetime, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        data = decompose_datetime(DateTime.to_naive(datetime)) ++ [datetime.time_zone]\n\n        Encoder.encode({@datetime_with_zone_id_signature, data}, bolt_version)\n      end\n\n      defp do_call_encode(\n             :datetime_with_tz_offset,\n             %DateTimeWithTZOffset{naive_datetime: ndt, timezone_offset: tz_offset},\n             bolt_version\n           )\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        data = decompose_datetime(ndt) ++ [tz_offset]\n        Encoder.encode({@datetime_with_zone_offset_signature, data}, bolt_version)\n      end\n\n      defp do_call_encode(:duration, %Duration{} = duration, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        Encoder.encode({@duration_signature, compact_duration(duration)}, bolt_version)\n      end\n\n      defp do_call_encode(:point, %Point{z: nil} = point, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        Encoder.encode({@point2d_signature, [point.srid, point.x, point.y]}, bolt_version)\n      end\n\n      defp do_call_encode(:point, %Point{} = point, bolt_version)\n           when bolt_version >= 2 and bolt_version <= @last_version do\n        Encoder.encode(\n          {@point3d_signature, [point.srid, point.x, point.y, point.z]},\n          bolt_version\n        )\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/internals/pack_stream.ex",
    "content": "defmodule Bolt.Sips.Internals.PackStream do\n  @moduledoc false\n\n  # The PackStream implementation for Bolt.\n  #\n  # This module defines a decode function, that will take a binary stream of data\n  # and recursively turn it into a list of Elixir data types.\n  #\n  # It further defines a function for encoding Elixir data types into a binary\n  # stream, using the Bolt.Sips.Internals.PackStream.Encoder protocol.\n\n  @type value :: <<_::8, _::_*8>>\n\n  @doc \"\"\"\n  Encodes a list of items into their binary representation.\n\n  As developers tend to be lazy, single objects may be passed.\n\n  ## Examples\n\n      iex> Bolt.Sips.Internals.PackStream.encode \"hello world\"\n      <<0x8B, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64>>\n  \"\"\"\n  @spec encode(any(), integer()) :: Bolt.Sips.Internals.PackStream.value() | <<_::16, _::_*8>>\n  def encode(item, bolt_version) do\n    Bolt.Sips.Internals.PackStream.Encoder.encode(item, bolt_version)\n  end\n\n  @doc \"\"\"\n  Decode data from Bolt binary format to Elixir type\n\n  ## Example\n\n      iex> Bolt.Sips.Internals.PackStream.decode(<<0xC3>>)\n      [true]\n  \"\"\"\n  @spec decode(binary(), integer()) :: list()\n  def decode(data, bolt_version) do\n    Bolt.Sips.Internals.PackStream.Decoder.decode(data, bolt_version)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/metadata.ex",
    "content": "defmodule Bolt.Sips.Metadata do\n  @moduledoc false\n  defstruct [:bookmarks, :tx_timeout, :metadata]\n\n  @type t :: %__MODULE__{\n          bookmarks: [String.t()],\n          tx_timeout: non_neg_integer(),\n          metadata: map()\n        }\n\n  alias Bolt.Sips.Metadata\n\n  @doc \"\"\"\n  Create a new metadata structure.\n  Data must be valid.\n  \"\"\"\n  @spec new(map()) :: {:ok, Bolt.Sips.Metadata.t()} | {:error, String.t()}\n  def new(data) do\n    with {:ok, data} <- check_keys(data),\n         {:ok, bookmarks} <- validate_bookmarks(Map.get(data, :bookmarks, [])),\n         {:ok, tx_timeout} <- validate_timeout(Map.get(data, :tx_timeout)),\n         {:ok, metadata} <- validate_metadata(Map.get(data, :metadata, %{})) do\n      {:ok,\n       %__MODULE__{\n         bookmarks: bookmarks,\n         tx_timeout: tx_timeout,\n         metadata: metadata\n       }}\n    else\n      error -> error\n    end\n  end\n\n  @doc \"\"\"\n  Convert the Metadata struct to a map.\n  All `nil` will be stripped\n  \"\"\"\n  @spec to_map(Bolt.Sips.Metadata.t()) :: map()\n  def to_map(metadata) do\n    with {:ok, metadata} <- check_keys(Map.from_struct(metadata)) do\n      metadata\n      |> Map.from_struct()\n      |> Enum.filter(fn {_, value} -> value != nil end)\n      |> Enum.into(%{})\n    else\n      error -> error\n    end\n  end\n\n  defp check_keys(data) do\n    try do\n      {:ok, struct!(Metadata, data)}\n    rescue\n      _ in KeyError -> {:error, \"[Metadata] Invalid keys\"}\n    end\n  end\n\n  @spec validate_bookmarks(any()) :: {:ok, list()} | {:ok, nil} | {:error, String.t()}\n  defp validate_bookmarks(bookmarks)\n       when (is_list(bookmarks) and length(bookmarks) > 0) or is_nil(bookmarks) do\n    {:ok, bookmarks}\n  end\n\n  defp validate_bookmarks([]) do\n    {:ok, nil}\n  end\n\n  defp validate_bookmarks(_) do\n    {:error, \"[Metadata] Invalid bookmkarks. Should be a list.\"}\n  end\n\n  @spec validate_timeout(any()) :: {:ok, integer()} | {:error, String.t()}\n  defp validate_timeout(timeout) when (is_integer(timeout) and timeout > 0) or is_nil(timeout) do\n    {:ok, timeout}\n  end\n\n  defp validate_timeout(nil) do\n    {:ok, nil}\n  end\n\n  defp validate_timeout(_) do\n    {:error, \"[Metadata] Invalid timeout. Should be a positive integer.\"}\n  end\n\n  @spec validate_metadata(any()) :: {:ok, map()} | {:ok, nil} | {:error, String.t()}\n  defp validate_metadata(metadata)\n       when (is_map(metadata) and map_size(metadata) > 0) or is_nil(metadata) do\n    {:ok, metadata}\n  end\n\n  defp validate_metadata(%{}) do\n    {:ok, nil}\n  end\n\n  defp validate_metadata(_) do\n    {:error, \"[Metadata] Invalid timeout. Should be a valid map or nil.\"}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/protocol.ex",
    "content": "defmodule Bolt.Sips.Protocol do\n  @moduledoc false\n  # Implements callbacks required by DBConnection.\n  # Each callback receives an open connection as a state.\n\n  defmodule ConnData do\n    @moduledoc false\n    # Defines the state used by DbConnection implementation\n    defstruct [:sock, :bolt_version, :configuration]\n\n    @type t :: %__MODULE__{\n            sock: port(),\n            bolt_version: integer(),\n            configuration: Keyword.t()\n          }\n  end\n\n  use DBConnection\n\n  require Logger\n\n  alias Bolt.Sips.QueryStatement\n  alias Bolt.Sips.Internals.Error, as: BoltError\n  alias Bolt.Sips.Internals.BoltProtocol\n\n  @doc \"Callback for DBConnection.connect/1\"\n\n  def connect(opts \\\\ [])\n  def connect([]), do: connect(Bolt.Sips.Utils.default_config())\n\n  def connect(opts) do\n    conf = opts |> Bolt.Sips.Utils.default_config()\n    host = _to_hostname(conf[:hostname])\n    port = conf[:port]\n    auth = extract_auth(conf[:basic_auth])\n    timeout = conf[:timeout]\n    socket = conf[:socket]\n    default_socket_options = [packet: :raw, mode: :binary, active: false]\n\n    socket_opts =\n      case conf[:ssl] do\n        list when is_list(list) -> Keyword.merge(default_socket_options, conf[:ssl])\n        _ -> default_socket_options\n      end\n\n    with {:ok, sock} <- socket.connect(host, port, socket_opts, timeout),\n         {:ok, bolt_version} <- BoltProtocol.handshake(socket, sock),\n         {:ok, server_version} <- do_init(socket, sock, bolt_version, auth),\n         :ok <- socket.setopts(sock, active: :once) do\n      {:ok,\n       %ConnData{\n         sock: sock,\n         bolt_version: bolt_version,\n         configuration: Keyword.merge(conf, server_version: server_version)\n       }}\n    else\n      {:error, %BoltError{}} = error ->\n        error\n\n      {:error, reason} ->\n        {:error, BoltError.exception(reason, nil, :connect)}\n    end\n  end\n\n  defp do_init(transport, port, 3, auth) do\n    BoltProtocol.hello(transport, port, 3, auth)\n  end\n\n  defp do_init(transport, port, bolt_version, auth) do\n    BoltProtocol.init(transport, port, bolt_version, auth)\n  end\n\n  @doc \"Callback for DBConnection.checkout/1\"\n  def checkout(%ConnData{sock: sock, configuration: conf} = conn_data) do\n    case conf[:socket].setopts(sock, active: false) do\n      :ok -> {:ok, conn_data}\n      other -> other\n    end\n  end\n\n  @doc \"Callback for DBConnection.checkin/1\"\n  def checkin(%ConnData{sock: sock, configuration: conf} = conn_data) do\n    case conf[:socket].setopts(sock, active: :once) do\n      :ok -> {:ok, conn_data}\n      other -> other\n    end\n  end\n\n  def disconnect(_err, %ConnData{sock: sock, bolt_version: 3, configuration: conf} = conn_data) do\n    socket = conf[:socket]\n    :ok = BoltProtocol.goodbye(socket, sock, conn_data.bolt_version)\n    socket.close(sock)\n\n    :ok\n  end\n\n  @doc \"Callback for DBConnection.disconnect/1\"\n  def disconnect(_err, %ConnData{sock: sock, configuration: conf}) do\n    conf[:socket].close(sock)\n\n    :ok\n  end\n\n  @doc \"Callback for DBConnection.handle_begin/1\"\n  def handle_begin(_, %ConnData{sock: sock, bolt_version: 3, configuration: conf} = conn_data) do\n    {:ok, _} = BoltProtocol.begin(conf[:socket], sock, conn_data.bolt_version)\n    {:ok, :began, conn_data}\n  end\n\n  def handle_begin(_opts, conn_data) do\n    %QueryStatement{statement: \"BEGIN\"}\n    |> handle_execute(%{}, [], conn_data)\n\n    {:ok, :began, conn_data}\n  end\n\n  @doc \"Callback for DBConnection.handle_rollback/1\"\n  def handle_rollback(_, %ConnData{sock: sock, bolt_version: 3, configuration: conf} = conn_data) do\n    :ok = BoltProtocol.rollback(conf[:socket], sock, conn_data.bolt_version)\n    {:ok, :rolledback, conn_data}\n  end\n\n  def handle_rollback(_opts, conn_data) do\n    %QueryStatement{statement: \"ROLLBACK\"}\n    |> handle_execute(%{}, [], conn_data)\n\n    {:ok, :rolledback, conn_data}\n  end\n\n  @doc \"Callback for DBConnection.handle_commit/1\"\n  def handle_commit(_, %ConnData{sock: sock, bolt_version: 3, configuration: conf} = conn_data) do\n    {:ok, _} = BoltProtocol.commit(conf[:socket], sock, conn_data.bolt_version)\n    {:ok, :committed, conn_data}\n  end\n\n  def handle_commit(_opts, conn_data) do\n    %QueryStatement{statement: \"COMMIT\"}\n    |> handle_execute(%{}, [], conn_data)\n\n    {:ok, :committed, conn_data}\n  end\n\n  @doc \"Callback for DBConnection.handle_execute/1\"\n  def handle_execute(query, params, opts, conn_data) do\n    execute(query, params, opts, conn_data)\n  end\n\n  def handle_info(msg, state) do\n    Logger.error(fn ->\n      [inspect(__MODULE__), ?\\s, inspect(self()), \" received unexpected message: \" | inspect(msg)]\n    end)\n\n    {:ok, state}\n  end\n\n  ### Calming the warnings\n  # Callbacks for ...\n  def ping(state), do: {:ok, state}\n  def handle_prepare(query, _opts, state), do: {:ok, query, state}\n  def handle_close(query, _opts, state), do: {:ok, query, state}\n  def handle_deallocate(query, _cursor, _opts, state), do: {:ok, query, state}\n  def handle_declare(query, _params, _opts, state), do: {:ok, query, state, nil}\n  def handle_fetch(query, _cursor, _opts, state), do: {:cont, query, state}\n  def handle_status(_opts, state), do: {:idle, state}\n\n  defp extract_auth(nil), do: {}\n\n  defp extract_auth(basic_auth), do: {basic_auth[:username], basic_auth[:password]}\n\n  defp execute(q, params, _, conn_data) do\n    %QueryStatement{statement: statement} = q\n    %ConnData{sock: sock, bolt_version: bolt_version, configuration: conf} = conn_data\n    socket = conf |> Keyword.get(:socket)\n\n    case BoltProtocol.run_statement(socket, sock, bolt_version, statement, params) do\n      [{:success, _} | _] = data ->\n        {:ok, q, data, conn_data}\n\n      %BoltError{type: :cypher_error} = error ->\n        BoltProtocol.reset(socket, sock, bolt_version)\n        {:error, error, conn_data}\n\n      %BoltError{type: :connection_error} = error ->\n        {:disconnect, error, conn_data}\n\n      %BoltError{} = error ->\n        {:error, error, conn_data}\n    end\n  rescue\n    e ->\n      msg =\n        case e do\n          %Bolt.Sips.Internals.PackStreamError{} ->\n            \"unable to encode value: #{inspect(e.data)}\"\n\n          %BoltError{} ->\n            \"#{e.message}, type: #{e.type}\"\n\n          _ ->\n            e.message\n        end\n\n      {:error, %{code: :failure, message: msg}, conn_data}\n  end\n\n  defp _to_hostname(hostname) when is_binary(hostname), do: String.to_charlist(hostname)\n  defp _to_hostname(hostname) when is_list(hostname), do: hostname\n  defp _to_hostname(hostname), do: hostname\nend\n"
  },
  {
    "path": "lib/bolt_sips/query.ex",
    "content": "defmodule Bolt.Sips.Query do\n  @moduledoc \"\"\"\n  Provides a simple Query DSL.\n\n  You can run simple Cypher queries with or w/o parameters, for example:\n\n      {:ok, row} = Bolt.Sips.query(conn, \"match (n:Person {bolt_sips: true}) return n.name as Name limit 5\")\n      assert List.first(row)[\"Name\"] == \"Patrick Rothfuss\"\n\n  Or more complex ones:\n\n      cypher = \\\"\"\"\n      MATCH (p:Person {bolt_sips: true})\n      RETURN p, p.name AS name, upper(p.name) as NAME,\n             coalesce(p.nickname,\"n/a\") AS nickname,\n             { name: p.name, label:head(labels(p))} AS person\n      \\\"\"\"\n      {:ok, r} = Bolt.Sips.query(conn, cypher)\n\n  As you can see, you can organize your longer queries using the Elixir multiple line conventions, for readability.\n\n  And there is one more trick, you can use for more complex Cypher commands: use `;` as a transactional separator.\n\n  For example, say you want to clean up the test database **before** creating some tests entities. You can do that like this:\n\n      cypher = \\\"\"\"\n      MATCH (n {bolt_sips: true}) OPTIONAL MATCH (n)-[r]-() DELETE n,r;\n\n      CREATE (BoltSips:BoltSips {title:'Elixir sipping from Neo4j, using Bolt', released:2016, license:'MIT', bolt_sips: true})\n      CREATE (TNOTW:Book {title:'The Name of the Wind', released:2007, genre:'fantasy', bolt_sips: true})\n      CREATE (Patrick:Person {name:'Patrick Rothfuss', bolt_sips: true})\n      CREATE (Kvothe:Person {name:'Kote', bolt_sips: true})\n      CREATE (Denna:Person {name:'Denna', bolt_sips: true})\n      CREATE (Chandrian:Deamon {name:'Chandrian', bolt_sips: true})\n\n      CREATE\n        (Kvothe)-[:ACTED_IN {roles:['sword fighter', 'magician', 'musician']}]->(TNOTW),\n        (Denna)-[:ACTED_IN {roles:['many talents']}]->(TNOTW),\n        (Chandrian)-[:ACTED_IN {roles:['killer']}]->(TNOTW),\n        (Patrick)-[:WROTE]->(TNOTW)\n      \\\"\"\"\n      assert {:ok, _r} = Bolt.Sips.query(conn, cypher)\n\n  In the example above, this command: `MATCH (n {bolt_sips: true}) OPTIONAL MATCH (n)-[r]-() DELETE n,r;` will be executed in a distinct transaction, before all the other queries\n\n  See the various tests, or more examples and implementation details.\n\n  \"\"\"\n  alias Bolt.Sips.{QueryStatement, Response, Types, Error, Exception}\n\n  @cypher_seps ~r/;(.){0,1}\\n/\n\n  @spec query!(Bolt.Sips.conn(), String.t()) :: Response.t() | Exception.t()\n  def query!(conn, statement), do: query!(conn, statement, %{})\n\n  @spec query!(Bolt.Sips.conn(), String.t(), map, Keyword.t()) :: Response.t() | Exception.t()\n  def query!(conn, statement, params, opts \\\\ []) when is_map(params) do\n    with {:ok, r} <- query_commit(conn, statement, params, opts) do\n      r\n    else\n      {:error, msg} ->\n        raise Exception, message: msg\n\n      e ->\n        raise Exception, message: \"unexpected error: #{inspect(e)}\"\n    end\n  end\n\n  @spec query(Bolt.Sips.conn(), String.t()) :: {:error, Error.t()} | {:ok, Response.t()}\n  def query(conn, statement), do: query(conn, statement, %{})\n\n  @spec query(Bolt.Sips.conn(), String.t(), map, Keyword.t()) ::\n          {:error, Error.t()} | {:ok, Response.t()}\n  def query(conn, statement, params, opts \\\\ []) when is_map(params) do\n    case query_commit(conn, statement, params, opts) do\n      {:error, message} -> {:error, %Error{message: message}}\n      r -> r\n    end\n  rescue\n    e in Bolt.Sips.Exception ->\n      {:error, %Bolt.Sips.Error{code: e.code, message: e.message}}\n  end\n\n  ###########\n  # Private #\n  ###########\n  @spec query_commit(Bolt.Sips.conn(), String.t(), map, Keyword.t()) ::\n          {:error, Error.t()} | {:ok, Response.t()}\n  defp query_commit(conn, statement, params, opts) do\n    statements =\n      statement\n      |> String.split(@cypher_seps, trim: true)\n      |> Enum.map(&String.trim/1)\n      |> Enum.filter(&(String.length(&1) > 0))\n\n    formatted_params =\n      params\n      |> Enum.map(&format_param/1)\n      |> Enum.map(fn {k, {:ok, value}} -> {k, value} end)\n      |> Map.new()\n\n    errors =\n      formatted_params\n      |> Enum.filter(fn {_, formated} ->\n        case formated do\n          {:error, _} -> true\n          _ -> false\n        end\n      end)\n      |> Enum.map(fn {k, {:error, error}} -> {k, error} end)\n\n    {:ok, commit!(errors, conn, statements, formatted_params, opts)}\n  rescue\n    e in [RuntimeError, DBConnection.ConnectionError] ->\n      {:error, Bolt.Sips.Error.new(e.message)}\n\n    e in Exception ->\n      {:current_stacktrace, stacktrace} = Process.info(self(), :current_stacktrace)\n\n      # n/a in newer Elixir version:\n      # reraise e, __STACKTRACE__\n\n      # using a safe call, for backward compatibility\n      reraise e, stacktrace\n\n    e ->\n      {:error, e}\n  end\n\n  defp commit!([], conn, statements, formatted_params, opts),\n    do: tx!(conn, statements, formatted_params, opts)\n\n  defp commit!(errors, _conn, _statements, _formatted_params, _opts),\n    do: raise(Exception, message: \"Unable to format params: #{inspect(errors)}\")\n\n  defp tx!(conn, [statement], params, opts), do: hd(send!(conn, statement, params, opts))\n\n  # todo It returns [Response.t] !!!\n  defp tx!(conn, statements, params, opts) when is_list(statements),\n    do: Enum.reduce(statements, [], &send!(conn, &1, params, opts, &2))\n\n  defp send!(conn, statement, params, opts, acc \\\\ [])\n\n  defp send!(conn, statement, params, opts, acc) do\n    # Retrieve timeout defined in config\n    prefix = Keyword.get(opts, :prefix, :default)\n\n    conf_timeout =\n      Bolt.Sips.info()\n      |> Map.get(prefix)\n      |> Map.get(:user_options)\n      |> Keyword.get(:timeout)\n\n    opts = Keyword.put_new(opts, :timeout, Keyword.get(opts, :timeout, conf_timeout))\n\n    case DBConnection.execute(conn, %QueryStatement{statement: statement}, params, opts) do\n      {:ok, _query, resp} ->\n        case Response.transform(resp) do\n          {:ok, %Response{} = r} -> acc ++ [r]\n          {:error, e} -> raise DBConnection.ConnectionError, message: e\n        end\n\n      {:error, %Bolt.Sips.Internals.Error{code: code, message: msg}} ->\n        raise Exception, code: code, message: msg\n\n      {:error, %{message: message}} ->\n        raise DBConnection.ConnectionError, message: message\n    end\n  end\n\n  # Format the param to be used in query\n  # must return a tuple of the form: {:ok, param} or {:error, param}\n  # In order to let query_commit handle the error\n  @spec format_param({String.t(), any()}) :: {String.t(), {:ok | :error, any()}}\n  defp format_param({name, %Types.Duration{} = duration}),\n    do: {name, Types.Duration.format_param(duration)}\n\n  defp format_param({name, %Types.Point{} = point}), do: {name, Types.Point.format_param(point)}\n\n  defp format_param({name, value}), do: {name, {:ok, value}}\nend\n"
  },
  {
    "path": "lib/bolt_sips/query_statement.ex",
    "content": "defmodule Bolt.Sips.QueryStatement do\n  @moduledoc false\n  defstruct statement: \"\"\nend\n\ndefimpl DBConnection.Query, for: Bolt.Sips.QueryStatement do\n  def describe(query, _), do: query\n\n  def parse(query, _), do: query\n\n  def encode(_query, data, _), do: data\n\n  def decode(_, result, _), do: result\nend\n"
  },
  {
    "path": "lib/bolt_sips/response.ex",
    "content": "defmodule Bolt.Sips.Response do\n  @moduledoc \"\"\"\n  Support for transforming a Bolt response to a list of Bolt.Sips.Types or arbitrary values.\n  A Bolt.Sips.Response is used for mapping any response received from a Neo4j server into a an Elixir struct.\n\n  You'll interact with this module every time you're needing to get and manipulate the data resulting from your queries.\n\n  For example, a simple Cypher query like this:\n\n      Bolt.Sips.query(Bolt.Sips.conn(), \"RETURN [10,11,21] AS arr\")\n\n\n  will return:\n\n      {:ok,\n       %Bolt.Sips.Response{\n        bookmark: \"neo4j:bookmark:v1:tx21868\",\n        fields: [\"arr\"],\n        notifications: [],\n        plan: nil,\n        profile: nil,\n        records: [[[10, 11, 21]]],\n        results: [%{\"arr\" => [10, 11, 21]}],\n        stats: [],\n        type: \"r\"\n       }}\n\n  and while you have now access to the full data returned by Neo4j, most of the times you'll just want the results:\n\n      iex> %Bolt.Sips.Response{results: results} = Bolt.Sips.query!(Bolt.Sips.conn(), \"RETURN [10,11,21] AS arr\")\n      iex> results\n      [%{\"arr\" => [10, 11, 21]}]\n\n  More complex queries, i.e.:\n\n      MATCH p=({name:'Alice'})-[r:KNOWS]->({name:'Bob'}) RETURN r\n\n  will return `results` like this:\n\n      [%{\"r\" => %Bolt.Sips.Types.Relationship{end: 647, id: 495,\n       properties: %{}, start: 646, type: \"KNOWS\"}}]\n\n  Note: the `Path` has also functionality for \"drawing\" a graph, from a given node-relationship path\n\n  Our Bolt.Sips.Response is implementing Elixir's [Enumerable Protocol](https://hexdocs.pm/elixir/Enumerable.html), to help you accessing the results. Hence something like this, is possible:\n\n      iex> Bolt.Sips.query!(Bolt.Sips.conn(), \"RETURN [10,11,21] AS arr\") |>\n      ...> Enum.reduce(0, &(Enum.sum(&1[\"arr\"]) + &2))\n      42\n\n  an overly complicated example, but you get the point?! :)\n\n  You can also quickly get the `first` of the results, returned by Neo4j. Example:\n\n      iex> Bolt.Sips.query!(Bolt.Sips.conn(), \"UNWIND range(1, 10) AS n RETURN n\") |>\n      ...> Bolt.Sips.Response.first()\n      %{\"n\" => 1}\n  \"\"\"\n\n  @type t :: %__MODULE__{\n          results: list,\n          fields: list,\n          records: list,\n          plan: map,\n          notifications: list,\n          stats: list | map,\n          profile: any,\n          type: String.t(),\n          bookmark: String.t()\n        }\n\n  @type key :: any\n  @type value :: any\n  @type acc :: any\n  @type element :: any\n\n  defstruct results: [],\n            fields: nil,\n            records: [],\n            plan: nil,\n            notifications: [],\n            stats: [],\n            profile: nil,\n            type: nil,\n            bookmark: nil\n\n  alias Bolt.Sips.Error\n\n  require Logger\n  require Integer\n\n  def first(%__MODULE__{results: []}), do: nil\n  def first(%__MODULE__{results: [head | _tail]}), do: head\n\n  @doc false\n  # transform a raw Bolt response to a list of Responses\n  def transform!(records, stats \\\\ :no) do\n    with {:ok, %__MODULE__{} = records} <- transform(records, stats) do\n      records\n    else\n      e -> e\n    end\n  end\n\n  def transform(records, _stats \\\\ :no) do\n    # records |> IO.inspect(label: \"raw>> lib/bolt_sips/response.ex:44\")\n\n    with %__MODULE__{fields: fields, records: records} = response <- parse(records) do\n      {:ok, %__MODULE__{response | results: create_results(fields, records)}}\n    else\n      e -> {:error, e}\n    end\n  rescue\n    e in Bolt.Sips.Exception -> {:error, e.message}\n    e -> {:error, e}\n  end\n\n  def fetch(%Bolt.Sips.Response{fields: fields, results: results}, key) do\n    with true <- Enum.member?(fields, key) do\n      findings =\n        Enum.map(results, fn map ->\n          Map.get(map, key)\n        end)\n        |> Enum.filter(&(&1 != nil))\n\n      {:ok, findings}\n    else\n      _ -> nil\n    end\n  end\n\n  def fetch!(%Bolt.Sips.Response{} = r, key) do\n    with {:ok, findings} <- fetch(r, key) do\n      findings\n    end\n  end\n\n  defp parse(records) do\n    with {err_type, error} when err_type in ~w(halt error failure)a <- Error.new(records) do\n      {:error, error}\n    else\n      records ->\n        response =\n          records\n          |> Enum.reduce(%__MODULE__{}, fn {type, record}, acc ->\n            with {:error, e} <- parse_record(type, record, acc) do\n              raise Bolt.Sips.Exception, e\n            else\n              acc -> acc\n            end\n          end)\n\n        %__MODULE__{response | records: response.records |> :lists.reverse()}\n    end\n  end\n\n  # defp parse_record(:success, %{\"fields\" => fields, \"t_first\" => 0}, response) do\n  defp parse_record(:success, %{\"fields\" => fields}, response) do\n    %{response | fields: fields}\n  end\n\n  defp parse_record(:success, %{\"profile\" => profile, \"type\" => type} = record, response) do\n    %{response | profile: profile, stats: Map.get(record, \"stats\", []), type: type}\n  end\n\n  defp parse_record(:success, %{\"notifications\" => n, \"plan\" => plan, \"type\" => type}, response) do\n    %{response | plan: plan, notifications: n, type: type}\n  end\n\n  defp parse_record(:success, %{\"plan\" => plan, \"type\" => type}, response) do\n    %{response | plan: plan, type: type}\n  end\n\n  defp parse_record(:success, %{\"stats\" => stats, \"type\" => type}, response) do\n    %{response | stats: stats, type: type}\n  end\n\n  defp parse_record(:success, %{\"bookmark\" => bookmark, \"type\" => type}, response) do\n    %{response | bookmark: bookmark, type: type}\n  end\n\n  defp parse_record(:success, %{\"type\" => type}, response) do\n    %{response | type: type}\n  end\n\n  defp parse_record(:success, %{}, response) do\n    %{response | type: \"boltkit?\"}\n  end\n\n  defp parse_record(:success, record, _response) do\n    line =\n      \"; around: #{String.replace_leading(\"#{__ENV__.file}\", \"#{File.cwd!()}\", \"\") |> Path.relative()}:#{__ENV__.line()}\"\n\n    err_msg = \"UNKNOWN success type: \" <> inspect(record) <> line\n    Logger.error(err_msg)\n    {:error, Bolt.Sips.Error.new(err_msg)}\n  end\n\n  # defp parse_record(:record, %{\"bookmark\" => \"neo4j:bookmark:v1:tx14519\", \"t_last\" => 1, \"type\" => \"r\"}, response) do\n\n  defp parse_record(:record, record, response) do\n    %{response | records: [record | response.records]}\n  end\n\n  defp parse_record(_type, record, _response) do\n    line =\n      \"; around: #{String.replace_leading(\"#{__ENV__.file}\", \"#{File.cwd!()}\", \"\") |> Path.relative()}:#{__ENV__.line()}\"\n\n    err_msg = \"UNKNOWN `:record`: \" <> inspect(record) <> line\n    Logger.error(err_msg)\n    {:error, Bolt.Sips.Error.new(err_msg)}\n  end\n\n  defp create_results(fields, records) do\n    records\n    |> Enum.map(fn recs -> Enum.zip(fields, recs) end)\n    |> Enum.map(fn data -> Enum.into(data, %{}) end)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/response_encoder/json/jason.ex",
    "content": "if Code.ensure_loaded?(Jason) do\n  defmodule Bolt.Sips.ResponseEncoder.Json.Jason do\n    @moduledoc \"\"\"\n    A default implementation for Jason encoding library.\n\n    More info about Jason: [https://hex.pm/packages/jason](https://hex.pm/packages/jason)\n\n    Allow this usage:\n    ```\n    conn = Bolt.Sips.conn()\n    {:ok, res} = Bolt.Sips.query(conn, \"MATCH (t:TestNode) RETURN t\")\n    Jason.encode!(res)\n    ```\n\n    Default implementation can be overriden by providing your own implementation.\n\n    More info about implementation: [https://hexdocs.pm/jason/readme.html#differences-to-poison](https://hexdocs.pm/jason/readme.html#differences-to-poison)\n\n    #### Note:\n    In order to benefit from Bolt.Sips.ResponseEncoder implementation, use\n    `Bolt.Sips.ResponseEncoder.Json.encode` and pass the result to the Jason\n    encoding functions.\n    \"\"\"\n    alias Bolt.Sips.Types\n    alias Bolt.Sips.ResponseEncoder.Json\n\n    defimpl Jason.Encoder, for: [Types.Node, Types.Relationship, Types.Path, Types.Point] do\n      @spec encode(struct(), Jason.Encode.opts()) :: iodata()\n      def encode(data, opts) do\n        data\n        |> Json.encode()\n        |> Jason.Encode.map(opts)\n      end\n    end\n\n    defimpl Jason.Encoder, for: [Types.DateTimeWithTZOffset, Types.TimeWithTZOffset, Types.Duration] do\n      @spec encode(struct(), Jason.Encode.opts()) :: iodata()\n      def encode(data, opts) do\n        data\n        |> Json.encode()\n        |> Jason.Encode.string(opts)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/response_encoder/json/poison.ex",
    "content": "if Code.ensure_loaded?(Poison) do\n  defmodule Bolt.Sips.ResponseEncoder.Json.Poison do\n    @moduledoc \"\"\"\n    A default implementation for Poison encoding library.\n    More info about poison here: [https://hex.pm/packages/poison](https://hex.pm/packages/poison)\n\n    Allow this usage:\n    ```\n    conn = Bolt.Sips.conn()\n    {:ok, res} = Bolt.Sips.query(conn, \"MATCH (t:TestNode) RETURN t\")\n    Poison.encode!(res)\n    ```\n\n    Default implementation can be overriden by providing your own implementation.\n\n    More info about implementation: [https://hexdocs.pm/poison/Poison.html#module-encoder](https://hexdocs.pm/poison/Poison.html#module-encoder)\n\n    #### Note:\n    In order to benefit from Bolt.Sips.ResponseEncoder implementation, use\n    `Bolt.Sips.ResponseEncoder.Json.encode` and pass the result to the Poison\n    encoding functions.\n    \"\"\"\n    alias Bolt.Sips.Types\n    alias Bolt.Sips.ResponseEncoder.Json\n\n    defimpl Poison.Encoder, for: [Types.Node, Types.Relationship, Types.Path, Types.Point] do\n      @spec encode(struct(), Poison.Encoder.options()) :: iodata\n\n      def encode(data, opts) do\n        data\n        |> Json.encode()\n        |> Poison.Encoder.Map.encode(opts)\n      end\n    end\n\n    defimpl Poison.Encoder,\n      for: [Types.DateTimeWithTZOffset, Types.TimeWithTZOffset, Types.Duration] do\n      @spec encode(struct(), Poison.Encoder.options()) :: iodata\n\n      def encode(data, opts) do\n        data\n        |> Json.encode()\n        |> Poison.Encoder.BitString.encode(opts)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/response_encoder/json.ex",
    "content": "defprotocol Bolt.Sips.ResponseEncoder.Json do\n  @moduledoc \"\"\"\n  Protocol controlling how a value is made jsonable.\n\n  Its only purpose is to convert Bolt Sips specific structures into elixir buit-in types\n  which can be encoed in json by Jason.\n\n  ## Deriving\n  If the provided default implementation don't fit your need, you can override with your own\n  implementation.\n\n  ### Example\n  Let's assume that you don't want Node's id available as they are Neo4j's ones and are not\n  reliable because of id reuse and you want to have you own `uuid` in place.\n  Instead of:\n  ```\n    {\n      id: 0,\n      labels: [\"TestNode\"],\n      properties: {\n        uuid: \"837806a7-6c37-4630-9f6c-9aa7ad0129ed\"\n        value: \"my node\"\n      }\n    }\n  ```\n  you want:\n  ```\n    {\n      uuid: \"837806a7-6c37-4630-9f6c-9aa7ad0129ed\",\n      labels: [\"TestNode\"],\n      properties: {\n        value: \"my node\"\n      }\n    }\n  ```\n\n  You can achieve that with the following implementation:\n  ```\n  defimpl Bolt.Sips.ResponseEncoder.Json, for: Bolt.Sips.Types.Node do\n    def encode(node) do\n      new_props = Map.drop(node.properties, :uuid)\n\n      node\n      |> Map.from_struct()\n      |> Map.put(:uuid, node.properties.uuid)\n      |> Map.put(:properties, new_props)\n    end\n  end\n  ```\n\n  It is also possible to provide implementation that returns structs or updated Bolt.Sips.Types,\n  the use of a final `Bolt.Sips.ResponseEncoder.Json.encode()` will ensure that these values will\n  be converted to jsonable ones.\n  \"\"\"\n  @fallback_to_any true\n\n  @doc \"\"\"\n  Convert a value in a jsonable format\n  \"\"\"\n  @spec encode(any()) :: any()\n  def encode(value)\nend\n\nalias Bolt.Sips.{Types, ResponseEncoder}\n\ndefimpl ResponseEncoder.Json, for: Types.DateTimeWithTZOffset do\n  @spec encode(Types.DateTimeWithTZOffset.t()) :: String.t()\n  def encode(value) do\n    {:ok, dt} = Types.DateTimeWithTZOffset.format_param(value)\n    ResponseEncoder.Json.encode(dt)\n  end\nend\n\ndefimpl ResponseEncoder.Json, for: Types.TimeWithTZOffset do\n  @spec encode(Types.TimeWithTZOffset.t()) :: String.t()\n  def encode(struct) do\n    {:ok, t} = Types.TimeWithTZOffset.format_param(struct)\n    ResponseEncoder.Json.encode(t)\n  end\nend\n\ndefimpl ResponseEncoder.Json, for: Types.Duration do\n  @spec encode(Types.Duration.t()) :: String.t()\n  def encode(struct) do\n    {:ok, d} = Types.Duration.format_param(struct)\n    ResponseEncoder.Json.encode(d)\n  end\nend\n\ndefimpl ResponseEncoder.Json, for: Types.Point do\n  @spec encode(Types.Point.t()) :: map()\n  def encode(struct) do\n    {:ok, pt} = Types.Point.format_param(struct)\n    ResponseEncoder.Json.encode(pt)\n  end\nend\n\ndefimpl ResponseEncoder.Json,\n  for: [Types.Node, Types.Relationship, Types.UnboundRelationship, Types.Path] do\n  @spec encode(struct()) :: map()\n  def encode(value) do\n    value\n    |> Map.from_struct()\n    |> ResponseEncoder.Json.encode()\n  end\nend\n\ndefimpl ResponseEncoder.Json, for: Any do\n  @spec encode(any()) :: any()\n  def encode(value) when is_list(value) do\n    value\n    |> Enum.map(&ResponseEncoder.Json.encode/1)\n  end\n\n  def encode(%{__struct__: _} = value) do\n    value\n    |> Map.from_struct()\n    |> ResponseEncoder.Json.encode()\n  end\n\n  def encode(value) when is_map(value) do\n    value\n    |> Enum.into(%{}, fn {k, val} -> {k, ResponseEncoder.Json.encode(val)} end)\n  end\n\n  def encode(value) do\n    value\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/response_encoder.ex",
    "content": "defmodule Bolt.Sips.ResponseEncoder do\n  @moduledoc \"\"\"\n  This module provides functions to encode a query result or data containing Bolt.Sips.Types\n  into various format.\n\n  For now, only JSON is supported.\n\n  Encoding is  handled by protocols to allow override if a  specific implemention is required.\n  See targeted protocol documentation for more information\n\n  \"\"\"\n\n  @doc \"\"\"\n  Encode the data in json format.\n\n  This is done is 2 steps:\n  - first, the data is converted into a jsonable format\n  - the result is encoded in json via Jason\n\n  Both of these steps are overridable, see:\n  - for step 1: `Bolt.Sips.ResponseEncoder.Json`\n  - for step 2 (depending of your preferred library):\n  - `Bolt.Sips.ResponseEncoder.Json.Jason`\n  - `Bolt.Sips.ResponseEncoder.Json.Poison`\n\n  ## Example\n\n      iex> data = %{\"t1\" => %Bolt.Sips.Types.Node{\n      ...>     id: 69,\n      ...>     labels: [\"Test\"],\n      ...>     properties: %{\n      ...>       \"created\" => %Bolt.Sips.Types.DateTimeWithTZOffset{\n      ...>         naive_datetime: ~N[2016-05-24 13:26:08.543],\n      ...>         timezone_offset: 7200\n      ...>       },\n      ...>       \"uuid\" => 12345\n      ...>     }\n      ...>   }\n      ...> }\n      iex> Bolt.Sips.ResponseEncoder.encode(data, :json)\n      {:ok, ~S|{\"t1\":{\"id\":69,\"labels\":[\"Test\"],\"properties\":{\"created\":\"2016-05-24T13:26:08.543+02:00\",\"uuid\":12345}}}|}\n\n      iex> Bolt.Sips.ResponseEncoder.encode(\"\\\\xFF\", :json)\n      {:error, %Jason.EncodeError{message: \"invalid byte 0xFF in <<255>>\"}}\n  \"\"\"\n  @spec encode(any(), :json) ::\n          {:ok, String.t()} | {:error, Jason.EncodeError.t() | Exception.t()}\n  def encode(response, :json) do\n    response\n    |> jsonable_response()\n    |> Jason.encode()\n  end\n\n  @doc \"\"\"\n  Encode the data in json format.\n\n  Similar to `encode/1` except it will unwrap the error tuple and raise in case of errors.\n\n  ## Example\n\n      iex> data = %{\"t1\" => %Bolt.Sips.Types.Node{\n      ...>     id: 69,\n      ...>     labels: [\"Test\"],\n      ...>     properties: %{\n      ...>       \"created\" => %Bolt.Sips.Types.DateTimeWithTZOffset{\n      ...>         naive_datetime: ~N[2016-05-24 13:26:08.543],\n      ...>         timezone_offset: 7200\n      ...>       },\n      ...>       \"uuid\" => 12345\n      ...>     }\n      ...>   }\n      ...> }\n      iex> Bolt.Sips.ResponseEncoder.encode!(data, :json)\n      ~S|{\"t1\":{\"id\":69,\"labels\":[\"Test\"],\"properties\":{\"created\":\"2016-05-24T13:26:08.543+02:00\",\"uuid\":12345}}}|\n\n      iex> Bolt.Sips.ResponseEncoder.encode!(\"\\\\xFF\", :json)\n      ** (Jason.EncodeError) invalid byte 0xFF in <<255>>\n  \"\"\"\n  @spec encode!(any(), :json) :: String.t() | no_return()\n  def encode!(response, :json) do\n    response\n    |> jsonable_response()\n    |> Jason.encode!()\n  end\n\n  defp jsonable_response(response) do\n    response\n    |> Bolt.Sips.ResponseEncoder.Json.encode()\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/router.ex",
    "content": "defmodule Bolt.Sips.Router do\n  @moduledoc \"\"\"\n  This \"driver\" works in tandem with Neo4j's [Causal Clustering](https://neo4j.com/docs/operations-manual/current/clustering/>) feature by directing read and write behaviour to appropriate cluster members\n  \"\"\"\n  use GenServer\n  require Logger\n\n  alias Bolt.Sips.Routing.RoutingTable\n  alias Bolt.Sips.{Protocol, ConnectionSupervisor, LoadBalancer, Response, Error}\n\n  defmodule State do\n    @moduledoc \"\"\"\n    todo:\n    this is work in progress and will be used for defining the state of the Router (Gen)Server\n    \"\"\"\n    @type role :: atom\n\n    @type t :: %__MODULE__{\n            connections: %{\n              role => %{String.t() => non_neg_integer},\n              updated_at: non_neg_integer,\n              ttl: non_neg_integer\n            }\n          }\n\n    @enforce_keys [:connections]\n    defstruct @enforce_keys\n  end\n\n  @no_routing nil\n  @routing_table_keys [:read, :write, :route, :updated_at, :ttl, :error]\n\n  def configure(opts), do: GenServer.call(__MODULE__, {:configure, opts})\n\n  def get_connection(role, prefix \\\\ :direct)\n\n  def get_connection(role, prefix),\n    do: GenServer.call(__MODULE__, {:get_connection, role, prefix})\n\n  def terminate_connections(role, prefix \\\\ :default)\n\n  def terminate_connections(role, prefix),\n    do: GenServer.call(__MODULE__, {:terminate_connections, role, prefix})\n\n  def info(), do: GenServer.call(__MODULE__, :info)\n  def routing_table(prefix), do: GenServer.call(__MODULE__, {:routing_table_info, prefix})\n\n  @spec start_link(Keyword.t()) :: :ignore | {:error, Keyword.t()} | {:ok, pid()}\n  def start_link(init_args) do\n    GenServer.start_link(__MODULE__, init_args, name: __MODULE__)\n  end\n\n  @impl true\n  @spec init(Keyword.t()) :: {:ok, State.t(), {:continue, :post_init}}\n  def init(options) do\n    {:ok, options, {:continue, :post_init}}\n  end\n\n  @impl true\n  def handle_call({:configure, opts}, _from, state) do\n    prefix = Keyword.get(opts, :prefix, :default)\n    %{connections: current_connections} = Map.get(state, prefix, %{connections: %{}})\n\n    %{user_options: user_options, connections: connections} =\n      try do\n        opts\n        |> _configure()\n        |> Map.get(prefix)\n      rescue\n        e in Bolt.Sips.Exception ->\n          %{user_options: opts, connections: %{error: e.message}}\n      end\n\n    updated_connections = Map.merge(current_connections, connections)\n\n    new_state =\n      Map.put(state, prefix, %{user_options: user_options, connections: updated_connections})\n\n    {:reply, new_state, new_state}\n  end\n\n  # getting connections for role in [:route, :read, :write]\n  @impl true\n  def handle_call({:get_connection, role, prefix}, _from, state)\n      when role in [:route, :read, :write] do\n    with %{connections: connections} <- Map.get(state, prefix),\n         {:ok, conn, updated_connections} <- _get_connection(role, connections, prefix) do\n      {:reply, {:ok, conn}, put_in(state, [prefix, :connections], updated_connections)}\n    else\n      e ->\n        err_msg = error_no_connection_available_for_role(role, e, prefix)\n        {:reply, {:error, err_msg}, state}\n    end\n  end\n\n  # getting connections for any user defined roles, or: `:direct`\n  @impl true\n  def handle_call({:get_connection, role, prefix}, _from, state) do\n    with %{connections: connections} <- Map.get(state, prefix),\n         true <- Map.has_key?(connections, role),\n         [url | _none] <- connections |> Map.get(role) |> Map.keys(),\n         {:ok, pid} <- ConnectionSupervisor.find_connection(role, url, prefix) do\n      {:reply, {:ok, pid}, state}\n    else\n      e ->\n        err_msg = error_no_connection_available_for_role(role, e, prefix)\n        {:reply, {:error, err_msg}, state}\n    end\n  end\n\n  @impl true\n  def handle_call({:terminate_connections, role, prefix}, _from, state) do\n    %{connections: connections} = Map.get(state, prefix, %{})\n\n    with true <- Map.has_key?(connections, role),\n         :ok <-\n           connections\n           |> Map.get(role)\n           |> Map.keys()\n           |> Enum.each(&ConnectionSupervisor.terminate_connection(role, &1, prefix)) do\n      new_connections = Map.delete(connections, role)\n      new_state = put_in(state, [prefix, :connections], new_connections)\n      {:reply, :ok, new_state}\n    else\n      _e ->\n        {:reply, {:error, :not_found}, state}\n    end\n  end\n\n  @impl true\n  def handle_call(:info, _from, state), do: {:reply, state, state}\n\n  def handle_call({:routing_table_info, prefix}, _from, state) do\n    routing_table =\n      with connections when not is_nil(connections) <- get_in(state, [prefix, :connections]) do\n        Map.take(connections, @routing_table_keys)\n      end\n\n    {:reply, routing_table, state}\n  end\n\n  @impl true\n  @spec handle_continue(:post_init, Keyword.t()) :: {:noreply, map}\n  def handle_continue(:post_init, opts), do: {:noreply, _configure(opts)}\n\n  defp _configure(opts) do\n    options = Bolt.Sips.Utils.default_config(opts)\n\n    prefix = Keyword.get(options, :prefix, :default)\n\n    ssl_or_sock = if(Keyword.get(options, :ssl), do: :ssl, else: Keyword.get(options, :socket))\n\n    user_options = Keyword.put(options, :socket, ssl_or_sock)\n    with_routing? = Keyword.get(user_options, :schema, \"bolt\") =~ ~r/(^bolt\\+routing$)/i\n\n    with {:ok, routing_table} <- get_routing_table(user_options, with_routing?),\n         {:ok, connections} <- start_connections(user_options, routing_table) do\n      connections = Map.put(connections, :routing_query, routing_table[:routing_query])\n\n      %{prefix => %{user_options: user_options, connections: connections}}\n    else\n      {:error, msg} ->\n        Logger.error(\"cannot load the routing table. Error: #{msg}\")\n        %{prefix => %{user_options: user_options, connections: %{error: \"Not a router\"}}}\n    end\n  end\n\n  defp get_routing_table(\n         %{routing_query: %{params: props, query: query}} = connections,\n         _,\n         prefix\n       ) do\n    with {:ok, conn, updated_connections} <- _get_connection(:route, connections, prefix),\n         {:ok, %Response{} = results} <- Bolt.Sips.query(conn, query, props) do\n      {:ok, Response.first(results), updated_connections}\n    else\n      {:error, %Error{code: code, message: message}} ->\n        err_msg = \"#{code}; #{message}\"\n        Logger.error(err_msg)\n        {:error, err_msg}\n\n      {:error, msg, _updated_connections} ->\n        Logger.error(msg)\n        {:error, :routing_table_not_available}\n\n      e ->\n        Logger.error(\"get_routing_table error: #{inspect(e)}\")\n        {:error, :routing_table_not_available_at_all}\n    end\n  end\n\n  defp get_routing_table(_opts, false), do: {:ok, @no_routing}\n\n  defp get_routing_table(opts, _) do\n    prefix = Keyword.get(opts, :prefix, :default)\n\n    with {:ok, %Protocol.ConnData{configuration: configuration}} <- Protocol.connect(opts),\n         # DON'T>  :ok <- Protocol.disconnect(:stop, conn),\n         {_long, short} <- parse_server_version(configuration[:server_version]) do\n      {query, params} =\n        if Version.match?(short, \">= 3.2.3\") do\n          props = Keyword.get(opts, :routing_context, %{})\n          {\"CALL dbms.cluster.routing.getRoutingTable($context)\", %{context: props}}\n        else\n          {\"CALL dbms.cluster.routing.getServers()\", %{}}\n        end\n\n      with {:ok, pid} <- DBConnection.start_link(Protocol, Keyword.delete(opts, :name)),\n           {:ok, %Response{} = results} <- Bolt.Sips.query(pid, query, params),\n           true <- Process.exit(pid, :normal) do\n        table =\n          results\n          |> Response.first()\n          |> Map.put(:routing_query, %{query: query, params: params})\n\n        ttl = Map.get(table, :ttl, 300) * 1000\n        # may overwrite the ttl, when desired in exceptional situations: tests, for example.\n        ttl = Keyword.get(opts, :ttl, ttl)\n\n        Process.send_after(self(), {:refresh, prefix}, ttl)\n\n        {:ok, table}\n      else\n        {:error, %Error{message: message}} ->\n          Logger.error(message)\n          {:error, message}\n\n        _e ->\n          \"Are you sure you're connected to a Neo4j cluster? The routing table, is not available.\"\n          |> Logger.error()\n\n          {:error, :routing_table_not_available}\n      end\n    end\n  end\n\n  @doc \"\"\"\n  start a new (DB)Connection process, supervised registered under a name following this convention:\n\n  - \"role@hostname:port\", the `role`, `hostname` and the `port` are collected from the user's\n   configuration: `opts`. The `role` parameter is ignored when the `routing_table` parameter represents\n   a neo4j map containing the definition for a neo4j cluster! It defaults to: `:direct`, when not specified!\n  \"\"\"\n  def start_connections(opts, routing_table)\n\n  def start_connections(opts, routing_table) when is_nil(routing_table) do\n    url = \"#{opts[:hostname]}:#{opts[:port]}\"\n    role = Keyword.get(opts, :role, :direct)\n\n    with {:ok, _pid} <- ConnectionSupervisor.start_child(role, url, opts) do\n      {:ok, %{role => %{url => 0}}}\n    end\n  end\n\n  def start_connections(opts, routing_table) do\n    connections =\n      with %Bolt.Sips.Routing.RoutingTable{roles: roles} = rt <- RoutingTable.parse(routing_table) do\n        roles\n        |> Enum.reduce(%{}, fn {role, addresses}, acc ->\n          addresses\n          |> Enum.reduce(acc, fn {address, count}, acc ->\n            # interim hack; force the schema to be `bolt`, otherwise the parse is not happening\n            url = \"bolt://\" <> address\n            %URI{host: host, port: port} = URI.parse(url)\n\n            # Important!\n            # We remove the url from the routing-specific configs, because the port and the address where the\n            # socket will be opened, is using the host and the port returned by the routing table, and not by the\n            # initial url param. The Utils will overwrite them if the `url` is defined!\n            config =\n              opts\n              |> Keyword.put(:host, String.to_charlist(host))\n              |> Keyword.put(:port, port)\n              |> Keyword.put(:name, role)\n              |> Keyword.put(:hits, count)\n              |> Keyword.delete(:url)\n\n            with {:ok, _pid} <- ConnectionSupervisor.start_child(role, address, config) do\n              Map.update(acc, role, %{address => 0}, fn urls -> Map.put(urls, address, 0) end)\n            else\n              _ -> acc\n            end\n          end)\n          |> Map.merge(acc)\n        end)\n        |> Map.put(:ttl, rt.ttl)\n        |> Map.put(:updated_at, rt.updated_at)\n      end\n\n    {:ok, connections}\n  end\n\n  @with_routing true\n  @impl true\n  def handle_info({:refresh, prefix}, state) do\n    %{connections: connections, user_options: user_options} = Map.get(state, prefix)\n\n    %{ttl: ttl} = connections\n    # may overwrite the ttl, when desired in exceptional situations: tests, for example.\n    ttl = Keyword.get(user_options, :ttl, ttl)\n\n    state =\n      with {:ok, routing_table, _updated_connections} <-\n             get_routing_table(connections, @with_routing, prefix),\n           {:ok, new_connections} <- start_connections(user_options, routing_table) do\n        connections =\n          connections\n          |> Map.put(:updated_at, Bolt.Sips.Utils.now())\n          |> merge_connections_maps(new_connections, prefix)\n\n        ttl = Keyword.get(user_options, :ttl, ttl * 1000)\n\n        Process.send_after(self(), {:refresh, prefix}, ttl)\n\n        new_state = %{user_options: user_options, connections: connections}\n        Map.put(state, prefix, new_state)\n      else\n        e ->\n          Logger.error(\"Cannot create any connections. Error: #{inspect(e)}\")\n          Map.put(state, prefix, %{user_options: user_options, connections: %{}})\n      end\n\n    {:noreply, state}\n  end\n\n  def handle_info(req, state) do\n    Logger.warning(\"An unusual request: #{inspect(req)}\")\n    {:noreply, state}\n  end\n\n  @server_version_stringex ~r/Neo4j\\/(?<M>\\d+)\\.(?<m>\\d+)\\.(?<p>\\d+)/\n  @spec parse_server_version(map) :: {binary, <<_::16, _::_*8>>}\n  @doc \"\"\"\n  parse the version string received from the server, while considering the lack of the\n  patch number in some situations\n\n  ## Examples\n\n  iex> Bolt.Sips.Router.parse_server_version(%{\"server\" => \"Neo4j/3.5.0\"})\n  {\"Neo4j/3.5.0\", \"3.5.0\"}\n\n  iex> Bolt.Sips.Router.parse_server_version(%{\"server\" => \"Neo4j/3.5\"})\n  {\"Neo4j/3.5\", \"3.5.0\"}\n\n  iex> Bolt.Sips.Router.parse_server_version(%{\"server\" => \"Neo4j/3.5.10\"})\n  {\"Neo4j/3.5.10\", \"3.5.10\"}\n\n  iex> Bolt.Sips.Router.parse_server_version(%{\"server\" => \"Neo4j/3.5.11.1\"})\n  {\"Neo4j/3.5.11.1\", \"3.5.11\"}\n\n  \"\"\"\n  def parse_server_version(%{\"server\" => server_version_string}) do\n    %{\"M\" => major, \"m\" => minor, \"p\" => patch} =\n      @server_version_stringex\n      |> Regex.named_captures(server_version_string <> \".0\")\n\n    {server_version_string, \"#{major}.#{minor}.#{patch}\"}\n  end\n\n  def parse_server_version(some_version),\n    do: raise(ArgumentError, \"not a Neo4J version info: \" <> inspect(some_version))\n\n  defp error_no_connection_available_for_role(role, _e, prefix \\\\ :default)\n\n  defp error_no_connection_available_for_role(role, _e, prefix) do\n    \"no connection exists with this role: #{role} (prefix: #{prefix})\"\n  end\n\n  @routing_roles ~w{read write route}a\n  @spec merge_connections_maps(any(), any(), any()) :: any()\n  def merge_connections_maps(current_connections, new_connections, prefix \\\\ :default)\n\n  def merge_connections_maps(current_connections, new_connections, prefix) do\n    @routing_roles\n    |> Enum.flat_map(fn role ->\n      new_urls = Map.keys(new_connections[role])\n\n      current_connections[role]\n      |> Map.keys()\n      |> Enum.flat_map(fn url -> remove_old_urls(role, url, new_urls) end)\n    end)\n    |> close_connections(prefix)\n\n    @routing_roles\n    |> Enum.reduce(current_connections, fn role, acc ->\n      Map.put(acc, role, new_connections[role])\n    end)\n  end\n\n  defp remove_old_urls(role, url, urls), do: if(url in urls, do: [], else: [{role, url}])\n\n  # [\n  #   read: \"localhost:7689\",\n  #   write: \"localhost:7687\",\n  #   write: \"localhost:7690\",\n  #   route: \"localhost:7688\",\n  #   route: \"localhost:7689\"\n  # ]\n  defp close_connections(connections, prefix) do\n    connections\n    |> Enum.each(fn {role, url} ->\n      with {:ok, _pid} = r <- ConnectionSupervisor.terminate_connection(role, url, prefix) do\n        r\n      else\n        {:error, :not_found} ->\n          Logger.debug(\"#{role}: #{url}; not a valid connection/process. It can't be terminated\")\n      end\n    end)\n  end\n\n  @spec _get_connection(role :: String.t() | atom, state :: map, prefix :: atom) ::\n          {:ok, pid, map} | {:error, any, map}\n  defp _get_connection(role, connections, prefix) do\n    with true <- Map.has_key?(connections, role),\n         {:ok, url} <-\n           LoadBalancer.least_reused_url(Map.get(connections, role)),\n         {:ok, pid} <- ConnectionSupervisor.find_connection(role, url, prefix) do\n      {_, updated_connections} =\n        connections\n        |> get_and_update_in([role, url], fn hits -> {hits, hits + 1} end)\n\n      {:ok, pid, updated_connections}\n    else\n      e ->\n        err_msg = error_no_connection_available_for_role(role, e)\n        {:error, err_msg, connections}\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/routing/connection_supervisor.ex",
    "content": "defmodule Bolt.Sips.ConnectionSupervisor do\n  @moduledoc false\n\n  use DynamicSupervisor\n\n  alias Bolt.Sips.Protocol\n  alias Bolt.Sips\n\n  require Logger\n  @name __MODULE__\n\n  def start_link(init_args) do\n    DynamicSupervisor.start_link(__MODULE__, init_args, name: @name)\n  end\n\n  @impl true\n  def init(_args) do\n    DynamicSupervisor.init(strategy: :one_for_one)\n  end\n\n  @doc \"\"\"\n  the resulting connection name i.e. \"write@localhost:7687\" will be used to spawn new\n  DBConnection processes, and for finding available connections\n  \"\"\"\n  def start_child(role, url, config) do\n    prefix = Keyword.get(config, :prefix, :default)\n\n    connection_name = \"#{prefix}_#{role}@#{url}\"\n\n    role_config =\n      config\n      |> Keyword.put(:role, role)\n      |> Keyword.put(:name, via_tuple(connection_name))\n\n    spec = %{\n      id: connection_name,\n      start: {DBConnection, :start_link, [Protocol, role_config]},\n      type: :worker,\n      restart: :transient,\n      shutdown: 500\n    }\n\n    # [Protocol, role_config];\n    with {:error, :not_found} <- find_connection(connection_name),\n         {:ok, _pid} = r <- DynamicSupervisor.start_child(@name, spec) do\n      [spec, r]\n\n      r\n    else\n      {:ok, pid, _info} -> {:ok, pid}\n      {:error, {:already_started, pid}} -> {:ok, pid}\n      pid -> {:ok, pid}\n    end\n  end\n\n  @spec find_connection(atom, String.t(), atom) :: {:error, :not_found} | {:ok, pid()}\n  def find_connection(role, url, prefix), do: find_connection(\"#{prefix}_#{role}@#{url}\")\n\n  @spec find_connection(any()) :: {:error, :not_found} | {:ok, pid()}\n  def find_connection(name) do\n    case Registry.lookup(Sips.registry_name(), name) do\n      [{pid, _}] -> {:ok, pid}\n      _ -> {:error, :not_found}\n    end\n  end\n\n  @spec terminate_connection(atom, String.t(), atom) :: {:error, :not_found} | {:ok, pid()}\n  def terminate_connection(role, url, prefix \\\\ :default) do\n    with {:ok, pid} = r <- find_connection(role, url, prefix),\n         true <- Process.exit(pid, :normal) do\n      connections()\n      r\n    end\n  end\n\n  def connections() do\n    _connections()\n    |> Enum.map(fn pid ->\n      Sips.registry_name()\n      |> Registry.keys(pid)\n      |> List.first()\n    end)\n  end\n\n  defp _connections() do\n    __MODULE__\n    |> DynamicSupervisor.which_children()\n    |> Enum.filter(fn {_, pid, _type, modules} ->\n      case {pid, modules} do\n        {:restarting, _} -> false\n        {_pid, _} -> true\n      end\n    end)\n    |> Enum.map(fn {_, pid, _, _} ->\n      pid\n    end)\n  end\n\n  def via_tuple(name) do\n    {:via, Registry, {Bolt.Sips.registry_name(), name}}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/routing/load_balancer.ex",
    "content": "defmodule Bolt.Sips.LoadBalancer do\n  @moduledoc \"\"\"\n  a simple load balancer used for selecting a server address from a map. The address is selected based on\n  how many hits has; least reused url.\n  \"\"\"\n\n  @doc \"\"\"\n  sort by total number of hits and return the least reused url\n\n   ## Examples\n\n      iex> least_reused_url(%{\"url1\" => 10, \"url2\" => 5})\n      {:ok, \"url2\"}\n\n      iex> least_reused_url(%{})\n      {:error, :not_found}\n  \"\"\"\n  @spec least_reused_url(map) :: {:ok, String.t()} | {:error, :not_found}\n  def least_reused_url(urls) do\n    {url, _hits} =\n      urls\n      |> Enum.sort(fn {_, hits1}, {_, hits2} -> hits1 <= hits2 end)\n      |> List.first()\n\n    {:ok, url}\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/routing/routing_table.ex",
    "content": "defmodule Bolt.Sips.Routing.RoutingTable do\n  @moduledoc ~S\"\"\"\n  representing the routing table elements\n\n  There are a couple of ways to get the routing table from the server, for recent Neo4j servers, and with the\n  latest version of Bolt.Sips, you could use this query:\n\n      Bolt.Sips.query!(Bolt.Sips.conn, \"call dbms.cluster.routing.getRoutingTable($props)\", %{props: %{}})\n\n      [\n        %{\n          \"servers\" => [\n            %{\"addresses\" => [\"localhost:7687\"], \"role\" => \"WRITE\"},\n            %{\"addresses\" => [\"localhost:7689\", \"localhost:7688\"], \"role\" => \"READ\"},\n            %{\n              \"addresses\" => [\"localhost:7688\", \"localhost:7689\", \"localhost:7687\"],\n              \"role\" => \"ROUTE\"\n            }\n          ],\n          \"ttl\" => 300\n        }\n      ]\n\n  \"\"\"\n\n  @type t :: %__MODULE__{\n          roles: %{(:read | :write | :route | :direct) => %{String.t() => non_neg_integer}},\n          updated_at: non_neg_integer,\n          ttl: non_neg_integer\n        }\n  defstruct roles: %{}, ttl: 300, updated_at: 0\n\n  alias Bolt.Sips.Utils\n\n  @write \"WRITE\"\n  @read \"READ\"\n  @route \"ROUTE\"\n\n  @spec parse(map) :: __MODULE__.t() | {:error, String.t()}\n  def parse(%{\"servers\" => servers, \"ttl\" => ttl}) do\n    with {:ok, roles} <- parse_servers(servers),\n         {:ok, ttl} <- parse_ttl(ttl) do\n      %__MODULE__{roles: roles, ttl: ttl, updated_at: Utils.now()}\n    end\n  end\n\n  def parse(map),\n    do: {:error, \"not a valid routing table: \" <> inspect(map)}\n\n  @spec parse_servers(list()) :: {:ok, map()}\n  defp parse_servers(servers) do\n    parsed_servers =\n      servers\n      |> Enum.reduce(%{}, fn %{\"addresses\" => addresses, \"role\" => role}, acc ->\n        with {:ok, atomized_role} <- to_atomic_role(role) do\n          roles =\n            addresses\n            |> Enum.reduce(acc, fn address, acc ->\n              Map.update(acc, atomized_role, %{address => 0}, &Map.put(&1, address, 0))\n            end)\n\n          roles\n        else\n          _ -> acc\n        end\n      end)\n\n    {:ok, parsed_servers}\n  end\n\n  defp to_atomic_role(role) when role in [@read, @write, @route] do\n    atomic_role =\n      case role do\n        @read -> :read\n        @write -> :write\n        @route -> :route\n        _ -> :direct\n      end\n\n    {:ok, atomic_role}\n  end\n\n  defp to_atomic_role(_), do: {:error, :alien_role}\n\n  def parse_ttl(ttl), do: {:ok, ensure_integer(ttl)}\n\n  @doc false\n  def ttl_expired?(updated_at, ttl) do\n    updated_at + ttl <= Utils.now()\n  end\n\n  defp ensure_integer(ttl) when is_nil(ttl), do: 0\n  defp ensure_integer(ttl) when is_binary(ttl), do: String.to_integer(ttl)\n  defp ensure_integer(ttl) when is_integer(ttl), do: ttl\n  defp ensure_integer(ttl), do: raise(ArgumentError, \"invalid ttl: \" <> inspect(ttl))\nend\n"
  },
  {
    "path": "lib/bolt_sips/socket.ex",
    "content": "defmodule Bolt.Sips.Socket do\n  @moduledoc \"\"\"\n  A default socket interface used to communicate to a Neo4j instance.\n\n  Any other socket implementing the same interface can be used\n  in place of this one. Actually, this module doesn't\n  implement the interface on its own, it delegates calls to\n  the gen_tcp (http://erlang.org/doc/man/gen_tcp.html)\n  and inet (http://erlang.org/doc/man/inet.html) modules. Any of these\n  modules doesn't fully implement the required interface,\n  hence, both of them must be used.\n  \"\"\"\n\n  @doc \"Delegates to :gen_tcp.connect/4\"\n  defdelegate connect(host, port, opts, timeout), to: :gen_tcp\n\n  @doc \"Delegates to :inet.setopts/2\"\n  defdelegate setopts(sock, opts), to: :inet\n\n  @doc \"Delegates to :gen_tcp.send/2\"\n  defdelegate send(sock, package), to: :gen_tcp\n\n  @doc \"Delegates to :gen_tcp.recv/3\"\n  defdelegate recv(sock, length, timeout), to: :gen_tcp\n\n  @doc \"Delegates to :gen_tcp.recv/2\"\n  defdelegate recv(sock, length), to: :gen_tcp\n\n  @doc \"Delegates to :inet.close/1\"\n  defdelegate close(sock), to: :inet\nend\n"
  },
  {
    "path": "lib/bolt_sips/types.ex",
    "content": "defmodule Bolt.Sips.Types do\n  @moduledoc \"\"\"\n  Basic support for representing nodes, relationships and paths belonging to\n  a Neo4j graph database.\n\n  Four supported types of entities:\n\n  - Node\n  - Relationship\n  - UnboundRelationship\n  - Path\n\n  More details, about the Bolt protocol, here:\n  https://github.com/boltprotocol/boltprotocol/blob/master/README.md\n\n  Additionally, since bolt V2, new types appears: spatial and temporal\n  Those are not documented in bolt protocol, but neo4j documentation can be found here:\n  https://neo4j.com/docs/cypher-manual/current/syntax/temporal/\n  https://neo4j.com/docs/cypher-manual/current/syntax/spatial/\n\n  To work with temporal types, the following Elixir structs are available:\n  - Time, DateTime, NaiveDateTime\n  - Calendar.DateTime to work with timezone (as string)\n  - TimeWithTZOffset, DateTimeWithTZOffset to work with (date)time and timezone offset(as integer)\n  - Duration\n\n  For spatial types, you only need Point struct as it covers:\n  - 2D point (cartesian or geographic)\n  - 3D point (cartesian or geographic)\n  \"\"\"\n\n  alias Bolt.Sips.TypesHelper\n\n  defmodule Entity do\n    @moduledoc \"\"\"\n      base structure for Node and Relationship\n    \"\"\"\n    @base_fields [id: nil, properties: nil]\n    defmacro __using__(fields) do\n      fields = @base_fields ++ fields\n\n      quote do\n        defstruct unquote(fields)\n      end\n    end\n  end\n\n  defmodule Node do\n    @moduledoc \"\"\"\n      Self-contained graph node.\n\n      A Node represents a node from a Neo4j graph and consists of a\n      unique identifier (within the scope of its origin graph), a list of\n      labels and a map of properties.\n\n      https://github.com/boltprotocol/boltprotocol/blob/master/v1/_serialization.asciidoc#node\n    \"\"\"\n\n    use Entity, labels: nil\n\n    @type t :: %__MODULE__{\n            id: integer,\n            labels: [String.t()],\n            properties: map\n          }\n  end\n\n  defmodule Relationship do\n    @moduledoc \"\"\"\n      Self-contained graph relationship.\n\n      A Relationship represents a relationship from a Neo4j graph and consists of\n      a unique identifier (within the scope of its origin graph), identifiers\n      for the start and end nodes of that relationship, a type and a map of properties.\n\n      https://github.com/boltprotocol/boltprotocol/blob/master/v1/_serialization.asciidoc#relationship\n    \"\"\"\n\n    use Entity, start: nil, end: nil, type: nil\n\n    @type t :: %__MODULE__{\n            id: integer,\n            properties: map\n          }\n  end\n\n  defmodule UnboundRelationship do\n    @moduledoc \"\"\"\n      Self-contained graph relationship without endpoints.\n      An UnboundRelationship represents a relationship relative to a\n      separately known start point and end point.\n\n      https://github.com/boltprotocol/boltprotocol/blob/master/v1/_serialization.asciidoc#unboundrelationship\n    \"\"\"\n\n    use Entity, start: nil, end: nil, type: nil\n\n    @type t :: %__MODULE__{\n            id: integer,\n            properties: map\n          }\n  end\n\n  defmodule Path do\n    @moduledoc \"\"\"\n      Self-contained graph path.\n\n      A Path is a sequence of alternating nodes and relationships corresponding to a\n      walk in the graph. The path always begins and ends with a node.\n      Its representation consists of a list of distinct nodes,\n      a list of distinct relationships and a sequence of integers describing the\n      path traversal\n\n      https://github.com/boltprotocol/boltprotocol/blob/master/v1/_serialization.asciidoc#path\n    \"\"\"\n    @type t :: %__MODULE__{\n            nodes: list() | nil,\n            relationships: list() | nil,\n            sequence: list() | nil\n          }\n    defstruct nodes: nil, relationships: nil, sequence: nil\n\n    @doc \"\"\"\n    represents a traversal or walk through a graph and maintains a direction\n    separate from that of any relationships traversed\n    \"\"\"\n    @spec graph(Path.t()) :: list() | nil\n    def graph(path) do\n      entities = [List.first(path.nodes)]\n\n      draw_path(\n        path.nodes,\n        path.relationships,\n        path.sequence,\n        0,\n        Enum.take_every(path.sequence, 2),\n        entities,\n        # last node\n        List.first(path.nodes),\n        # next node\n        nil\n      )\n    end\n\n    # @lint false\n    defp draw_path(_n, _r, _s, _i, [], acc, _ln, _nn), do: acc\n\n    defp draw_path(n, r, s, i, [h | t] = _rel_index, acc, ln, _nn) do\n      next_node = Enum.at(n, Enum.at(s, 2 * i + 1))\n\n      urel =\n        if h > 0 && h < 255 do\n          # rel: rels[rel_index - 1], start/end: (ln.id, next_node.id)\n          rel = Enum.at(r, h - 1)\n\n          unbound_relationship =\n            [:id, :type, :properties, :start, :end]\n            |> Enum.zip([rel.id, rel.type, rel.properties, ln.id, next_node.id])\n\n          struct(UnboundRelationship, unbound_relationship)\n        else\n          # rel: rels[-rel_index - 1], start/end: (next_node.id, ln.id)\n          # Neo4j sends: -1, and Bolt.Sips.Internals. returns 255 instead? Investigating,\n          # meanwhile ugly path:\n          # oh dear ...\n          haha = if h == 255, do: -1, else: h\n          rel = Enum.at(r, -haha - 1)\n\n          unbound_relationship =\n            [:id, :type, :properties, :start, :end]\n            |> Enum.zip([rel.id, rel.type, rel.properties, next_node.id, ln.id])\n\n          struct(UnboundRelationship, unbound_relationship)\n        end\n\n      draw_path(n, r, s, i + 1, t, (acc ++ [urel]) ++ [next_node], next_node, ln)\n    end\n  end\n\n  defmodule TimeWithTZOffset do\n    @moduledoc \"\"\"\n    Manage a Time and its time zone offset.\n\n    This temporal types hs been added in bolt v2\n    \"\"\"\n    defstruct [:time, :timezone_offset]\n\n    @type t :: %__MODULE__{\n            time: Calendar.time(),\n            timezone_offset: integer()\n          }\n\n    @doc \"\"\"\n    Create a valid TimeWithTZOffset from a Time and offset in seconds\n    \"\"\"\n    @spec create(Calendar.time(), integer()) :: TimeWithTZOffset.t()\n    def create(%Time{} = time, offset) when is_integer(offset) do\n      %TimeWithTZOffset{\n        time: time,\n        timezone_offset: offset\n      }\n    end\n\n    @doc \"\"\"\n    Convert TimeWithTZOffset struct in a cypher-compliant  string\n    \"\"\"\n    @spec format_param(TimeWithTZOffset.t()) :: {:ok, String.t()} | {:error, any()}\n    def format_param(%TimeWithTZOffset{time: time, timezone_offset: offset})\n        when is_integer(offset) do\n      param = Time.to_iso8601(time) <> TypesHelper.formated_time_offset(offset)\n      {:ok, param}\n    end\n\n    def format_param(param) do\n      {:error, param}\n    end\n  end\n\n  defmodule DateTimeWithTZOffset do\n    @moduledoc \"\"\"\n    Manage a Time and its time zone offset.\n\n    This temporal types hs been added in bolt v2\n    \"\"\"\n    defstruct [:naive_datetime, :timezone_offset]\n\n    @type t :: %__MODULE__{\n            naive_datetime: Calendar.naive_datetime(),\n            timezone_offset: integer()\n          }\n\n    @doc \"\"\"\n    Create a valid DateTimeWithTZOffset from a NaiveDateTime and offset in seconds\n    \"\"\"\n    @spec create(Calendar.naive_datetime(), integer()) :: DateTimeWithTZOffset.t()\n    def create(%NaiveDateTime{} = naive_datetime, offset) when is_integer(offset) do\n      %DateTimeWithTZOffset{\n        naive_datetime: naive_datetime,\n        timezone_offset: offset\n      }\n    end\n\n    @doc \"\"\"\n    Convert DateTimeWithTZOffset struct in a cypher-compliant  string\n    \"\"\"\n    @spec format_param(DateTimeWithTZOffset.t()) :: {:ok, String.t()} | {:error, any()}\n    def format_param(%DateTimeWithTZOffset{naive_datetime: ndt, timezone_offset: offset})\n        when is_integer(offset) do\n      formated = NaiveDateTime.to_iso8601(ndt) <> TypesHelper.formated_time_offset(offset)\n      {:ok, formated}\n    end\n\n    def format_param(param) do\n      {:error, param}\n    end\n  end\n\n  defmodule Duration do\n    @moduledoc \"\"\"\n    a Duration type, as introduced in bolt V2.\n\n    Composed of months, days, seconds and nanoseconds, it can be used in date operations\n    \"\"\"\n    defstruct years: 0,\n              months: 0,\n              weeks: 0,\n              days: 0,\n              hours: 0,\n              minutes: 0,\n              seconds: 0,\n              nanoseconds: 0\n\n    @type t :: %__MODULE__{\n            years: non_neg_integer(),\n            months: non_neg_integer(),\n            weeks: non_neg_integer(),\n            days: non_neg_integer(),\n            hours: non_neg_integer(),\n            minutes: non_neg_integer(),\n            seconds: non_neg_integer(),\n            nanoseconds: non_neg_integer()\n          }\n\n    @period_prefix \"P\"\n    @time_prefix \"T\"\n\n    @year_suffix \"Y\"\n    @month_suffix \"M\"\n    @week_suffix \"W\"\n    @day_suffix \"D\"\n    @hour_suffix \"H\"\n    @minute_suffix \"M\"\n    @second_suffix \"S\"\n\n    @doc \"\"\"\n    Create a Duration struct from data returned by Neo4j.\n\n    Neo4j returns a list of 4 integers:\n      - months\n      - days\n      - seconds\n      - nanoseconds\n\n    Struct elements are computed in a logical way, then for exmple 65 seconds is 1min and 5\n    seconds. Beware that you may not retrieve the same data you send!\n    Note: days are not touched as they are not a fixed number of days for each month.\n\n\n    ## Example:\n\n        iex> Duration.create(15, 53, 125, 54)\n        %Bolt.Sips.Types.Duration{\n          days: 53,\n          hours: 0,\n          minutes: 2,\n          months: 3,\n          nanoseconds: 54,\n          seconds: 5,\n          weeks: 0,\n          years: 1\n        }\n    \"\"\"\n    @spec create(integer(), integer(), integer(), integer()) :: Duration.t()\n    def create(months, days, seconds, nanoseconds)\n        when is_integer(months) and is_integer(days) and is_integer(seconds) and\n               is_integer(nanoseconds) do\n      years = div(months, 12)\n      months_ = rem(months, 12)\n      {hours, minutes, seconds_inter} = TypesHelper.decompose_in_hms(seconds)\n      {seconds_, nanoseconds_} = manage_nanoseconds(seconds_inter, nanoseconds)\n\n      %Duration{\n        years: years,\n        months: months_,\n        days: days,\n        hours: hours,\n        minutes: minutes,\n        seconds: seconds_,\n        nanoseconds: nanoseconds_\n      }\n    end\n\n    @spec manage_nanoseconds(integer(), integer()) :: {integer(), integer()}\n    defp manage_nanoseconds(seconds, nanoseconds) when nanoseconds >= 1_000_000_000 do\n      seconds_ = seconds + div(nanoseconds, 1_000_000_000)\n      nanoseconds_ = rem(nanoseconds, 1_000_000_000)\n      {seconds_, nanoseconds_}\n    end\n\n    defp manage_nanoseconds(seconds, nanoseconds) do\n      {seconds, nanoseconds}\n    end\n\n    @doc \"\"\"\n    Convert a %Duration in a cypher-compliant string.\n    To know everything about duration format, please see:\n    https://neo4j.com/docs/cypher-manual/current/syntax/temporal/#cypher-temporal-durations\n    \"\"\"\n    @spec format_param(Duration.t()) :: {:ok, String.t()} | {:error, any()}\n    def format_param(\n          %Duration{\n            years: y,\n            months: m,\n            days: d,\n            hours: h,\n            minutes: mm,\n            seconds: s,\n            nanoseconds: ss\n          } = duration\n        )\n        when is_integer(y) and is_integer(m) and is_integer(d) and is_integer(h) and\n               is_integer(mm) and is_integer(s) and is_integer(ss) do\n      formated = format_date(duration) <> format_time(duration)\n\n      param =\n        case formated do\n          \"\" -> \"\"\n          formated_duration -> @period_prefix <> formated_duration\n        end\n\n      {:ok, param}\n    end\n\n    def format_param(param) do\n      {:error, param}\n    end\n\n    @spec format_date(Duration.t()) :: String.t()\n    defp format_date(%Duration{years: years, months: months, weeks: weeks, days: days}) do\n      format_duration_part(years, @year_suffix) <>\n        format_duration_part(months, @month_suffix) <>\n        format_duration_part(weeks, @week_suffix) <> format_duration_part(days, @day_suffix)\n    end\n\n    @spec format_time(Duration.t()) :: String.t()\n    defp format_time(%Duration{\n           hours: hours,\n           minutes: minutes,\n           seconds: s,\n           nanoseconds: ns\n         })\n         when hours > 0 or minutes > 0 or s > 0 or ns > 0 do\n      {seconds, nanoseconds} = manage_nanoseconds(s, ns)\n      nanoseconds_f = nanoseconds |> Integer.to_string() |> String.pad_leading(9, \"0\")\n      seconds_f = \"#{Integer.to_string(seconds)}.#{nanoseconds_f}\" |> String.to_float()\n\n      @time_prefix <>\n        format_duration_part(hours, @hour_suffix) <>\n        format_duration_part(minutes, @minute_suffix) <>\n        format_duration_part(seconds_f, @second_suffix)\n    end\n\n    defp format_time(_) do\n      \"\"\n    end\n\n    @spec format_duration_part(number(), String.t()) :: String.t()\n    defp format_duration_part(duration_part, suffix)\n         when duration_part > 0 and is_bitstring(suffix) do\n      \"#{stringify_number(duration_part)}#{suffix}\"\n    end\n\n    defp format_duration_part(_, _) do\n      \"\"\n    end\n\n    @spec stringify_number(number()) :: String.t()\n    defp stringify_number(number) when is_integer(number) do\n      Integer.to_string(number)\n    end\n\n    defp stringify_number(number) do\n      Float.to_string(number)\n    end\n  end\n\n  defmodule Point do\n    @moduledoc \"\"\"\n    Manage spatial data introduced in Bolt V2\n\n    Point can be:\n      - Cartesian 2D\n      - Geographic 2D\n      - Cartesian 3D\n      - Geographic 3D\n    \"\"\"\n    @srid_cartesian 7203\n    @srid_cartesian_3d 9157\n    @srid_wgs_84 4326\n    @srid_wgs_84_3d 4979\n\n    defstruct [:crs, :srid, :x, :y, :z, :longitude, :latitude, :height]\n\n    @type t :: %__MODULE__{\n            crs: String.t(),\n            srid: integer(),\n            x: number() | nil,\n            y: number() | nil,\n            z: number() | nil,\n            longitude: number() | nil,\n            latitude: number() | nil,\n            height: number() | nil\n          }\n\n    defguardp is_crs(crs) when crs in [\"cartesian\", \"cartesian-3d\", \"wgs-84\", \"wgs-84-3d\"]\n\n    defguardp is_srid(srid)\n              when srid in [@srid_cartesian, @srid_cartesian_3d, @srid_wgs_84, @srid_wgs_84_3d]\n\n    defguardp are_coords(lt, lg, h, x, y, z)\n              when (is_number(lt) or is_nil(lt)) and (is_number(lg) or is_nil(lg)) and\n                     (is_number(h) or is_nil(h)) and (is_number(x) or is_nil(x)) and\n                     (is_number(y) or is_nil(y)) and (is_number(z) or is_nil(z))\n\n    defguardp is_valid_coords(x, y) when is_number(x) and is_number(y)\n    defguardp is_valid_coords(x, y, z) when is_number(x) and is_number(y) and is_number(z)\n\n    @doc \"\"\"\n    A 2D point either needs:\n    - 2 coordinates and a atom (:cartesian or :wgs_84) to define its type\n    - 2 coordinates and a srid (4326 or 7203) to define its type\n\n    ## Examples:\n        iex> Point.create(:cartesian, 10, 20.0)\n        %Bolt.Sips.Types.Point{\n          crs: \"cartesian\",\n          height: nil,\n          latitude: nil,\n          longitude: nil,\n          srid: 7203,\n          x: 10.0,\n          y: 20.0,\n          z: nil\n        }\n        iex> Point.create(4326, 10, 20.0)\n        %Bolt.Sips.Types.Point{\n          crs: \"wgs-84\",\n          height: nil,\n          latitude: 20.0,\n          longitude: 10.0,\n          srid: 4326,\n          x: 10.0,\n          y: 20.0,\n          z: 30.0\n        }\n    \"\"\"\n    @spec create(:cartesian | :wgs_84 | 4326 | 7203, number(), number()) :: Point.t()\n    def create(:cartesian, x, y) do\n      create(@srid_cartesian, x, y)\n    end\n\n    def create(:wgs_84, longitude, latitude) do\n      create(@srid_wgs_84, longitude, latitude)\n    end\n\n    def create(@srid_cartesian, x, y) when is_valid_coords(x, y) do\n      %Point{\n        crs: crs(@srid_cartesian),\n        srid: @srid_cartesian,\n        x: format_coord(x),\n        y: format_coord(y)\n      }\n    end\n\n    def create(@srid_wgs_84, longitude, latitude) when is_valid_coords(longitude, latitude) do\n      %Point{\n        crs: crs(@srid_wgs_84),\n        srid: @srid_wgs_84,\n        x: format_coord(longitude),\n        y: format_coord(latitude),\n        longitude: format_coord(longitude),\n        latitude: format_coord(latitude)\n      }\n    end\n\n    @doc \"\"\"\n    Create a 3D point\n\n    A 3D point either needs:\n    - 3 coordinates and a atom (:cartesian or :wgs_84) to define its type\n    - 3 coordinates and a srid (4979 or 9147) to define its type\n\n    ## Examples:\n        iex> Point.create(:cartesian, 10, 20.0, 30)\n        %Bolt.Sips.Types.Point{\n          crs: \"cartesian-3d\",\n          height: nil,\n          latitude: nil,\n          longitude: nil,\n          srid: 9157,\n          x: 10.0,\n          y: 20.0,\n          z: 30.0\n        }\n        iex> Point.create(4979, 10, 20.0, 30)\n        %Bolt.Sips.Types.Point{\n          crs: \"wgs-84-3d\",\n          height: 30.0,\n          latitude: 20.0,\n          longitude: 10.0,\n          srid: 4979,\n          x: 10.0,\n          y: 20.0,\n          z: 30.0\n        }\n    \"\"\"\n    @spec create(:cartesian | :wgs_84 | 4979 | 9157, number(), number(), number()) :: Point.t()\n    def create(:cartesian, x, y, z) do\n      create(@srid_cartesian_3d, x, y, z)\n    end\n\n    def create(:wgs_84, longitude, latitude, height) do\n      create(@srid_wgs_84_3d, longitude, latitude, height)\n    end\n\n    def create(@srid_cartesian_3d, x, y, z) when is_valid_coords(x, y, z) do\n      %Point{\n        crs: crs(@srid_cartesian_3d),\n        srid: @srid_cartesian_3d,\n        x: format_coord(x),\n        y: format_coord(y),\n        z: format_coord(z)\n      }\n    end\n\n    def create(@srid_wgs_84_3d, longitude, latitude, height)\n        when is_valid_coords(longitude, latitude, height) do\n      %Point{\n        crs: crs(@srid_wgs_84_3d),\n        srid: @srid_wgs_84_3d,\n        x: format_coord(longitude),\n        y: format_coord(latitude),\n        z: format_coord(height),\n        longitude: format_coord(longitude),\n        latitude: format_coord(latitude),\n        height: format_coord(height)\n      }\n    end\n\n    @spec crs(4326 | 4979 | 7203 | 9157) :: String.t()\n    defp crs(@srid_cartesian), do: \"cartesian\"\n    defp crs(@srid_cartesian_3d), do: \"cartesian-3d\"\n    defp crs(@srid_wgs_84), do: \"wgs-84\"\n    defp crs(@srid_wgs_84_3d), do: \"wgs-84-3d\"\n\n    defp format_coord(coord) when is_integer(coord), do: coord / 1\n    defp format_coord(coord), do: coord\n\n    @doc \"\"\"\n    Convert a Point struct into a cypher-compliant map\n\n    ## Example\n        iex(8)> Point.create(4326, 10, 20.0) |> Point.format_to_param\n        %{crs: \"wgs-84\", latitude: 20.0, longitude: 10.0, x: 10.0, y: 20.0}\n    \"\"\"\n    @spec format_param(Point.t()) :: {:ok, map()} | {:error, any()}\n    def format_param(\n          %Point{crs: crs, srid: srid, latitude: lt, longitude: lg, height: h, x: x, y: y, z: z} =\n            point\n        )\n        when is_crs(crs) and is_srid(srid) and are_coords(lt, lg, h, x, y, z) do\n      param =\n        point\n        |> Map.from_struct()\n        |> Enum.filter(fn {_, val} -> not is_nil(val) end)\n        |> Map.new()\n        |> Map.drop([:srid])\n\n      {:ok, param}\n    end\n\n    def format_param(param) do\n      {:error, param}\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/types_helper.ex",
    "content": "defmodule Bolt.Sips.TypesHelper do\n  @doc \"\"\"\n  Decompose an amount seconds into the tuple {hours, minutes, seconds}\n  \"\"\"\n  @spec decompose_in_hms(integer()) :: {integer(), integer(), integer()}\n  def decompose_in_hms(seconds) do\n    [{minutes, seconds}, {hours, _}, _] =\n      [3600, 60]\n      |> Enum.reduce([{0, seconds}], fn\n        divisor, acc ->\n          {_, num} = hd(acc)\n          [{div(num, divisor), rem(num, divisor)} | acc]\n      end)\n\n    {hours, minutes, seconds}\n  end\n\n  @doc \"\"\"\n  Convert NaiveDateTime and timezone into a Calendar.DateTime\n  Without losing micorsecond data!\n  \"\"\"\n  @spec datetime_with_micro(Calendar.naive_datetime(), String.t()) :: Calendar.datetime()\n  def datetime_with_micro(%NaiveDateTime{} = naive_dt, timezone) do\n    DateTime.from_naive!(naive_dt, timezone)\n  end\n\n  @doc \"\"\"\n  Convert an amount of seconds in a +hours:minutes offset\n  \"\"\"\n  @spec formated_time_offset(integer()) :: String.t()\n  def formated_time_offset(offset_seconds) do\n    {hours, minutes, _} = offset_seconds |> abs() |> decompose_in_hms()\n    get_sign_string(offset_seconds) <> format_time_part(hours) <> \":\" <> format_time_part(minutes)\n  end\n\n  defp get_sign_string(number) when number >= 0 do\n    \"+\"\n  end\n\n  defp get_sign_string(_) do\n    \"-\"\n  end\n\n  defp format_time_part(time_part) when time_part < 10 do\n    \"0\" <> Integer.to_string(time_part)\n  end\n\n  defp format_time_part(time_part) do\n    Integer.to_string(time_part)\n  end\nend\n"
  },
  {
    "path": "lib/bolt_sips/utils.ex",
    "content": "defmodule Bolt.Sips.Utils do\n  @moduledoc false\n  # Common utilities\n\n  @default_hostname \"localhost\"\n  @default_bolt_port 7687\n\n  @default_driver_options [\n    hostname: @default_hostname,\n    port: @default_bolt_port,\n    pool_size: 15,\n    max_overflow: 0,\n    timeout: 15_000,\n    ssl: false,\n    socket: Bolt.Sips.Socket,\n    with_etls: false,\n    schema: \"bolt\",\n    prefix: :default\n  ]\n\n  @doc \"\"\"\n  Generate a random string.\n  \"\"\"\n  def random_id, do: :rand.uniform() |> Float.to_string() |> String.slice(2..10)\n\n  @doc \"\"\"\n  Fills in the given `opts` with default options.\n  \"\"\"\n  @spec default_config(Keyword.t()) :: Keyword.t()\n  def default_config(), do: Application.get_env(:bolt_sips, Bolt, []) |> default_config\n\n  def default_config(opts) do\n    config =\n      @default_driver_options\n      |> Keyword.merge(opts)\n\n    ssl_or_sock = if(Keyword.get(config, :ssl), do: :ssl, else: Keyword.get(config, :socket))\n\n    config\n    |> Keyword.put_new(:hostname, System.get_env(\"NEO4J_HOST\") || @default_hostname)\n    |> Keyword.put_new(:port, System.get_env(\"NEO4J_PORT\") || @default_bolt_port)\n    |> Keyword.put_new(:pool_size, 5)\n    |> Keyword.put_new(:max_overflow, 2)\n    |> Keyword.put_new(:timeout, 15_000)\n    |> Keyword.put_new(:ssl, false)\n    |> Keyword.put_new(:socket, Bolt.Sips.Socket)\n    |> Keyword.put_new(:with_etls, false)\n    |> Keyword.put_new(:schema, \"bolt\")\n    |> Keyword.put_new(:path, \"\")\n    |> Keyword.put_new(:prefix, :default)\n    |> or_use_url_if_present\n    |> Enum.reject(fn {_k, v} -> is_nil(v) end)\n    |> Keyword.put(:socket, ssl_or_sock)\n  end\n\n  @doc \"\"\"\n  source: https://github.com/eksperimental/experimental_elixir/blob/master/lib/kernel_modulo.ex\n  Modulo operation.\n\n  Returns the remainder after division of `number` by `modulus`.\n  The sign of the result will always be the same sign as the `modulus`.\n\n  More information: [Modulo operation](https://en.wikipedia.org/wiki/Modulo_operation) on Wikipedia.\n  ## Examples\n    iex> mod(17, 17)\n    0\n    iex> mod(17, 1)\n    0\n    iex> mod(17, 13)\n    4\n    iex> mod(-17, 13)\n    9\n    iex> mod(17, -13)\n    -9\n    iex> mod(-17, -13)\n    -4\n    iex> mod(17, 26)\n    17\n    iex> mod(-17, 26)\n    9\n    iex> mod(17, -26)\n    -9\n    iex> mod(-17, -26)\n    -17\n    iex> mod(17, 0)\n    ** (ArithmeticError) bad argument in arithmetic expression\n    iex> mod(1.5, 2)\n    ** (FunctionClauseError) no function clause matching in Experimental.KernelModulo.mod/2\n  \"\"\"\n  @spec mod(integer, integer) :: non_neg_integer\n  def mod(number, modulus) when is_integer(number) and is_integer(modulus) do\n    remainder = rem(number, modulus)\n\n    if (remainder > 0 and modulus < 0) or (remainder < 0 and modulus > 0) do\n      remainder + modulus\n    else\n      remainder\n    end\n  end\n\n  @doc false\n  defp or_use_url_if_present(config) do\n    if Keyword.has_key?(config, :url) do\n      f =\n        config[:url]\n        |> to_string()\n        |> URI.parse()\n\n      schema = if f.scheme, do: f.scheme, else: \"bolt\"\n\n      config\n      |> Keyword.put(:hostname, f.host)\n      |> Keyword.put(:schema, schema)\n      |> Keyword.put(:query, f.query)\n      |> Keyword.put(:path, f.path)\n      |> Keyword.put(:fragment, f.fragment)\n      |> Keyword.put(:routing_context, routing_context(f.query))\n      |> Keyword.put(:port, port_from_url(f.port))\n      |> username_and_password(f.userinfo)\n      |> Enum.reject(fn {_k, v} -> is_nil(v) end)\n    else\n      config\n    end\n  end\n\n  @username_password ~r\"\"\"\n  ^\n  (?: (?<username> \\* | [a-zA-Z0-9%_.!~*'();&=+$,-]+)\n    (?: : (?<password> \\* | [a-zA-Z0-9%_.!~*'();&=+$,-]*))?\n  )?\n  $\n  \"\"\"x\n  @spec username_and_password(Keyword.t(), String.t()) :: Keyword.t()\n  defp username_and_password(config, uri_user_info) when is_binary(uri_user_info) do\n    case Regex.named_captures(@username_password, uri_user_info) do\n      %{\"username\" => username, \"password\" => password} ->\n        config\n        |> Keyword.put(:basic_auth, username: username, password: password)\n\n      _ ->\n        config\n    end\n  end\n\n  defp username_and_password(config, _), do: config\n\n  @spec port_from_url(integer) :: integer\n  defp port_from_url(port)\n       when is_nil(port)\n       when not is_integer(port),\n       do: @default_bolt_port\n\n  defp port_from_url(port) when is_integer(port), do: port\n\n  defp port_from_url(_port), do: @default_bolt_port\n\n  @accepted_units_of_time [:seconds, :millisecond, :microsecond, :nanosecond]\n  @accepted_units_of_time_str Enum.join(@accepted_units_of_time, \", \")\n\n  @doc \"\"\"\n  return now for UTC, in :seconds, :millisecond, :microsecond and :nanosecond\n  \"\"\"\n  @spec now(unit :: :seconds | :millisecond | :microsecond | :nanosecond) :: integer\n  def now(unit \\\\ :seconds)\n  def now(unit) when unit in @accepted_units_of_time, do: :os.system_time(unit)\n\n  def now(unit),\n    do:\n      raise(\n        ArgumentError,\n        \"expected one of these: #{@accepted_units_of_time_str}, but received: #{inspect(unit)}, instead.\"\n      )\n\n  defp routing_context(nil), do: decode(\"\")\n  defp routing_context(query), do: decode(query)\n\n  def decode(query) do\n    do_decode(:binary.split(query, [\";\", \",\", \"&\"], [:global]), %{})\n  end\n\n  defp do_decode([], acc), do: acc\n\n  defp do_decode([h | t], acc) do\n    case decode_kv(h) do\n      {k, v} -> do_decode(t, Map.put(acc, k, v))\n      false -> do_decode(t, acc)\n    end\n  end\n\n  # borrowed some code from Plug\n  defp decode_kv(\"\"), do: false\n  defp decode_kv(<<?$, _::binary>>), do: false\n  defp decode_kv(<<h, t::binary>>) when h in [?\\s, ?\\t], do: decode_kv(t)\n  defp decode_kv(kv), do: decode_key(kv, \"\")\n\n  defp decode_key(\"\", _key), do: false\n  defp decode_key(<<?=, _::binary>>, \"\"), do: false\n  defp decode_key(<<?=, t::binary>>, key), do: decode_value(t, \"\", key, \"\")\n  defp decode_key(<<h, _::binary>>, _key) when h in [?\\s, ?\\t, ?\\r, ?\\n, ?\\v, ?\\f], do: false\n  defp decode_key(<<h, t::binary>>, key), do: decode_key(t, <<key::binary, h>>)\n\n  defp decode_value(\"\", _spaces, key, value), do: {key, value}\n\n  defp decode_value(<<?\\s, t::binary>>, spaces, key, value),\n    do: decode_value(t, <<spaces::binary, ?\\s>>, key, value)\n\n  defp decode_value(<<h, _::binary>>, _spaces, _key, _value) when h in [?\\t, ?\\r, ?\\n, ?\\v, ?\\f],\n    do: false\n\n  defp decode_value(<<h, t::binary>>, spaces, key, value),\n    do: decode_value(t, \"\", key, <<value::binary, spaces::binary, h>>)\nend\n"
  },
  {
    "path": "lib/bolt_sips.ex",
    "content": "defmodule Bolt.Sips do\n  @moduledoc \"\"\"\n  A Neo4j driver for Elixir providing many useful features:\n\n  - using the Bolt protocol, the Elixir implementation - the Neo4j's newest network protocol, designed for high-performance; latest Bolt versions, are supported.\n  - Can connect to a standalone Neo4j server (`:direct` mode) or to a Neo4j causal cluster, using the `bolt+routing` or the newer `neo4j` schemes; connecting in `:routing` mode.\n  - Provides the user with the ability to create and manage distinct ad-hoc `role-based` connections to one or more Neo4j servers/databases\n  - Supports transactions, simple and complex Cypher queries with or w/o parameters\n  - Multi-tenancy\n  - Supports Neo4j versions: 3.0.x/3.1.x/3.2.x/3.4.x/3.5.x\n\n  To start, add the `:bolt_sips` dependency to you project, run `mix do deps.get, compile` on it and then you can quickly start experimenting with Neo4j from the convenience of your IEx shell. Example:\n\n      iex> {:ok, _neo} = Bolt.Sips.start_link(url: \"bolt://neo4j:test@localhost\")\n      {:ok, #PID<0.250.0>}\n      iex>   conn = Bolt.Sips.conn()\n      #PID<0.256.0>\n      iex> Bolt.Sips.query!(conn, \"RETURN 1 as n\")\n      %Bolt.Sips.Response{\n        records: [[1]],\n        results: [%{\"n\" => 1}]\n      }\n\n  the example above presumes that you have a Neo4j server available locally, using the Bolt protocol and requiring authentication.\n  \"\"\"\n\n  use Supervisor\n\n  @registry_name :bolt_sips_registry\n\n  # @timeout 15_000\n  # @max_rows     500\n\n  alias Bolt.Sips.{Query, ConnectionSupervisor, Router, Error, Response, Exception}\n\n  @type conn :: DBConnection.conn()\n  @type transaction :: DBConnection.t()\n\n  @doc \"\"\"\n  Start or add a new Neo4j connection\n\n  ## Options:\n\n  - `:url`- a full url to pointing to a running Neo4j server. Please remember you must specify the scheme used to connect to the server. Valid schemes:`bolt`,`bolt+routing`and`neo4j` - the last two being used for connecting to a Neo4j causal cluster.\n  - `:pool_size` - the size of the connection pool. Default: 15\n  - `:timeout` - a connection timeout value defined in milliseconds. Default: 15_000\n  - `:ssl`-`true`, if the connection must be encrypted. Default:`false`\n  - `:prefix`- used for differentiating between multiple connections available in the same app. Default:`:default`\n\n  ## Example of valid configurations (i.e. defined in config/dev.exs) and usage:\n\n  This is the most basic configuration:\n\n      config :bolt_sips, Bolt,\n        url: \"bolt://localhost:7687\"\n\n  and if you need to connect to remote servers:\n\n      config :bolt_sips, Bolt,\n        url: \"bolt://Bilbo:Baggins@hobby-hobbits.dbs.graphenedb.com:24786\",\n        ssl: true,\n        timeout: 15_000,\n\n  Example with a configuration defined in the `config/dev.exs`:\n\n      opts = Application.get_env(:bolt_sips, Bolt)\n      {:ok, pid} = Bolt.Sips.start_link(opts)\n\n      Bolt.Sips.query!(pid, \"CREATE (a:Person {name:'Bob'})\")\n      Bolt.Sips.query!(pid, \"MATCH (a:Person) RETURN a.name AS name\")\n      |> Enum.map(&(&1[\"name\"]))\n\n  Or defining an ad-hoc configuration:\n\n  Example with a configuration defined in the `config/dev.exs`:\n\n      {:ok, _neo} = Bolt.Sips.start_link(url: \"bolt://neo4j:test@localhost\")\n\n      conn = Bolt.Sips.conn()\n      Bolt.Sips.query!(conn, \"return 1 as n\")\n\n  \"\"\"\n  @spec start_link(Keyword.t()) :: Supervisor.on_start()\n  def start_link(opts) do\n    with nil <- Process.whereis(__MODULE__) do\n      Supervisor.start_link(__MODULE__, opts, name: __MODULE__)\n    else\n      pid ->\n        Router.configure(opts)\n        {:ok, pid}\n\n      {pid, node} ->\n        Router.configure(opts)\n        {:ok, {pid, node}}\n    end\n  end\n\n  @doc false\n  def init(opts) do\n    [\n      Registry.child_spec(keys: :unique, name: registry_name()),\n      ConnectionSupervisor,\n      {Router, opts}\n    ]\n    |> Supervisor.init(strategy: :one_for_one)\n  end\n\n  @doc \"\"\"\n  Returns a pool name which can be used to acquire a connection from a pool of servers\n  responsible with a specific type of operations: read, write and route, or all of the above: \"direct\"\n  \"\"\"\n  @spec conn(atom, keyword) :: conn\n  def conn(role \\\\ :direct, opts \\\\ [prefix: :default])\n\n  def conn(role, opts) do\n    prefix = Keyword.get(opts, :prefix, :default)\n\n    with {:ok, conn} <- Router.get_connection(role, prefix) do\n      conn\n    else\n      {:error, e} ->\n        raise Exception, e\n\n      e ->\n        {:error, e}\n    end\n  end\n\n  ## Query\n  ########################\n\n  @doc \"\"\"\n  sends the query (and its parameters) to the server and returns `{:ok, Response.t()}` or\n  `{:error, Error}` otherwise\n  \"\"\"\n  @spec query(conn, String.t()) :: {:ok, Response.t() | [Response.t()]} | {:error, Error.t()}\n  defdelegate query(conn, statement), to: Query\n\n  @doc \"\"\"\n  The same as query/2 but raises a Exception if it fails.\n  Returns the server response otherwise.\n  \"\"\"\n  @spec query!(conn, String.t()) :: Response.t() | [Response.t()] | Exception.t()\n  defdelegate query!(conn, statement), to: Query\n\n  @doc \"\"\"\n  send a query and an associated map of parameters. Returns the server response or an error\n  \"\"\"\n  @spec query(conn, String.t(), map()) ::\n          {:ok, Response.t() | [Response.t()]} | {:error, Error.t()}\n  defdelegate query(conn, statement, params), to: Query\n\n  @doc \"\"\"\n  The same as query/3 but raises a Exception if it fails.\n  \"\"\"\n  @spec query!(conn, String.t(), map()) :: Response.t() | [Response.t()] | Exception.t()\n  defdelegate query!(conn, statement, params), to: Query\n\n  @doc \"\"\"\n  send a query and an associated map of parameters with options. Returns the server response or an error\n  The `opts` keyword list will be passed to `DbConnection.execute/4`.\n  By default, the query will timeout after `15_000` milliseconds, this may be overriden by setting the `timeout`\n  option in `opts`.\n  \"\"\"\n  @spec query(conn, String.t(), map(), Keyword.t()) ::\n          {:ok, Response.t() | [Response.t()]} | {:error, Error.t()}\n  defdelegate query(conn, statement, params, opts), to: Query\n\n  @doc \"\"\"\n  The same as query/4 but raises a Exception if it fails.\n  \"\"\"\n  @spec query!(conn, String.t(), map(), Keyword.t()) ::\n          Response.t() | [Response.t()] | Exception.t()\n  defdelegate query!(conn, statement, params, opts), to: Query\n\n  ## Transaction\n  ########################\n\n  @doc \"\"\"\n    Example:\n\n    ```elixir\n    setup do\n      {:ok, [main_conn: Bolt.Sips.conn()]}\n    end\n\n    test \"execute statements in transaction\", %{main_conn: main_conn} do\n      Bolt.Sips.transaction(main_conn, fn conn ->\n        book =\n          Bolt.Sips.query!(conn, \"CREATE (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n          |> Response.first()\n\n        assert %{\"b\" => g_o_t} = book\n        assert g_o_t.properties[\"title\"] == \"The Game Of Trolls\"\n        Bolt.Sips.rollback(conn, :changed_my_mind)\n      end)\n\n      books = Bolt.Sips.query!(main_conn, \"MATCH (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n      assert Enum.count(books) == 0\n    end\n    ```\n  \"\"\"\n\n  defdelegate transaction(conn, fun, opts \\\\ []), to: DBConnection\n\n  @doc \"\"\"\n  Rollback a database transaction and release lock on connection.\n\n  When inside of a `transaction/3` call does a non-local return, using a\n  `throw/1` to cause the transaction to enter a failed state and the\n  `transaction/3` call returns `{:error, reason}`. If `transaction/3` calls are\n  nested the connection is marked as failed until the outermost transaction call\n  does the database rollback.\n\n  ### Example\n\n      {:error, :oops} = Bolt.Sips.transaction(pool, fn(conn) ->\n        Bolt.Sips.rollback(conn, :oops)\n      end)\n  \"\"\"\n  @spec rollback(DBConnection.t(), reason :: any) :: no_return\n  defdelegate rollback(conn, opts), to: DBConnection\n\n  @doc \"\"\"\n  terminate a pool of connections with the role specified\n  \"\"\"\n  defdelegate terminate_connections(role), to: Router\n\n  @doc \"\"\"\n  peek into the main Router state, and return the internal state controlling the connections\n  to the server/server. Mostly for internal use or for helping driver developers.\n\n  The authentication credentials will be sanitized, if any\n\n  ### Examples:\n\n  iex> Bolt.Sips.info()\n  %{default: %{connections: %{direct: %{\"localhost:7687\" => 0}, routing_query: nil, zorba: %{\"localhost:7687\" => 0}}, user_options: [basic_auth: [username: \"******\", password: \"******\"], socket: Bolt.Sips.Socket, port: 7687, routing_context: %{}, schema: \"bolt\", hostname: \"localhost\", timeout: 15000, ssl: false, with_etls: false, prefix: :default, url: \"bolt://localhost\", pool_size: 10, max_overflow: 2, role: :zorba]}}\n  \"\"\"\n  @spec info() :: map\n  def info(), do: sanitized_info(Bolt.Sips.Router.info())\n\n  @doc \"\"\"\n  extract the routing table from the router\n  \"\"\"\n  @spec routing_table(any) :: map\n  def routing_table(prefix \\\\ :default)\n\n  def routing_table(prefix) do\n    Bolt.Sips.Router.routing_table(prefix)\n  end\n\n  @doc \"\"\"\n  the registry name used across the various driver components\n  \"\"\"\n  @spec registry_name() :: :bolt_sips_registry\n  def registry_name(), do: @registry_name\n\n  @hide_auth [username: \"******\", password: \"******\"]\n  defp sanitized_info(info) when is_map(info) do\n    for {k, v} <- info, into: %{} do\n      {k, Map.update(v, :user_options, @hide_auth, &Keyword.put(&1, :basic_auth, @hide_auth))}\n    end\n  end\n\n  defp sanitized_info(info), do: info\nend\n"
  },
  {
    "path": "lib/mix/tasks/cypher.ex",
    "content": "defmodule Mix.Tasks.Bolt.Cypher do\n  use Mix.Task\n\n  @shortdoc \"Execute a Cypher command\"\n  @recursive true\n\n  @moduledoc \"\"\"\n  Quickly run Cypher commands from a mix task\n\n  ## Command line options\n\n    - `--url`, `-u` - Neo4j server URL\n    - `--ssl`, `-s` - use ssl\n\n  The command line options have lower precedence than the options\n  specified in your `mix.exs` file, if defined.\n\n  Examples:\n\n      MIX_ENV=test mix bolt.cypher \"MATCH (people:Person) RETURN people.name LIMIT 5\"\n\n      Output sample:\n\n      \"MATCH (people:Person) RETURN people.name as name LIMIT 5\"\n      [%{\"name\" => \"Keanu Reeves\"}, %{\"name\" => \"Carrie-Anne Moss\"},\n       %{\"name\" => \"Andy Wachowski\"}, %{\"name\" => \"Lana Wachowski\"},\n       %{\"name\" => \"Joel Silver\"}]\n  \"\"\"\n\n  alias Bolt.Sips, as: Neo4j\n\n  @doc false\n  def run(args) do\n    Application.ensure_all_started(:bolt_sips)\n\n    {cli_opts, args, _} = OptionParser.parse(args, aliases: [u: :url, s: :ssl], switches: [])\n\n    options = run_options(cli_opts, Application.get_env(:bolt_sips, Bolt))\n\n    if args == [], do: Mix.raise(\"Try entering a Cypher command\")\n\n    cypher = args |> List.first()\n\n    {:ok, _pid} = Neo4j.start_link(options)\n\n    # display the cypher command\n    log_cypher(cypher)\n\n    with {:ok, response} <- Neo4j.query(Bolt.Sips.conn(), cypher) do\n      response |> log_response\n    else\n      {:error, [code: code, message: message]} ->\n        \"#{inspect(code)} - cannot execute the command, see error above. Details: #{message}\"\n        |> log_error()\n\n      e ->\n        log_error(\"Unknown error: #{inspect(e)}\")\n    end\n  end\n\n  defp run_options(_, nil) do\n    Mix.raise(\n      \"can't find a valid configuration file, use: MIX_ENV=test mix bolt.cypher \\\"MATCH...\\\", for example\"\n    )\n  end\n\n  defp run_options(args, config) do\n    Keyword.merge(config, args)\n  end\n\n  defp log_cypher(msg), do: Mix.shell().info([:green, \"#{inspect(msg)}\"])\n  defp log_response(msg), do: Mix.shell().info([:yellow, \"#{inspect(msg)}\"])\n  defp log_error(msg), do: Mix.shell().info([:white, \"#{msg}\"])\nend\n"
  },
  {
    "path": "mix.exs",
    "content": "defmodule BoltSips.Mixfile do\n  use Mix.Project\n\n  @version \"2.1.0\"\n  @url_docs \"https://hexdocs.pm/bolt_sips\"\n  @url_github \"https://github.com/florinpatrascu/bolt_sips\"\n\n  def project do\n    [\n      app: :bolt_sips,\n      version: @version,\n      elixir: \"~> 1.7\",\n      elixirc_paths: elixirc_paths(Mix.env()),\n      deps: deps(),\n      package: package(),\n      description: \"Neo4j driver for Elixir, using the fast Bolt protocol\",\n      name: \"Bolt.Sips\",\n      build_embedded: Mix.env() == :prod,\n      start_permanent: Mix.env() == :prod,\n      docs: docs(),\n      dialyzer: [plt_add_apps: [:jason, :poison, :mix], ignore_warnings: \".dialyzer_ignore.exs\"],\n      test_coverage: [\n        tool: ExCoveralls\n      ],\n      preferred_cli_env: [\n        bench: :bench,\n        credo: :dev,\n        bolt_sips: :test,\n        coveralls: :test,\n        \"coveralls.html\": :test,\n        \"coveralls.travis\": :test\n      ],\n      aliases: aliases()\n    ]\n  end\n\n  def application do\n    [\n      extra_applications: [ :logger ]\n    ]\n  end\n\n  defp aliases do\n    [\n      test: [\n        \"test --exclude bolt_v1 --exclude routing --exclude boltkit --exclude enterprise\"\n      ]\n    ]\n  end\n\n  defp elixirc_paths(:test), do: [\"lib\", \"test/support\"]\n  defp elixirc_paths(_), do: [\"lib\"]\n\n  defp package do\n    %{\n      files: [\n        \"lib\",\n        \"mix.exs\",\n        \"LICENSE\"\n      ],\n      licenses: [\"Apache 2.0\"],\n      maintainers: [\n        \"Florin T.PATRASCU\",\n        \"Dmitriy Nesteryuk\",\n        \"Dominique VASSARD\",\n        \"Kristof Semjen\"\n      ],\n      links: %{\n        \"Docs\" => @url_docs,\n        \"Github\" => @url_github\n      }\n    }\n  end\n\n  defp docs do\n    [\n      name: \"Bolt.Sips\",\n      logo: \"assets/bolt_sips_white_transparent.png\",\n      assets: \"assets\",\n      source_ref: \"v#{@version}\",\n      source_url: @url_github,\n      main: \"Bolt.Sips\",\n      extra_section: \"guides\",\n      extras: [\n        \"README.md\",\n        \"CHANGELOG.md\",\n        \"docs/getting-started.md\",\n        \"docs/features/configuration.md\",\n        \"docs/features/using-cypher.md\",\n        \"docs/features/using-temporal-and-spatial-types.md\",\n        \"docs/features/about-transactions.md\",\n        \"docs/features/about-encoding.md\",\n        \"docs/features/routing.md\",\n        \"docs/features/multi-tenancy.md\",\n        \"docs/features/using-with-phoenix.md\"\n      ]\n    ]\n  end\n\n  # Type \"mix help deps\" for more examples and options\n  defp deps do\n    [\n      {:db_connection, \"~> 2.4.2\"},\n      {:jason, \"~> 1.4\", optional: true},\n      {:poison, \"~> 5.0\", optional: true},\n\n      # Testing dependencies\n      {:excoveralls, \"~> 0.15.0\", optional: true, only: [:test, :dev]},\n      {:mix_test_watch, \"~> 1.1.0\", only: [:dev, :test]},\n      {:porcelain, \"~> 2.0.3\", only: [:test, :dev], runtime: false},\n      {:uuid, \"~> 1.1.8\", only: [:test, :dev], runtime: false},\n      {:tzdata, \"~> 1.1\", only: [:test, :dev]},\n\n      # Benchmarking dependencies\n      {:benchee, \"~> 1.1.0\", optional: true, only: [:dev, :test]},\n      {:benchee_html, \"~> 1.0.0\", optional: true, only: [:dev]},\n\n      # Linting dependencies\n      {:credo, \"~> 1.6.7\", only: [:dev]},\n      {:dialyxir, \"~> 1.2.0\", only: [:dev], runtime: false},\n      # mix eye_drops\n      {:eye_drops, github: \"florinpatrascu/eye_drops\", only: [:dev, :test], runtime: false},\n\n      # Documentation dependencies\n      # Run me like this: `mix docs`\n      {:ex_doc, \"~> 0.29\", only: :dev, runtime: false}\n    ]\n  end\nend\n"
  },
  {
    "path": "requirements.txt",
    "content": "boto==2.48.0\ncertifi\nclick<8,>=7\ndocker\nurllib3\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_all_bolt_version_test.exs",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolAllBoltVersionTest do\n  use Bolt.Sips.InternalCase\n  alias Bolt.Sips.Internals.BoltProtocol\n\n  test \"works for small queries\", %{port: port, bolt_version: bolt_version} do\n    string = Enum.to_list(0..100) |> Enum.join()\n\n    query = \"\"\"\n      RETURN $string as string\n    \"\"\"\n\n    params = %{string: string}\n\n    [{:success, _} | records] =\n      BoltProtocol.run_statement(:gen_tcp, port, bolt_version, query, params)\n\n    assert [record: [^string], success: _] = records\n  end\n\n  test \"works for big queries\", %{port: port, bolt_version: bolt_version} do\n    string = Enum.to_list(0..25_000) |> Enum.join()\n\n    query = \"\"\"\n      RETURN $string as string\n    \"\"\"\n\n    params = %{string: string}\n\n    [{:success, _} | records] =\n      BoltProtocol.run_statement(:gen_tcp, port, bolt_version, query, params)\n\n    assert [record: [^string], success: _] = records\n  end\n\n  test \"returns errors for wrong cypher queris\", %{port: port, bolt_version: bolt_version} do\n    assert %Bolt.Sips.Internals.Error{type: :cypher_error} =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"What?\")\n  end\n\n  test \"allows to recover from error with reset\", %{port: port, bolt_version: bolt_version} do\n    assert %Bolt.Sips.Internals.Error{type: :cypher_error} =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"What?\")\n\n    assert :ok = BoltProtocol.reset(:gen_tcp, port, bolt_version)\n\n    assert [{:success, _} | _] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 as num\")\n  end\n\n  test \"returns proper error when using a bad session\", %{port: port, bolt_version: bolt_version} do\n    assert %Bolt.Sips.Internals.Error{type: :cypher_error} =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"What?\")\n\n    error = BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 as num\")\n\n    assert %Bolt.Sips.Internals.Error{} = error\n    assert error.message =~ ~r/The session is in a failed state/\n  end\n\n  test \"returns proper error when misusing reset\", %{\n    port: port,\n    bolt_version: bolt_version\n  } do\n    :gen_tcp.close(port)\n    assert %Bolt.Sips.Internals.Error{} = BoltProtocol.reset(:gen_tcp, port, bolt_version)\n  end\n\n  test \"returns proper error when using a closed port\", %{port: port, bolt_version: bolt_version} do\n    :gen_tcp.close(port)\n\n    assert %Bolt.Sips.Internals.Error{type: :connection_error} =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 as num\")\n  end\n\n  test \"an invalid parameter value yields an error\", %{port: port, bolt_version: bolt_version} do\n    cypher = \"MATCH (n:Person {invalid: {a_tuple}}) RETURN TRUE\"\n\n    assert_raise Bolt.Sips.Internals.PackStreamError, ~r/^unable to encode/i, fn ->\n      BoltProtocol.run_statement(:gen_tcp, port, bolt_version, cypher, %{\n        a_tuple: {:error, \"don't work\"}\n      })\n    end\n  end\n\n  test \"encode value -128\", %{port: port, bolt_version: bolt_version} do\n    query = \"CREATE (t:Test) SET t.value = $value RETURN t\"\n\n    [{:success, _} | records] =\n      BoltProtocol.run_statement(:gen_tcp, port, bolt_version, query, %{value: -128})\n\n    assert [\n             record: [\n               %Bolt.Sips.Types.Node{\n                 labels: [\"Test\"],\n                 properties: %{\"value\" => -128}\n               }\n             ],\n             success: _\n           ] = records\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_bolt_v1_test.exs",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolV1Test do\n  use Bolt.Sips.InternalCase\n  @moduletag :bolt_v1\n  alias Bolt.Sips.Internals.BoltProtocol\n\n  test \"allows to recover from error with ack_failure\", %{port: port, bolt_version: bolt_version} do\n    assert %Bolt.Sips.Internals.Error{type: :cypher_error} =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"What?\")\n\n    assert :ok = BoltProtocol.ack_failure(:gen_tcp, port, bolt_version)\n\n    assert [{:success, _} | _] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 as num\")\n  end\n\n  test \"returns proper error when misusing ack_failure and reset\", %{\n    port: port,\n    bolt_version: bolt_version\n  } do\n    assert %Bolt.Sips.Internals.Error{} = BoltProtocol.ack_failure(:gen_tcp, port, bolt_version)\n  end\n\n  test \"works within a transaction\", %{port: port, bolt_version: bolt_version} do\n    assert [{:success, _}, {:success, _}] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"BEGIN\")\n\n    assert [{:success, _} | _] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 as num\")\n\n    assert [{:success, _}, {:success, _}] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"COMMIT\")\n  end\n\n  test \"works with rolled-back transactions\", %{port: port, bolt_version: bolt_version} do\n    assert [{:success, _}, {:success, _}] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"BEGIN\")\n\n    assert [{:success, _} | _] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 as num\")\n\n    assert [{:success, _}, {:success, _}] =\n             BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"ROLLBACK\")\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_bolt_v2_test.exs",
    "content": "defmodule Bolt.Sips.Internals.BoltProtoolBoltV2Test do\n  use Bolt.Sips.InternalCase\n  @moduletag :bolt_v2\n\n  alias Bolt.Sips.Internals.BoltProtocol\n\n  describe \"Temporal types\" do\n    test \"Local date\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"d\"]},\n               record: [~D[2017-01-01]],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN date('2017-01-01') as d\"\n               )\n    end\n\n    test \"Time with Timezone Offset\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"t\"]},\n               record: [\n                 %Bolt.Sips.Types.TimeWithTZOffset{\n                   time: ~T[12:45:30.250000],\n                   timezone_offset: 3600\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN time('12:45:30.25+01:00') AS t\"\n               )\n    end\n\n    test \"Local time\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"t\"]},\n               record: [~T[12:45:30.250000]],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN localtime('12:45:30.25') AS t\"\n               )\n    end\n\n    test \"Duration\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"d\"]},\n               record: [\n                 %Bolt.Sips.Types.Duration{\n                   days: 34,\n                   hours: 0,\n                   minutes: 0,\n                   months: 3,\n                   nanoseconds: 5550,\n                   seconds: 54,\n                   weeks: 0,\n                   years: 1\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN duration('P1Y3M34DT54.00000555S') AS d\"\n               )\n    end\n\n    test \"Local datetime\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"d\"]},\n               record: [~N[2018-04-05 12:34:00.654321]],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN localdatetime('2018-04-05T12:34:00.654321') AS d\"\n               )\n    end\n\n    test \"datetime with timezone offset\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"d\"]},\n               record: [\n                 %Bolt.Sips.Types.DateTimeWithTZOffset{\n                   naive_datetime: ~N[2018-04-05 12:34:23.654321],\n                   timezone_offset: 3600\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN datetime('2018-04-05T12:34:23.654321+01:00') AS d\"\n               )\n    end\n\n    test \"datetime with timezone id\", %{port: port, bolt_version: bolt_version} do\n      dt =\n        Bolt.Sips.TypesHelper.datetime_with_micro(~N[2018-04-05T12:34:23.654321], \"Europe/Berlin\")\n\n      assert [\n               success: %{\"fields\" => [\"d\"]},\n               record: [^dt],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN datetime('2018-04-05T12:34:23.654321[Europe/Berlin]') AS d\"\n               )\n    end\n  end\n\n  describe \"Spatial types\" do\n    test \"Point 2D cartesian\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"p\"]},\n               record: [\n                 %Bolt.Sips.Types.Point{\n                   crs: \"cartesian\",\n                   height: nil,\n                   latitude: nil,\n                   longitude: nil,\n                   srid: 7203,\n                   x: 40.0,\n                   y: 45.0,\n                   z: nil\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN point({x: 40, y: 45}) AS p\"\n               )\n    end\n\n    test \"Point2D geographic\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"p\"]},\n               record: [\n                 %Bolt.Sips.Types.Point{\n                   crs: \"wgs-84\",\n                   height: nil,\n                   latitude: 45.0,\n                   longitude: 40.0,\n                   srid: 4326,\n                   x: 40.0,\n                   y: 45.0,\n                   z: nil\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN point({longitude: 40, latitude: 45}) AS p\"\n               )\n    end\n\n    test \"Point 3D cartesian\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"p\"]},\n               record: [\n                 %Bolt.Sips.Types.Point{\n                   crs: \"cartesian-3d\",\n                   height: nil,\n                   latitude: nil,\n                   longitude: nil,\n                   srid: 9157,\n                   x: 40.0,\n                   y: 45.0,\n                   z: 150.0\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN point({x: 40, y: 45, z: 150}) AS p\"\n               )\n    end\n\n    test \"Point 3D geographic\", %{port: port, bolt_version: bolt_version} do\n      assert [\n               success: %{\"fields\" => [\"p\"]},\n               record: [\n                 %Bolt.Sips.Types.Point{\n                   crs: \"wgs-84-3d\",\n                   height: 150.0,\n                   latitude: 45.0,\n                   longitude: 40.0,\n                   srid: 4979,\n                   x: 40.0,\n                   y: 45.0,\n                   z: 150.0\n                 }\n               ],\n               success: %{\"type\" => \"r\"}\n             ] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN point({longitude: 40, latitude: 45, height: 150}) AS p\"\n               )\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_bolt_v3_test.exs",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolBoltV3Test do\n  use ExUnit.Case, async: true\n  @moduletag :bolt_v3\n\n  alias Bolt.Sips.Internals.BoltProtocol\n  alias Bolt.Sips.Metadata\n  alias Bolt.Sips.Utils\n\n  setup do\n    app_config = Application.get_env(:bolt_sips, Bolt)\n\n    port = Keyword.get(app_config, :port, 7687)\n    auth = {app_config[:basic_auth][:username], app_config[:basic_auth][:password]}\n\n    config =\n      app_config\n      |> Keyword.put(:port, port)\n      |> Keyword.put(:auth, auth)\n      |> Utils.default_config()\n\n    {:ok, port} =\n      config[:hostname]\n      |> String.to_charlist()\n      |> :gen_tcp.connect(config[:port],\n        active: false,\n        mode: :binary,\n        packet: :raw\n      )\n\n    {:ok, bolt_version} = BoltProtocol.handshake(:gen_tcp, port, [])\n    {:ok, _} = BoltProtocol.hello(:gen_tcp, port, bolt_version, auth)\n\n    on_exit(fn ->\n      :gen_tcp.close(port)\n    end)\n\n    {:ok, config: config, port: port, bolt_version: bolt_version}\n  end\n\n  describe \"run/7\" do\n    test \"(no params, no metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\")\n    end\n\n    test \"(params, no metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(:gen_tcp, port, bolt_version, \"RETURN $num AS num\", %{num: 14})\n    end\n\n    test \"(no params, metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\", %{}, metadata)\n    end\n\n    test \"(params, metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN $num AS num\",\n                 %{num: 14},\n                 metadata\n               )\n    end\n\n    test \"(no params, no metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\", %{}, %{},\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"(params, no metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN $num AS num\",\n                 %{num: 14},\n                 %{},\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"(no params, metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\", %{}, metadata,\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"(params, metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert {:ok, {:success, _}} =\n               BoltProtocol.run(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN $num AS num\",\n                 %{num: 14},\n                 metadata,\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"Bolt >=2 run syntax should upscale nicely\", %{port: port, bolt_version: bolt_version} do\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocol.run(:gen_tcp, port, bolt_version, \"RETURN 5 AS num\", %{},\n                 recv_timeout: 5000\n               )\n    end\n  end\n\n  describe \"run_statement/7\" do\n    test \"(no params, no metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\")\n    end\n\n    test \"(params, no metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN $num AS num\", %{\n                 num: 14\n               })\n    end\n\n    test \"(no params, metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN 1 AS num\",\n                 %{},\n                 metadata\n               )\n    end\n\n    test \"(params, metadata, no options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN $num AS num\",\n                 %{num: 14},\n                 metadata\n               )\n    end\n\n    test \"(no params, no metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN 1 AS num\",\n                 %{},\n                 %{},\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"(params, no metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN $num AS num\",\n                 %{num: 14},\n                 %{},\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"(no params, metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN 1 AS num\",\n                 %{},\n                 metadata,\n                 recv_timeout: 5000\n               )\n    end\n\n    test \"(params, metadata, options)\", %{port: port, bolt_version: bolt_version} do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 1_000})\n\n      assert [success: _, record: _, success: _] =\n               BoltProtocol.run_statement(\n                 :gen_tcp,\n                 port,\n                 bolt_version,\n                 \"RETURN $num AS num\",\n                 %{num: 14},\n                 metadata,\n                 recv_timeout: 5000\n               )\n    end\n  end\n\n  describe \"transactions\" do\n    test \"Successful committed transaction (begin + run_statement + commit)\", %{\n      port: port,\n      bolt_version: bolt_version\n    } do\n      {:ok, _} = BoltProtocol.begin(:gen_tcp, port, bolt_version)\n\n      [success: _, record: _, success: _] =\n        BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\")\n\n      assert {:ok, _} = BoltProtocol.commit(:gen_tcp, port, bolt_version)\n    end\n\n    test \"Successful rollbacked transaction (begin + run_statement + rollback)\", %{\n      port: port,\n      bolt_version: bolt_version\n    } do\n      {:ok, _} = BoltProtocol.begin(:gen_tcp, port, bolt_version)\n\n      [success: _, record: _, success: _] =\n        BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETURN 1 AS num\")\n\n      assert :ok = BoltProtocol.rollback(:gen_tcp, port, bolt_version)\n    end\n\n    test \"If an error occurs during transaction, ROLLBACK is performed at server-level\", %{\n      port: port,\n      bolt_version: bolt_version\n    } do\n      {:ok, _} = BoltProtocol.begin(:gen_tcp, port, bolt_version)\n\n      [success: _, record: _, success: _] =\n        BoltProtocol.run_statement(\n          :gen_tcp,\n          port,\n          bolt_version,\n          \"CREATE (t:Test {value: 555}) RETURN t\"\n        )\n\n      %Bolt.Sips.Internals.Error{} =\n        BoltProtocol.run_statement(:gen_tcp, port, bolt_version, \"RETRN 1 AS num\")\n\n      assert :ok = BoltProtocol.reset(:gen_tcp, port, bolt_version)\n\n      [success: _, record: [0], success: _] =\n        BoltProtocol.run_statement(\n          :gen_tcp,\n          port,\n          bolt_version,\n          \"MATCH (t:Test {value: 555}) RETURN COUNT(t) AS num_node\"\n        )\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_v1_test.exs",
    "content": "defmodule BoltProtocolV1.Sips.Internals.BoltProtocolV1Test do\n  use ExUnit.Case, async: true\n  @moduletag :bolt_v1\n\n  alias Bolt.Sips.Internals.BoltProtocolV1\n  alias Bolt.Sips.Internals.BoltVersionHelper\n\n  setup do\n    app_config = Application.get_env(:bolt_sips, Bolt)\n\n    port = Keyword.get(app_config, :port, 7687)\n    auth = {app_config[:basic_auth][:username], app_config[:basic_auth][:password]}\n\n    config =\n      app_config\n      |> Keyword.put(:port, port)\n      |> Keyword.put(:auth, auth)\n\n    {:ok, port} =\n      :gen_tcp.connect(config[:url], config[:port], active: false, mode: :binary, packet: :raw)\n\n    on_exit(fn ->\n      :gen_tcp.close(port)\n    end)\n\n    {:ok, config: config, port: port}\n  end\n\n  test \"handshake/3\", %{port: port} do\n    assert {:ok, version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n    assert is_integer(version)\n    assert version in BoltVersionHelper.available_versions()\n  end\n\n  describe \"init/5:\" do\n    test \"ok\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n\n      assert {:ok, _} =\n               BoltProtocolV1.init(\n                 :gen_tcp,\n                 port,\n                 1,\n                 config[:auth],\n                 []\n               )\n    end\n\n    test \"invalid auth\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n\n      assert {:error, _} =\n               BoltProtocolV1.init(\n                 :gen_tcp,\n                 port,\n                 1,\n                 {config[:basic_auth][:username], \"wrong!\"},\n                 []\n               )\n    end\n  end\n\n  describe \"run/6:\" do\n    test \"ok without parameters\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n      assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n    end\n\n    test \"ok with parameters\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n      assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN $num AS num\", %{num: 5}, [])\n    end\n\n    test \"returns IGNORED when sending RUN on a FAILURE state\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n      assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n      assert {:error, _} = BoltProtocolV1.run(:gen_tcp, port, 1, \"Invalid cypher\", %{}, [])\n\n      assert {:error, _} = BoltProtocolV1.pull_all(:gen_tcp, port, 1, [])\n    end\n\n    test \"ok after IGNORED AND ACK_FAILURE\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n      assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n      assert {:error, _} = BoltProtocolV1.run(:gen_tcp, port, 1, \"Invalid cypher\", %{}, [])\n\n      assert {:error, _} = BoltProtocolV1.pull_all(:gen_tcp, port, 1, [])\n      :ok = BoltProtocolV1.ack_failure(:gen_tcp, port, 1, [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n\n      assert {:ok,\n              [\n                record: [1],\n                success: %{\"type\" => \"r\"}\n              ]} = BoltProtocolV1.pull_all(:gen_tcp, port, 1, [])\n    end\n  end\n\n  test \"pull_all/4 (successful)\", %{config: config, port: port} do\n    assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n    assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n\n    assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n             BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n\n    assert {:ok,\n            [\n              record: [1],\n              success: %{\"type\" => \"r\"}\n            ]} = BoltProtocolV1.pull_all(:gen_tcp, port, 1, [])\n  end\n\n  test \"run_statement/6 (successful)\", %{config: config, port: port} do\n    assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n    assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n    assert [_ | _] = BoltProtocolV1.run_statement(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n  end\n\n  test \"discard_all/4 (successful)\", %{config: config, port: port} do\n    assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n    assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n\n    assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n             BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n\n    assert :ok = BoltProtocolV1.discard_all(:gen_tcp, port, 1, [])\n  end\n\n  test \"ack_failure/4 (successful)\", %{config: config, port: port} do\n    assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n    assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n    assert {:error, _} = BoltProtocolV1.run(:gen_tcp, port, 1, \"Invalid cypher\", %{}, [])\n    assert :ok = BoltProtocolV1.ack_failure(:gen_tcp, port, 1, [])\n  end\n\n  describe \"reset/4\" do\n    test \"ok\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n      assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n\n      assert :ok = BoltProtocolV1.reset(:gen_tcp, port, 1, [])\n    end\n\n    test \"ok during process\", %{config: config, port: port} do\n      assert {:ok, _bolt_version} = BoltProtocolV1.handshake(:gen_tcp, port, [])\n      assert {:ok, _} = BoltProtocolV1.init(:gen_tcp, port, 1, config[:auth], [])\n      assert {:error, _} = BoltProtocolV1.run(:gen_tcp, port, 1, \"Invalid cypher\", %{}, [])\n\n      {:error, _} = BoltProtocolV1.pull_all(:gen_tcp, port, 1, [])\n      assert :ok = BoltProtocolV1.reset(:gen_tcp, port, 1, [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV1.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, [])\n\n      assert {:ok, [{:record, _}, {:success, _}]} = BoltProtocolV1.pull_all(:gen_tcp, port, 1, [])\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_protocol_v3_test.exs",
    "content": "defmodule Bolt.Sips.Internals.BoltProtocolV3Test do\n  use ExUnit.Case, async: true\n  @moduletag :bolt_v3\n\n  alias Bolt.Sips.Internals.BoltProtocol\n  alias Bolt.Sips.Internals.BoltProtocolV3\n  alias Bolt.Sips.Metadata\n\n  setup do\n    app_config = Application.get_env(:bolt_sips, Bolt)\n\n    port = Keyword.get(app_config, :port, 7687)\n    auth = {app_config[:basic_auth][:username], app_config[:basic_auth][:password]}\n\n    config =\n      app_config\n      |> Keyword.put(:port, port)\n      |> Keyword.put(:auth, auth)\n      |> Bolt.Sips.Utils.default_config()\n\n    {:ok, port} =\n      config[:hostname]\n      |> String.to_charlist()\n      |> :gen_tcp.connect(config[:port],\n        active: false,\n        mode: :binary,\n        packet: :raw\n      )\n\n    {:ok, _} = BoltProtocol.handshake(:gen_tcp, port, [])\n\n    on_exit(fn ->\n      :gen_tcp.close(port)\n    end)\n\n    {:ok, config: config, port: port}\n  end\n\n  describe \"hello/5:\" do\n    test \"ok\", %{config: config, port: port} do\n      assert {:ok, _} =\n               BoltProtocolV3.hello(\n                 :gen_tcp,\n                 port,\n                 3,\n                 config[:auth],\n                 []\n               )\n    end\n\n    test \"invalid auth\", %{config: config, port: port} do\n      assert {:error, _} =\n               BoltProtocolV3.hello(\n                 :gen_tcp,\n                 port,\n                 3,\n                 {config[:basic_auth][:username], \"wrong!\"},\n                 []\n               )\n    end\n  end\n\n  test \"goodbye/5\", %{config: config, port: port} do\n    assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n    assert :ok = BoltProtocolV3.goodbye(:gen_tcp, port, 3)\n  end\n\n  describe \"run/7:\" do\n    test \"ok without parameters nor metadata\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n    end\n\n    test \"ok without parameters with metadata\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 10_000})\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, metadata, [])\n    end\n\n    test \"ok with parameters without metadata\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN $num AS num\", %{num: 5}, %{}, [])\n    end\n\n    test \"ok with parameters with metadata\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 10_000})\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(\n                 :gen_tcp,\n                 port,\n                 3,\n                 \"RETURN $num AS num\",\n                 %{num: 5},\n                 metadata,\n                 []\n               )\n    end\n\n    test \"returns IGNORED when sending RUN on a FAILURE state\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n      assert {:error, _} = BoltProtocolV3.run(:gen_tcp, port, 3, \"Invalid cypher\", %{}, %{}, [])\n\n      assert {:error, _} = BoltProtocol.pull_all(:gen_tcp, port, 3, [])\n    end\n\n    test \"ok after IGNORED and RESET\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n      assert {:error, _} = BoltProtocolV3.run(:gen_tcp, port, 3, \"Invalid cypher\", %{}, %{}, [])\n\n      assert {:error, _} = BoltProtocol.pull_all(:gen_tcp, port, 3, [])\n      :ok = BoltProtocol.reset(:gen_tcp, port, 3, [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n\n      assert {:ok,\n              [\n                record: [1],\n                success: %{\"type\" => \"r\"}\n              ]} = BoltProtocol.pull_all(:gen_tcp, port, 3, [])\n    end\n  end\n\n  test \"run_statement/7 (successful)\", %{config: config, port: port} do\n    assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n    assert [_ | _] =\n             BoltProtocolV3.run_statement(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n  end\n\n  test \"pull_all/4 (successful)\", %{config: config, port: port} do\n    assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n    assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n             BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n\n    assert {:ok,\n            [\n              record: [1],\n              success: %{\"type\" => \"r\"}\n            ]} = BoltProtocol.pull_all(:gen_tcp, port, 3, [])\n  end\n\n  test \"discard_all/4 (successful)\", %{config: config, port: port} do\n    assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n    assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n             BoltProtocolV3.run(:gen_tcp, port, 1, \"RETURN 1 AS num\", %{}, %{}, [])\n\n    assert :ok = BoltProtocol.discard_all(:gen_tcp, port, 3, [])\n  end\n\n  test \"reset/4 (successful)\", %{config: config, port: port} do\n    assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n    assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n             BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n\n    assert :ok = BoltProtocol.reset(:gen_tcp, port, 3, [])\n  end\n\n  describe \"Transaction management\" do\n    test \"Open a transaction without metadata\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n      {:ok, _} = BoltProtocolV3.begin(:gen_tcp, port, 3, %{}, [])\n    end\n\n    # Work only with Neo4j Enterprise\n    @tag :enterprise\n    test \"Open a transaction with metadata\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n      {:ok, metadata} = Metadata.new(%{bookmarks: [\"neo4j:bookmark:v1:tx234\"], tx_timeout: 1_000})\n\n      {:ok, _} = BoltProtocolV3.begin(:gen_tcp, port, 3, metadata, [])\n    end\n\n    test \"Commit a transaction\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n      {:ok, _} = BoltProtocolV3.begin(:gen_tcp, port, 3, %{}, [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n\n      assert {:ok, _} = BoltProtocol.pull_all(:gen_tcp, port, 3, [])\n      {:ok, %{\"bookmark\" => _}} = BoltProtocolV3.commit(:gen_tcp, port, 3, [])\n    end\n\n    test \"Rollback a transaction\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(:gen_tcp, port, 3, config[:auth], [])\n\n      {:ok, _} = BoltProtocolV3.begin(:gen_tcp, port, 3, %{}, [])\n\n      assert {:ok, {:success, %{\"fields\" => [\"num\"]}}} =\n               BoltProtocolV3.run(:gen_tcp, port, 3, \"RETURN 1 AS num\", %{}, %{}, [])\n\n      BoltProtocol.discard_all(:gen_tcp, port, 3, [])\n      assert :ok = BoltProtocolV3.rollback(:gen_tcp, port, 3, [])\n    end\n\n    # It works with Neo4j Enterprise only\n    @tag :enterprise\n    test \"With socket instead of gent_tcp\", %{config: config, port: port} do\n      assert {:ok, _} = BoltProtocolV3.hello(Bolt.Sips.Socket, port, 3, config[:auth], [])\n      {:ok, metadata} = Metadata.new(%{bookmarks: [\"neo4j:bookmark:v1:tx234\"], tx_timeout: 1_000})\n\n      {:ok, _} = BoltProtocolV3.begin(Bolt.Sips.Socket, port, 3, metadata, [])\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/bolt_version_helper_test.exs",
    "content": "defmodule Bolt.Sips.Internals.BoltVersionHelperTest do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.Internals.BoltVersionHelper\n\n  alias Bolt.Sips.Internals.BoltVersionHelper\n\n  test \"available_bolt_versions/0 returns a list\" do\n    assert [_ | _] = BoltVersionHelper.available_versions()\n  end\n\n  describe \"previous/1\" do\n    test \"successfully return the previous version\" do\n      assert 1 == BoltVersionHelper.previous(2)\n      assert 2 == BoltVersionHelper.previous(3)\n      assert 3 == BoltVersionHelper.previous(4)\n    end\n\n    test \"return nil if there is no previous version\" do\n      assert nil == BoltVersionHelper.previous(1)\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/logger_test.exs",
    "content": "defmodule Bolt.Sips.Internals.LoggerTest do\n  use ExUnit.Case\n  import ExUnit.CaptureLog\n\n  alias Bolt.Sips.Internals.Logger\n\n  test \"Log from formed message\" do\n    assert capture_log(fn -> Logger.log_message(:client, {:success, %{data: \"ok\"}}) end) =~\n             \"C: SUCCESS ~ %{data: \\\"ok\\\"}\"\n  end\n\n  test \"Log from non-formed message\" do\n    assert capture_log(fn -> Logger.log_message(:client, :success, %{data: \"ok\"}) end) =~\n             \"C: SUCCESS ~ %{data: \\\"ok\\\"}\"\n  end\n\n  # Excluded as another test has a long result and therefore a long hex and slow down tests\n  # test \"Log hex data\" do\n  #   assert capture_log(fn -> Logger.log_message(:client, :success, <<0x01, 0xAF>>, :hex) end) =~\n  #            \"C: SUCCESS ~ <<0x1, 0xAF>>\"\n  # end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/decoder_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.PackStream.Decoder\n  alias Bolt.Sips.Internals.PackStreamError\n  alias Bolt.Sips.Internals.BoltVersionHelper\n  alias Bolt.Sips.Types\n\n  describe \"Decode common types\" do\n    Enum.each(BoltVersionHelper.available_versions(), fn bolt_version ->\n      test \"Null (bolt_version: #{bolt_version})\" do\n        assert [nil] == Decoder.decode(<<0xC0>>, unquote(bolt_version))\n      end\n\n      test \"Boolean (bolt_version: #{bolt_version})\" do\n        assert [false] == Decoder.decode(<<0xC2>>, unquote(bolt_version))\n        assert [true] == Decoder.decode(<<0xC3>>, unquote(bolt_version))\n      end\n\n      test \"Float (bolt_version: #{bolt_version})\" do\n        assert [7.7] ==\n                 Decoder.decode(\n                   <<0xC1, 0x40, 0x1E, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCD>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"String (bolt_version: #{bolt_version})\" do\n        assert [\"hello\"] ==\n                 Decoder.decode(<<0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F>>, unquote(bolt_version))\n      end\n\n      test \"List (bolt_version: #{bolt_version})\" do\n        assert [[]] == Decoder.decode(<<0x90>>, unquote(bolt_version))\n        assert [[2, 4]] == Decoder.decode(<<0x92, 0x2, 0x4>>, unquote(bolt_version))\n      end\n\n      test \"Integer (bolt_version: #{bolt_version})\" do\n        assert [42] == Decoder.decode(<<0x2A>>, unquote(bolt_version))\n      end\n\n      test \"Node (bolt_version: #{bolt_version})\" do\n        node =\n          <<0x91, 0xB3, 0x4E, 0x11, 0x91, 0x86, 0x50, 0x65, 0x72, 0x73, 0x6F, 0x6E, 0xA2, 0x84,\n            0x6E, 0x61, 0x6D, 0x65, 0xD0, 0x10, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x6B, 0x20,\n            0x52, 0x6F, 0x74, 0x68, 0x66, 0x75, 0x73, 0x73, 0x89, 0x62, 0x6F, 0x6C, 0x74, 0x5F,\n            0x73, 0x69, 0x70, 0x73, 0xC3>>\n\n        assert [\n                 [\n                   %Bolt.Sips.Types.Node{\n                     id: 17,\n                     labels: [\"Person\"],\n                     properties: %{\"bolt_sips\" => true, \"name\" => \"Patrick Rothfuss\"}\n                   }\n                 ]\n               ] == Decoder.decode(node, unquote(bolt_version))\n      end\n\n      test \"Relationship (bolt_version: #{bolt_version})\" do\n        rel = <<0x91, 0xB5, 0x52, 0x50, 0x46, 0x43, 0x85, 0x57, 0x52, 0x4F, 0x54, 0x45, 0xA0>>\n\n        assert [\n                 [\n                   %Bolt.Sips.Types.Relationship{\n                     end: 67,\n                     id: 80,\n                     properties: %{},\n                     start: 70,\n                     type: \"WROTE\"\n                   }\n                 ]\n               ] = Decoder.decode(rel, unquote(bolt_version))\n      end\n\n      # test \"UnboundRelationship (bolt_version: #{bolt_version})\" do\n      # end\n\n      test \"Path (bolt_version: #{bolt_version})\" do\n        path =\n          <<0x91, 0xB3, 0x50, 0x92, 0xB3, 0x4E, 0x30, 0x90, 0xA2, 0x84, 0x6E, 0x61, 0x6D, 0x65,\n            0x85, 0x41, 0x6C, 0x69, 0x63, 0x65, 0x89, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x73, 0x69,\n            0x70, 0x73, 0xC3, 0xB3, 0x4E, 0x38, 0x90, 0xA2, 0x84, 0x6E, 0x61, 0x6D, 0x65, 0x83,\n            0x42, 0x6F, 0x62, 0x89, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x73, 0x69, 0x70, 0x73, 0xC3,\n            0x91, 0xB3, 0x72, 0x13, 0x85, 0x4B, 0x4E, 0x4F, 0x57, 0x53, 0xA0, 0x92, 0x1, 0x1>>\n\n        [\n          [\n            %Bolt.Sips.Types.Path{\n              nodes: [\n                %Bolt.Sips.Types.Node{\n                  id: 48,\n                  labels: [],\n                  properties: %{\"bolt_sips\" => true, \"name\" => \"Alice\"}\n                },\n                %Bolt.Sips.Types.Node{\n                  id: 56,\n                  labels: [],\n                  properties: %{\"bolt_sips\" => true, \"name\" => \"Bob\"}\n                }\n              ],\n              relationships: [\n                %Bolt.Sips.Types.UnboundRelationship{\n                  end: nil,\n                  id: 19,\n                  properties: %{},\n                  start: nil,\n                  type: \"KNOWS\"\n                }\n              ],\n              sequence: [1, 1]\n            }\n          ]\n        ] = Decoder.decode(path, unquote(bolt_version))\n      end\n\n      test \"Fails to decode something unknown (bolt_version: #{bolt_version})\" do\n        assert_raise PackStreamError, fn ->\n          Decoder.decode(0xFF, unquote(bolt_version))\n        end\n      end\n    end)\n  end\n\n  describe \"Decodes Bolt >= 2 specific types\" do\n    BoltVersionHelper.available_versions()\n    |> Enum.filter(&(&1 >= 2))\n    |> Enum.each(fn bolt_version ->\n      test \"Local Date (bolt_version: #{bolt_version})\" do\n        assert [~D[2013-12-15]] ==\n                 Decoder.decode(<<0xB1, 0x44, 0xC9, 0x3E, 0xB6>>, unquote(bolt_version))\n      end\n\n      test \"Local Time (bolt_version: #{bolt_version})\" do\n        assert [~T[09:34:23.654321]] ==\n                 Decoder.decode(\n                   <<0xB1, 0x74, 0xCB, 0x0, 0x0, 0x1F, 0x58, 0x31, 0xDF, 0x9B, 0x68>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Local DateTime (bolt_version: #{bolt_version})\" do\n        assert [~N[2018-04-05 12:34:00.654321]] ==\n                 Decoder.decode(\n                   <<0xB2, 0x64, 0xCA, 0x5A, 0xC6, 0x17, 0xB8, 0xCA, 0x27, 0x0, 0x25, 0x68>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Time with timezone offset (bolt_version: #{bolt_version})\" do\n        ttz = Types.TimeWithTZOffset.create(~T[12:45:30.654321], 3600)\n\n        assert [ttz] ==\n                 Decoder.decode(\n                   <<0xB2, 0x54, 0xCB, 0x0, 0x0, 0x29, 0xC6, 0x10, 0x55, 0xC9, 0x68, 0xC9, 0xE,\n                     0x10>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Datetime with timezone id (bolt_version: #{bolt_version})\" do\n        dt =\n          Bolt.Sips.TypesHelper.datetime_with_micro(\n            ~N[2016-05-24 13:26:08.654321],\n            \"Europe/Berlin\"\n          )\n\n        assert [dt] ==\n                 Decoder.decode(\n                   <<0xB3, 0x66, 0xCA, 0x57, 0x44, 0x56, 0x70, 0xCA, 0x27, 0x0, 0x25, 0x68, 0x8D,\n                     0x45, 0x75, 0x72, 0x6F, 0x70, 0x65, 0x2F, 0x42, 0x65, 0x72, 0x6C, 0x69,\n                     0x6E>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Datetime with timezone offset (bolt_version: #{bolt_version})\" do\n        assert [\n                 %Types.DateTimeWithTZOffset{\n                   naive_datetime: ~N[2016-05-24 13:26:08.654321],\n                   timezone_offset: 7200\n                 }\n               ] =\n                 Decoder.decode(\n                   <<0xB3, 0x46, 0xCA, 0x57, 0x44, 0x56, 0x70, 0xCA, 0x27, 0x0, 0x25, 0x68, 0xC9,\n                     0x1C, 0x20>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Duration (bolt_version: #{bolt_version})\" do\n        assert [\n                 %Types.Duration{\n                   years: 1,\n                   months: 3,\n                   days: 34,\n                   hours: 2,\n                   minutes: 32,\n                   seconds: 54,\n                   nanoseconds: 5550\n                 }\n               ] ==\n                 Decoder.decode(\n                   <<0xB4, 0x45, 0xF, 0x22, 0xC9, 0x23, 0xD6, 0xC9, 0x15, 0xAE>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Point 2D cartesian (bolt_version: #{bolt_version})\" do\n        assert [\n                 %Types.Point{\n                   crs: \"cartesian\",\n                   height: nil,\n                   latitude: nil,\n                   longitude: nil,\n                   srid: 7203,\n                   x: 40.0,\n                   y: 45.0,\n                   z: nil\n                 }\n               ] =\n                 Decoder.decode(\n                   <<0xB3, 0x58, 0xC9, 0x1C, 0x23, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,\n                     0xC1, 0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Point 2D geographic (bolt_version: #{bolt_version})\" do\n        assert [\n                 %Types.Point{\n                   crs: \"wgs-84\",\n                   height: nil,\n                   latitude: 45.0,\n                   longitude: 40.0,\n                   srid: 4326,\n                   x: 40.0,\n                   y: 45.0,\n                   z: nil\n                 }\n               ] =\n                 Decoder.decode(\n                   <<0xB3, 0x58, 0xC9, 0x10, 0xE6, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,\n                     0xC1, 0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Point 3D cartesian (bolt_version: #{bolt_version})\" do\n        assert [\n                 %Types.Point{\n                   crs: \"cartesian-3d\",\n                   height: nil,\n                   latitude: nil,\n                   longitude: nil,\n                   srid: 9157,\n                   x: 40.0,\n                   y: 45.0,\n                   z: 150.0\n                 }\n               ] =\n                 Decoder.decode(\n                   <<0xB4, 0x59, 0xC9, 0x23, 0xC5, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,\n                     0xC1, 0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1, 0x40, 0x62, 0xC0, 0x0,\n                     0x0, 0x0, 0x0, 0x0>>,\n                   unquote(bolt_version)\n                 )\n      end\n\n      test \"Point 3D geographic (bolt_version: #{bolt_version})\" do\n        assert [\n                 %Types.Point{\n                   crs: \"wgs-84-3d\",\n                   height: 150.0,\n                   latitude: 45.0,\n                   longitude: 40.0,\n                   srid: 4979,\n                   x: 40.0,\n                   y: 45.0,\n                   z: 150.0\n                 }\n               ] =\n                 Decoder.decode(\n                   <<0xB4, 0x59, 0xC9, 0x13, 0x73, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,\n                     0xC1, 0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1, 0x40, 0x62, 0xC0, 0x0,\n                     0x0, 0x0, 0x0, 0x0>>,\n                   unquote(bolt_version)\n                 )\n      end\n    end)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/decoder_v1_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderV1Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.PackStream\n  alias Bolt.Sips.Internals.PackStream.DecoderV1\n\n  test \"decodes null\" do\n    assert DecoderV1.decode(<<0xC0>>, 1) == [nil]\n  end\n\n  test \"decodes boolean\" do\n    assert DecoderV1.decode(<<0xC3>>, 1) == [true]\n    assert DecoderV1.decode(<<0xC2>>, 1) == [false]\n  end\n\n  test \"decodes floats\" do\n    positive = <<0xC1, 0x3F, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A>>\n    negative = <<0xC1, 0xBF, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A>>\n\n    assert DecoderV1.decode(positive, 1) == [1.1]\n    assert DecoderV1.decode(negative, 1) == [-1.1]\n  end\n\n  test \"decodes strings\" do\n    longstr =\n      <<0xD0, 0x1A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D,\n        0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A>>\n\n    specialcharstr =\n      <<0xD0, 0x18, 0x45, 0x6E, 0x20, 0xC3, 0xA5, 0x20, 0x66, 0x6C, 0xC3, 0xB6, 0x74, 0x20, 0xC3,\n        0xB6, 0x76, 0x65, 0x72, 0x20, 0xC3, 0xA4, 0x6E, 0x67, 0x65, 0x6E>>\n\n    assert DecoderV1.decode(<<0x80>>, 1) == [\"\"]\n    assert DecoderV1.decode(<<0x81, 0x61>>, 1) == [\"a\"]\n    assert DecoderV1.decode(longstr, 1) == [\"abcdefghijklmnopqrstuvwxyz\"]\n    assert DecoderV1.decode(specialcharstr, 1) == [\"En å flöt över ängen\"]\n  end\n\n  test \"decodes lists\" do\n    assert DecoderV1.decode(<<0x90>>, 1) == [[]]\n    assert DecoderV1.decode(<<0x93, 0x01, 0x02, 0x03>>, 1) == [[1, 2, 3]]\n\n    list_8 = <<0xD4, 16::8>> <> (1..16 |> Enum.map(&PackStream.encode(&1, 1)) |> Enum.join())\n    assert DecoderV1.decode(list_8, 1) == [1..16 |> Enum.to_list()]\n\n    list_16 = <<0xD5, 256::16>> <> (1..256 |> Enum.map(&PackStream.encode(&1, 1)) |> Enum.join())\n    assert DecoderV1.decode(list_16, 1) == [1..256 |> Enum.to_list()]\n\n    list_32 =\n      <<0xD6, 66_000::32>> <> (1..66_000 |> Enum.map(&PackStream.encode(&1, 1)) |> Enum.join())\n\n    assert DecoderV1.decode(list_32, 1) == [1..66_000 |> Enum.to_list()]\n\n    ending_0_list = <<0x93, 0x91, 0x1, 0x92, 0x2, 0x0, 0x0>>\n    assert DecoderV1.decode(ending_0_list, 1) == [[[1], [2, 0], 0]]\n  end\n\n  test \"decodes maps\" do\n    assert DecoderV1.decode(<<0xA0>>, 1) == [%{}]\n    assert DecoderV1.decode(<<0xA1, 0x81, 0x61, 0x01>>, 1) == [%{\"a\" => 1}]\n    assert DecoderV1.decode(<<0xAB, 0x81, 0x61, 0x01>>, 1) == [%{\"a\" => 1}]\n\n    map_8 =\n      <<0xD8, 16::8>> <>\n        (1..16\n         |> Enum.map(fn i -> :erlang.iolist_to_binary(PackStream.encode(\"#{i}\", 1)) <> <<1>> end)\n         |> Enum.join())\n\n    assert DecoderV1.decode(map_8, 1) |> List.first() |> map_size == 16\n\n    map_16 =\n      <<0xD9, 256::16>> <>\n        (1..256\n         |> Enum.map(fn i -> :erlang.iolist_to_binary(PackStream.encode(\"#{i}\", 1)) <> <<1>> end)\n         |> Enum.join())\n\n    assert DecoderV1.decode(map_16, 1) |> List.first() |> map_size == 256\n\n    map_32 =\n      <<0xDA, 66_000::32>> <>\n        (1..66_000\n         |> Enum.map(fn i -> :erlang.iolist_to_binary(PackStream.encode(\"#{i}\", 1)) <> <<1>> end)\n         |> Enum.join())\n\n    assert DecoderV1.decode(map_32, 1) |> List.first() |> map_size == 66_000\n  end\n\n  test \"decodes integers\" do\n    assert DecoderV1.decode(<<0x2A>>, 1) == [42]\n    assert DecoderV1.decode(<<0xC8, 0x2A>>, 1) == [42]\n    assert DecoderV1.decode(<<0xC9, 0, 0x2A>>, 1) == [42]\n    assert DecoderV1.decode(<<0xCA, 0, 0, 0, 0x2A>>, 1) == [42]\n    assert DecoderV1.decode(<<0xCB, 0, 0, 0, 0, 0, 0, 0, 0x2A>>, 1) == [42]\n  end\n\n  test \"decodes negative integers\" do\n    assert DecoderV1.decode(<<0xC8, 0xD6>>, 1) == [-42]\n  end\n\n  test \"decodes Node\" do\n    node =\n      <<0x91, 0xB3, 0x4E, 0x11, 0x91, 0x86, 0x50, 0x65, 0x72, 0x73, 0x6F, 0x6E, 0xA2, 0x84, 0x6E,\n        0x61, 0x6D, 0x65, 0xD0, 0x10, 0x50, 0x61, 0x74, 0x72, 0x69, 0x63, 0x6B, 0x20, 0x52, 0x6F,\n        0x74, 0x68, 0x66, 0x75, 0x73, 0x73, 0x89, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x73, 0x69, 0x70,\n        0x73, 0xC3>>\n\n    assert [\n             [\n               %Bolt.Sips.Types.Node{\n                 id: 17,\n                 labels: [\"Person\"],\n                 properties: %{\"bolt_sips\" => true, \"name\" => \"Patrick Rothfuss\"}\n               }\n             ]\n           ] == DecoderV1.decode(node, 1)\n  end\n\n  test \"decodes Relationship\" do\n    rel = <<0x91, 0xB5, 0x52, 0x50, 0x46, 0x43, 0x85, 0x57, 0x52, 0x4F, 0x54, 0x45, 0xA0>>\n\n    assert [\n             [\n               %Bolt.Sips.Types.Relationship{\n                 end: 67,\n                 id: 80,\n                 properties: %{},\n                 start: 70,\n                 type: \"WROTE\"\n               }\n             ]\n           ] = DecoderV1.decode(rel, 1)\n  end\n\n  test \"decodes path\" do\n    path =\n      <<0x91, 0xB3, 0x50, 0x92, 0xB3, 0x4E, 0x30, 0x90, 0xA2, 0x84, 0x6E, 0x61, 0x6D, 0x65, 0x85,\n        0x41, 0x6C, 0x69, 0x63, 0x65, 0x89, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x73, 0x69, 0x70, 0x73,\n        0xC3, 0xB3, 0x4E, 0x38, 0x90, 0xA2, 0x84, 0x6E, 0x61, 0x6D, 0x65, 0x83, 0x42, 0x6F, 0x62,\n        0x89, 0x62, 0x6F, 0x6C, 0x74, 0x5F, 0x73, 0x69, 0x70, 0x73, 0xC3, 0x91, 0xB3, 0x72, 0x13,\n        0x85, 0x4B, 0x4E, 0x4F, 0x57, 0x53, 0xA0, 0x92, 0x1, 0x1>>\n\n    [\n      [\n        %Bolt.Sips.Types.Path{\n          nodes: [\n            %Bolt.Sips.Types.Node{\n              id: 48,\n              labels: [],\n              properties: %{\"bolt_sips\" => true, \"name\" => \"Alice\"}\n            },\n            %Bolt.Sips.Types.Node{\n              id: 56,\n              labels: [],\n              properties: %{\"bolt_sips\" => true, \"name\" => \"Bob\"}\n            }\n          ],\n          relationships: [\n            %Bolt.Sips.Types.UnboundRelationship{\n              end: nil,\n              id: 19,\n              properties: %{},\n              start: nil,\n              type: \"KNOWS\"\n            }\n          ],\n          sequence: [1, 1]\n        }\n      ]\n    ] = DecoderV1.decode(path, 1)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/decoder_v2_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.DecoderV2Test do\n  use ExUnit.Case, async: true\n  alias Bolt.Sips.Internals.PackStream.DecoderV2\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}\n\n  describe \"Decode temporal data:\" do\n    test \"date post 1970-01-01\" do\n      assert [~D[2018-07-29]] == DecoderV2.decode({0x44, <<0xC9, 0x45, 0x4D>>, 1}, 2)\n    end\n\n    test \"date pre 1970-01-01\" do\n      assert [~D[1918-07-29]] == DecoderV2.decode({0x44, <<0xC9, 0xB6, 0xA0>>, 1}, 2)\n    end\n\n    test \"local time\" do\n      assert [~T[13:25:01.952456]] ==\n               DecoderV2.decode(\n                 {0x74, <<0xCB, 0x0, 0x0, 0x2B, 0xEE, 0x2C, 0xB7, 0xD5, 0x40>>, 1},\n                 2\n               )\n    end\n\n    test \"local datetime\" do\n      assert [~N[2014-11-30 16:15:01.435432]] ==\n               DecoderV2.decode(\n                 {0x64, <<0xCA, 0x54, 0x7B, 0x42, 0x85, 0xCA, 0x19, 0xF4, 0x2A, 0x40>>, 2},\n                 2\n               )\n    end\n\n    test \"Time with timezone offzet\" do\n      assert [%TimeWithTZOffset{time: ~T[04:45:32.123456], timezone_offset: 7200}] ==\n               DecoderV2.decode(\n                 {0x54, <<0xCB, 0x0, 0x0, 0xF, 0x94, 0xE2, 0x22, 0x2, 0x0, 0xC9, 0x1C, 0x20>>, 2},\n                 2\n               )\n    end\n\n    test \"Datetime with zone id\" do\n      dt =\n        Bolt.Sips.TypesHelper.datetime_with_micro(~N[1998-03-18 06:25:12.123456], \"Europe/Paris\")\n\n      assert [dt] ==\n               DecoderV2.decode(\n                 {0x66,\n                  <<0xCA, 0x35, 0xF, 0x68, 0xC8, 0xCA, 0x7, 0x5B, 0xCA, 0x0, 0x8C, 0x45, 0x75,\n                    0x72, 0x6F, 0x70, 0x65, 0x2F, 0x50, 0x61, 0x72, 0x69, 0x73>>, 3},\n                 2\n               )\n    end\n\n    test \"Datetime with zone offset\" do\n      assert [\n               %DateTimeWithTZOffset{\n                 naive_datetime: ~N[1998-03-18 06:25:12.123456],\n                 timezone_offset: 7200\n               }\n             ] ==\n               DecoderV2.decode(\n                 {0x46,\n                  <<0xCA, 0x35, 0xF, 0x68, 0xC8, 0xCA, 0x7, 0x5B, 0xCA, 0x0, 0xC9, 0x1C, 0x20>>,\n                  3},\n                 2\n               )\n    end\n\n    test \"Duration\" do\n      assert [\n               %Duration{\n                 days: 11,\n                 hours: 15,\n                 minutes: 0,\n                 months: 8,\n                 nanoseconds: 5550,\n                 seconds: 21,\n                 weeks: 0,\n                 years: 3\n               }\n             ] ==\n               DecoderV2.decode(\n                 {0x45, <<0x2C, 0xB, 0xCA, 0x0, 0x0, 0xD3, 0x5, 0xC9, 0x15, 0xAE>>, 4},\n                 2\n               )\n    end\n\n    test \"Point2D (cartesian)\" do\n      assert [\n               %Point{\n                 crs: \"cartesian\",\n                 height: nil,\n                 latitude: nil,\n                 longitude: nil,\n                 srid: 7203,\n                 x: 45.0003,\n                 y: 34.5434,\n                 z: nil\n               }\n             ] ==\n               DecoderV2.decode(\n                 {0x58,\n                  <<0xC9, 0x1C, 0x23, 0xC1, 0x40, 0x46, 0x80, 0x9, 0xD4, 0x95, 0x18, 0x2B, 0xC1,\n                    0x40, 0x41, 0x45, 0x8E, 0x21, 0x96, 0x52, 0xBD>>, 3},\n                 2\n               )\n    end\n\n    test \"Point2D (geographic)\" do\n      assert [\n               %Point{\n                 crs: \"wgs-84\",\n                 height: nil,\n                 latitude: 15.00943,\n                 longitude: 20.45352,\n                 srid: 4326,\n                 x: 20.45352,\n                 y: 15.00943,\n                 z: nil\n               }\n             ] ==\n               DecoderV2.decode(\n                 {0x58,\n                  <<0xC9, 0x10, 0xE6, 0xC1, 0x40, 0x34, 0x74, 0x19, 0xE3, 0x0, 0x14, 0xF9, 0xC1,\n                    0x40, 0x2E, 0x4, 0xD4, 0x2, 0x4B, 0x33, 0xDB>>, 3},\n                 2\n               )\n    end\n\n    test \"Point3D (cartesian)\" do\n      assert [\n               %Point{\n                 crs: \"cartesian-3d\",\n                 height: nil,\n                 latitude: nil,\n                 longitude: nil,\n                 srid: 9157,\n                 x: 48.8354,\n                 y: 12.72468,\n                 z: 50.004\n               }\n             ] ==\n               DecoderV2.decode(\n                 {0x59,\n                  <<0xC9, 0x23, 0xC5, 0xC1, 0x40, 0x48, 0x6A, 0xEE, 0x63, 0x1F, 0x8A, 0x9, 0xC1,\n                    0x40, 0x29, 0x73, 0x9, 0x41, 0xC8, 0x21, 0x6C, 0xC1, 0x40, 0x49, 0x0, 0x83,\n                    0x12, 0x6E, 0x97, 0x8D>>, 4},\n                 2\n               )\n    end\n\n    test \"Point3D (geographic)\" do\n      assert [\n               %Point{\n                 crs: \"wgs-84-3d\",\n                 height: -123.0004,\n                 latitude: 70.40958,\n                 longitude: 13.39538,\n                 srid: 4979,\n                 x: 13.39538,\n                 y: 70.40958,\n                 z: -123.0004\n               }\n             ] ==\n               DecoderV2.decode(\n                 {0x59,\n                  <<0xC9, 0x13, 0x73, 0xC1, 0x40, 0x2A, 0xCA, 0x6F, 0x3F, 0x52, 0xFC, 0x26, 0xC1,\n                    0x40, 0x51, 0x9A, 0x36, 0x8F, 0x8, 0x46, 0x20, 0xC1, 0xC0, 0x5E, 0xC0, 0x6,\n                    0x8D, 0xB8, 0xBA, 0xC7>>, 4},\n                 2\n               )\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_helper_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderHelperTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.PackStream.EncoderHelper\n  alias Bolt.Sips.Internals.PackStreamError\n\n  describe \"call_encode/3\" do\n    test \"successfull when call with existing bolt_version\" do\n      assert <<_::binary>> = EncoderHelper.call_encode(:atom, true, 1)\n    end\n\n    test \"successfull when call with superior bolt_version\" do\n      assert <<_::binary>> = EncoderHelper.call_encode(:atom, true, 4)\n    end\n\n    test \"fails when call with bolt_version <= 0\" do\n      assert_raise PackStreamError, fn ->\n        EncoderHelper.call_encode(:atom, true, -1)\n      end\n    end\n\n    test \"fails when call with a non integer bolt_version\" do\n      assert_raise PackStreamError, fn ->\n        EncoderHelper.call_encode(:atom, true, :invalid)\n      end\n    end\n\n    test \"fails when call with a non supported data type\" do\n      assert_raise PackStreamError, fn ->\n        EncoderHelper.call_encode(:non_supported, true, 1)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderTest do\n  use ExUnit.Case, async: false\n\n  alias Bolt.Sips.Internals.PackStream.Encoder\n  alias Bolt.Sips.Internals.BoltVersionHelper\n  alias Bolt.Sips.Types\n  alias Bolt.Sips.TypesHelper\n\n  defmodule TestStruct do\n    defstruct foo: \"bar\"\n  end\n\n  describe \"Encode common types:\" do\n    Enum.each(BoltVersionHelper.available_versions(), fn bolt_version ->\n      test \"Null (bolt_version: #{bolt_version})\" do\n        assert <<0xC0>> == :erlang.iolist_to_binary(Encoder.encode(nil, unquote(bolt_version)))\n      end\n\n      test \"Boolean (bolt_version: #{bolt_version})\" do\n        assert <<0xC3>> == :erlang.iolist_to_binary(Encoder.encode(true, unquote(bolt_version)))\n        assert <<0xC2>> == :erlang.iolist_to_binary(Encoder.encode(false, unquote(bolt_version)))\n      end\n\n      test \"Atom (bolt_version: #{bolt_version})\" do\n        assert <<0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F>> ==\n                 :erlang.iolist_to_binary(Encoder.encode(:hello, unquote(bolt_version)))\n      end\n\n      test \"String (bolt_version: #{bolt_version})\" do\n        assert <<0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F>> ==\n                 :erlang.iolist_to_binary(Encoder.encode(\"hello\", unquote(bolt_version)))\n      end\n\n      test \"Integer (bolt_version: #{bolt_version})\" do\n        assert <<0x7>> == :erlang.iolist_to_binary(Encoder.encode(7, unquote(bolt_version)))\n      end\n\n      test \"Float (bolt_version: #{bolt_version})\" do\n        assert <<0xC1, 0x40, 0x1E, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCD>> ==\n                 :erlang.iolist_to_binary(Encoder.encode(7.7, unquote(bolt_version)))\n      end\n\n      test \"List (bolt_version: #{bolt_version})\" do\n        assert <<0x90>> == :erlang.iolist_to_binary(Encoder.encode([], unquote(bolt_version)))\n        assert <<0x92, 0x2, 0x4>> == :erlang.iolist_to_binary(Encoder.encode([2, 4], unquote(bolt_version)))\n      end\n\n      test \"Map (bolt_version: #{bolt_version})\" do\n        assert <<0xA1, 0x82, 0x6F, 0x6B, 0x5>> == :erlang.iolist_to_binary(Encoder.encode(%{ok: 5}, unquote(bolt_version)))\n      end\n\n      test \"Struct (bolt_version: #{bolt_version})\" do\n        assert <<0xB3, 0x1, 0x81, 0x69, 0x82, 0x61, 0x6D, 0x86, 0x70, 0x61, 0x72, 0x61, 0x6D,\n                 0x73>> ==\n                 :erlang.iolist_to_binary(Encoder.encode({0x01, [\"i\", \"am\", \"params\"]}, unquote(bolt_version)))\n      end\n\n      test \"raises error when trying to encode with unknown signature (bolt_version: #{\n             bolt_version\n           })\" do\n        assert_raise Bolt.Sips.Internals.PackStreamError, ~r/^unable to encode/i, fn ->\n          Encoder.encode({128, []}, unquote(bolt_version))\n        end\n\n        assert_raise Bolt.Sips.Internals.PackStreamError, ~r/^unable to encode/i, fn ->\n          Encoder.encode({-1, []}, unquote(bolt_version))\n        end\n\n        assert_raise Bolt.Sips.Internals.PackStreamError, ~r/^unable to encode/i, fn ->\n          Encoder.encode({\"a\", []}, unquote(bolt_version))\n        end\n      end\n\n      test \"unkown type (bolt_version: #{bolt_version})\" do\n        assert_raise Bolt.Sips.Internals.PackStreamError, fn ->\n          Encoder.encode({:error, \"unencodable\"}, unquote(bolt_version))\n        end\n      end\n    end)\n  end\n\n  describe \"Encode types for bolt >= 2\" do\n    BoltVersionHelper.available_versions()\n    |> Enum.filter(&(&1 >= 2))\n    |> Enum.each(fn bolt_version ->\n      test \"Local time (bolt_version: #{bolt_version})\" do\n        assert <<0xB1, 0x74, _::binary>> = :erlang.iolist_to_binary(Encoder.encode(~T[14:45:53.34], unquote(bolt_version)))\n      end\n\n      test \"Time with TZ Offset (bolt_version: #{bolt_version})\" do\n        assert <<0xB2, 0x54, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(\n                   Types.TimeWithTZOffset.create(~T[12:45:30.250000], 3600),\n                   unquote(bolt_version)\n                 ))\n      end\n\n      test \"Date (bolt_version: #{bolt_version})\" do\n        assert <<0xB1, 0x44, _::binary>> = :erlang.iolist_to_binary(Encoder.encode(~D[2013-05-06], unquote(bolt_version)))\n      end\n\n      test \"Local date time: NaiveDateTime (bolt_version: #{bolt_version})\" do\n        assert <<0xB2, 0x64, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(~N[2018-04-05 12:34:00.543], unquote(bolt_version)))\n      end\n\n      test \"Datetime with timezone offset (bolt_version: #{bolt_version})\" do\n        assert <<0xB3, 0x46, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(\n                   Types.DateTimeWithTZOffset.create(~N[2016-05-24 13:26:08.543], 7200),\n                   unquote(bolt_version)\n                 ))\n      end\n\n      test \"Datetime with timezone id (bolt_version: #{bolt_version})\" do\n        assert <<0xB3, 0x66, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(\n                   TypesHelper.datetime_with_micro(~N[2016-05-24 13:26:08.543], \"Europe/Berlin\"),\n                   unquote(bolt_version)\n                 ))\n      end\n\n      test \"Duration (bolt_version: #{bolt_version})\" do\n        duration = %Types.Duration{\n          years: 2,\n          months: 3,\n          weeks: 2,\n          days: 23,\n          hours: 8,\n          minutes: 2,\n          seconds: 4,\n          nanoseconds: 3234\n        }\n\n        assert <<0xB4, 0x45, _::binary>> = :erlang.iolist_to_binary(Encoder.encode(duration, unquote(bolt_version)))\n      end\n\n      test \"Point 2D cartesian (bolt_version: #{bolt_version})\" do\n        assert <<0xB3, 0x58, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(Types.Point.create(:cartesian, 40, 45), unquote(bolt_version)))\n      end\n\n      test \"Point 2D geographic (bolt_version: #{bolt_version})\" do\n        assert <<0xB3, 0x58, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(Types.Point.create(:wgs_84, 40, 45), unquote(bolt_version)))\n      end\n\n      test \"Point 3D cartesian (bolt_version: #{bolt_version})\" do\n        assert <<0xB4, 0x59, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(\n                   Types.Point.create(:cartesian, 40, 45, 150),\n                   unquote(bolt_version)\n                 ))\n      end\n\n      test \"Point 3D geographic (bolt_version: #{bolt_version})\" do\n        assert <<0xB4, 0x59, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode(Types.Point.create(:wgs_84, 40, 45, 150), unquote(bolt_version)))\n      end\n    end)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_v1_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderV1Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.PackStream.EncoderV1\n\n  defmodule TestStruct do\n    defstruct foo: \"bar\"\n  end\n\n  doctest Bolt.Sips.Internals.PackStream.EncoderV1\n\n  test \"encodes null\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_atom(nil, 1)) == <<0xC0>>\n  end\n\n  test \"encodes boolean\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_atom(true, 1)) == <<0xC3>>\n    assert :erlang.iolist_to_binary(EncoderV1.encode_atom(false, 1)) == <<0xC2>>\n  end\n\n  test \"encodes atom\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_atom(:hello, 1)) == <<0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F>>\n  end\n\n  test \"encodes string\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_string(\"\", 1)) == <<0x80>>\n    assert :erlang.iolist_to_binary(EncoderV1.encode_string(\"Short\", 1)) == <<0x85, 0x53, 0x68, 0x6F, 0x72, 0x74>>\n\n    # 30 bytes due to umlauts\n    long_8 = \"This is a räther löng string\"\n    assert <<0xD0, 0x1E, _::binary-size(30)>> = :erlang.iolist_to_binary(EncoderV1.encode_string(long_8, 1))\n\n    long_16 = \"\"\"\n    For encoded string containing fewer than 16 bytes, including empty strings,\n    the marker byte should contain the high-order nibble `1000` followed by a\n    low-order nibble containing the size. The encoded data then immediately\n    follows the marker.\n\n    For encoded string containing 16 bytes or more, the marker 0xD0, 0xD1 or\n    0xD2 should be used, depending on scale. This marker is followed by the\n    size and the UTF-8 encoded data.\n    \"\"\"\n\n    assert <<0xD1, 0x01, 0xA5, _::binary-size(421)>> = :erlang.iolist_to_binary(EncoderV1.encode_string(long_16, 1))\n\n    long_32 = String.duplicate(\"a\", 66_000)\n    assert <<0xD2, 66_000::32, _::binary-size(66_000)>> = :erlang.iolist_to_binary(EncoderV1.encode_string(long_32, 1))\n  end\n\n  test \"encodes integer\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_integer(0, 1)) == <<0x00>>\n    assert :erlang.iolist_to_binary(EncoderV1.encode_integer(42, 1)) == <<0x2A>>\n    assert :erlang.iolist_to_binary(EncoderV1.encode_integer(-42, 1)) == <<0xC8, 0xD6>>\n    assert :erlang.iolist_to_binary(EncoderV1.encode_integer(420, 1)) == <<0xC9, 0x01, 0xA4>>\n    assert :erlang.iolist_to_binary(EncoderV1.encode_integer(33_000, 1)) == <<0xCA, 0x00, 0x00, 0x80, 0xE8>>\n\n    assert :erlang.iolist_to_binary(EncoderV1.encode_integer(2_150_000_000, 1)) ==\n             <<0xCB, 0x00, 0x00, 0x00, 0x00, 0x80, 0x26, 0x65, 0x80>>\n  end\n\n  test \"encodes float\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_float(+1.1, 1)) ==\n             <<0xC1, 0x3F, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A>>\n\n    assert :erlang.iolist_to_binary(EncoderV1.encode_float(-1.1, 1)) ==\n             <<0xC1, 0xBF, 0xF1, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A>>\n  end\n\n  test \"encodes list\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_list([], 1)) == <<0x90>>\n\n    list_8 = Stream.repeatedly(fn -> \"a\" end) |> Enum.take(16)\n    assert <<0xD4, 16::8, _::binary-size(32)>> = :erlang.iolist_to_binary(EncoderV1.encode_list(list_8, 1))\n\n    list_16 = Stream.repeatedly(fn -> \"a\" end) |> Enum.take(256)\n    assert <<0xD5, 256::16, _::binary-size(512)>> = :erlang.iolist_to_binary(EncoderV1.encode_list(list_16, 1))\n\n    list_32 = Stream.repeatedly(fn -> \"a\" end) |> Enum.take(66_000)\n    assert <<0xD6, 66_000::32, _::binary-size(132_000)>> = :erlang.iolist_to_binary(EncoderV1.encode_list(list_32, 1))\n  end\n\n  test \"encodes map\" do\n    assert :erlang.iolist_to_binary(EncoderV1.encode_map(%{}, 1)) == <<0xA0>>\n\n    map_8 = 1..16 |> Enum.map(&{&1, \"a\"}) |> Map.new()\n    assert <<0xD8, 16::8>> <> _rest = :erlang.iolist_to_binary(EncoderV1.encode_map(map_8, 1))\n\n    map_16 = 1..256 |> Enum.map(&{&1, \"a\"}) |> Map.new()\n    assert <<0xD9, 256::16>> <> _rest = :erlang.iolist_to_binary(EncoderV1.encode_map(map_16, 1))\n\n    map_32 = 1..66_000 |> Enum.map(&{&1, \"a\"}) |> Map.new()\n    assert <<0xDA, 66_000::32>> <> _rest = :erlang.iolist_to_binary(EncoderV1.encode_map(map_32, 1))\n  end\n\n  test \"encodes a struct\" do\n    assert <<0xB2, 0x1, 0x85, 0x66, 0x69, 0x72, 0x73, 0x74, 0x86, 0x73, 0x65, 0x63, 0x6F, 0x6E,\n             0x64>> == :erlang.iolist_to_binary(EncoderV1.encode_struct({0x01, [\"first\", \"second\"]}, 1))\n\n    assert <<0xDC, 0x6F, _::binary>> = :erlang.iolist_to_binary(EncoderV1.encode_struct({0x01, Enum.into(1..111, [])}, 1))\n\n    assert <<0xDD, 0x1, 0x4D, _::binary>> =\n             :erlang.iolist_to_binary(EncoderV1.encode_struct({0x01, Enum.into(1..333, [])}, 1))\n\n    # Test for a fixed bug\n    assert <<0xB1, 0x1, 0xA1, 0x83, 0x66, 0x6F, 0x6F, 0x83, 0x62, 0x61, 0x72>> ==\n             :erlang.iolist_to_binary(EncoderV1.encode_struct({0x01, [%TestStruct{}]}, 1))\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/encoder_v2_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.EncoderV2Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.PackStream.EncoderV2\n  alias Bolt.Sips.Types.{TimeWithTZOffset, DateTimeWithTZOffset, Duration, Point}\n  alias Bolt.Sips.TypesHelper\n\n  doctest Bolt.Sips.Internals.PackStream.EncoderV2\n\n  describe \"Encode temporal types:\" do\n    test \"time without timezone\" do\n      assert <<0xB1, 0x74, 0xCB, 0x0, 0x0, 0x39, 0x8E, 0xD6, 0xF1, 0xF7, 0x68>> ==\n               :erlang.iolist_to_binary(EncoderV2.encode_local_time(~T[17:34:45.654321], 2))\n    end\n\n    test \"time with timezone\" do\n      ttz = TimeWithTZOffset.create(~T[12:45:30.250000], 3600)\n\n      assert <<0xB2, 0x54, 0xCB, 0x0, 0x0, 0x29, 0xC5, 0xF8, 0x3C, 0x56, 0x80, 0xC9, 0xE, 0x10>> ==\n               :erlang.iolist_to_binary(EncoderV2.encode_time_with_tz(ttz, 2))\n    end\n\n    test \"date post 1970-01-01\" do\n      assert <<0xB1, 0x44, 0xC9, 0x45, 0x4D>> ==\n               :erlang.iolist_to_binary(EncoderV2.encode_date(~D[2018-07-29], 2))\n    end\n\n    test \"date pre 1970-01-01\" do\n      assert <<0xB1, 0x44, 0xC9, 0xB6, 0xA0>> ==\n               :erlang.iolist_to_binary(EncoderV2.encode_date(~D[1918-07-29], 2))\n    end\n\n    test \"local datetime\" do\n      assert <<0xB2, 0x64, 0xCA, 0x5A, 0xC6, 0x17, 0xB8, 0xCA, 0x27, 0x0, 0x25, 0x68>>\n      :erlang.iolist_to_binary(EncoderV2.encode_local_datetime(~N[2018-04-05 12:34:00.654321], 2))\n    end\n\n    test \"datetime with timezone id\" do\n      dt = TypesHelper.datetime_with_micro(~N[2016-05-24 13:26:08.654321], \"Europe/Berlin\")\n\n      assert <<0xB3, 0x66, 0xCA, 0x57, 0x44, 0x56, 0x70, 0xCA, 0x27, 0x0, 0x25, 0x68, 0x8D, 0x45,\n               0x75, 0x72, 0x6F, 0x70, 0x65, 0x2F, 0x42, 0x65, 0x72, 0x6C, 0x69,\n               0x6E>> ==\n               :erlang.iolist_to_binary(EncoderV2.encode_datetime_with_tz_id(dt, 2))\n    end\n\n    test \"datetime with timezone offset\" do\n      dt = DateTimeWithTZOffset.create(~N[2016-05-24 13:26:08.654321], 7200)\n\n      assert <<0xB3, 0x46, 0xCA, 0x57, 0x44, 0x56, 0x70, 0xCA, 0x27, 0x0, 0x25, 0x68, 0xC9, 0x1C,\n               0x20>> == :erlang.iolist_to_binary(EncoderV2.encode_datetime_with_tz_offset(dt, 2))\n    end\n\n    test \"duration with all values\" do\n      duration = %Duration{\n        years: 1,\n        months: 3,\n        weeks: 2,\n        days: 20,\n        hours: 2,\n        minutes: 32,\n        seconds: 54,\n        nanoseconds: 5550\n      }\n\n      assert <<0xB4, 0x45, 0xF, 0x22, 0xC9, 0x23, 0xD6, 0xC9, 0x15, 0xAE>> ==\n               :erlang.iolist_to_binary(EncoderV2.encode_duration(duration, 2))\n    end\n  end\n\n  describe \"Encode spatial types:\" do\n    test \"cartesian point 2D\" do\n      assert <<0xB3, 0x58, 0xC9, 0x1C, 0x23, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1,\n               0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0,\n               0x0>> =\n               :erlang.iolist_to_binary(\n                 EncoderV2.encode_point(Point.create(:cartesian, 40, 45), 2)\n               )\n    end\n\n    test \"geographic point 2D\" do\n      assert <<0xB3, 0x58, 0xC9, 0x10, 0xE6, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1,\n               0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0,\n               0x0>> =\n               :erlang.iolist_to_binary(EncoderV2.encode_point(Point.create(:wgs_84, 40, 45), 2))\n    end\n\n    test \"cartesian point 3D\" do\n      assert <<0xB4, 0x59, 0xC9, 0x23, 0xC5, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1,\n               0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1, 0x40, 0x62, 0xC0, 0x0, 0x0, 0x0,\n               0x0,\n               0x0>> =\n               :erlang.iolist_to_binary(\n                 EncoderV2.encode_point(Point.create(:cartesian, 40, 45, 150), 2)\n               )\n    end\n\n    test \"geographic point 3D\" do\n      assert <<0xB4, 0x59, 0xC9, 0x13, 0x73, 0xC1, 0x40, 0x44, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1,\n               0x40, 0x46, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0xC1, 0x40, 0x62, 0xC0, 0x0, 0x0, 0x0,\n               0x0,\n               0x0>> =\n               :erlang.iolist_to_binary(\n                 EncoderV2.encode_point(Point.create(:wgs_84, 40, 45, 150), 2)\n               )\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/decoder_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.DecoderTest do\n  use ExUnit.Case, async: true\n  alias Bolt.Sips.Internals.PackStream.Message.Decoder\n  alias Bolt.Sips.Internals.BoltVersionHelper\n\n  describe \"Decode common messages\" do\n    Enum.each(BoltVersionHelper.available_versions(), fn bolt_version ->\n      test \"SUCCESS (bolt_version: #{bolt_version})\" do\n        success_hex =\n          <<0xB1, 0x70, 0xA1, 0x86, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x8B, 0x4E, 0x65, 0x6F,\n            0x34, 0x6A, 0x2F, 0x33, 0x2E, 0x34, 0x2E, 0x31>>\n\n        assert {:success, %{\"server\" => \"Neo4j/3.4.1\"}} ==\n                 Decoder.decode(success_hex, unquote(bolt_version))\n      end\n\n      test \"FAILURE (bolt_version: #{bolt_version})\" do\n        failure_hex =\n          <<0xB1, 0x7F, 0xA2, 0x84, 0x63, 0x6F, 0x64, 0x65, 0xD0, 0x25, 0x4E, 0x65, 0x6F, 0x2E,\n            0x43, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x2E, 0x53, 0x65,\n            0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2E, 0x55, 0x6E, 0x61, 0x75, 0x74, 0x68, 0x6F,\n            0x72, 0x69, 0x7A, 0x65, 0x64, 0x87, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0xD0,\n            0x39, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x69, 0x73,\n            0x20, 0x75, 0x6E, 0x61, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x65, 0x64, 0x20,\n            0x64, 0x75, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6E, 0x74,\n            0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x75, 0x72,\n            0x65, 0x2E>>\n\n        failure =\n          {:failure,\n           %{\n             \"code\" => \"Neo.ClientError.Security.Unauthorized\",\n             \"message\" => \"The client is unauthorized due to authentication failure.\"\n           }}\n\n        assert failure == Decoder.decode(failure_hex, unquote(bolt_version))\n      end\n\n      test \"RECORD (bolt_version: #{bolt_version})\" do\n        assert {:record, [1]} == Decoder.decode(<<0xB1, 0x71, 0x91, 0x1>>, unquote(bolt_version))\n      end\n\n      test \"IGNORED (bolt_version: #{bolt_version})\" do\n        assert {:ignored, _} = Decoder.decode(<<0xB0, 0x7E>>, unquote(bolt_version))\n      end\n    end)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/encoder_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderTest do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.Internals.PackStream.Message.Encoder\n\n  alias Bolt.Sips.Internals.PackStream.Message.Encoder\n  alias Bolt.Sips.Metadata\n  alias Bolt.Sips.Internals.BoltVersionHelper\n\n  defmodule TestUser do\n    defstruct name: \"\", bolt_sips: true\n  end\n\n  describe \"Encode common messages\" do\n    Enum.each(BoltVersionHelper.available_versions(), fn bolt_version ->\n      test \"DISCARD_ALL (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x2, 0xB0, 0x2F, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:discard_all, []}, unquote(bolt_version))\n                 )\n      end\n\n      test \"PULL_ALL (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x2, 0xB0, 0x3F, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(Encoder.encode({:pull_all, []}, unquote(bolt_version)))\n      end\n\n      test \"RESET (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x2, 0xB0, 0xF, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(Encoder.encode({:reset, []}, unquote(bolt_version)))\n      end\n    end)\n  end\n\n  @doc \"\"\"\n  INIT is not valid in bolt >= 3\n  RUN has one more params (metadata) in bolt >=3\n  \"\"\"\n  describe \"Encode message available only in Bolt <= 2\" do\n    BoltVersionHelper.available_versions()\n    |> Enum.filter(&(&1 <= 2))\n    |> Enum.each(fn bolt_version ->\n      test \"INIT without auth (bolt_version: #{bolt_version})\" do\n        assert <<0x0, _, 0xB2, 0x1, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode({:init, []}, unquote(bolt_version)))\n      end\n\n      test \"INIT wit auth (bolt_version: #{bolt_version})\" do\n        assert <<0x0, _, 0xB2, 0x1, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:init, [{\"neo4j\", \"test\"}]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"ACK_FAILURE (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x2, 0xB0, 0xE, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:ack_failure, []}, unquote(bolt_version))\n                 )\n      end\n\n      test \"RUN without params (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x13, 0xB2, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31,\n                 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:run, [\"RETURN 1 AS num\"]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"RUN with params (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x1C, 0xB2, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n                 0x24, 0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83,\n                 0x6E, 0x75, 0x6D, 0x5, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode(\n                     {:run, [\"RETURN $num AS num\", %{num: 5}]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n\n      test \"Bug fix: encoding struct fails (bolt_version: #{bolt_version})\" do\n        query = \"CREATE (n:User $props)\"\n        params = %{props: %TestUser{bolt_sips: true, name: \"Strut\"}}\n\n        assert <<0x0, 0x38, 0xB2, 0x10, 0xD0, 0x16, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x20,\n                 0x28, 0x6E, 0x3A, 0x55, 0x73, 0x65, 0x72, 0x20, 0x24, 0x70, 0x72, 0x6F, 0x70,\n                 0x73, 0x29, 0xA1, 0x85, 0x70, 0x72, 0x6F, 0x70, 0x73, 0xA2, 0x89, 0x62, 0x6F,\n                 0x6C, 0x74, 0x5F, 0x73, 0x69, 0x70, 0x73, 0xC3, 0x84,\n                 _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:run, [query, params]}, unquote(bolt_version))\n                 )\n      end\n    end)\n  end\n\n  describe \"Encode message available in Bolt >= 3\" do\n    BoltVersionHelper.available_versions()\n    |> Enum.filter(&(&1 >= 3))\n    |> Enum.each(fn bolt_version ->\n      nil\n\n      test \"HELLO without params (bolt_version: #{bolt_version})\" do\n        assert <<0x0, _, 0xB1, 0x1, _::binary>> =\n                 :erlang.iolist_to_binary(Encoder.encode({:hello, []}, unquote(bolt_version)))\n      end\n\n      test \"HELLO with params (bolt_version: #{bolt_version})\" do\n        assert <<0x0, _, 0xB1, 0x1, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:hello, [{\"neo4j\", \"test\"}]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"Encode GOODBYE (bolt_version: #{bolt_version})\" do\n        assert assert <<0x0, 0x2, 0xB0, 0x02, 0x0, 0x0>> ==\n                        :erlang.iolist_to_binary(\n                          Encoder.encode({:goodbye, []}, unquote(bolt_version))\n                        )\n      end\n\n      test \"BEGIN without params (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x3, 0xB1, 0x11, 0xA0, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(Encoder.encode({:begin, []}, unquote(bolt_version)))\n      end\n\n      test \"BEGIN with params (bolt_version: #{bolt_version})\" do\n        {:ok, metadata} = Metadata.new(%{tx_timeout: 15000})\n\n        assert <<0x0, 0x11, 0xB1, 0x11, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65,\n                 0x6F, 0x75, 0x74, 0xC9, 0x3A, 0x98, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:begin, [metadata]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"Encode COMMIT (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x2, 0xB0, 0x12, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(Encoder.encode({:commit, []}, unquote(bolt_version)))\n      end\n\n      test \"Encode ROLLBACK (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x2, 0xB0, 0x13, 0x0, 0x0>> ==\n                 :erlang.iolist_to_binary(Encoder.encode({:rollback, []}, unquote(bolt_version)))\n      end\n\n      test \"RUN without params nor metadata (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x16, 0xB3, 0x10, 0xD0, 0x10, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n                 0x31, 0x36, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0xA0, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:run, [\"RETURN 16 AS num\"]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"RUN without params but with metadata (bolt_version: #{bolt_version})\" do\n        {:ok, metadata} = Metadata.new(%{tx_timeout: 15000})\n\n        assert <<0x0, 0x24, 0xB3, 0x10, 0xD0, 0x10, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n                 0x31, 0x36, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0xA1, 0x8A, 0x74,\n                 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74, 0xC9, 0x3A, 0x98, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode(\n                     {:run, [\"RETURN 16 AS num\", %{}, metadata]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n\n      test \"RUN with params but without metadata (bolt_version: #{bolt_version})\" do\n        assert <<0x0, 0x1D, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n                 0x24, 0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83,\n                 0x6E, 0x75, 0x6D, 0x10, 0xA0, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode(\n                     {:run, [\"RETURN $num AS num\", %{num: 16}]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n\n      test \"RUN with params and  metadata (bolt_version: #{bolt_version})\" do\n        {:ok, metadata} = Metadata.new(%{tx_timeout: 15000})\n\n        assert <<0x0, 0x2B, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20,\n                 0x24, 0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83,\n                 0x6E, 0x75, 0x6D, 0x10, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65,\n                 0x6F, 0x75, 0x74, 0xC9, 0x3A, 0x98, 0x0,\n                 0x0>> ==\n                 :erlang.iolist_to_binary(\n                   Encoder.encode(\n                     {:run, [\"RETURN $num AS num\", %{num: 16}, metadata]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n\n      test \"Bug fix: encoding struct fails (bolt_version: #{bolt_version})\" do\n        query = \"CREATE (n:User $props)\"\n        params = %{props: %TestUser{bolt_sips: true, name: \"Strut\"}}\n\n        assert <<0x0, 0x39, 0xB3, 0x10, 0xD0, 0x16, 0x43, 0x52, 0x45, 0x41, 0x54, 0x45, 0x20,\n                 0x28, 0x6E, 0x3A, 0x55, 0x73, 0x65, 0x72, 0x20, 0x24, 0x70, 0x72, 0x6F, 0x70,\n                 0x73, 0x29, 0xA1, 0x85, 0x70, 0x72, 0x6F, 0x70, 0x73, 0xA2, 0x89, 0x62, 0x6F,\n                 0x6C, 0x74, 0x5F, 0x73, 0x69, 0x70, 0x73, 0xC3, 0x84,\n                 _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Encoder.encode({:run, [query, params]}, unquote(bolt_version))\n                 )\n      end\n    end)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/encoder_v1_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV1Test do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.Internals.PackStream.Message.EncoderV1\n\n  alias Bolt.Sips.Internals.PackStream.Message.EncoderV1\n\n  test \"Encode ACK_FAILURE\" do\n    assert <<0x0, 0x2, 0xB0, 0xE, 0x0, 0x0>> ==\n             :erlang.iolist_to_binary(EncoderV1.encode({:ack_failure, []}, 1))\n  end\n\n  test \"Encode DISCARD_ALL\" do\n    assert <<0x0, 0x2, 0xB0, 0x2F, 0x0, 0x0>> ==\n             :erlang.iolist_to_binary(EncoderV1.encode({:discard_all, []}, 1))\n  end\n\n  describe \"Encode INIT:\" do\n    test \"without params\" do\n      assert <<0x0, _, 0xB2, 0x1, _::binary>> =\n               :erlang.iolist_to_binary(EncoderV1.encode({:init, []}, 1))\n    end\n\n    test \"with params\" do\n      assert <<0x0, _, 0xB2, 0x1, _::binary>> =\n               :erlang.iolist_to_binary(EncoderV1.encode({:init, [{\"neo4j\", \"test\"}]}, 1))\n    end\n  end\n\n  test \"Encode PULL_ALL\" do\n    assert <<0x0, 0x2, 0xB0, 0x3F, 0x0, 0x0>> ==\n             :erlang.iolist_to_binary(EncoderV1.encode({:pull_all, []}, 1))\n  end\n\n  test \"Encode RESET\" do\n    assert <<0x0, 0x2, 0xB0, 0xF, 0x0, 0x0>> ==\n             :erlang.iolist_to_binary(EncoderV1.encode({:reset, []}, 1))\n  end\n\n  describe \"Encode RUN:\" do\n    test \"without params\" do\n      assert <<0x0, 0x13, 0xB2, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x35, 0x20,\n               0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0x0,\n               0x0>> == :erlang.iolist_to_binary(EncoderV1.encode({:run, [\"RETURN 5 AS num\"]}, 1))\n    end\n\n    test \"with params\" do\n      assert <<0x0, 0x21, 0xB2, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x24,\n               0x73, 0x74, 0x72, 0x20, 0x41, 0x53, 0x20, 0x73, 0x74, 0x72, 0xA1, 0x83, 0x73, 0x74,\n               0x72, 0x85, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x0,\n               0x0>> ==\n               :erlang.iolist_to_binary(\n                 EncoderV1.encode({:run, [\"RETURN $str AS str\", %{str: \"hello\"}]}, 1)\n               )\n    end\n  end\n\n  test \"fails for unknown message type\" do\n    assert {:error, :not_implemented} == EncoderV1.encode({:invalid, []}, 1)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message/encoder_v3_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.Message.EncoderV3Test do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.Internals.PackStream.Message.EncoderV3\n\n  alias Bolt.Sips.Internals.PackStream.Message.EncoderV3\n  alias Bolt.Sips.Metadata\n\n  describe \"Encode BEGIN\" do\n    test \"without params\" do\n      assert <<0x0, 0x3, 0xB1, 0x11, 0xA0, 0x0, 0x0>> ==\n               :erlang.iolist_to_binary(EncoderV3.encode({:begin, []}, 3))\n    end\n\n    test \"with empty params\" do\n      assert <<0x0, 0x3, 0xB1, 0x11, 0xA0, 0x0, 0x0>> ==\n               :erlang.iolist_to_binary(EncoderV3.encode({:begin, [%{}]}, 3))\n    end\n\n    test \"with params\" do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 5000})\n\n      assert <<0x0, 0x11, 0xB1, 0x11, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F,\n               0x75, 0x74, 0xC9, 0x13, 0x88, 0x0,\n               0x0>> == :erlang.iolist_to_binary(EncoderV3.encode({:begin, [metadata]}, 3))\n    end\n\n    test \"fails with non-metadata params\" do\n      assert {:error, _} = EncoderV3.encode({:begin, [%{tx_timeout: 5000}]}, 3)\n    end\n  end\n\n  test \"Encode COMMIT\" do\n    assert <<0x0, 0x2, 0xB0, 0x12, 0x0, 0x0>> ==\n             :erlang.iolist_to_binary(EncoderV3.encode({:commit, []}, 3))\n  end\n\n  test \"Encode GOODBYE\" do\n    assert assert <<0x0, 0x2, 0xB0, 0x02, 0x0, 0x0>> ==\n                    :erlang.iolist_to_binary(EncoderV3.encode({:goodbye, []}, 3))\n  end\n\n  describe \"Encode HELLO\" do\n    test \"without params\" do\n      assert <<0x0, _, 0xB1, 0x1, _::binary>> =\n               :erlang.iolist_to_binary(EncoderV3.encode({:hello, []}, 3))\n    end\n\n    test \"with params\" do\n      assert <<0x0, _, 0xB1, 0x1, _::binary>> =\n               :erlang.iolist_to_binary(EncoderV3.encode({:hello, [{\"neo4j\", \"test\"}]}, 3))\n    end\n  end\n\n  test \"Encode ROLLBACK\" do\n    assert <<0x0, 0x2, 0xB0, 0x13, 0x0, 0x0>> ==\n             :erlang.iolist_to_binary(EncoderV3.encode({:rollback, []}, 3))\n  end\n\n  describe \"Encode RUN\" do\n    test \"without params nor metadata\" do\n      assert <<0x0, 0x14, 0xB3, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31, 0x20,\n               0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0xA0, 0x0,\n               0x0>> == :erlang.iolist_to_binary(EncoderV3.encode({:run, [\"RETURN 1 AS num\"]}, 3))\n    end\n\n    test \"without params but with metadata\" do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 5000})\n\n      assert <<0x0, 0x22, 0xB3, 0x10, 0x8F, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x31, 0x20,\n               0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA0, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69,\n               0x6D, 0x65, 0x6F, 0x75, 0x74, 0xC9, 0x13, 0x88, 0x0,\n               0x0>> ==\n               :erlang.iolist_to_binary(\n                 EncoderV3.encode({:run, [\"RETURN 1 AS num\", %{}, metadata]}, 3)\n               )\n    end\n\n    test \"with params but without metadata\" do\n      assert <<0x0, 0x1D, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x24,\n               0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83, 0x6E, 0x75,\n               0x6D, 0x5, 0xA0, 0x0,\n               0x0>> ==\n               :erlang.iolist_to_binary(\n                 EncoderV3.encode({:run, [\"RETURN $num AS num\", %{num: 5}]}, 3)\n               )\n    end\n\n    test \"with params and  metadata\" do\n      {:ok, metadata} = Metadata.new(%{tx_timeout: 5000})\n\n      assert <<0x0, 0x2B, 0xB3, 0x10, 0xD0, 0x12, 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x20, 0x24,\n               0x6E, 0x75, 0x6D, 0x20, 0x41, 0x53, 0x20, 0x6E, 0x75, 0x6D, 0xA1, 0x83, 0x6E, 0x75,\n               0x6D, 0x5, 0xA1, 0x8A, 0x74, 0x78, 0x5F, 0x74, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0x74,\n               0xC9, 0x13, 0x88, 0x0,\n               0x0>> ==\n               :erlang.iolist_to_binary(\n                 EncoderV3.encode({:run, [\"RETURN $num AS num\", %{num: 5}, metadata]}, 3)\n               )\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/internals/pack_stream/message_test.exs",
    "content": "defmodule Bolt.Sips.Internals.PackStream.MessageTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Internals.PackStream.Message\n  alias Bolt.Sips.Metadata\n  alias Bolt.Sips.Internals.BoltVersionHelper\n\n  describe \"Encode all-bolt-version-compliant message:\" do\n    Enum.each(BoltVersionHelper.available_versions(), fn bolt_version ->\n      test \"DISCARD_ALL (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x2F, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode({:discard_all, []}, unquote(bolt_version))\n                 )\n      end\n\n      test \"PULL_ALL (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x3F, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:pull_all, []}, unquote(bolt_version)))\n      end\n\n      test \"RESET (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x0F, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:reset, []}, unquote(bolt_version)))\n      end\n\n      test \"RUN without params (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x10, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode({:run, [\"RETURN 1 AS num\"]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"RUN with params (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x10, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode(\n                     {:run, [\"RETURN $num AS num\", %{num: 5}]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n    end)\n  end\n\n  describe \"Encode Bolt <= 2 only message:\" do\n    BoltVersionHelper.available_versions()\n    |> Enum.filter(&(&1 <= 2))\n    |> Enum.each(fn bolt_version ->\n      test \"ACK_FAILURE (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x0E, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode({:ack_failure, []}, unquote(bolt_version))\n                 )\n      end\n\n      test \"INIT without auth (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x01, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:init, []}, unquote(bolt_version)))\n      end\n\n      test \"INIT with auth (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x01, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode({:init, [{\"neo4j\", \"password\"}]}, unquote(bolt_version))\n                 )\n      end\n    end)\n  end\n\n  describe \"Encode Bolt >= 3 only message\" do\n    BoltVersionHelper.available_versions()\n    |> Enum.filter(&(&1 >= 3))\n    |> Enum.each(fn bolt_version ->\n      test \"HELLO without auth (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x01, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:hello, []}, unquote(bolt_version)))\n      end\n\n      test \"HELLO with auth (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x01, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode({:hello, [{\"neo4j\", \"password\"}]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"GOODBYE (bolt_version: #{bolt_version})\" do\n        assert assert <<_, _, _, 0x02, _::binary>> =\n                        :erlang.iolist_to_binary(\n                          Message.encode({:goodbye, []}, unquote(bolt_version))\n                        )\n      end\n\n      test \"BEGIN without params (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x11, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:begin, []}, unquote(bolt_version)))\n      end\n\n      test \"BEGIN with params (bolt_version: #{bolt_version})\" do\n        {:ok, metadata} = Metadata.new(%{tx_timeout: 15000})\n\n        assert <<_, _, _, 0x11, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode({:begin, [metadata]}, unquote(bolt_version))\n                 )\n      end\n\n      test \"COMMIT (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x12, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:commit, []}, unquote(bolt_version)))\n      end\n\n      test \"ROLLBACK (bolt_version: #{bolt_version})\" do\n        assert <<_, _, _, 0x13, _::binary>> =\n                 :erlang.iolist_to_binary(Message.encode({:rollback, []}, unquote(bolt_version)))\n      end\n\n      test \"RUN without params but with metadata (bolt_version: #{bolt_version})\" do\n        {:ok, metadata} = Metadata.new(%{tx_timeout: 15000})\n\n        assert <<_, _, _, 0x10, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode(\n                     {:run, [\"RETURN 16 AS num\", %{}, metadata]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n\n      test \"RUN with params and  metadata (bolt_version: #{bolt_version})\" do\n        {:ok, metadata} = Metadata.new(%{tx_timeout: 15000})\n\n        assert <<_, _, _, 0x10, _::binary>> =\n                 :erlang.iolist_to_binary(\n                   Message.encode(\n                     {:run, [\"RETURN $num AS num\", %{num: 16}, metadata]},\n                     unquote(bolt_version)\n                   )\n                 )\n      end\n    end)\n  end\n\n  describe \"Decode all-bolt-version-compliant message:\" do\n    Enum.each(BoltVersionHelper.available_versions(), fn bolt_version ->\n      test \"SUCESS (bolt_version: #{bolt_version})\" do\n        success_hex =\n          <<0xB1, 0x70, 0xA1, 0x86, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x8B, 0x4E, 0x65, 0x6F,\n            0x34, 0x6A, 0x2F, 0x33, 0x2E, 0x34, 0x2E, 0x31>>\n\n        assert {:success, _} = Message.decode(success_hex, unquote(bolt_version))\n      end\n\n      test \"FAILURE (bolt_version: #{bolt_version})\" do\n        failure_hex =\n          <<0xB1, 0x7F, 0xA2, 0x84, 0x63, 0x6F, 0x64, 0x65, 0xD0, 0x25, 0x4E, 0x65, 0x6F, 0x2E,\n            0x43, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x45, 0x72, 0x72, 0x6F, 0x72, 0x2E, 0x53, 0x65,\n            0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2E, 0x55, 0x6E, 0x61, 0x75, 0x74, 0x68, 0x6F,\n            0x72, 0x69, 0x7A, 0x65, 0x64, 0x87, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0xD0,\n            0x39, 0x54, 0x68, 0x65, 0x20, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x20, 0x69, 0x73,\n            0x20, 0x75, 0x6E, 0x61, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x7A, 0x65, 0x64, 0x20,\n            0x64, 0x75, 0x65, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6E, 0x74,\n            0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x75, 0x72,\n            0x65, 0x2E>>\n\n        assert {:failure, _} = Message.decode(failure_hex, unquote(bolt_version))\n      end\n\n      test \"RECORD (bolt_version: #{bolt_version})\" do\n        assert {:record, _} = Message.decode(<<0xB1, 0x71, 0x91, 0x1>>, unquote(bolt_version))\n      end\n\n      test \"IGNORED (bolt_version: #{bolt_version})\" do\n        assert {:ignored, _} = Message.decode(<<0xB0, 0x7E>>, unquote(bolt_version))\n      end\n    end)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/metadata_test.exs",
    "content": "defmodule Bolt.Sips.MetadataTest do\n  use ExUnit.Case, async: true\n  alias Bolt.Sips.Metadata\n\n  @valid_metadata %{\n    bookmarks: [\"neo4j:bookmark:v1:tx1111\"],\n    tx_timeout: 5000,\n    metadata: %{\n      desc: \"Not lost in transaction\"\n    }\n  }\n\n  describe \"Create new metadata from map:\" do\n    test \"with compelete data\" do\n      expected = %Metadata{\n        bookmarks: [\"neo4j:bookmark:v1:tx1111\"],\n        tx_timeout: 5000,\n        metadata: %{\n          desc: \"Not lost in transaction\"\n        }\n      }\n\n      assert {:ok, result} = Metadata.new(@valid_metadata)\n\n      assert expected == result\n    end\n\n    test \"return error with invalid keys\" do\n      data = Map.put(@valid_metadata, :invalid, \"invalid\")\n      assert {:error, _} = Metadata.new(data)\n    end\n\n    test \"return error with invalid bookmarks\" do\n      data = Map.put(@valid_metadata, :bookmarks, \"invalid\")\n      assert {:error, _} = Metadata.new(data)\n    end\n\n    test \"return nil with empty bookmarks list\" do\n      data = Map.put(@valid_metadata, :bookmarks, [])\n\n      expected = %Metadata{\n        bookmarks: nil,\n        tx_timeout: data.tx_timeout,\n        metadata: data.metadata\n      }\n\n      assert {:ok, result} = Metadata.new(data)\n      assert expected == result\n    end\n\n    test \"return error with invalid timeout\" do\n      data = Map.put(@valid_metadata, :tx_timeout, -12)\n      assert {:error, _} = Metadata.new(data)\n    end\n\n    test \"return error with invalid metadata\" do\n      data = Map.put(@valid_metadata, :metadata, \"invalid\")\n      assert {:error, _} = Metadata.new(data)\n    end\n\n    test \"return nil with empty metadata map\" do\n      data = Map.put(@valid_metadata, :metadata, %{})\n\n      expected = %Metadata{\n        bookmarks: data.bookmarks,\n        tx_timeout: data.tx_timeout,\n        metadata: nil\n      }\n\n      assert {:ok, result} = Metadata.new(data)\n      assert expected == result\n    end\n  end\n\n  test \"to_map remove nullified data\" do\n    data = %{\n      bookmarks: [\"neo4j:bookmark:v1:tx1111\"],\n      tx_timeout: 5000\n    }\n\n    assert {:ok, metadata} = Metadata.new(data)\n\n    assert %Metadata{bookmarks: [\"neo4j:bookmark:v1:tx1111\"], metadata: nil, tx_timeout: 5000} ==\n             metadata\n\n    expected = %{\n      bookmarks: [\"neo4j:bookmark:v1:tx1111\"],\n      tx_timeout: 5000\n    }\n\n    assert expected == Metadata.to_map(metadata)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/performance_test.exs",
    "content": "defmodule Bolt.Sips.PerformanceTest do\n  use Bolt.Sips.ConnCase, async: false\n\n  setup(%{conn: conn} = context) do\n    Bolt.Sips.Test.Support.Database.clear(conn)\n    {:ok, context}\n  end\n\n  @tag :bench\n  test \"Querying 500 nodes should under 100ms\", context do\n    conn = context[:conn]\n\n    cql_create =\n      1..500\n      |> Enum.map(fn x ->\n        \"CREATE (:Test {value: 'test_#{inspect(x)}'})\"\n      end)\n      |> Enum.join(\"\\n\")\n\n    assert %Bolt.Sips.Response{stats: %{\"nodes-created\" => 500}} =\n             Bolt.Sips.query!(Bolt.Sips.conn(), cql_create)\n\n    simple_cypher = \"\"\"\n      MATCH (t:Test)\n      RETURN t AS test\n    \"\"\"\n\n    output =\n      Benchee.run(\n        %{\n          # \"run\" => fn -> query.(conn, simple_cypher) end\n          \"run\" => fn -> Bolt.Sips.Query.query(conn, simple_cypher) end\n          # \" new conn\" => fn -> query.(Bolt.Sips.conn(), simple_cypher) end\n        },\n        time: 5\n      )\n\n    # Query should take less than 50ms in average\n    assert Enum.at(output.scenarios, 0).run_time_data.statistics.average < 125_000_000\n  end\n\n  @tag :bench\n  test \"Creating nodes with properties and a long list should take less than 100ms\", context do\n    conn = context[:conn]\n\n    long_list = Enum.to_list(1..10_000)\n\n    simple_cypher = \"\"\"\n      CREATE (t:Test $props)\n      RETURN t AS test\n    \"\"\"\n\n    output =\n      Benchee.run(\n        %{\n          # \"run\" => fn -> query.(conn, simple_cypher) end\n          \"run with properties\" => fn ->\n            Bolt.Sips.Query.query(conn, simple_cypher, %{\n              props: %{test_int: 124, test_float: 12.5, list: long_list}\n            })\n          end\n          # \" new conn\" => fn -> query.(Bolt.Sips.conn(), simple_cypher) end\n        },\n        time: 5\n      )\n\n    # Query should take less than 50ms in average\n    assert Enum.at(output.scenarios, 0).run_time_data.statistics.average < 125_000_000\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/protocol_test.exs",
    "content": "defmodule Bolt.Sips.ProtocolTest do\n  use ExUnit.Case, async: false\n\n  alias Bolt.Sips.Protocol\n\n  # Transactions are not tested as BEGIN fails\n  # But works fine from Bolt.Sips.transaction\n\n  test \"connect/1 - disconnect/1 successful\" do\n    assert {:ok, %Protocol.ConnData{sock: _, bolt_version: _, configuration: _} = conn_data} =\n             Protocol.connect([])\n\n    assert :ok = Protocol.disconnect(:stop, conn_data)\n  end\n\n  test \"checkout/1 successful\" do\n    {:ok, %Protocol.ConnData{sock: _, bolt_version: _, configuration: _} = conn_data} =\n      Protocol.connect([])\n\n    assert {:ok, %Protocol.ConnData{sock: _, bolt_version: _, configuration: _} = conn_data} =\n             Protocol.checkout(conn_data)\n\n    :ok = Protocol.disconnect(:stop, conn_data)\n  end\n\n  test \"checkin/1 successful\" do\n    {:ok, %Protocol.ConnData{sock: _, bolt_version: _, configuration: _} = conn_data} =\n      Protocol.connect([])\n\n    assert {:ok, %Protocol.ConnData{sock: _, bolt_version: _, configuration: _} = conn_data} =\n             Protocol.checkin(conn_data)\n\n    :ok = Protocol.disconnect(:stop, conn_data)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/response_encoder/json_implementations_test.exs",
    "content": "defmodule Bolt.Sips.JsonImplementationsTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Types.{\n    DateTimeWithTZOffset,\n    TimeWithTZOffset,\n    Duration,\n    Point,\n    Node,\n    Relationship,\n    UnboundRelationship,\n    Path\n  }\n\n  defmodule TestStruct do\n    defstruct [:id, :name]\n  end\n\n  test \"Jason implementation OK\" do\n    assert result(:jason) == Jason.encode!(fixture())\n  end\n\n  test \"Poison implementation OK\" do\n    assert result(:poison) == Poison.encode!(fixture())\n  end\n\n  defp fixture() do\n    %Path{\n      nodes: [\n        %Node{\n          id: 56,\n          labels: [],\n          properties: %{\n            \"bolt_sips\" => true,\n            \"name\" => \"Alice\",\n            geoloc: Point.create(:wgs_84, 45.006, 40.32332, 50),\n            duration: %Duration{\n              days: 0,\n              hours: 0,\n              minutes: 54,\n              months: 12,\n              nanoseconds: 0,\n              seconds: 65,\n              weeks: 0,\n              years: 1\n            }\n          }\n        },\n        %Node{\n          id: 57,\n          labels: [],\n          properties: %{\n            \"bolt_sips\" => true,\n            \"name\" => \"Bob\",\n            created: DateTimeWithTZOffset.create(~N[2019-03-05 12:34:56], 3600),\n            user_strut: %TestStruct{id: 43, name: \"Test\"}\n          }\n        }\n      ],\n      relationships: [\n        %UnboundRelationship{\n          end: nil,\n          id: 58,\n          properties: %{\n            creation_time: TimeWithTZOffset.create(~T[12:34:56], 7200)\n          },\n          start: nil,\n          type: \"KNOWS\"\n        },\n        %Relationship{\n          end: 57,\n          id: 58,\n          properties: %{},\n          start: 56,\n          type: \"LIKES\"\n        }\n      ],\n      sequence: [1, 1]\n    }\n  end\n\n  # Poison and Jason doesn't order keys the same way\n  defp result(:jason) do\n    # Pretty formated:\n\n    # {\n    # \"nodes\": [\n    # {\n    #   \"id\": 56,\n    #   \"labels\": [],\n    #   \"properties\": {\n    #     \"duration\": \"P1Y12MT54M65.0S\",\n    #     \"geoloc\": {\n    #       \"crs\": \"wgs-84-3d\",\n    #       \"height\": 50.0,\n    #       \"latitude\": 40.32332,\n    #       \"longitude\": 45.006,\n    #       \"x\": 45.006,\n    #       \"y\": 40.32332,\n    #       \"z\": 50.0\n    #     },\n    #     \"bolt_sips\": true,\n    #     \"name\": \"Alice\"\n    #   }\n    # },\n    # {\n    #   \"id\": 57,\n    #   \"labels\": [],\n    #   \"properties\": {\n    #     \"created\": \"2019-03-05T12:34:56+01:00\",\n    #    \"user_struct\": {\n    #       id: 43,\n    #       name: \"Test\"\n    #     },\n    #     \"bolt_sips\": true,\n    #     \"name\": \"Bob\"\n    #   }\n    # }\n    # ],\n    # \"relationships\": [\n    # {\n    #   \"end\": null,\n    #   \"id\": 58,\n    #   \"properties\": {\n    #     \"creation_time\": \"12:34:56+02:00\"\n    #   },\n    #   \"start\": null,\n    #   \"type\": \"KNOWS\"\n    # },\n    # {\n    #   \"end\": 57,\n    #   \"id\": 58,\n    #   \"properties\": {},\n    #   \"start\": 56,\n    #   \"type\": \"LIKES\"\n    # }\n    # ],\n    # \"sequence\": [\n    # 1,\n    # 1\n    # ]\n    # }\n    \"{\\\"nodes\\\":[{\\\"id\\\":56,\\\"labels\\\":[],\\\"properties\\\":{\\\"duration\\\":\\\"P1Y12MT54M65.0S\\\",\\\"geoloc\\\":{\\\"crs\\\":\\\"wgs-84-3d\\\",\\\"height\\\":50.0,\\\"latitude\\\":40.32332,\\\"longitude\\\":45.006,\\\"x\\\":45.006,\\\"y\\\":40.32332,\\\"z\\\":50.0},\\\"bolt_sips\\\":true,\\\"name\\\":\\\"Alice\\\"}},{\\\"id\\\":57,\\\"labels\\\":[],\\\"properties\\\":{\\\"created\\\":\\\"2019-03-05T12:34:56+01:00\\\",\\\"user_strut\\\":{\\\"id\\\":43,\\\"name\\\":\\\"Test\\\"},\\\"bolt_sips\\\":true,\\\"name\\\":\\\"Bob\\\"}}],\\\"relationships\\\":[{\\\"end\\\":null,\\\"id\\\":58,\\\"properties\\\":{\\\"creation_time\\\":\\\"12:34:56+02:00\\\"},\\\"start\\\":null,\\\"type\\\":\\\"KNOWS\\\"},{\\\"end\\\":57,\\\"id\\\":58,\\\"properties\\\":{},\\\"start\\\":56,\\\"type\\\":\\\"LIKES\\\"}],\\\"sequence\\\":[1,1]}\"\n  end\n\n  defp result(:poison) do\n    \"{\\\"sequence\\\":[1,1],\\\"relationships\\\":[{\\\"type\\\":\\\"KNOWS\\\",\\\"start\\\":null,\\\"properties\\\":{\\\"creation_time\\\":\\\"12:34:56+02:00\\\"},\\\"id\\\":58,\\\"end\\\":null},{\\\"type\\\":\\\"LIKES\\\",\\\"start\\\":56,\\\"properties\\\":{},\\\"id\\\":58,\\\"end\\\":57}],\\\"nodes\\\":[{\\\"properties\\\":{\\\"name\\\":\\\"Alice\\\",\\\"bolt_sips\\\":true,\\\"geoloc\\\":{\\\"z\\\":50.0,\\\"y\\\":40.32332,\\\"x\\\":45.006,\\\"longitude\\\":45.006,\\\"latitude\\\":40.32332,\\\"height\\\":50.0,\\\"crs\\\":\\\"wgs-84-3d\\\"},\\\"duration\\\":\\\"P1Y12MT54M65.0S\\\"},\\\"labels\\\":[],\\\"id\\\":56},{\\\"properties\\\":{\\\"name\\\":\\\"Bob\\\",\\\"bolt_sips\\\":true,\\\"user_strut\\\":{\\\"name\\\":\\\"Test\\\",\\\"id\\\":43},\\\"created\\\":\\\"2019-03-05T12:34:56+01:00\\\"},\\\"labels\\\":[],\\\"id\\\":57}]}\"\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/response_encoder/json_test.exs",
    "content": "defmodule Bolt.Sips.ResponseEncode.JsonTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Types.{\n    DateTimeWithTZOffset,\n    TimeWithTZOffset,\n    Duration,\n    Point,\n    Node,\n    Relationship,\n    UnboundRelationship,\n    Path\n  }\n\n  alias Bolt.Sips.ResponseEncoder.Json\n\n  defmodule TestStruct do\n    defstruct [:id, :name]\n  end\n\n  test \"Encode a DateTimeWithTZOffset\" do\n    dt = DateTimeWithTZOffset.create(~N[2019-03-05 12:34:56], 3600)\n    assert \"2019-03-05T12:34:56+01:00\" == Json.encode(dt)\n  end\n\n  test \"Encode a TimeWithTZOffset\" do\n    t = TimeWithTZOffset.create(~T[12:34:56], 7200)\n    assert \"12:34:56+02:00\" == Json.encode(t)\n  end\n\n  test \"Encode a Duration\" do\n    d = %Duration{\n      days: 0,\n      hours: 0,\n      minutes: 54,\n      months: 12,\n      nanoseconds: 0,\n      seconds: 65,\n      weeks: 0,\n      years: 1\n    }\n\n    assert \"P1Y12MT54M65.0S\" == Json.encode(d)\n  end\n\n  test \"Encode a Point\" do\n    p = Point.create(:cartesian, 50, 60.5)\n    assert %{crs: \"cartesian\", x: 50.0, y: 60.5} == Json.encode(p)\n  end\n\n  test \"Encode a Node\" do\n    n = %Node{\n      id: 69,\n      labels: [\"Test\"],\n      properties: %{\n        \"uuid\" => 12345,\n        \"name\" => \"First node\"\n      }\n    }\n\n    expected = %{id: 69, labels: [\"Test\"], properties: %{\"name\" => \"First node\", \"uuid\" => 12345}}\n    assert expected == Json.encode(n)\n  end\n\n  test \"Encode a Relationship\" do\n    r = %Relationship{\n      end: 30,\n      id: 5,\n      properties: %{\n        is_valid: true\n      },\n      start: 69,\n      type: \"UPDATED_TO\"\n    }\n\n    expected = %{end: 30, id: 5, properties: %{is_valid: true}, start: 69, type: \"UPDATED_TO\"}\n    assert expected == Json.encode(r)\n  end\n\n  test \"Encode a UnboundRelationship\" do\n    r = %UnboundRelationship{\n      end: 30,\n      id: 5,\n      properties: %{\n        is_valid: true\n      },\n      start: 69,\n      type: \"UPDATED_TO\"\n    }\n\n    expected = %{end: 30, id: 5, properties: %{is_valid: true}, start: 69, type: \"UPDATED_TO\"}\n    assert expected == Json.encode(r)\n  end\n\n  test \"Encode a Path\" do\n    p = %Path{\n      nodes: [\n        %Node{\n          id: 56,\n          labels: [],\n          properties: %{\"bolt_sips\" => true, \"name\" => \"Alice\"}\n        },\n        %Node{\n          id: 57,\n          labels: [],\n          properties: %{\"bolt_sips\" => true, \"name\" => \"Bob\"}\n        }\n      ],\n      relationships: [\n        %UnboundRelationship{\n          end: nil,\n          id: 58,\n          properties: %{},\n          start: nil,\n          type: \"KNOWS\"\n        }\n      ],\n      sequence: [1, 1]\n    }\n\n    expected = %{\n      nodes: [\n        %{id: 56, labels: [], properties: %{\"bolt_sips\" => true, \"name\" => \"Alice\"}},\n        %{id: 57, labels: [], properties: %{\"bolt_sips\" => true, \"name\" => \"Bob\"}}\n      ],\n      relationships: [%{end: nil, id: 58, properties: %{}, start: nil, type: \"KNOWS\"}],\n      sequence: [1, 1]\n    }\n\n    assert expected == Json.encode(p)\n  end\n\n  test \"Encode user-defined struct\" do\n    s = %TestStruct{id: 1, name: \"test\"}\n    expected = %{id: 1, name: \"test\"}\n    assert expected == Json.encode(s)\n  end\n\n  test \"Encode Nested types\" do\n    nested = [\n      %{\n        \"rel\" => %Relationship{\n          end: 30,\n          id: 5,\n          properties: %{\n            \"created\" => %DateTimeWithTZOffset{\n              naive_datetime: ~N[2016-05-24 13:26:08.543],\n              timezone_offset: 7200\n            }\n          },\n          start: 69,\n          type: \"UPDATED_TO_\"\n        },\n        \"t1\" => %Node{\n          id: 69,\n          labels: [\"Test\"],\n          properties: %{\n            \"created\" => %DateTimeWithTZOffset{\n              naive_datetime: ~N[2016-05-24 13:26:08.543],\n              timezone_offset: 7200\n            },\n            \"uuid\" => 12345\n          }\n        },\n        \"t2\" => %Node{\n          id: 30,\n          labels: [\"Test\"],\n          properties: %{\"uuid\" => 6789}\n        }\n      }\n    ]\n\n    expected = [\n      %{\n        \"rel\" => %{\n          end: 30,\n          id: 5,\n          properties: %{\"created\" => \"2016-05-24T13:26:08.543+02:00\"},\n          start: 69,\n          type: \"UPDATED_TO_\"\n        },\n        \"t1\" => %{\n          id: 69,\n          labels: [\"Test\"],\n          properties: %{\n            \"created\" => \"2016-05-24T13:26:08.543+02:00\",\n            \"uuid\" => 12345\n          }\n        },\n        \"t2\" => %{\n          id: 30,\n          labels: [\"Test\"],\n          properties: %{\"uuid\" => 6789}\n        }\n      }\n    ]\n\n    assert expected == Json.encode(nested)\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/response_encoder_test.exs",
    "content": "defmodule Bolt.Sips.ResponseEncoderTest do\n  use ExUnit.Case, async: true\n\n  doctest Bolt.Sips.ResponseEncoder\nend\n"
  },
  {
    "path": "test/bolt_sips/types_helpers_test.exs",
    "content": "defmodule Bolt.Sips.TypesHelperTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.TypesHelper\n\n  describe \"decompose_in_hms/1:\" do\n    test \"Ok if more than a hour\" do\n      assert {1, 6, 27} = TypesHelper.decompose_in_hms(3987)\n    end\n\n    test \"Ok if less than a hour\" do\n      assert {0, 44, 35} = TypesHelper.decompose_in_hms(2675)\n    end\n\n    test \"Ok if less than a minute\" do\n      assert {0, 0, 43} = TypesHelper.decompose_in_hms(43)\n    end\n\n    test \"edge case: 1 hour\" do\n      assert {1, 0, 0} = TypesHelper.decompose_in_hms(3600)\n    end\n\n    test \"edge case: 1 minute\" do\n      assert {0, 1, 0} = TypesHelper.decompose_in_hms(60)\n    end\n  end\n\n  describe \"datetime_with_micro/2:\" do\n    test \"Successful with valid data\" do\n      expected = %DateTime{\n        calendar: Calendar.ISO,\n        day: 1,\n        hour: 23,\n        microsecond: {0, 0},\n        minute: 0,\n        month: 1,\n        second: 7,\n        std_offset: 0,\n        time_zone: \"Europe/Paris\",\n        utc_offset: 3600,\n        year: 2000,\n        zone_abbr: \"CET\"\n      }\n\n      assert ^expected = TypesHelper.datetime_with_micro(~N[2000-01-01 23:00:07], \"Europe/Paris\")\n    end\n\n    test \"Fails with invalid timezone\" do\n      assert_raise ArgumentError, fn ->\n        TypesHelper.datetime_with_micro(~N[2000-01-01 23:00:07], \"Invalid\")\n      end\n    end\n  end\n\n  describe \"formated-time_offset/1\" do\n    test \"Valid positive offset\" do\n      assert \"+01:03\" == TypesHelper.formated_time_offset(3783)\n    end\n\n    test \"Valid negative offset\" do\n      assert \"-01:03\" == TypesHelper.formated_time_offset(-3783)\n    end\n  end\nend\n"
  },
  {
    "path": "test/bolt_sips/types_test.exs",
    "content": "defmodule Bolt.Sips.TypesTest do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Types.{DateTimeWithTZOffset, Duration, TimeWithTZOffset, Point}\n\n  describe \"TimeWithTZOffset struct:\" do\n    test \"create/2\" do\n      expected = %TimeWithTZOffset{time: ~T[20:00:43], timezone_offset: 3600}\n      assert ^expected = TimeWithTZOffset.create(~T[20:00:43], 3600)\n    end\n\n    test \"format_param/1 successful with valid data\" do\n      t = %TimeWithTZOffset{time: ~T[23:00:07], timezone_offset: 3600}\n      assert {:ok, \"23:00:07+01:00\"} = TimeWithTZOffset.format_param(t)\n    end\n\n    test \"format_param/1 fails for invalid data\" do\n      t = %TimeWithTZOffset{time: ~T[23:00:07], timezone_offset: 3600.543}\n      assert {:error, ^t} = TimeWithTZOffset.format_param(t)\n    end\n  end\n\n  describe \"DateTimeWithTZOffset struct:\" do\n    test \"create/2\" do\n      expected = %DateTimeWithTZOffset{\n        naive_datetime: ~N[2000-01-01 23:00:07],\n        timezone_offset: 3600\n      }\n\n      assert ^expected = DateTimeWithTZOffset.create(~N[2000-01-01 23:00:07], 3600)\n    end\n\n    test \"format_param/1 successful with valid data\" do\n      t = %DateTimeWithTZOffset{\n        naive_datetime: ~N[2000-01-01 23:00:07],\n        timezone_offset: 3600\n      }\n\n      assert {:ok, \"2000-01-01T23:00:07+01:00\"} = DateTimeWithTZOffset.format_param(t)\n    end\n\n    test \"format_param/2 fails for invalid data\" do\n      # timezone_offset can't be a float\n      t = %DateTimeWithTZOffset{\n        naive_datetime: ~N[2000-01-01 23:00:07],\n        timezone_offset: 3600.43\n      }\n\n      assert {:error, ^t} = DateTimeWithTZOffset.format_param(t)\n    end\n  end\n\n  describe \"Duration struct:\" do\n    test \"create/4\" do\n      expected = %Duration{\n        days: 53,\n        hours: 0,\n        minutes: 2,\n        months: 3,\n        nanoseconds: 54,\n        seconds: 5,\n        weeks: 0,\n        years: 1\n      }\n\n      assert expected == Duration.create(15, 53, 125, 54)\n    end\n\n    test \"format_param/1 successful with valid data\" do\n      duration = Duration.create(15, 53, 125, 54)\n\n      assert {:ok, \"P1Y3M53DT2M5.000000054S\"} = Duration.format_param(duration)\n    end\n\n    test \"format_param/1 successfull with large amount of nanoseconds (use create/4 to build struct)\" do\n      duration = Duration.create(0, 0, 0, 12_545_876_654)\n      assert {:ok, \"PT12.545876654S\"} = Duration.format_param(duration)\n    end\n\n    test \"format_param/1 successfull with large amount of nanoseconds (use %Duration{} to build struct)\" do\n      duration = %Duration{\n        days: 0,\n        hours: 0,\n        minutes: 0,\n        months: 0,\n        nanoseconds: 12_545_876_654,\n        seconds: 0,\n        weeks: 0,\n        years: 0\n      }\n\n      assert {:ok, \"PT12.545876654S\"} = Duration.format_param(duration)\n    end\n\n    test \"format_param/1 fails for invalid data\" do\n      duration = %Duration{\n        days: 53.45,\n        hours: 0,\n        minutes: 2,\n        months: 3,\n        nanoseconds: 54,\n        seconds: 5,\n        weeks: 0,\n        years: 1\n      }\n\n      assert {:error, ^duration} = Duration.format_param(duration)\n    end\n  end\n\n  describe \"Point struct:\" do\n    test \"create/3 succesfully creates a CARTESIAN point 2D\" do\n      expected = %Point{\n        crs: \"cartesian\",\n        srid: 7203,\n        latitude: nil,\n        longitude: nil,\n        height: nil,\n        x: 10.0,\n        y: 20.0,\n        z: nil\n      }\n\n      assert expected == Point.create(:cartesian, 10, 20.0)\n    end\n\n    test \"create/3 succesfully creates a GEOGRAPHIC point 2D\" do\n      expected = %Point{\n        crs: \"wgs-84\",\n        srid: 4326,\n        latitude: 20.0,\n        longitude: 10.0,\n        height: nil,\n        x: 10.0,\n        y: 20.0,\n        z: nil\n      }\n\n      assert expected == Point.create(:wgs_84, 10, 20.0)\n    end\n\n    test \"create/4 succesfully creates a CARTESIAN point 3D\" do\n      expected = %Point{\n        crs: \"cartesian-3d\",\n        srid: 9157,\n        latitude: nil,\n        longitude: nil,\n        height: nil,\n        x: 10.0,\n        y: 20.0,\n        z: 25.43\n      }\n\n      assert expected == Point.create(:cartesian, 10, 20.0, 25.43)\n    end\n\n    test \"create/4 succesfully creates a GEOGRAPHIC point 3D\" do\n      expected = %Point{\n        crs: \"wgs-84-3d\",\n        srid: 4979,\n        latitude: 20.0,\n        longitude: 10.0,\n        height: 25.43,\n        x: 10.0,\n        y: 20.0,\n        z: 25.43\n      }\n\n      assert expected == Point.create(:wgs_84, 10, 20.0, 25.43)\n    end\n\n    test \"format_param/1 successful with valid param\" do\n      point = Point.create(:wgs_84, 10, 20.0, 25.43)\n\n      expected = %{\n        crs: \"wgs-84-3d\",\n        height: 25.43,\n        latitude: 20.0,\n        longitude: 10.0,\n        x: 10.0,\n        y: 20.0,\n        z: 25.43\n      }\n\n      assert {:ok, expected} == Point.format_param(point)\n    end\n\n    test \"format_param/2 fails for invalid param\" do\n      point = %Point{\n        crs: \"wgs-84-3d\",\n        srid: 4979,\n        latitude: 20.0,\n        longitude: 10.0,\n        height: 25.43,\n        x: 10.0,\n        y: 20.0,\n        z: \"invalid\"\n      }\n\n      assert {:error, ^point} = Point.format_param(point)\n    end\n  end\nend\n"
  },
  {
    "path": "test/boltkit_test.exs",
    "content": "defmodule Bolt.Sips.BoltStubTest do\n  @moduledoc \"\"\"\n  !!Remember!!\n    you cannot reuse the boltstub across the tests, therefore must use a\n    unique prefix or use the one returned by the BoltKitCase\n  \"\"\"\n  use Bolt.Sips.BoltKitCase, async: false\n\n  @moduletag :boltkit\n\n  @tag boltkit: %{\n         url: \"bolt://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/return_x.bolt\", 9001}\n         ],\n         debug: true\n       }\n  test \"test/scripts/return_x.bolt\", %{prefix: prefix} do\n    assert %Bolt.Sips.Response{results: [%{\"x\" => 1}]} =\n             Bolt.Sips.conn(:direct, prefix: prefix)\n             |> Bolt.Sips.query!(\"RETURN $x\", %{x: 1})\n  end\n\n  @tag boltkit: %{\n         url: \"bolt://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/count.bolt\", 9001}\n         ]\n       }\n  test \"test/scripts/count.bolt\", %{prefix: prefix} do\n    assert %Bolt.Sips.Response{\n             results: [\n               %{\"n\" => 1},\n               %{\"n\" => 2},\n               %{\"n\" => 3},\n               %{\"n\" => 4},\n               %{\"n\" => 5},\n               %{\"n\" => 6},\n               %{\"n\" => 7},\n               %{\"n\" => 8},\n               %{\"n\" => 9},\n               %{\"n\" => 10}\n             ]\n           } =\n             Bolt.Sips.conn(:direct, prefix: prefix)\n             |> Bolt.Sips.query!(\"UNWIND range(1, 10) AS n RETURN n\")\n  end\nend\n"
  },
  {
    "path": "test/config_test.exs",
    "content": "defmodule Config.Test do\n  use ExUnit.Case\n  alias Bolt.Sips.Utils\n\n  doctest Bolt.Sips\n\n  @graphenedb_like_url \"bolt://hobby-happyHoHoHo.dbs.graphenedb.com:24786\"\n\n  @basic_tls_config [\n    url: @graphenedb_like_url,\n    basic_auth: [username: \"xmas\", password: \"Kr1ngl3\"],\n    ssl: [verify: :verify_none]\n  ]\n\n  @light_tls_config [\n    url: \"bolt://xmas:Kr1ngl3@hobby-happyHoHoHo.dbs.graphenedb.com:24786\",\n    ssl: true\n  ]\n\n  @mixed_config [\n    hostname: \"127.0.0.1\",\n    port: 0,\n    url: @graphenedb_like_url\n  ]\n\n  @basic_config [\n    hostname: \"hobby\",\n    basic_auth: [username: \"neo4j\", password: \"neo4j\"],\n    port: 1234,\n    pool_size: 15,\n    max_overflow: 3,\n    prefix: :default\n  ]\n\n  test \"parsing the host and the port, from a url string config parameter\" do\n    config = Utils.default_config(@basic_tls_config)\n\n    assert config[:url] == @graphenedb_like_url\n    assert config[:hostname] == \"hobby-happyHoHoHo.dbs.graphenedb.com\"\n    assert config[:basic_auth] == [username: \"xmas\", password: \"Kr1ngl3\"]\n    assert config[:port] == 24786\n    assert config[:ssl] == [verify: :verify_none]\n    assert config[:prefix] == :default\n  end\n\n  test \"url string in config overides the :hostname and the :port\" do\n    config = Utils.default_config(@mixed_config)\n\n    assert config[:url] == @graphenedb_like_url\n    assert config[:hostname] == \"hobby-happyHoHoHo.dbs.graphenedb.com\"\n    assert config[:port] == 24786\n  end\n\n  test \"standard Bolt.Sips configuration parameters\" do\n    config = Utils.default_config(@basic_config)\n\n    assert config[:url] == nil\n    assert config[:hostname] == \"hobby\"\n    assert config[:basic_auth] == [username: \"neo4j\", password: \"neo4j\"]\n    assert config[:port] == 1234\n    assert config[:ssl] == false\n  end\n\n  test \"url containing authentication details, the hostname, the protocol and port, all together\" do\n    config = Utils.default_config(@light_tls_config)\n\n    assert config[:url] != nil\n    assert config[:hostname] == \"hobby-happyHoHoHo.dbs.graphenedb.com\"\n    assert config[:basic_auth] == [username: \"xmas\", password: \"Kr1ngl3\"]\n    assert config[:port] == 24786\n    assert config[:ssl] == true\n  end\n\n  test \"standard Bolt.Sips default configuration\" do\n    config = Utils.default_config([])\n\n    assert config[:hostname] == \"localhost\"\n    assert config[:port] == 7687\n    assert config[:ssl] == false\n  end\n\n  test \"invalid url in configuration and explicit :port\" do\n    config = Utils.default_config(url: \"example.com\", port: 123)\n\n    assert config[:hostname] == nil\n    assert config[:port] == 7687\n  end\n\n  test \"url with routing context\" do\n    config =\n      [url: \"bolt+routing://neo4j:password@neo01.graph.example.com:123456?policy=europe\"]\n      |> Utils.default_config()\n\n    assert config[:routing_context] == %{\"policy\" => \"europe\"}\n    assert config[:schema] == \"bolt+routing\"\n    assert config[:hostname] == \"neo01.graph.example.com\"\n    assert config[:port] == 123_456\n    assert config[:query] == \"policy=europe\"\n    assert config[:basic_auth] == [username: \"neo4j\", password: \"password\"]\n  end\nend\n"
  },
  {
    "path": "test/errors_test.exs",
    "content": "defmodule ErrorsTest do\n  @moduledoc \"\"\"\n  every new error, and related tests\n  \"\"\"\n  use ExUnit.Case, async: true\n\n  @simple_map %{foo: \"bar\", bolt_sips: true}\n  @nested_map %{\n    foo: \"bar\",\n    bolt_sips: true,\n    a_map: %{unu: 1, doi: 2, baz: \"foo\"},\n    a_list: [1, 2, 3.14]\n  }\n\n  test \"create a node using SET properties and a simple map\" do\n    %Bolt.Sips.Response{stats: stats, type: type} =\n      Bolt.Sips.query!(Bolt.Sips.conn(), \"CREATE (report:Report) SET report = $props\", %{\n        props: @simple_map\n      })\n\n    assert %{\"labels-added\" => 1, \"nodes-created\" => 1, \"properties-set\" => 2} == stats\n    assert \"w\" == type\n  end\n\n  test \"exception when creating a node using SET properties with a nested map\" do\n    err = \"Property values can only be of primitive types or arrays thereof\"\n\n    assert_raise Bolt.Sips.Exception, err, fn ->\n      Bolt.Sips.query!(\n        Bolt.Sips.conn(),\n        \"CREATE (report:Report) SET report = $props\",\n        %{props: @nested_map}\n      )\n    end\n  end\n\n  test \"exception when creating a node using SET properties with a list\" do\n    assert_raise Bolt.Sips.Exception, fn ->\n      Bolt.Sips.query!(Bolt.Sips.conn(), \"CREATE (report:Report) SET report = $props\", %{\n        props: [\"foo\", \"bar\"]\n      })\n    end\n  end\nend\n"
  },
  {
    "path": "test/invalid_param_type_test.exs",
    "content": "defmodule Bolt.Sips.InvalidParamType.Test do\n  use ExUnit.Case\n\n  setup_all do\n    Bolt.Sips.ConnectionSupervisor.connections()\n    {:ok, [conn: Bolt.Sips.conn()]}\n  end\n\n  test \"executing a Cypher query, with invalid parameter value yields an error\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (n:Person {invalid: {an_elixir_datetime}}) RETURN TRUE\n    \"\"\"\n\n    {:error, %Bolt.Sips.Error{message: message}} =\n      Bolt.Sips.query(conn, cypher, %{an_elixir_tuple: {:not, :valid}})\n\n    assert String.match?(message, ~r/unable to encode value: {:not, :valid}/i)\n  end\nend\n"
  },
  {
    "path": "test/one_test.exs",
    "content": "defmodule One.Test do\n  # use Bolt.Sips.RoutingConnCase\n  # @moduletag :routing\n\n  # # alias Bolt.Sips.{Success, Error, Response}\n  # # alias Bolt.Sips.Types.{Node, Relationship, UnboundRelationship, Path}\n\n  # @tag :routing\n  # test \"temporary placeholder for focused tests during development/debugging\" do\n  #   assert %{\"r\" => 300} ==\n  #            Bolt.Sips.conn(:write) |> Bolt.Sips.query!(\"RETURN 300 AS r\") |> List.first()\n  # end\n\n  use ExUnit.Case\n  alias Bolt.Sips.Response\n\n  test \"a simple query\" do\n    conn = Bolt.Sips.conn()\n    response = Bolt.Sips.query!(conn, \"RETURN 300 AS r\")\n\n    assert %Response{results: [%{\"r\" => 300}]} = response\n    assert response |> Enum.member?(\"r\")\n    assert 1 = response |> Enum.count()\n    assert [%{\"r\" => 300}] = response |> Enum.take(1)\n    assert %{\"r\" => 300} = response |> Response.first()\n  end\n\n  # @tag :skip\n  test \"multiple statements\" do\n    conn = Bolt.Sips.conn()\n\n    q = \"\"\"\n    MATCH (n {bolt_sips: true}) OPTIONAL MATCH (n)-[r]-() DELETE n,r;\n    CREATE (BoltSip:BoltSip {title:'Elixir sipping from Neo4j, using Bolt', released:2016, license:'MIT', bolt_sips: true});\n    MATCH (b:BoltSips{bolt_sips: true}) RETURN b\n    \"\"\"\n\n    l = Bolt.Sips.query!(conn, q)\n    assert is_list(l)\n\n    assert 3 ==\n             Enum.filter(l, fn\n               %Response{} -> true\n               _ -> false\n             end)\n             |> Enum.count()\n  end\nend\n"
  },
  {
    "path": "test/query_bolt_v2_test.exs",
    "content": "defmodule Bolt.Sips.QueryBoltV2Test do\n  use Bolt.Sips.ConnCase, async: true\n  @moduletag :bolt_v2\n\n  alias Bolt.Sips.Types.{Duration, DateTimeWithTZOffset, Point, TimeWithTZOffset}\n  alias Bolt.Sips.{TypesHelper, Response}\n\n  setup_all do\n    # reuse the same connection for all the tests in the suite\n    conn = Bolt.Sips.conn()\n    {:ok, [conn: conn]}\n  end\n\n  test \"transform Point in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN point($point_data) AS pt\"\n    params = %{point_data: Point.create(:cartesian, 50, 60.5)}\n\n    assert {:ok, %Response{results: res}} = Bolt.Sips.query(conn, query, params)\n\n    assert res == [\n             %{\n               \"pt\" => %Bolt.Sips.Types.Point{\n                 crs: \"cartesian\",\n                 height: nil,\n                 latitude: nil,\n                 longitude: nil,\n                 srid: 7203,\n                 x: 50.0,\n                 y: 60.5,\n                 z: nil\n               }\n             }\n           ]\n  end\n\n  test \"transform Duration in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN duration($d) AS d\"\n\n    params = %{\n      d: %Duration{\n        days: 0,\n        hours: 0,\n        minutes: 54,\n        months: 12,\n        nanoseconds: 0,\n        seconds: 65,\n        weeks: 0,\n        years: 1\n      }\n    }\n\n    expected = %Duration{\n      days: 0,\n      hours: 0,\n      minutes: 55,\n      months: 0,\n      nanoseconds: 0,\n      seconds: 5,\n      weeks: 0,\n      years: 2\n    }\n\n    assert {:ok, %Response{results: [%{\"d\" => ^expected}]}} = Bolt.Sips.query(conn, query, params)\n  end\n\n  test \"transform Date in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN date($d) AS d\"\n    params = %{d: ~D[2019-02-04]}\n\n    assert {:ok, %Response{results: res}} = Bolt.Sips.query(conn, query, params)\n    assert res == [%{\"d\" => ~D[2019-02-04]}]\n  end\n\n  test \"transform TimeWithTZOffset in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN time($t) AS t\"\n    time_with_tz = %TimeWithTZOffset{time: ~T[12:45:30.250876], timezone_offset: 3600}\n    params = %{t: time_with_tz}\n\n    assert {:ok, %Response{results: [%{\"t\" => ^time_with_tz}]}} =\n             Bolt.Sips.query(conn, query, params)\n  end\n\n  test \"transform DateTimeWithTZOffset in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN datetime($t) AS t\"\n\n    date_time_with_tz = %DateTimeWithTZOffset{\n      naive_datetime: ~N[2016-05-24 13:26:08.543267],\n      timezone_offset: 7200\n    }\n\n    params = %{t: date_time_with_tz}\n\n    assert {:ok, %Response{results: [%{\"t\" => ^date_time_with_tz}]}} =\n             Bolt.Sips.query(conn, query, params)\n  end\n\n  test \"transform DateTime With TimeZone id (UTC) in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN datetime($t) AS t\"\n\n    date_time_with_tz_id =\n      TypesHelper.datetime_with_micro(~N[2016-05-24 13:26:08.543218], \"Etc/UTC\")\n\n    params = %{t: date_time_with_tz_id}\n\n    assert {:ok, %Response{results: [%{\"t\" => ^date_time_with_tz_id}]}} =\n             Bolt.Sips.query(conn, query, params)\n  end\n\n  test \"transform DateTime With TimeZone id (Non-UTC) in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN datetime($t) AS t\"\n\n    date_time_with_tz_id =\n      TypesHelper.datetime_with_micro(~N[2016-05-24 13:26:08.543789], \"Europe/Paris\")\n\n    params = %{t: date_time_with_tz_id}\n\n    assert {:ok, %Response{results: [%{\"t\" => ^date_time_with_tz_id}]}} =\n             Bolt.Sips.query(conn, query, params)\n  end\n\n  test \"transform NaiveDateTime in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN localdatetime($t) AS t\"\n\n    ndt = ~N[2016-05-24 13:26:08.543156]\n    params = %{t: ndt}\n\n    assert {:ok, %Response{results: [%{\"t\" => ^ndt}]}} = Bolt.Sips.query(conn, query, params)\n  end\n\n  test \"transform Time in cypher-compliant data\", context do\n    conn = context[:conn]\n    query = \"RETURN localtime($t) AS t\"\n\n    t = ~T[13:26:08.543440]\n    params = %{t: t}\n\n    assert {:ok, %Response{results: [%{\"t\" => ^t}]}} = Bolt.Sips.query(conn, query, params)\n  end\nend\n"
  },
  {
    "path": "test/query_test.exs",
    "content": "defmodule Query.Test do\n  use Bolt.Sips.ConnCase, async: true\n\n  alias Query.Test\n  alias Bolt.Sips.Test.Support.Database\n  alias Bolt.Sips.Response\n\n  defmodule TestUser do\n    defstruct name: \"\", bolt_sips: true\n  end\n\n  defp rebuild_fixtures(conn) do\n    Database.clear(conn)\n    Bolt.Sips.Fixture.create_graph(conn, :bolt_sips)\n  end\n\n  setup(%{conn: conn} = context) do\n    rebuild_fixtures(conn)\n    {:ok, context}\n  end\n\n  test \"a simple query that should work\", context do\n    conn = context[:conn]\n\n    cyp = \"\"\"\n      MATCH (n:Person {bolt_sips: true})\n      RETURN n.name AS Name\n      ORDER BY Name DESC\n      LIMIT 5\n    \"\"\"\n\n    {:ok, %Response{} = row} = Bolt.Sips.query(conn, cyp)\n\n    assert Response.first(row)[\"Name\"] == \"Patrick Rothfuss\",\n           \"missing 'The Name of the Wind' database, or data incomplete\"\n  end\n\n  test \"A procedure call failure should send reset and not lock the db\", context do\n    expected_neo4j = System.get_env(\"NEO4J_VERSION\") || \"3.0.0\"\n\n    if Version.match?(expected_neo4j, \"~> 3.5.0\") do\n      conn = context[:conn]\n\n      cyp_fail = \"\"\"\n        CALL db.index.fulltext.queryNodes(\\\"topic_label\\\", \\\"badparen)\\\") YIELD node RETURN node\n      \"\"\"\n\n      {:error, %Bolt.Sips.Error{code: \"Neo.ClientError.Procedure.ProcedureCallFailed\"}} =\n        Bolt.Sips.query(conn, cyp_fail)\n\n      cyp = \"\"\"\n        MATCH (n:Person {bolt_sips: true})\n        RETURN n.name AS Name\n        ORDER BY Name DESC\n        LIMIT 5\n      \"\"\"\n\n      {:ok, %Response{} = row} = Bolt.Sips.query(conn, cyp)\n\n      assert Response.first(row)[\"Name\"] == \"Patrick Rothfuss\",\n             \"missing 'The Name of the Wind' database, or data incomplete\"\n    end\n  end\n\n  @tag :apoc\n  test \"Passing a timeout option to the query should prevent a timeout\", context do\n    conn = context[:conn]\n\n    cyp_wait = \"\"\"\n      CALL apoc.util.sleep(20000) RETURN 1 as test\n    \"\"\"\n\n    {:ok, %Response{} = _row} = Bolt.Sips.query(conn, cyp_wait, %{}, timeout: 21_000)\n  end\n\n  @tag :apoc\n  test \"After a timeout, subsequent queries should work\", context do\n    conn = context[:conn]\n\n    cyp_wait = \"\"\"\n      CALL apoc.util.sleep(10000) RETURN 1 as test\n    \"\"\"\n\n    {:error, _} = Bolt.Sips.query(conn, cyp_wait, %{}, timeout: 5_000)\n\n    cyp = \"\"\"\n      MATCH (n:Person {bolt_sips: true})\n      RETURN n.name AS Name\n      ORDER BY Name DESC\n      LIMIT 5\n    \"\"\"\n\n    {:ok, %Response{} = row} = Bolt.Sips.query(conn, cyp)\n\n    assert Response.first(row)[\"Name\"] == \"Patrick Rothfuss\",\n           \"missing 'The Name of the Wind' database, or data incomplete\"\n  end\n\n  test \"executing a Cypher query, with parameters\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (n:Person {bolt_sips: true})\n      WHERE n.name = $name\n      RETURN n.name AS name\n    \"\"\"\n\n    case Bolt.Sips.query(conn, cypher, %{name: \"Kote\"}) do\n      {:ok, %Response{} = rows} ->\n        refute Enum.count(rows) == 0,\n               \"Did you initialize the 'The Name of the Wind' database?\"\n\n        refute Enum.count(rows) > 1, \"Kote?! There is only one!\"\n        assert Response.first(rows)[\"name\"] == \"Kote\", \"expecting to find Kote\"\n\n      {:error, reason} ->\n        IO.puts(\"Error: #{reason[\"message\"]}\")\n    end\n  end\n\n  test \"executing a Cypher query, with struct parameters\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      CREATE(n:User $props)\n    \"\"\"\n\n    assert {:ok,\n            %Response{\n              stats: %{\n                \"labels-added\" => 1,\n                \"nodes-created\" => 1,\n                \"properties-set\" => 2\n              },\n              type: \"w\"\n            }} =\n             Bolt.Sips.query(conn, cypher, %{\n               props: %Test.TestUser{name: \"Strut\", bolt_sips: true}\n             })\n  end\n\n  test \"executing a Cpyher query, with map parameters\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      CREATE(n:User $props)\n    \"\"\"\n\n    assert {:ok, %Response{}} =\n             Bolt.Sips.query(conn, cypher, %{props: %{name: \"Mep\", bolt_sips: true}})\n  end\n\n  test \"executing a raw Cypher query with alias, and no parameters\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (p:Person {bolt_sips: true})\n      RETURN p, p.name AS name, toUpper(p.name) as NAME,\n             coalesce(p.nickname,\"n/a\") AS nickname,\n             { name: p.name, label:head(labels(p))} AS person\n      ORDER BY name DESC\n    \"\"\"\n\n    {:ok, %Response{} = r} = Bolt.Sips.query(conn, cypher)\n\n    assert Enum.count(r) == 3,\n           \"you're missing some characters from the 'The Name of the Wind' db\"\n\n    if row = Response.first(r) do\n      assert row[\"p\"].properties[\"name\"] == \"Patrick Rothfuss\"\n      assert is_map(row[\"p\"]), \"was expecting a map `p`\"\n      assert row[\"person\"][\"label\"] == \"Person\"\n      assert row[\"NAME\"] == \"PATRICK ROTHFUSS\"\n      assert row[\"nickname\"] == \"n/a\"\n      assert row[\"p\"].properties[\"bolt_sips\"] == true\n    else\n      IO.puts(\"Did you initialize the 'The Name of the Wind' database?\")\n    end\n  end\n\n  test \"if Patrick Rothfuss wrote The Name of the Wind\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (p:Person)-[r:WROTE]->(b:Book {title: 'The Name of the Wind'})\n      RETURN p\n    \"\"\"\n\n    %Response{} = rows = Bolt.Sips.query!(conn, cypher)\n    assert Response.first(rows)[\"p\"].properties[\"name\"] == \"Patrick Rothfuss\"\n  end\n\n  test \"it returns only known role names\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (p)-[r:ACTED_IN]->() where p.bolt_sips RETURN r.roles as roles\n      LIMIT 25\n    \"\"\"\n\n    %Response{results: rows} = Bolt.Sips.query!(conn, cypher)\n    roles = [\"killer\", \"sword fighter\", \"magician\", \"musician\", \"many talents\"]\n    my_roles = Enum.map(rows, & &1[\"roles\"]) |> List.flatten()\n    assert my_roles -- roles == [], \"found more roles in the db than expected\"\n  end\n\n  test \"path from: MERGE p=({name:'Alice'})-[:KNOWS]-> ...\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n    MERGE p = ({name:'Alice', bolt_sips: true})-[:KNOWS]->({name:'Bob', bolt_sips: true})\n    RETURN p\n    \"\"\"\n\n    path =\n      Bolt.Sips.query!(conn, cypher)\n      |> Response.first()\n      |> Map.get(\"p\")\n\n    assert {2, 1} == {length(path.nodes), length(path.relationships)}\n  end\n\n  test \"return a single number from a statement with params\", context do\n    conn = context[:conn]\n    row = Bolt.Sips.query!(conn, \"RETURN $n AS num\", %{n: 10}) |> Response.first()\n    assert row[\"num\"] == 10\n  end\n\n  test \"run simple statement with complex params\", context do\n    conn = context[:conn]\n\n    row =\n      Bolt.Sips.query!(conn, \"RETURN $x AS n\", %{x: %{abc: [\"d\", \"e\", \"f\"]}})\n      |> Response.first()\n\n    assert row[\"n\"][\"abc\"] == [\"d\", \"e\", \"f\"]\n  end\n\n  test \"return an array of numbers\", context do\n    conn = context[:conn]\n    row = Bolt.Sips.query!(conn, \"RETURN [10,11,21] AS arr\") |> Response.first()\n    assert row[\"arr\"] == [10, 11, 21]\n  end\n\n  test \"return a string\", context do\n    conn = context[:conn]\n    row = Bolt.Sips.query!(conn, \"RETURN 'Hello' AS salute\") |> Response.first()\n    assert row[\"salute\"] == \"Hello\"\n  end\n\n  test \"UNWIND range(1, 10) AS n RETURN n\", context do\n    conn = context[:conn]\n\n    assert %Response{results: rows} = Bolt.Sips.query!(conn, \"UNWIND range(1, 10) AS n RETURN n\")\n\n    assert {1, 10} == rows |> Enum.map(& &1[\"n\"]) |> Enum.min_max()\n  end\n\n  test \"MERGE (k:Person {name:'Kote'}) RETURN k\", context do\n    conn = context[:conn]\n\n    k =\n      Bolt.Sips.query!(conn, \"MERGE (k:Person {name:'Kote', bolt_sips: true}) RETURN k LIMIT 1\")\n      |> Response.first()\n      |> Map.get(\"k\")\n\n    assert k.labels == [\"Person\"]\n    assert k.properties[\"name\"] == \"Kote\"\n  end\n\n  test \"query/2 and query!/2\", context do\n    conn = context[:conn]\n\n    assert r = Bolt.Sips.query!(conn, \"RETURN [10,11,21] AS arr\")\n    assert [10, 11, 21] = Response.first(r)[\"arr\"]\n\n    assert {:ok, %Response{} = r} = Bolt.Sips.query(conn, \"RETURN [10,11,21] AS arr\")\n    assert [10, 11, 21] = Response.first(r)[\"arr\"]\n  end\n\n  test \"create a Bob node and check it was deleted afterwards\", context do\n    conn = context[:conn]\n\n    assert %Response{stats: stats} = Bolt.Sips.query!(conn, \"CREATE (a:Person {name:'Bob'})\")\n    assert stats == %{\"labels-added\" => 1, \"nodes-created\" => 1, \"properties-set\" => 1}\n\n    assert [\"Bob\"] ==\n             Bolt.Sips.query!(conn, \"MATCH (a:Person {name: 'Bob'}) RETURN a.name AS name\")\n             |> Enum.map(& &1[\"name\"])\n\n    assert %Response{stats: stats} =\n             Bolt.Sips.query!(conn, \"MATCH (a:Person {name:'Bob'}) DELETE a\")\n\n    assert stats[\"nodes-deleted\"] == 1\n  end\n\n  test \"Cypher version 3\", context do\n    conn = context[:conn]\n\n    assert %Response{plan: plan} = Bolt.Sips.query!(conn, \"EXPLAIN RETURN 1\")\n    refute plan == nil\n    assert Regex.match?(~r/CYPHER [3|4]/iu, plan[\"args\"][\"version\"])\n  end\n\n  test \"EXPLAIN MATCH (n), (m) RETURN n, m\", context do\n    conn = context[:conn]\n\n    assert %Response{notifications: notifications, plan: plan} =\n             Bolt.Sips.query!(conn, \"EXPLAIN MATCH (n), (m) RETURN n, m\")\n\n    refute notifications == nil\n    refute plan == nil\n\n    if Regex.match?(~r/CYPHER 3/iu, plan[\"args\"][\"version\"]) do\n      assert \"CartesianProduct\" ==\n               plan[\"children\"]\n               |> List.first()\n               |> Map.get(\"operatorType\")\n    else\n      assert(\n        \"CartesianProduct@neo4j\" ==\n          plan[\"children\"]\n          |> List.first()\n          |> Map.get(\"operatorType\")\n      )\n    end\n  end\n\n  test \"can execute a query after a failure\", context do\n    conn = context[:conn]\n    assert {:error, _} = Bolt.Sips.query(conn, \"INVALID CYPHER\")\n    assert {:ok, %Response{results: [%{\"n\" => 22}]}} = Bolt.Sips.query(conn, \"RETURN 22 as n\")\n  end\n\n  test \"negative numbers are returned as negative numbers\", context do\n    conn = context[:conn]\n    assert {:ok, %Response{results: [%{\"n\" => -1}]}} = Bolt.Sips.query(conn, \"RETURN -1 as n\")\n  end\n\n  test \"return a simple node\", context do\n    conn = context[:conn]\n\n    assert %Response{\n             results: [\n               %{\n                 \"p\" => %Bolt.Sips.Types.Node{\n                   id: _,\n                   labels: [\"Person\"],\n                   properties: %{\"bolt_sips\" => true, \"name\" => \"Patrick Rothfuss\"}\n                 }\n               }\n             ]\n           } = Bolt.Sips.query!(conn, \"MATCH (p:Person {name: 'Patrick Rothfuss'}) RETURN p\")\n  end\n\n  test \"Simple relationship\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (p:Person)-[r:WROTE]->(b:Book {title: 'The Name of the Wind'})\n      RETURN r\n    \"\"\"\n\n    assert %Response{\n             results: [\n               %{\n                 \"r\" => %Bolt.Sips.Types.Relationship{\n                   end: _,\n                   id: _,\n                   properties: %{},\n                   start: _,\n                   type: \"WROTE\"\n                 }\n               }\n             ]\n           } = Bolt.Sips.query!(conn, cypher)\n  end\n\n  test \"simple path\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n    MERGE p = ({name:'Alice', bolt_sips: true})-[:KNOWS]->({name:'Bob', bolt_sips: true})\n    RETURN p\n    \"\"\"\n\n    assert %Response{\n             results: [\n               %{\n                 \"p\" => %Bolt.Sips.Types.Path{\n                   nodes: [\n                     %Bolt.Sips.Types.Node{\n                       id: _,\n                       labels: [],\n                       properties: %{\"bolt_sips\" => true, \"name\" => \"Alice\"}\n                     },\n                     %Bolt.Sips.Types.Node{\n                       id: _,\n                       labels: [],\n                       properties: %{\"bolt_sips\" => true, \"name\" => \"Bob\"}\n                     }\n                   ],\n                   relationships: [\n                     %Bolt.Sips.Types.UnboundRelationship{\n                       end: nil,\n                       id: _,\n                       properties: %{},\n                       start: nil,\n                       type: \"KNOWS\"\n                     }\n                   ],\n                   sequence: [1, 1]\n                 }\n               }\n             ]\n           } = Bolt.Sips.query!(conn, cypher)\n  end\n\n  test \"transaction (commit)\", context do\n    conn = context[:conn]\n\n    Bolt.Sips.transaction(conn, fn conn ->\n      book =\n        Bolt.Sips.query!(conn, \"CREATE (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n        |> Response.first()\n\n      assert %{\"b\" => g_o_t} = book\n      assert g_o_t.properties[\"title\"] == \"The Game Of Trolls\"\n    end)\n\n    %Response{} =\n      books = Bolt.Sips.query!(conn, \"MATCH (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n\n    assert 1 == Enum.count(books)\n\n    # Clean data\n\n    rem_books = \"MATCH (b:Book {title: \\\"The Game Of Trolls\\\"}) DELETE b\"\n    Bolt.Sips.query!(conn, rem_books)\n  end\n\n  test \"transaction (rollback)\", context do\n    conn = context[:conn]\n\n    Bolt.Sips.transaction(conn, fn conn ->\n      book =\n        Bolt.Sips.query!(conn, \"CREATE (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n        |> Response.first()\n\n      assert %{\"b\" => g_o_t} = book\n      assert g_o_t.properties[\"title\"] == \"The Game Of Trolls\"\n      Bolt.Sips.rollback(conn, :changed_my_mind)\n    end)\n\n    assert %Response{} =\n             r = Bolt.Sips.query!(conn, \"MATCH (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n\n    assert Enum.count(r) == 0\n  end\nend\n"
  },
  {
    "path": "test/response_test.exs",
    "content": "defmodule ResponseTest do\n  use ExUnit.Case\n\n  alias Bolt.Sips.Response\n  # import ExUnit.CaptureLog\n\n  @explain [\n    success: %{\"fields\" => [\"n\"], \"t_first\" => 1},\n    success: %{\n      \"bookmark\" => \"neo4j:bookmark:v1:tx13440\",\n      \"plan\" => %{\n        \"args\" => %{\n          \"EstimatedRows\" => 1.0,\n          \"planner\" => \"COST\",\n          \"planner-impl\" => \"IDP\",\n          \"planner-version\" => \"3.5\",\n          \"runtime\" => \"INTERPRETED\",\n          \"runtime-impl\" => \"INTERPRETED\",\n          \"runtime-version\" => \"3.5\",\n          \"version\" => \"CYPHER 3.5\"\n        },\n        \"children\" => [\n          %{\n            \"args\" => %{\"EstimatedRows\" => 1.0},\n            \"children\" => [],\n            \"identifiers\" => [\"n\"],\n            \"operatorType\" => \"Create\"\n          }\n        ],\n        \"identifiers\" => [\"n\"],\n        \"operatorType\" => \"ProduceResults\"\n      },\n      \"t_last\" => 0,\n      \"type\" => \"rw\"\n    }\n  ]\n\n  @notifications [\n    success: %{\"fields\" => [\"n\", \"m\"], \"t_first\" => 0},\n    success: %{\n      \"bookmark\" => \"neo4j:bookmark:v1:tx13440\",\n      \"notifications\" => [\n        %{\n          \"code\" => \"Neo.ClientNotification.Statement.CartesianProductWarning\",\n          \"description\" => \"bad juju\",\n          \"position\" => %{\"column\" => 9, \"line\" => 1, \"offset\" => 8},\n          \"severity\" => \"WARNING\",\n          \"title\" => \"This query builds a cartesian product between disconnected patterns.\"\n        }\n      ],\n      \"plan\" => %{\n        \"args\" => %{\n          \"EstimatedRows\" => 36.0,\n          \"planner\" => \"COST\",\n          \"planner-impl\" => \"IDP\",\n          \"planner-version\" => \"3.5\",\n          \"runtime\" => \"INTERPRETED\",\n          \"runtime-impl\" => \"INTERPRETED\",\n          \"runtime-version\" => \"3.5\",\n          \"version\" => \"CYPHER 3.5\"\n        },\n        \"children\" => [\n          %{\n            \"args\" => %{\"EstimatedRows\" => 36.0},\n            \"children\" => [\n              %{\n                \"args\" => %{\"EstimatedRows\" => 6.0},\n                \"children\" => [],\n                \"identifiers\" => [\"n\"],\n                \"operatorType\" => \"AllNodesScan\"\n              },\n              %{\n                \"args\" => %{\"EstimatedRows\" => 6.0},\n                \"children\" => [],\n                \"identifiers\" => [\"m\"],\n                \"operatorType\" => \"AllNodesScan\"\n              }\n            ],\n            \"identifiers\" => [\"m\", \"n\"],\n            \"operatorType\" => \"CartesianProduct\"\n          }\n        ],\n        \"identifiers\" => [\"m\", \"n\"],\n        \"operatorType\" => \"ProduceResults\"\n      },\n      \"t_last\" => 0,\n      \"type\" => \"r\"\n    }\n  ]\n\n  @profile_no_results [\n    success: %{\"fields\" => [], \"t_first\" => 20},\n    success: %{\n      \"bookmark\" => \"neo4j:bookmark:v1:tx48642\",\n      \"profile\" => %{\n        \"args\" => %{\n          \"DbHits\" => 0,\n          \"EstimatedRows\" => 1.0,\n          \"PageCacheHitRatio\" => 0.0,\n          \"PageCacheHits\" => 0,\n          \"PageCacheMisses\" => 0,\n          \"Rows\" => 0,\n          \"planner\" => \"COST\",\n          \"planner-impl\" => \"IDP\",\n          \"planner-version\" => \"3.5\",\n          \"runtime\" => \"SLOTTED\",\n          \"runtime-impl\" => \"SLOTTED\",\n          \"runtime-version\" => \"3.5\",\n          \"version\" => \"CYPHER 3.5\"\n        },\n        \"children\" => [\n          %{\n            \"args\" => %{\n              \"DbHits\" => 0,\n              \"EstimatedRows\" => 1.0,\n              \"PageCacheHitRatio\" => 0.0,\n              \"PageCacheHits\" => 0,\n              \"PageCacheMisses\" => 0,\n              \"Rows\" => 0\n            },\n            \"children\" => [\n              %{\n                \"args\" => %{\n                  \"DbHits\" => 3,\n                  \"EstimatedRows\" => 1.0,\n                  \"PageCacheHitRatio\" => 0.0,\n                  \"PageCacheHits\" => 0,\n                  \"PageCacheMisses\" => 0,\n                  \"Rows\" => 1\n                },\n                \"children\" => [],\n                \"dbHits\" => 3,\n                \"identifiers\" => [\"n\"],\n                \"operatorType\" => \"Create\",\n                \"pageCacheHitRatio\" => 0.0,\n                \"pageCacheHits\" => 0,\n                \"pageCacheMisses\" => 0,\n                \"rows\" => 1\n              }\n            ],\n            \"dbHits\" => 0,\n            \"identifiers\" => [\"n\"],\n            \"operatorType\" => \"EmptyResult\",\n            \"pageCacheHitRatio\" => 0.0,\n            \"pageCacheHits\" => 0,\n            \"pageCacheMisses\" => 0,\n            \"rows\" => 0\n          }\n        ],\n        \"dbHits\" => 0,\n        \"identifiers\" => [\"n\"],\n        \"operatorType\" => \"ProduceResults\",\n        \"pageCacheHitRatio\" => 0.0,\n        \"pageCacheHits\" => 0,\n        \"pageCacheMisses\" => 0,\n        \"rows\" => 0\n      },\n      \"stats\" => %{\n        \"labels-added\" => 1,\n        \"nodes-created\" => 1,\n        \"properties-set\" => 1\n      },\n      \"t_last\" => 0,\n      \"type\" => \"w\"\n    }\n  ]\n\n  @profile_results [\n    success: %{\"fields\" => [\"num\"], \"t_first\" => 1},\n    record: [1],\n    success: %{\n      \"bookmark\" => \"neo4j:bookmark:v1:tx48642\",\n      \"profile\" => %{\n        \"args\" => %{\n          \"DbHits\" => 0,\n          \"EstimatedRows\" => 1.0,\n          \"PageCacheHitRatio\" => 0.0,\n          \"PageCacheHits\" => 0,\n          \"PageCacheMisses\" => 0,\n          \"Rows\" => 1,\n          \"Time\" => 25980,\n          \"planner\" => \"COST\",\n          \"planner-impl\" => \"IDP\",\n          \"planner-version\" => \"3.5\",\n          \"runtime\" => \"COMPILED\",\n          \"runtime-impl\" => \"COMPILED\",\n          \"runtime-version\" => \"3.5\",\n          \"version\" => \"CYPHER 3.5\"\n        },\n        \"children\" => [\n          %{\n            \"args\" => %{\n              \"DbHits\" => 0,\n              \"EstimatedRows\" => 1.0,\n              \"Expressions\" => \"{num : $`  AUTOINT0`}\",\n              \"PageCacheHitRatio\" => 0.0,\n              \"PageCacheHits\" => 0,\n              \"PageCacheMisses\" => 0,\n              \"Rows\" => 1,\n              \"Time\" => 42285\n            },\n            \"children\" => [],\n            \"dbHits\" => 0,\n            \"identifiers\" => [\"num\"],\n            \"operatorType\" => \"Projection\",\n            \"pageCacheHitRatio\" => 0.0,\n            \"pageCacheHits\" => 0,\n            \"pageCacheMisses\" => 0,\n            \"rows\" => 1\n          }\n        ],\n        \"dbHits\" => 0,\n        \"identifiers\" => [\"num\"],\n        \"operatorType\" => \"ProduceResults\",\n        \"pageCacheHitRatio\" => 0.0,\n        \"pageCacheHits\" => 0,\n        \"pageCacheMisses\" => 0,\n        \"rows\" => 1\n      },\n      \"t_last\" => 0,\n      \"type\" => \"r\"\n    }\n  ]\n\n  describe \"Response as Enumerable\" do\n    test \"a simple query\" do\n      conn = Bolt.Sips.conn()\n      response = Bolt.Sips.query!(conn, \"RETURN 300 AS r\")\n\n      assert %Response{results: [%{\"r\" => 300}]} = response\n      assert response |> Enum.member?(\"r\")\n      assert 1 = response |> Enum.count()\n      assert [%{\"r\" => 300}] = response |> Enum.take(1)\n      assert %{\"r\" => 300} = response |> Response.first()\n    end\n\n    @unwind %Bolt.Sips.Response{\n      records: [[1], [2], [3], [4], [5], [6], '\\a', '\\b', '\\t', '\\n'],\n      results: [\n        %{\"n\" => 1},\n        %{\"n\" => 2},\n        %{\"n\" => 3},\n        %{\"n\" => 4},\n        %{\"n\" => 5},\n        %{\"n\" => 6},\n        %{\"n\" => 7},\n        %{\"n\" => 8},\n        %{\"n\" => 9},\n        %{\"n\" => 10}\n      ]\n    }\n\n    test \"reduce: UNWIND range(1, 10) AS n RETURN n\" do\n      sum = Enum.reduce(@unwind, 0, &(&1[\"n\"] + &2))\n      assert 55 == sum\n    end\n\n    test \"slice: UNWIND range(1, 10) AS n RETURN n\" do\n      slice = Enum.slice(@unwind, 0..2)\n      assert [%{\"n\" => 1}, %{\"n\" => 2}, %{\"n\" => 3}] == slice\n    end\n  end\n\n  describe \"Success\" do\n    test \"with valid EXPLAIN\" do\n      assert %Response{\n               bookmark: nil,\n               fields: [\"n\"],\n               notifications: [],\n               plan: %{\n                 \"args\" => %{\n                   \"EstimatedRows\" => 1.0,\n                   \"planner\" => \"COST\",\n                   \"planner-impl\" => \"IDP\",\n                   \"planner-version\" => \"3.5\",\n                   \"runtime\" => \"INTERPRETED\",\n                   \"runtime-impl\" => \"INTERPRETED\",\n                   \"runtime-version\" => \"3.5\",\n                   \"version\" => \"CYPHER 3.5\"\n                 },\n                 \"children\" => [\n                   %{\n                     \"args\" => %{\"EstimatedRows\" => 1.0},\n                     \"children\" => [],\n                     \"identifiers\" => [\"n\"],\n                     \"operatorType\" => \"Create\"\n                   }\n                 ],\n                 \"identifiers\" => [\"n\"],\n                 \"operatorType\" => \"ProduceResults\"\n               },\n               profile: nil,\n               records: [],\n               results: [],\n               stats: [],\n               type: \"rw\"\n             } = Response.transform!(@explain)\n    end\n\n    test \"with Notifications\" do\n      %Response{notifications: [notifications | _rest]} = Response.transform!(@notifications)\n\n      assert %{\n               \"code\" => \"Neo.ClientNotification.Statement.CartesianProductWarning\",\n               \"description\" => \"bad juju\",\n               \"position\" => %{\"column\" => 9, \"line\" => 1, \"offset\" => 8},\n               \"severity\" => \"WARNING\",\n               \"title\" => \"This query builds a cartesian product between disconnected patterns.\"\n             } = notifications\n    end\n\n    test \"with Profile (without results)\" do\n      %Response{plan: nil, profile: profile, stats: stats} =\n        Response.transform!(@profile_no_results)\n\n      assert %{\n               \"args\" => %{\n                 \"DbHits\" => 0,\n                 \"EstimatedRows\" => 1.0,\n                 \"PageCacheHitRatio\" => 0.0,\n                 \"PageCacheHits\" => 0,\n                 \"PageCacheMisses\" => 0,\n                 \"Rows\" => 0,\n                 \"planner\" => \"COST\",\n                 \"planner-impl\" => \"IDP\",\n                 \"planner-version\" => \"3.5\",\n                 \"runtime\" => \"SLOTTED\",\n                 \"runtime-impl\" => \"SLOTTED\",\n                 \"runtime-version\" => \"3.5\",\n                 \"version\" => \"CYPHER 3.5\"\n               },\n               \"children\" => [\n                 %{\n                   \"args\" => %{\n                     \"DbHits\" => 0,\n                     \"EstimatedRows\" => 1.0,\n                     \"PageCacheHitRatio\" => 0.0,\n                     \"PageCacheHits\" => 0,\n                     \"PageCacheMisses\" => 0,\n                     \"Rows\" => 0\n                   },\n                   \"children\" => [\n                     %{\n                       \"args\" => %{\n                         \"DbHits\" => 3,\n                         \"EstimatedRows\" => 1.0,\n                         \"PageCacheHitRatio\" => 0.0,\n                         \"PageCacheHits\" => 0,\n                         \"PageCacheMisses\" => 0,\n                         \"Rows\" => 1\n                       },\n                       \"children\" => [],\n                       \"dbHits\" => 3,\n                       \"identifiers\" => [\"n\"],\n                       \"operatorType\" => \"Create\",\n                       \"pageCacheHitRatio\" => 0.0,\n                       \"pageCacheHits\" => 0,\n                       \"pageCacheMisses\" => 0,\n                       \"rows\" => 1\n                     }\n                   ],\n                   \"dbHits\" => 0,\n                   \"identifiers\" => [\"n\"],\n                   \"operatorType\" => \"EmptyResult\",\n                   \"pageCacheHitRatio\" => 0.0,\n                   \"pageCacheHits\" => 0,\n                   \"pageCacheMisses\" => 0,\n                   \"rows\" => 0\n                 }\n               ],\n               \"dbHits\" => 0,\n               \"identifiers\" => [\"n\"],\n               \"operatorType\" => \"ProduceResults\",\n               \"pageCacheHitRatio\" => 0.0,\n               \"pageCacheHits\" => 0,\n               \"pageCacheMisses\" => 0,\n               \"rows\" => 0\n             } = profile\n\n      assert %{\n               \"labels-added\" => 1,\n               \"nodes-created\" => 1,\n               \"properties-set\" => 1\n             } = stats\n    end\n\n    test \"with Profile (with results)\" do\n      %Response{plan: nil, profile: profile, stats: [], records: _records, results: results} =\n        Response.transform!(@profile_results)\n\n      assert %{\n               \"args\" => %{\n                 \"DbHits\" => 0,\n                 \"EstimatedRows\" => 1.0,\n                 \"PageCacheHitRatio\" => 0.0,\n                 \"PageCacheHits\" => 0,\n                 \"PageCacheMisses\" => 0,\n                 \"Rows\" => 1,\n                 \"Time\" => 25980,\n                 \"planner\" => \"COST\",\n                 \"planner-impl\" => \"IDP\",\n                 \"planner-version\" => \"3.5\",\n                 \"runtime\" => \"COMPILED\",\n                 \"runtime-impl\" => \"COMPILED\",\n                 \"runtime-version\" => \"3.5\",\n                 \"version\" => \"CYPHER 3.5\"\n               },\n               \"children\" => [\n                 %{\n                   \"args\" => %{\n                     \"DbHits\" => 0,\n                     \"EstimatedRows\" => 1.0,\n                     \"Expressions\" => \"{num : $`  AUTOINT0`}\",\n                     \"PageCacheHitRatio\" => 0.0,\n                     \"PageCacheHits\" => 0,\n                     \"PageCacheMisses\" => 0,\n                     \"Rows\" => 1,\n                     \"Time\" => 42285\n                   },\n                   \"children\" => [],\n                   \"dbHits\" => 0,\n                   \"identifiers\" => [\"num\"],\n                   \"operatorType\" => \"Projection\",\n                   \"pageCacheHitRatio\" => 0.0,\n                   \"pageCacheHits\" => 0,\n                   \"pageCacheMisses\" => 0,\n                   \"rows\" => 1\n                 }\n               ],\n               \"dbHits\" => 0,\n               \"identifiers\" => [\"num\"],\n               \"operatorType\" => \"ProduceResults\",\n               \"pageCacheHitRatio\" => 0.0,\n               \"pageCacheHits\" => 0,\n               \"pageCacheMisses\" => 0,\n               \"rows\" => 1\n             } = profile\n\n      assert [%{\"num\" => 1}] = results\n    end\n  end\nend\n"
  },
  {
    "path": "test/router_test.exs",
    "content": "defmodule Bolt.Sips.Routing.RouterTest do\n  use ExUnit.Case\n  doctest Bolt.Sips.Router\n\n  alias Bolt.Sips.Response\n\n  # @routing_table %{\n  #   \"servers\" => [\n  #     %{\"addresses\" => [\"localhost:7687\"], \"role\" => \"WRITE\"},\n  #     %{\"addresses\" => [\"localhost:7688\", \"localhost:7689\"], \"role\" => \"READ\"},\n  #     %{\n  #       \"addresses\" => [\"localhost:7688\", \"localhost:7687\", \"localhost:7689\"],\n  #       \"role\" => \"ROUTE\"\n  #     }\n  #   ],\n  #   \"ttl\" => 300\n  # }\n\n  # @connections %{\n  #   read: %{\"localhost:7688\" => 0, \"localhost:7689\" => 0},\n  #   route: %{\n  #     \"localhost:7687\" => 0,\n  #     \"localhost:7688\" => 0,\n  #     \"localhost:7689\" => 0\n  #   },\n  #   write: %{\"localhost:7687\" => 0},\n  #   ttl: 300\n  # }\n\n  @router_address \"bolt+routing://localhost:7687?key=value,foo=bar;policy=EU\"\n\n  @bolt_sips_config [\n    url: @router_address,\n    ssl: true\n  ]\n\n  @role_based_configuration [\n    url: \"bolt://localhost\",\n    basic_auth: [username: \"neo4j\", password: \"test\"],\n    pool_size: 10,\n    max_overflow: 2,\n    role: :zorba\n  ]\n\n  describe \"Role based configuration\" do\n    test \"context attributes for routed connections\" do\n      conf = Bolt.Sips.Utils.default_config(@bolt_sips_config)\n\n      assert \"bolt+routing\" == conf[:schema]\n      assert \"key=value,foo=bar;policy=EU\" == conf[:query]\n      assert %{\"key\" => \"value\", \"foo\" => \"bar\", \"policy\" => \"EU\"} == conf[:routing_context]\n    end\n\n    test \"user defined ad-hoc roles for standard (community) instances\" do\n      assert {:ok, _pid} = Bolt.Sips.start_link(@role_based_configuration)\n      assert conn = Bolt.Sips.conn(@role_based_configuration[:role])\n      assert %Response{results: [%{\"n\" => 1}]} = Bolt.Sips.query!(conn, \"RETURN 1 as n\")\n    end\n\n    test \"user defined ad-hoc roles can coexist, and act as distinct connection pools\" do\n      assert {:ok, pid1} =\n               @role_based_configuration\n               |> Keyword.put(:role, :alpha)\n               |> Bolt.Sips.start_link()\n\n      assert conn1 = Bolt.Sips.conn(:alpha)\n      assert %Response{results: [%{\"n\" => 1}]} = Bolt.Sips.query!(conn1, \"RETURN 1 as n\")\n\n      assert {:ok, pid2} = Bolt.Sips.start_link(@role_based_configuration)\n      assert pid1 == pid2\n\n      assert conn2 = Bolt.Sips.conn(@role_based_configuration[:role])\n      refute conn1 == conn2\n\n      assert %Response{results: [%{\"n\" => 1}]} = Bolt.Sips.query!(conn2, \"RETURN 1 as n\")\n\n      assert %{\n               default: %{\n                 connections: %{\n                   alpha: %{\"localhost:7687\" => 0},\n                   direct: %{\"localhost:7687\" => 0},\n                   routing_query: nil,\n                   zorba: %{\"localhost:7687\" => 0}\n                 }\n               }\n             } = Bolt.Sips.info()\n\n      assert :ok == Bolt.Sips.terminate_connections(:alpha)\n\n      assert_raise Bolt.Sips.Exception,\n                   \"no connection exists with this role: alpha (prefix: default)\",\n                   fn -> Bolt.Sips.conn(:alpha) end\n\n      refute Map.has_key?(Bolt.Sips.info(), :alpha)\n    end\n  end\nend\n"
  },
  {
    "path": "test/routing/connections_test.exs",
    "content": "defmodule Bolt.Sips.Routing.ConnectionsTest do\n  use ExUnit.Case, async: true\n  @moduletag :routing\n\n  alias Bolt.Sips.Router\n\n  @current_connections %{\n    read: %{\"localhost:7688\" => 10, \"localhost:7689\" => 20},\n    route: %{\n      \"localhost:7687\" => 2,\n      \"localhost:7688\" => 1,\n      \"localhost:7689\" => 9\n    },\n    routing_query: %{\n      params: %{props: %{}},\n      query: \"call dbms.cluster.routing.getRoutingTable($props)\"\n    },\n    ttl: 300,\n    updated_at: 1_555_705_797,\n    write: %{\"localhost:7687\" => 200, \"localhost:7690\" => 500}\n  }\n\n  @new_connections %{\n    read: %{\"localhost:7688\" => 0},\n    route: %{\"localhost:7687\" => 0},\n    write: %{\"localhost:7689\" => 0}\n  }\n\n  describe \"Router\" do\n    test \"connection information, after refresh\" do\n      assert %{\n               read: %{\"localhost:7688\" => 0},\n               route: %{\"localhost:7687\" => 0},\n               write: %{\"localhost:7689\" => 0},\n               routing_query: %{\n                 params: %{props: %{}},\n                 query: \"call dbms.cluster.routing.getRoutingTable($props)\"\n               },\n               ttl: 300\n             } = Router.merge_connections_maps(@current_connections, @new_connections)\n    end\n  end\nend\n"
  },
  {
    "path": "test/routing/crud_test.exs",
    "content": "defmodule Bolt.Sips.Routing.CrudTest do\n  use Bolt.Sips.RoutingConnCase\n  @moduletag :routing\n\n  alias Bolt.Sips\n\n  describe \"Basic Read/Write; \" do\n    test \"read\" do\n      cypher = \"return 10 as n\"\n\n      assert [%{\"n\" => 10}] ==\n               Sips.query!(Sips.conn(:read), cypher)\n    end\n\n    test \"write\" do\n      conn = Sips.conn(:write)\n      cypher = \"CREATE (elf:Elf { name: $name, from: $from, klout: 99 })\"\n\n      assert %{\n               stats: %{\n                 \"labels-added\" => 1,\n                 \"nodes-created\" => 1,\n                 \"properties-set\" => 3\n               },\n               type: \"w\"\n             } == Sips.query!(conn, cypher, %{name: \"Arameil\", from: \"Sweden\"})\n    end\n\n    # https://neo4j.com/docs/cypher-manual/current/clauses/set/#set-adding-properties-from-maps\n    test \"update\" do\n      create_cypher = \"CREATE (p:Person { first: $person.first, last: $person.last })\"\n\n      update_cypher = \"\"\"\n      MATCH (p:Person{ first: 'Green', last: 'Alien' })\n        SET p.first = { person }.first, p.last = $person.last\n        RETURN p.first as first_name, p.last as last_name\n      \"\"\"\n\n      conn = Sips.conn(:write)\n\n      assert %{\n               stats: %{\n                 \"labels-added\" => 1,\n                 \"nodes-created\" => 1,\n                 \"properties-set\" => 2\n               },\n               type: \"w\"\n             } ==\n               Sips.query!(conn, create_cypher, %{person: %{first: \"Green\", last: \"Alien\"}})\n\n      assert [%{\"last_name\" => \"Alien\"}] ==\n               Sips.query!(\n                 conn,\n                 \"MATCH (p:Person { first: 'Green', last: 'Alien' }) RETURN p.last AS last_name\"\n               )\n\n      assert [%{\"first_name\" => \"Florin\", \"last_name\" => \"Pătraşcu\"}] ==\n               Sips.query!(conn, update_cypher, %{person: %{first: \"Florin\", last: \"Pătraşcu\"}})\n    end\n\n    test \"upsert\" do\n      # MERGE (p:Person{ first: { map }.name, last: { map }.last }\n      # ON CREATE SET n = { map }\n      # ON MATCH  SET n += { map }\n    end\n  end\nend\n"
  },
  {
    "path": "test/routing/routing_table_parser_test.exs",
    "content": "defmodule Routing.Routing.TableParserTest do\n  use ExUnit.Case, async: true\n  @moduletag :routing\n\n  alias Bolt.Sips.Routing.RoutingTable\n\n  @valid_routing_table %{\n    \"servers\" => [\n      %{\n        \"addresses\" => [\"127.0.0.1:9001\", \"127.0.0.1:9002\", \"127.0.0.1:9003\"],\n        \"role\" => \"ROUTE\"\n      },\n      %{\"addresses\" => [\"127.0.0.1:9004\", \"127.0.0.1:9005\"], \"role\" => \"READ\"},\n      %{\"addresses\" => [\"127.0.0.1:9006\"], \"role\" => \"WRITE\"}\n    ],\n    \"ttl\" => 300\n  }\n\n  @magic_routing_table %{\n    \"servers\" => [\n      %{\n        \"addresses\" => [\"127.0.0.1:9001\", \"127.0.0.1:9002\", \"127.0.0.1:9003\"],\n        \"role\" => \"ROUTE\"\n      },\n      %{\"addresses\" => [\"127.0.0.1:9004\", \"127.0.0.1:9005\"], \"role\" => \"READ\"},\n      %{\"addresses\" => [\"127.0.0.1:9006\"], \"role\" => \"WRITE\"},\n      %{\"addresses\" => [\"127.0.0.1:9005\"], \"role\" => \"WARLOCK\"},\n      %{\"addresses\" => [\"127.0.0.1:9004\"], \"role\" => \"WIZARD\"}\n    ],\n    \"ttl\" => 300\n  }\n\n  describe \"Routing table\" do\n    test \"parse a valid server response having valid roles\" do\n      assert %Bolt.Sips.Routing.RoutingTable{\n               roles: %{\n                 read: %{\"127.0.0.1:9004\" => 0, \"127.0.0.1:9005\" => 0},\n                 route: %{\n                   \"127.0.0.1:9001\" => 0,\n                   \"127.0.0.1:9002\" => 0,\n                   \"127.0.0.1:9003\" => 0\n                 },\n                 write: %{\"127.0.0.1:9006\" => 0}\n               },\n               updated_at: route_updated_at,\n               ttl: ttl\n             } = RoutingTable.parse(@valid_routing_table)\n\n      refute RoutingTable.ttl_expired?(route_updated_at, ttl)\n    end\n\n    test \"parse a valid server response containing some Magic roles\" do\n      assert %Bolt.Sips.Routing.RoutingTable{\n               roles: %{\n                 read: %{\"127.0.0.1:9004\" => 0, \"127.0.0.1:9005\" => 0},\n                 route: %{\n                   \"127.0.0.1:9001\" => 0,\n                   \"127.0.0.1:9002\" => 0,\n                   \"127.0.0.1:9003\" => 0\n                 },\n                 write: %{\"127.0.0.1:9006\" => 0}\n               },\n               updated_at: route_updated_at,\n               ttl: ttl\n             } = RoutingTable.parse(@magic_routing_table)\n\n      refute RoutingTable.ttl_expired?(route_updated_at, ttl)\n    end\n  end\nend\n"
  },
  {
    "path": "test/routing/routing_test.exs",
    "content": "defmodule Bolt.Sips.RoutingTest do\n  @moduledoc \"\"\"\n\n  \"\"\"\n  use Bolt.Sips.BoltKitCase, async: false\n\n  alias Bolt.Sips.Response\n\n  @moduletag :boltkit\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/non_router.script\", 9001}\n         ]\n       }\n  test \"non_router.script\", %{prefix: prefix} do\n    assert %{error: error} = Bolt.Sips.routing_table(prefix)\n    assert error =~ ~r/not a router/i\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/get_routing_table.script\", 9001}\n         ]\n       }\n  test \"get_routing_table.script\", %{prefix: prefix} do\n    assert %{\n             read: %{\"127.0.0.1:9002\" => 0},\n             route: %{\"127.0.0.1:9001\" => 0, \"127.0.0.1:9002\" => 0},\n             write: %{\"127.0.0.1:9001\" => 0}\n           } = Bolt.Sips.routing_table(prefix)\n\n    assert %Bolt.Sips.Response{\n             results: [\n               %{\"name\" => \"Alice\"},\n               %{\"name\" => \"Bob\"},\n               %{\"name\" => \"Eve\"}\n             ]\n           } =\n             Bolt.Sips.conn(:read, prefix: prefix)\n             |> Bolt.Sips.query!(\"MATCH (n) RETURN n.name AS name\")\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001/?name=molly&age=1\",\n         scripts: [\n           {\"test/scripts/get_routing_table_with_context.script\", 9001},\n           {\"test/scripts/return_x.bolt\", 9002}\n         ]\n       }\n  test \"get_routing_table_with_context.script\", %{prefix: prefix} do\n    assert %{\n             read: %{\"127.0.0.1:9002\" => 0},\n             route: %{\"127.0.0.1:9001\" => 0, \"127.0.0.1:9002\" => 0},\n             write: %{\"127.0.0.1:9001\" => 0}\n           } = Bolt.Sips.routing_table(prefix)\n\n    Bolt.Sips.conn(:read, prefix: prefix)\n    |> Bolt.Sips.query!(\"RETURN $x\", %{x: 1})\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/router.script\", 9001},\n           {\"test/scripts/create_a.script\", 9006}\n         ]\n       }\n  test \"create_a.script\", %{prefix: prefix} do\n    assert %{write: %{\"127.0.0.1:9006\" => 0}} = Bolt.Sips.routing_table(prefix)\n\n    assert %Response{results: []} =\n             Bolt.Sips.conn(:write, prefix: prefix)\n             |> Bolt.Sips.query!(\"CREATE (a $x)\", %{x: %{name: \"Alice\"}})\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/router.script\", 9001},\n           {\"test/scripts/return_1.script\", 9004}\n         ]\n       }\n  test \"return_1.script\", %{prefix: prefix} do\n    assert %{read: %{\"127.0.0.1:9004\" => 0}} = Bolt.Sips.routing_table(prefix)\n\n    assert %Response{results: [%{\"x\" => 1}]} =\n             Bolt.Sips.conn(:read, prefix: prefix)\n             |> Bolt.Sips.query!(\"RETURN $x\", %{x: 1})\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/router.script\", 9001},\n           {\"test/scripts/return_1_in_tx_twice.script\", 9004},\n           {\"test/scripts/return_1_in_tx_twice.script\", 9005}\n         ]\n       }\n  test \"return_1_in_tx_twice.script\", %{prefix: prefix} do\n    Bolt.Sips.conn(:read, prefix: prefix)\n    |> Bolt.Sips.transaction(fn conn ->\n      assert %Response{fields: [\"1\"]} = Bolt.Sips.query!(conn, \"RETURN 1\")\n    end)\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/router.script\", 9001},\n           {\"test/scripts/return_1_twice.script\", 9004},\n           {\"test/scripts/return_1_twice.script\", 9005}\n         ]\n       }\n  test \"return_1_twice.script\", %{prefix: prefix} do\n    rconn1 = Bolt.Sips.conn(:read, prefix: prefix)\n    rconn2 = Bolt.Sips.conn(:read, prefix: prefix)\n    assert %Response{results: [%{\"x\" => 1}]} = Bolt.Sips.query!(rconn1, \"RETURN $x\", %{x: 1})\n    assert %Response{results: [%{\"x\" => 1}]} = Bolt.Sips.query!(rconn2, \"RETURN $x\", %{x: 1})\n  end\n\n  @tag boltkit: %{\n         url: \"neo4j://127.0.0.1:9001\",\n         scripts: [\n           {\"test/scripts/router.script\", 9001},\n           {\"test/scripts/forbidden_on_read_only_database.script\", 9006}\n         ]\n       }\n  test \"forbidden_on_read_only_database.script\", %{prefix: prefix} do\n    conn = Bolt.Sips.conn(:write, prefix: prefix)\n\n    assert_raise Bolt.Sips.Exception, ~r/unable to write/i, fn ->\n      Bolt.Sips.query!(conn, \"CREATE (n {name:'Bob'})\")\n    end\n  end\nend\n"
  },
  {
    "path": "test/routing/transaction_test.exs",
    "content": "defmodule Bolt.Sips.Routing.TransactionTest do\n  use Bolt.Sips.RoutingConnCase\n  @moduletag :routing\n\n  setup do\n    {:ok, [write_conn: Bolt.Sips.conn(:write)]}\n  end\n\n  test \"execute statements in transaction\", %{write_conn: write_conn} do\n    Bolt.Sips.transaction(write_conn, fn conn ->\n      book =\n        Bolt.Sips.query!(conn, \"CREATE (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n        |> List.first()\n\n      assert %{\"b\" => g_o_t} = book\n      assert g_o_t.properties[\"title\"] == \"The Game Of Trolls\"\n      Bolt.Sips.rollback(conn, :changed_my_mind)\n    end)\n\n    books =\n      Bolt.Sips.query!(write_conn, \"MATCH (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n\n    assert length(books) == 0\n  end\n\n  ###\n  ### NOTE:\n  ###\n  ### The labels used in these examples MUST be unique across all tests!\n  ### These tests depend on being able to expect that a node either exists\n  ### or does not, and asynchronous testing with the same names will cause\n  ### random cases where the underlying state changes.\n  ###\n\n  test \"rollback statements in transaction\", %{write_conn: write_conn} do\n    try do\n      # In case there's already a copy in our DB, count them...\n      {:ok, [result]} = Bolt.Sips.query(write_conn, \"MATCH (x:XactRollback) RETURN count(x)\")\n      original_count = result[\"count(x)\"]\n\n      Bolt.Sips.transaction(write_conn, fn conn ->\n        book =\n          Bolt.Sips.query(conn, \"CREATE (x:XactRollback {title:\\\"The Game Of Trolls\\\"}) return x\")\n\n        assert {:ok, [row]} = book\n        assert row[\"x\"].properties[\"title\"] == \"The Game Of Trolls\"\n\n        # Original connection (outside the transaction) should not see this node.\n        {:ok, [result]} = Bolt.Sips.query(write_conn, \"MATCH (x:XactRollback) RETURN count(x)\")\n\n        assert result[\"count(x)\"] == original_count,\n               \"Main connection should not be able to see transactional change\"\n\n        Bolt.Sips.rollback(conn, :changed_my_mind)\n      end)\n\n      # Original connection should still not see this node committed.\n      {:ok, [result]} = Bolt.Sips.query(write_conn, \"MATCH (x:XactRollback) RETURN count(x)\")\n      assert result[\"count(x)\"] == original_count\n    after\n      # Delete all XactRollback nodes in case the rollback() didn't work!\n      Bolt.Sips.query(write_conn, \"MATCH (x:XactRollback) DETACH DELETE x\")\n    end\n  end\n\n  test \"commit statements in transaction\", %{write_conn: write_conn} do\n    try do\n      Bolt.Sips.transaction(write_conn, fn conn ->\n        book = Bolt.Sips.query(conn, \"CREATE (x:XactCommit {foo: 'bar'}) return x\")\n        assert {:ok, [row]} = book\n        assert row[\"x\"].properties[\"foo\"] == \"bar\"\n\n        # Main connection should not see this new node.\n        {:ok, results} = Bolt.Sips.query(write_conn, \"MATCH (x:XactCommit) RETURN x\")\n        assert is_list(results)\n\n        assert Enum.count(results) == 0,\n               \"Main connection should not be able to see transactional changes\"\n      end)\n\n      # And we should see it now with the main connection.\n      {:ok, [%{\"x\" => node}]} = Bolt.Sips.query(write_conn, \"MATCH (x:XactCommit) RETURN x\")\n      assert node.labels == [\"XactCommit\"]\n      assert node.properties[\"foo\"] == \"bar\"\n    after\n      # Delete any XactCommit nodes that were succesfully committed!\n      Bolt.Sips.query(write_conn, \"MATCH (x:XactCommit) DETACH DELETE x\")\n    end\n  end\nend\n"
  },
  {
    "path": "test/scripts/count.bolt",
    "content": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nC: RUN \"UNWIND range(1, 10) AS n RETURN n\" {}\nS: SUCCESS {\"fields\": [\"n\"]}\nC: PULL_ALL\nS: RECORD [1]\n   RECORD [2]\n   RECORD [3]\n   RECORD [4]\n   RECORD [5]\n   RECORD [6]\n   RECORD [7]\n   RECORD [8]\n   RECORD [9]\n   RECORD [10]\n   SUCCESS {\"type\": \"r\"}"
  },
  {
    "path": "test/scripts/create_a.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CREATE (a $x)\" {\"x\": {\"name\": \"Alice\"}}\nS: SUCCESS {\"fields\": []}\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/forbidden_on_read_only_database.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO DISCARD_ALL\n!: AUTO RUN \"ROLLBACK\" {}\n!: AUTO RUN \"BEGIN\" {}\n!: AUTO RUN \"COMMIT\" {}\n\nC: RUN \"CREATE (n {name:'Bob'})\" {}\nS: FAILURE {\"code\": \"Neo.ClientError.General.ForbiddenOnReadOnlyDatabase\", \"message\": \"Unable to write\"}\nS: IGNORED\n"
  },
  {
    "path": "test/scripts/get_routing_table.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nS: SUCCESS {\"server\": \"Neo4j/3.2.3\"}\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\nS: SUCCESS {\"fields\": [\"ttl\", \"servers\"]}\n   RECORD [9223372036854775807, [{\"addresses\": [\"127.0.0.1:9001\", \"127.0.0.1:9002\"],\"role\": \"WRITE\"}, {\"addresses\": [\"127.0.0.1:9001\", \"127.0.0.1:9002\"], \"role\": \"READ\"},{\"addresses\": [\"127.0.0.1:9001\", \"127.0.0.1:9002\"], \"role\": \"ROUTE\"}]]\nC: RUN \"MATCH (n) RETURN n.name AS name\" {}\nS: SUCCESS {\"fields\": [\"name\"]}\n   RECORD [\"Alice\"]\n   RECORD [\"Bob\"]\n   RECORD [\"Eve\"]\n   SUCCESS {}\nS: <EXIT>\n"
  },
  {
    "path": "test/scripts/get_routing_table_with_context.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nS: SUCCESS {\"server\": \"Neo4j/3.2.3\"}\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {\"name\": \"molly\", \"age\": \"1\"}}\nS: SUCCESS {\"fields\": [\"ttl\", \"servers\"]}\n   RECORD [9223372036854775807, [{\"addresses\": [\"127.0.0.1:9001\"],\"role\": \"WRITE\"}, {\"addresses\": [\"127.0.0.1:9002\"], \"role\": \"READ\"},{\"addresses\": [\"127.0.0.1:9001\", \"127.0.0.1:9002\"], \"role\": \"ROUTE\"}]]\n   SUCCESS {}"
  },
  {
    "path": "test/scripts/non_router.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\nS: FAILURE {\"code\": \"Neo.ClientError.Procedure.ProcedureNotFound\", \"message\": \"Not a router\"}\n   IGNORED\nC: RESET\nS: SUCCESS {}\n"
  },
  {
    "path": "test/scripts/return_1.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/return_1_in_tx_twice.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"BEGIN\" {}\nS: SUCCESS {\"fields\": []}\n   SUCCESS {}\n\nC: RUN \"RETURN 1\" {}\nS: SUCCESS {\"fields\": [\"1\"]}\n   RECORD [1]\n   SUCCESS {}\n\nC: RUN \"COMMIT\" {}\n   DISCARD_ALL\nS: SUCCESS {\"bookmark\": \"bookmark:1\", \"bookmarks\": [\"bookmark:1\"]}\n   SUCCESS {}\n\nC: RUN \"BEGIN\" {\"bookmark\": \"bookmark:1\", \"bookmarks\": [\"bookmark:1\"]}\n   DISCARD_ALL\nS: SUCCESS {\"fields\": []}\n   SUCCESS {}\n\nC: RUN \"\" {}\nS: SUCCESS {\"fields\": [\"1\"]}\n   RECORD [1]\n   SUCCESS {}\n\nC: RUN \"COMMIT\" {}\n   DISCARD_ALL\nS: SUCCESS {\"bookmark\": \"bookmark:2\", \"bookmarks\": [\"bookmark:2\"]}\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/return_1_twice.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   SUCCESS {}\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/return_x.bolt",
    "content": "!: AUTO INIT\n!: AUTO RESET\n!: AUTO PULL_ALL\n\nC: RUN \"RETURN $x\" {\"x\": 1}\nS: SUCCESS {\"fields\": [\"x\"]}\n   RECORD [1]\n   SUCCESS {}"
  },
  {
    "path": "test/scripts/router.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\nS: SUCCESS {\"fields\": [\"ttl\", \"servers\"]}\n   RECORD [300, [{\"role\":\"ROUTE\",\"addresses\":[\"127.0.0.1:9001\",\"127.0.0.1:9002\",\"127.0.0.1:9003\"]},{\"role\":\"READ\",\"addresses\":[\"127.0.0.1:9004\",\"127.0.0.1:9005\"]},{\"role\":\"WRITE\",\"addresses\":[\"127.0.0.1:9006\"]}]]\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/router_no_readers.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\n   PULL_ALL\nS: SUCCESS {\"fields\": [\"ttl\", \"servers\"]}\n   RECORD [300, [{\"role\":\"ROUTE\",\"addresses\":[\"127.0.0.1:9001\",\"127.0.0.1:9002\",\"127.0.0.1:9003\"]},{\"role\":\"READ\",\"addresses\":[]},{\"role\":\"WRITE\",\"addresses\":[\"127.0.0.1:9006\"]}]]\n   SUCCESS {}\n"
  },
  {
    "path": "test/scripts/router_no_writers.script",
    "content": "!: AUTO INIT\n!: AUTO RESET\n\nC: RUN \"CALL dbms.cluster.routing.getRoutingTable($context)\" {\"context\": {}}\n   PULL_ALL\nS: SUCCESS {\"fields\": [\"ttl\", \"servers\"]}\n   RECORD [300, [{\"role\":\"ROUTE\",\"addresses\":[\"127.0.0.1:9001\",\"127.0.0.1:9002\",\"127.0.0.1:9003\"]},{\"role\":\"READ\",\"addresses\":[\"127.0.0.1:9004\",\"127.0.0.1:9005\"]},{\"role\":\"WRITE\",\"addresses\":[]}]]\n   SUCCESS {}\n"
  },
  {
    "path": "test/support/boltkit_case.ex",
    "content": "defmodule Bolt.Sips.BoltKitCase do\n  _doc = \"\"\"\n  tag your tests with `boltkit`, like this:\n\n      @tag boltkit: %{\n            url: \"neo4j://127.0.0.1:9001/?name=molly&age=1\",\n            scripts: [\n              {\"test/scripts/get_routing_table_with_context.script\", 9001},\n              {\"test/scripts/return_x.bolt\", 9002}\n            ],\n            debug: true\n          }\n  and then use the prefix returned via the context, for working with the stubbed connection(s)\n\n      test \"get_routing_table_with_context.script\", %{prefix: prefix} do\n        assert ...\n      end\n\n  \"\"\"\n\n  use ExUnit.CaseTemplate\n\n  alias Porcelain.Process, as: Proc\n\n  require Logger\n\n  setup_all do\n    Porcelain.reinit(Porcelain.Driver.Basic)\n  end\n\n  setup %{boltkit: boltkit} do\n    prefix = Map.get(boltkit, :prefix, UUID.uuid4())\n    url = Map.get(boltkit, :url, \"bolt://127.0.0.1\")\n\n    porcelains = stub_servers(boltkit)\n\n    pid =\n      with {:ok, pid} <- connect(url, prefix) do\n        pid\n      else\n        _ -> raise RuntimeError, \"cannot create a Bolt.Sips process\"\n      end\n\n    on_exit(fn ->\n      porcelains\n      |> Enum.each(fn\n        {:ok, porcelain} ->\n          # wait for boltstub to finish\n          :timer.sleep(150)\n\n          with true <- Proc.alive?(porcelain),\n               %Proc{out: out} <- porcelain do\n            try do\n              Enum.into(out, IO.stream(:stdio, :line))\n            rescue\n              _ ->\n                Logger.debug(\"BoltStub's out was flushed.\")\n                :rescued\n            end\n          else\n            _e ->\n              Logger.debug(\"BoltStub ended prematurely.\")\n          end\n\n        e ->\n          Logger.error(inspect(e))\n      end)\n    end)\n\n    {:ok, porcelains: porcelains, prefix: prefix, sips: pid, url: url}\n  end\n\n  defp stub_servers(%{scripts: scripts} = args) do\n    opts =\n      if Map.get(args, :debug, false) do\n        [out: IO.stream(:stderr, :line)]\n      else\n        []\n      end\n\n    scripts\n    |> Enum.map(fn {script, port} ->\n      with true <- File.exists?(script) do\n        sport = Integer.to_string(port)\n        porcelain = Porcelain.spawn(\"boltstub\", [sport, script], opts)\n        wait_for_socket('127.0.0.1', port)\n        {:ok, porcelain}\n      else\n        _ -> {:error, script <> \", not found.\"}\n      end\n    end)\n  end\n\n  @sock_opts [:binary, active: false]\n  defp wait_for_socket(address, port) do\n      with {:ok, socket} <- :gen_tcp.connect(address, port, @sock_opts, 1000) do\n        socket\n      end\n  end\n\n\n  defp connect(url, prefix) do\n    conf = [\n      url: url,\n      basic_auth: [username: \"neo4j\", password: \"password\"],\n      # pool: DBConnection.Ownership,\n      pool_size: 1,\n      prefix: prefix\n      # after_connect_timeout: fn _ -> nil end,\n      # queue_timeout: 100,\n      # queue_target: 100,\n      # queue_interval: 10\n    ]\n\n    Logger.debug(\"creating #{url}, prefix: #{prefix}\")\n    Bolt.Sips.start_link(conf)\n  end\nend\n"
  },
  {
    "path": "test/support/conn_case.ex",
    "content": "defmodule Bolt.Sips.ConnCase do\n  use ExUnit.CaseTemplate\n\n  setup_all do\n    Bolt.Sips.start_link(Application.get_env(:bolt_sips, Bolt))\n    conn = Bolt.Sips.conn()\n\n    on_exit(fn ->\n      Bolt.Sips.Test.Support.Database.clear(conn)\n    end)\n\n    {:ok, conn: conn}\n  end\nend\n"
  },
  {
    "path": "test/support/conn_routing_case.ex",
    "content": "defmodule Bolt.Sips.RoutingConnCase do\n  @moduletag :routing\n\n  use ExUnit.CaseTemplate\n\n  alias Bolt.Sips\n\n  @routing_connection_config [\n    url: \"bolt+routing://localhost:9001\",\n    basic_auth: [username: \"neo4j\", password: \"test\"],\n    pool_size: 10,\n    max_overflow: 2,\n    queue_interval: 500,\n    queue_target: 1500,\n    tag: @moduletag\n  ]\n\n  setup_all do\n    {:ok, _pid} = Sips.start_link(@routing_connection_config)\n    conn = Sips.conn(:write)\n\n    on_exit(fn ->\n      with conn when not is_nil(conn) <- Sips.conn(:write) do\n        Sips.Test.Support.Database.clear(conn)\n      else\n        e -> {:error, e}\n      end\n    end)\n\n    {:ok, write_conn: conn}\n  end\nend\n"
  },
  {
    "path": "test/support/database.ex",
    "content": "defmodule Bolt.Sips.Test.Support.Database do\n  def clear(conn) do\n    Bolt.Sips.query!(conn, \"MATCH (n) DETACH DELETE n\")\n  end\nend\n"
  },
  {
    "path": "test/support/fixture.ex",
    "content": "defmodule Bolt.Sips.Fixture do\n  def create_graph(conn, :movie) do\n    Bolt.Sips.query!(conn, movie_cypher())\n  end\n\n  def create_graph(conn, :bolt_sips) do\n    Bolt.Sips.query!(conn, bolt_sips_cypher())\n  end\n\n  def bolt_sips_cypher() do\n    \"\"\"\n    MATCH (n {bolt_sips: true}) OPTIONAL MATCH (n)-[r]-() DELETE n,r;\n\n    CREATE (BoltSips:BoltSips {title:'Elixir sipping from Neo4j, using Bolt', released:2016, license:'MIT', bolt_sips: true})\n    CREATE (TNOTW:Book {title:'The Name of the Wind', released:2007, genre:'fantasy', bolt_sips: true})\n    CREATE (Patrick:Person {name:'Patrick Rothfuss', bolt_sips: true})\n    CREATE (Kvothe:Person {name:'Kote', bolt_sips: true})\n    CREATE (Denna:Person {name:'Denna', bolt_sips: true})\n    CREATE (Chandrian:Deamon {name:'Chandrian', bolt_sips: true})\n\n    CREATE\n      (Kvothe)-[:ACTED_IN {roles:['sword fighter', 'magician', 'musician']}]->(TNOTW),\n      (Denna)-[:ACTED_IN {roles:['many talents']}]->(TNOTW),\n      (Chandrian)-[:ACTED_IN {roles:['killer']}]->(TNOTW),\n      (Patrick)-[:WROTE]->(TNOTW)\n    \"\"\"\n  end\n\n  def movie_cypher() do\n    \"\"\"\n    CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'})\n    CREATE (Keanu:Person {name:'Keanu Reeves', born:1964})\n    CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967})\n    CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961})\n    CREATE (Hugo:Person {name:'Hugo Weaving', born:1960})\n    CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967})\n    CREATE (LanaW:Person {name:'Lana Wachowski', born:1965})\n    CREATE (JoelS:Person {name:'Joel Silver', born:1952})\n    CREATE\n    (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix),\n    (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix),\n    (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix),\n    (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix),\n    (LillyW)-[:DIRECTED]->(TheMatrix),\n    (LanaW)-[:DIRECTED]->(TheMatrix),\n    (JoelS)-[:PRODUCED]->(TheMatrix)\n\n    CREATE (Emil:Person {name:\"Emil Eifrem\", born:1978})\n    CREATE (Emil)-[:ACTED_IN {roles:[\"Emil\"]}]->(TheMatrix)\n\n    CREATE (TheMatrixReloaded:Movie {title:'The Matrix Reloaded', released:2003, tagline:'Free your mind'})\n    CREATE\n    (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixReloaded),\n    (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixReloaded),\n    (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixReloaded),\n    (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixReloaded),\n    (LillyW)-[:DIRECTED]->(TheMatrixReloaded),\n    (LanaW)-[:DIRECTED]->(TheMatrixReloaded),\n    (JoelS)-[:PRODUCED]->(TheMatrixReloaded)\n\n    CREATE (TheMatrixRevolutions:Movie {title:'The Matrix Revolutions', released:2003, tagline:'Everything that has a beginning has an end'})\n    CREATE\n    (Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrixRevolutions),\n    (Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrixRevolutions),\n    (Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrixRevolutions),\n    (Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrixRevolutions),\n    (LillyW)-[:DIRECTED]->(TheMatrixRevolutions),\n    (LanaW)-[:DIRECTED]->(TheMatrixRevolutions),\n    (JoelS)-[:PRODUCED]->(TheMatrixRevolutions)\n\n    CREATE (TheDevilsAdvocate:Movie {title:\"The Devil's Advocate\", released:1997, tagline:'Evil has its winning ways'})\n    CREATE (Charlize:Person {name:'Charlize Theron', born:1975})\n    CREATE (Al:Person {name:'Al Pacino', born:1940})\n    CREATE (Taylor:Person {name:'Taylor Hackford', born:1944})\n    CREATE\n    (Keanu)-[:ACTED_IN {roles:['Kevin Lomax']}]->(TheDevilsAdvocate),\n    (Charlize)-[:ACTED_IN {roles:['Mary Ann Lomax']}]->(TheDevilsAdvocate),\n    (Al)-[:ACTED_IN {roles:['John Milton']}]->(TheDevilsAdvocate),\n    (Taylor)-[:DIRECTED]->(TheDevilsAdvocate)\n\n    CREATE (AFewGoodMen:Movie {title:\"A Few Good Men\", released:1992, tagline:\"In the heart of the nation's capital, in a courthouse of the U.S. government, one man will stop at nothing to keep his honor, and one will stop at nothing to find the truth.\"})\n    CREATE (TomC:Person {name:'Tom Cruise', born:1962})\n    CREATE (JackN:Person {name:'Jack Nicholson', born:1937})\n    CREATE (DemiM:Person {name:'Demi Moore', born:1962})\n    CREATE (KevinB:Person {name:'Kevin Bacon', born:1958})\n    CREATE (KieferS:Person {name:'Kiefer Sutherland', born:1966})\n    CREATE (NoahW:Person {name:'Noah Wyle', born:1971})\n    CREATE (CubaG:Person {name:'Cuba Gooding Jr.', born:1968})\n    CREATE (KevinP:Person {name:'Kevin Pollak', born:1957})\n    CREATE (JTW:Person {name:'J.T. Walsh', born:1943})\n    CREATE (JamesM:Person {name:'James Marshall', born:1967})\n    CREATE (ChristopherG:Person {name:'Christopher Guest', born:1948})\n    CREATE (RobR:Person {name:'Rob Reiner', born:1947})\n    CREATE (AaronS:Person {name:'Aaron Sorkin', born:1961})\n    CREATE\n    (TomC)-[:ACTED_IN {roles:['Lt. Daniel Kaffee']}]->(AFewGoodMen),\n    (JackN)-[:ACTED_IN {roles:['Col. Nathan R. Jessup']}]->(AFewGoodMen),\n    (DemiM)-[:ACTED_IN {roles:['Lt. Cdr. JoAnne Galloway']}]->(AFewGoodMen),\n    (KevinB)-[:ACTED_IN {roles:['Capt. Jack Ross']}]->(AFewGoodMen),\n    (KieferS)-[:ACTED_IN {roles:['Lt. Jonathan Kendrick']}]->(AFewGoodMen),\n    (NoahW)-[:ACTED_IN {roles:['Cpl. Jeffrey Barnes']}]->(AFewGoodMen),\n    (CubaG)-[:ACTED_IN {roles:['Cpl. Carl Hammaker']}]->(AFewGoodMen),\n    (KevinP)-[:ACTED_IN {roles:['Lt. Sam Weinberg']}]->(AFewGoodMen),\n    (JTW)-[:ACTED_IN {roles:['Lt. Col. Matthew Andrew Markinson']}]->(AFewGoodMen),\n    (JamesM)-[:ACTED_IN {roles:['Pfc. Louden Downey']}]->(AFewGoodMen),\n    (ChristopherG)-[:ACTED_IN {roles:['Dr. Stone']}]->(AFewGoodMen),\n    (AaronS)-[:ACTED_IN {roles:['Man in Bar']}]->(AFewGoodMen),\n    (RobR)-[:DIRECTED]->(AFewGoodMen),\n    (AaronS)-[:WROTE]->(AFewGoodMen)\n\n    CREATE (TopGun:Movie {title:\"Top Gun\", released:1986, tagline:'I feel the need, the need for speed.'})\n    CREATE (KellyM:Person {name:'Kelly McGillis', born:1957})\n    CREATE (ValK:Person {name:'Val Kilmer', born:1959})\n    CREATE (AnthonyE:Person {name:'Anthony Edwards', born:1962})\n    CREATE (TomS:Person {name:'Tom Skerritt', born:1933})\n    CREATE (MegR:Person {name:'Meg Ryan', born:1961})\n    CREATE (TonyS:Person {name:'Tony Scott', born:1944})\n    CREATE (JimC:Person {name:'Jim Cash', born:1941})\n    CREATE\n    (TomC)-[:ACTED_IN {roles:['Maverick']}]->(TopGun),\n    (KellyM)-[:ACTED_IN {roles:['Charlie']}]->(TopGun),\n    (ValK)-[:ACTED_IN {roles:['Iceman']}]->(TopGun),\n    (AnthonyE)-[:ACTED_IN {roles:['Goose']}]->(TopGun),\n    (TomS)-[:ACTED_IN {roles:['Viper']}]->(TopGun),\n    (MegR)-[:ACTED_IN {roles:['Carole']}]->(TopGun),\n    (TonyS)-[:DIRECTED]->(TopGun),\n    (JimC)-[:WROTE]->(TopGun)\n\n    CREATE (JerryMaguire:Movie {title:'Jerry Maguire', released:2000, tagline:'The rest of his life begins now.'})\n    CREATE (ReneeZ:Person {name:'Renee Zellweger', born:1969})\n    CREATE (KellyP:Person {name:'Kelly Preston', born:1962})\n    CREATE (JerryO:Person {name:\"Jerry O'Connell\", born:1974})\n    CREATE (JayM:Person {name:'Jay Mohr', born:1970})\n    CREATE (BonnieH:Person {name:'Bonnie Hunt', born:1961})\n    CREATE (ReginaK:Person {name:'Regina King', born:1971})\n    CREATE (JonathanL:Person {name:'Jonathan Lipnicki', born:1996})\n    CREATE (CameronC:Person {name:'Cameron Crowe', born:1957})\n    CREATE\n    (TomC)-[:ACTED_IN {roles:['Jerry Maguire']}]->(JerryMaguire),\n    (CubaG)-[:ACTED_IN {roles:['Rod Tidwell']}]->(JerryMaguire),\n    (ReneeZ)-[:ACTED_IN {roles:['Dorothy Boyd']}]->(JerryMaguire),\n    (KellyP)-[:ACTED_IN {roles:['Avery Bishop']}]->(JerryMaguire),\n    (JerryO)-[:ACTED_IN {roles:['Frank Cushman']}]->(JerryMaguire),\n    (JayM)-[:ACTED_IN {roles:['Bob Sugar']}]->(JerryMaguire),\n    (BonnieH)-[:ACTED_IN {roles:['Laurel Boyd']}]->(JerryMaguire),\n    (ReginaK)-[:ACTED_IN {roles:['Marcee Tidwell']}]->(JerryMaguire),\n    (JonathanL)-[:ACTED_IN {roles:['Ray Boyd']}]->(JerryMaguire),\n    (CameronC)-[:DIRECTED]->(JerryMaguire),\n    (CameronC)-[:PRODUCED]->(JerryMaguire),\n    (CameronC)-[:WROTE]->(JerryMaguire)\n\n    CREATE (StandByMe:Movie {title:\"Stand By Me\", released:1986, tagline:\"For some, it's the last real taste of innocence, and the first real taste of life. But for everyone, it's the time that memories are made of.\"})\n    CREATE (RiverP:Person {name:'River Phoenix', born:1970})\n    CREATE (CoreyF:Person {name:'Corey Feldman', born:1971})\n    CREATE (WilW:Person {name:'Wil Wheaton', born:1972})\n    CREATE (JohnC:Person {name:'John Cusack', born:1966})\n    CREATE (MarshallB:Person {name:'Marshall Bell', born:1942})\n    CREATE\n    (WilW)-[:ACTED_IN {roles:['Gordie Lachance']}]->(StandByMe),\n    (RiverP)-[:ACTED_IN {roles:['Chris Chambers']}]->(StandByMe),\n    (JerryO)-[:ACTED_IN {roles:['Vern Tessio']}]->(StandByMe),\n    (CoreyF)-[:ACTED_IN {roles:['Teddy Duchamp']}]->(StandByMe),\n    (JohnC)-[:ACTED_IN {roles:['Denny Lachance']}]->(StandByMe),\n    (KieferS)-[:ACTED_IN {roles:['Ace Merrill']}]->(StandByMe),\n    (MarshallB)-[:ACTED_IN {roles:['Mr. Lachance']}]->(StandByMe),\n    (RobR)-[:DIRECTED]->(StandByMe)\n\n    CREATE (AsGoodAsItGets:Movie {title:'As Good as It Gets', released:1997, tagline:'A comedy from the heart that goes for the throat.'})\n    CREATE (HelenH:Person {name:'Helen Hunt', born:1963})\n    CREATE (GregK:Person {name:'Greg Kinnear', born:1963})\n    CREATE (JamesB:Person {name:'James L. Brooks', born:1940})\n    CREATE\n    (JackN)-[:ACTED_IN {roles:['Melvin Udall']}]->(AsGoodAsItGets),\n    (HelenH)-[:ACTED_IN {roles:['Carol Connelly']}]->(AsGoodAsItGets),\n    (GregK)-[:ACTED_IN {roles:['Simon Bishop']}]->(AsGoodAsItGets),\n    (CubaG)-[:ACTED_IN {roles:['Frank Sachs']}]->(AsGoodAsItGets),\n    (JamesB)-[:DIRECTED]->(AsGoodAsItGets)\n\n    CREATE (WhatDreamsMayCome:Movie {title:'What Dreams May Come', released:1998, tagline:'After life there is more. The end is just the beginning.'})\n    CREATE (AnnabellaS:Person {name:'Annabella Sciorra', born:1960})\n    CREATE (MaxS:Person {name:'Max von Sydow', born:1929})\n    CREATE (WernerH:Person {name:'Werner Herzog', born:1942})\n    CREATE (Robin:Person {name:'Robin Williams', born:1951})\n    CREATE (VincentW:Person {name:'Vincent Ward', born:1956})\n    CREATE\n    (Robin)-[:ACTED_IN {roles:['Chris Nielsen']}]->(WhatDreamsMayCome),\n    (CubaG)-[:ACTED_IN {roles:['Albert Lewis']}]->(WhatDreamsMayCome),\n    (AnnabellaS)-[:ACTED_IN {roles:['Annie Collins-Nielsen']}]->(WhatDreamsMayCome),\n    (MaxS)-[:ACTED_IN {roles:['The Tracker']}]->(WhatDreamsMayCome),\n    (WernerH)-[:ACTED_IN {roles:['The Face']}]->(WhatDreamsMayCome),\n    (VincentW)-[:DIRECTED]->(WhatDreamsMayCome)\n\n    CREATE (SnowFallingonCedars:Movie {title:'Snow Falling on Cedars', released:1999, tagline:'First loves last. Forever.'})\n    CREATE (EthanH:Person {name:'Ethan Hawke', born:1970})\n    CREATE (RickY:Person {name:'Rick Yune', born:1971})\n    CREATE (JamesC:Person {name:'James Cromwell', born:1940})\n    CREATE (ScottH:Person {name:'Scott Hicks', born:1953})\n    CREATE\n    (EthanH)-[:ACTED_IN {roles:['Ishmael Chambers']}]->(SnowFallingonCedars),\n    (RickY)-[:ACTED_IN {roles:['Kazuo Miyamoto']}]->(SnowFallingonCedars),\n    (MaxS)-[:ACTED_IN {roles:['Nels Gudmundsson']}]->(SnowFallingonCedars),\n    (JamesC)-[:ACTED_IN {roles:['Judge Fielding']}]->(SnowFallingonCedars),\n    (ScottH)-[:DIRECTED]->(SnowFallingonCedars)\n\n    CREATE (YouveGotMail:Movie {title:\"You've Got Mail\", released:1998, tagline:'At odds in life... in love on-line.'})\n    CREATE (ParkerP:Person {name:'Parker Posey', born:1968})\n    CREATE (DaveC:Person {name:'Dave Chappelle', born:1973})\n    CREATE (SteveZ:Person {name:'Steve Zahn', born:1967})\n    CREATE (TomH:Person {name:'Tom Hanks', born:1956})\n    CREATE (NoraE:Person {name:'Nora Ephron', born:1941})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Joe Fox']}]->(YouveGotMail),\n    (MegR)-[:ACTED_IN {roles:['Kathleen Kelly']}]->(YouveGotMail),\n    (GregK)-[:ACTED_IN {roles:['Frank Navasky']}]->(YouveGotMail),\n    (ParkerP)-[:ACTED_IN {roles:['Patricia Eden']}]->(YouveGotMail),\n    (DaveC)-[:ACTED_IN {roles:['Kevin Jackson']}]->(YouveGotMail),\n    (SteveZ)-[:ACTED_IN {roles:['George Pappas']}]->(YouveGotMail),\n    (NoraE)-[:DIRECTED]->(YouveGotMail)\n\n    CREATE (SleeplessInSeattle:Movie {title:'Sleepless in Seattle', released:1993, tagline:'What if someone you never met, someone you never saw, someone you never knew was the only someone for you?'})\n    CREATE (RitaW:Person {name:'Rita Wilson', born:1956})\n    CREATE (BillPull:Person {name:'Bill Pullman', born:1953})\n    CREATE (VictorG:Person {name:'Victor Garber', born:1949})\n    CREATE (RosieO:Person {name:\"Rosie O'Donnell\", born:1962})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Sam Baldwin']}]->(SleeplessInSeattle),\n    (MegR)-[:ACTED_IN {roles:['Annie Reed']}]->(SleeplessInSeattle),\n    (RitaW)-[:ACTED_IN {roles:['Suzy']}]->(SleeplessInSeattle),\n    (BillPull)-[:ACTED_IN {roles:['Walter']}]->(SleeplessInSeattle),\n    (VictorG)-[:ACTED_IN {roles:['Greg']}]->(SleeplessInSeattle),\n    (RosieO)-[:ACTED_IN {roles:['Becky']}]->(SleeplessInSeattle),\n    (NoraE)-[:DIRECTED]->(SleeplessInSeattle)\n\n    CREATE (JoeVersustheVolcano:Movie {title:'Joe Versus the Volcano', released:1990, tagline:'A story of love, lava and burning desire.'})\n    CREATE (JohnS:Person {name:'John Patrick Stanley', born:1950})\n    CREATE (Nathan:Person {name:'Nathan Lane', born:1956})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Joe Banks']}]->(JoeVersustheVolcano),\n    (MegR)-[:ACTED_IN {roles:['DeDe', 'Angelica Graynamore', 'Patricia Graynamore']}]->(JoeVersustheVolcano),\n    (Nathan)-[:ACTED_IN {roles:['Baw']}]->(JoeVersustheVolcano),\n    (JohnS)-[:DIRECTED]->(JoeVersustheVolcano)\n\n    CREATE (WhenHarryMetSally:Movie {title:'When Harry Met Sally', released:1998, tagline:'Can two friends sleep together and still love each other in the morning?'})\n    CREATE (BillyC:Person {name:'Billy Crystal', born:1948})\n    CREATE (CarrieF:Person {name:'Carrie Fisher', born:1956})\n    CREATE (BrunoK:Person {name:'Bruno Kirby', born:1949})\n    CREATE\n    (BillyC)-[:ACTED_IN {roles:['Harry Burns']}]->(WhenHarryMetSally),\n    (MegR)-[:ACTED_IN {roles:['Sally Albright']}]->(WhenHarryMetSally),\n    (CarrieF)-[:ACTED_IN {roles:['Marie']}]->(WhenHarryMetSally),\n    (BrunoK)-[:ACTED_IN {roles:['Jess']}]->(WhenHarryMetSally),\n    (RobR)-[:DIRECTED]->(WhenHarryMetSally),\n    (RobR)-[:PRODUCED]->(WhenHarryMetSally),\n    (NoraE)-[:PRODUCED]->(WhenHarryMetSally),\n    (NoraE)-[:WROTE]->(WhenHarryMetSally)\n\n    CREATE (ThatThingYouDo:Movie {title:'That Thing You Do', released:1996, tagline:'In every life there comes a time when that thing you dream becomes that thing you do'})\n    CREATE (LivT:Person {name:'Liv Tyler', born:1977})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Mr. White']}]->(ThatThingYouDo),\n    (LivT)-[:ACTED_IN {roles:['Faye Dolan']}]->(ThatThingYouDo),\n    (Charlize)-[:ACTED_IN {roles:['Tina']}]->(ThatThingYouDo),\n    (TomH)-[:DIRECTED]->(ThatThingYouDo)\n\n    CREATE (TheReplacements:Movie {title:'The Replacements', released:2000, tagline:'Pain heals, Chicks dig scars... Glory lasts forever'})\n    CREATE (Brooke:Person {name:'Brooke Langton', born:1970})\n    CREATE (Gene:Person {name:'Gene Hackman', born:1930})\n    CREATE (Orlando:Person {name:'Orlando Jones', born:1968})\n    CREATE (Howard:Person {name:'Howard Deutch', born:1950})\n    CREATE\n    (Keanu)-[:ACTED_IN {roles:['Shane Falco']}]->(TheReplacements),\n    (Brooke)-[:ACTED_IN {roles:['Annabelle Farrell']}]->(TheReplacements),\n    (Gene)-[:ACTED_IN {roles:['Jimmy McGinty']}]->(TheReplacements),\n    (Orlando)-[:ACTED_IN {roles:['Clifford Franklin']}]->(TheReplacements),\n    (Howard)-[:DIRECTED]->(TheReplacements)\n\n    CREATE (RescueDawn:Movie {title:'RescueDawn', released:2006, tagline:\"Based on the extraordinary true story of one man's fight for freedom\"})\n    CREATE (ChristianB:Person {name:'Christian Bale', born:1974})\n    CREATE (ZachG:Person {name:'Zach Grenier', born:1954})\n    CREATE\n    (MarshallB)-[:ACTED_IN {roles:['Admiral']}]->(RescueDawn),\n    (ChristianB)-[:ACTED_IN {roles:['Dieter Dengler']}]->(RescueDawn),\n    (ZachG)-[:ACTED_IN {roles:['Squad Leader']}]->(RescueDawn),\n    (SteveZ)-[:ACTED_IN {roles:['Duane']}]->(RescueDawn),\n    (WernerH)-[:DIRECTED]->(RescueDawn)\n\n    CREATE (TheBirdcage:Movie {title:'The Birdcage', released:1996, tagline:'Come as you are'})\n    CREATE (MikeN:Person {name:'Mike Nichols', born:1931})\n    CREATE\n    (Robin)-[:ACTED_IN {roles:['Armand Goldman']}]->(TheBirdcage),\n    (Nathan)-[:ACTED_IN {roles:['Albert Goldman']}]->(TheBirdcage),\n    (Gene)-[:ACTED_IN {roles:['Sen. Kevin Keeley']}]->(TheBirdcage),\n    (MikeN)-[:DIRECTED]->(TheBirdcage)\n\n    CREATE (Unforgiven:Movie {title:'Unforgiven', released:1992, tagline:\"It's a hell of a thing, killing a man\"})\n    CREATE (RichardH:Person {name:'Richard Harris', born:1930})\n    CREATE (ClintE:Person {name:'Clint Eastwood', born:1930})\n    CREATE\n    (RichardH)-[:ACTED_IN {roles:['English Bob']}]->(Unforgiven),\n    (ClintE)-[:ACTED_IN {roles:['Bill Munny']}]->(Unforgiven),\n    (Gene)-[:ACTED_IN {roles:['Little Bill Daggett']}]->(Unforgiven),\n    (ClintE)-[:DIRECTED]->(Unforgiven)\n\n    CREATE (JohnnyMnemonic:Movie {title:'Johnny Mnemonic', released:1995, tagline:'The hottest data on earth. In the coolest head in town'})\n    CREATE (Takeshi:Person {name:'Takeshi Kitano', born:1947})\n    CREATE (Dina:Person {name:'Dina Meyer', born:1968})\n    CREATE (IceT:Person {name:'Ice-T', born:1958})\n    CREATE (RobertL:Person {name:'Robert Longo', born:1953})\n    CREATE\n    (Keanu)-[:ACTED_IN {roles:['Johnny Mnemonic']}]->(JohnnyMnemonic),\n    (Takeshi)-[:ACTED_IN {roles:['Takahashi']}]->(JohnnyMnemonic),\n    (Dina)-[:ACTED_IN {roles:['Jane']}]->(JohnnyMnemonic),\n    (IceT)-[:ACTED_IN {roles:['J-Bone']}]->(JohnnyMnemonic),\n    (RobertL)-[:DIRECTED]->(JohnnyMnemonic)\n\n    CREATE (CloudAtlas:Movie {title:'Cloud Atlas', released:2012, tagline:'Everything is connected'})\n    CREATE (HalleB:Person {name:'Halle Berry', born:1966})\n    CREATE (JimB:Person {name:'Jim Broadbent', born:1949})\n    CREATE (TomT:Person {name:'Tom Tykwer', born:1965})\n    CREATE (DavidMitchell:Person {name:'David Mitchell', born:1969})\n    CREATE (StefanArndt:Person {name:'Stefan Arndt', born:1961})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Zachry', 'Dr. Henry Goose', 'Isaac Sachs', 'Dermot Hoggins']}]->(CloudAtlas),\n    (Hugo)-[:ACTED_IN {roles:['Bill Smoke', 'Haskell Moore', 'Tadeusz Kesselring', 'Nurse Noakes', 'Boardman Mephi', 'Old Georgie']}]->(CloudAtlas),\n    (HalleB)-[:ACTED_IN {roles:['Luisa Rey', 'Jocasta Ayrs', 'Ovid', 'Meronym']}]->(CloudAtlas),\n    (JimB)-[:ACTED_IN {roles:['Vyvyan Ayrs', 'Captain Molyneux', 'Timothy Cavendish']}]->(CloudAtlas),\n    (TomT)-[:DIRECTED]->(CloudAtlas),\n    (LillyW)-[:DIRECTED]->(CloudAtlas),\n    (LanaW)-[:DIRECTED]->(CloudAtlas),\n    (DavidMitchell)-[:WROTE]->(CloudAtlas),\n    (StefanArndt)-[:PRODUCED]->(CloudAtlas)\n\n    CREATE (TheDaVinciCode:Movie {title:'The Da Vinci Code', released:2006, tagline:'Break The Codes'})\n    CREATE (IanM:Person {name:'Ian McKellen', born:1939})\n    CREATE (AudreyT:Person {name:'Audrey Tautou', born:1976})\n    CREATE (PaulB:Person {name:'Paul Bettany', born:1971})\n    CREATE (RonH:Person {name:'Ron Howard', born:1954})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Dr. Robert Langdon']}]->(TheDaVinciCode),\n    (IanM)-[:ACTED_IN {roles:['Sir Leight Teabing']}]->(TheDaVinciCode),\n    (AudreyT)-[:ACTED_IN {roles:['Sophie Neveu']}]->(TheDaVinciCode),\n    (PaulB)-[:ACTED_IN {roles:['Silas']}]->(TheDaVinciCode),\n    (RonH)-[:DIRECTED]->(TheDaVinciCode)\n\n    CREATE (VforVendetta:Movie {title:'V for Vendetta', released:2006, tagline:'Freedom! Forever!'})\n    CREATE (NatalieP:Person {name:'Natalie Portman', born:1981})\n    CREATE (StephenR:Person {name:'Stephen Rea', born:1946})\n    CREATE (JohnH:Person {name:'John Hurt', born:1940})\n    CREATE (BenM:Person {name: 'Ben Miles', born:1967})\n    CREATE\n    (Hugo)-[:ACTED_IN {roles:['V']}]->(VforVendetta),\n    (NatalieP)-[:ACTED_IN {roles:['Evey Hammond']}]->(VforVendetta),\n    (StephenR)-[:ACTED_IN {roles:['Eric Finch']}]->(VforVendetta),\n    (JohnH)-[:ACTED_IN {roles:['High Chancellor Adam Sutler']}]->(VforVendetta),\n    (BenM)-[:ACTED_IN {roles:['Dascomb']}]->(VforVendetta),\n    (JamesM)-[:DIRECTED]->(VforVendetta),\n    (LillyW)-[:PRODUCED]->(VforVendetta),\n    (LanaW)-[:PRODUCED]->(VforVendetta),\n    (JoelS)-[:PRODUCED]->(VforVendetta),\n    (LillyW)-[:WROTE]->(VforVendetta),\n    (LanaW)-[:WROTE]->(VforVendetta)\n\n    CREATE (SpeedRacer:Movie {title:'Speed Racer', released:2008, tagline:'Speed has no limits'})\n    CREATE (EmileH:Person {name:'Emile Hirsch', born:1985})\n    CREATE (JohnG:Person {name:'John Goodman', born:1960})\n    CREATE (SusanS:Person {name:'Susan Sarandon', born:1946})\n    CREATE (MatthewF:Person {name:'Matthew Fox', born:1966})\n    CREATE (ChristinaR:Person {name:'Christina Ricci', born:1980})\n    CREATE (Rain:Person {name:'Rain', born:1982})\n    CREATE\n    (EmileH)-[:ACTED_IN {roles:['Speed Racer']}]->(SpeedRacer),\n    (JohnG)-[:ACTED_IN {roles:['Pops']}]->(SpeedRacer),\n    (SusanS)-[:ACTED_IN {roles:['Mom']}]->(SpeedRacer),\n    (MatthewF)-[:ACTED_IN {roles:['Racer X']}]->(SpeedRacer),\n    (ChristinaR)-[:ACTED_IN {roles:['Trixie']}]->(SpeedRacer),\n    (Rain)-[:ACTED_IN {roles:['Taejo Togokahn']}]->(SpeedRacer),\n    (BenM)-[:ACTED_IN {roles:['Cass Jones']}]->(SpeedRacer),\n    (LillyW)-[:DIRECTED]->(SpeedRacer),\n    (LanaW)-[:DIRECTED]->(SpeedRacer),\n    (LillyW)-[:WROTE]->(SpeedRacer),\n    (LanaW)-[:WROTE]->(SpeedRacer),\n    (JoelS)-[:PRODUCED]->(SpeedRacer)\n\n    CREATE (NinjaAssassin:Movie {title:'Ninja Assassin', released:2009, tagline:'Prepare to enter a secret world of assassins'})\n    CREATE (NaomieH:Person {name:'Naomie Harris'})\n    CREATE\n    (Rain)-[:ACTED_IN {roles:['Raizo']}]->(NinjaAssassin),\n    (NaomieH)-[:ACTED_IN {roles:['Mika Coretti']}]->(NinjaAssassin),\n    (RickY)-[:ACTED_IN {roles:['Takeshi']}]->(NinjaAssassin),\n    (BenM)-[:ACTED_IN {roles:['Ryan Maslow']}]->(NinjaAssassin),\n    (JamesM)-[:DIRECTED]->(NinjaAssassin),\n    (LillyW)-[:PRODUCED]->(NinjaAssassin),\n    (LanaW)-[:PRODUCED]->(NinjaAssassin),\n    (JoelS)-[:PRODUCED]->(NinjaAssassin)\n\n    CREATE (TheGreenMile:Movie {title:'The Green Mile', released:1999, tagline:\"Walk a mile you'll never forget.\"})\n    CREATE (MichaelD:Person {name:'Michael Clarke Duncan', born:1957})\n    CREATE (DavidM:Person {name:'David Morse', born:1953})\n    CREATE (SamR:Person {name:'Sam Rockwell', born:1968})\n    CREATE (GaryS:Person {name:'Gary Sinise', born:1955})\n    CREATE (PatriciaC:Person {name:'Patricia Clarkson', born:1959})\n    CREATE (FrankD:Person {name:'Frank Darabont', born:1959})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Paul Edgecomb']}]->(TheGreenMile),\n    (MichaelD)-[:ACTED_IN {roles:['John Coffey']}]->(TheGreenMile),\n    (DavidM)-[:ACTED_IN {roles:['Brutus \"Brutal\" Howell']}]->(TheGreenMile),\n    (BonnieH)-[:ACTED_IN {roles:['Jan Edgecomb']}]->(TheGreenMile),\n    (JamesC)-[:ACTED_IN {roles:['Warden Hal Moores']}]->(TheGreenMile),\n    (SamR)-[:ACTED_IN {roles:['\"Wild Bill\" Wharton']}]->(TheGreenMile),\n    (GaryS)-[:ACTED_IN {roles:['Burt Hammersmith']}]->(TheGreenMile),\n    (PatriciaC)-[:ACTED_IN {roles:['Melinda Moores']}]->(TheGreenMile),\n    (FrankD)-[:DIRECTED]->(TheGreenMile)\n\n    CREATE (FrostNixon:Movie {title:'Frost/Nixon', released:2008, tagline:'400 million people were waiting for the truth.'})\n    CREATE (FrankL:Person {name:'Frank Langella', born:1938})\n    CREATE (MichaelS:Person {name:'Michael Sheen', born:1969})\n    CREATE (OliverP:Person {name:'Oliver Platt', born:1960})\n    CREATE\n    (FrankL)-[:ACTED_IN {roles:['Richard Nixon']}]->(FrostNixon),\n    (MichaelS)-[:ACTED_IN {roles:['David Frost']}]->(FrostNixon),\n    (KevinB)-[:ACTED_IN {roles:['Jack Brennan']}]->(FrostNixon),\n    (OliverP)-[:ACTED_IN {roles:['Bob Zelnick']}]->(FrostNixon),\n    (SamR)-[:ACTED_IN {roles:['James Reston, Jr.']}]->(FrostNixon),\n    (RonH)-[:DIRECTED]->(FrostNixon)\n\n    CREATE (Hoffa:Movie {title:'Hoffa', released:1992, tagline:\"He didn't want law. He wanted justice.\"})\n    CREATE (DannyD:Person {name:'Danny DeVito', born:1944})\n    CREATE (JohnR:Person {name:'John C. Reilly', born:1965})\n    CREATE\n    (JackN)-[:ACTED_IN {roles:['Hoffa']}]->(Hoffa),\n    (DannyD)-[:ACTED_IN {roles:['Robert \"Bobby\" Ciaro']}]->(Hoffa),\n    (JTW)-[:ACTED_IN {roles:['Frank Fitzsimmons']}]->(Hoffa),\n    (JohnR)-[:ACTED_IN {roles:['Peter \"Pete\" Connelly']}]->(Hoffa),\n    (DannyD)-[:DIRECTED]->(Hoffa)\n\n    CREATE (Apollo13:Movie {title:'Apollo 13', released:1995, tagline:'Houston, we have a problem.'})\n    CREATE (EdH:Person {name:'Ed Harris', born:1950})\n    CREATE (BillPax:Person {name:'Bill Paxton', born:1955})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Jim Lovell']}]->(Apollo13),\n    (KevinB)-[:ACTED_IN {roles:['Jack Swigert']}]->(Apollo13),\n    (EdH)-[:ACTED_IN {roles:['Gene Kranz']}]->(Apollo13),\n    (BillPax)-[:ACTED_IN {roles:['Fred Haise']}]->(Apollo13),\n    (GaryS)-[:ACTED_IN {roles:['Ken Mattingly']}]->(Apollo13),\n    (RonH)-[:DIRECTED]->(Apollo13)\n\n    CREATE (Twister:Movie {title:'Twister', released:1996, tagline:\"Don't Breathe. Don't Look Back.\"})\n    CREATE (PhilipH:Person {name:'Philip Seymour Hoffman', born:1967})\n    CREATE (JanB:Person {name:'Jan de Bont', born:1943})\n    CREATE\n    (BillPax)-[:ACTED_IN {roles:['Bill Harding']}]->(Twister),\n    (HelenH)-[:ACTED_IN {roles:['Dr. Jo Harding']}]->(Twister),\n    (ZachG)-[:ACTED_IN {roles:['Eddie']}]->(Twister),\n    (PhilipH)-[:ACTED_IN {roles:['Dustin \"Dusty\" Davis']}]->(Twister),\n    (JanB)-[:DIRECTED]->(Twister)\n\n    CREATE (CastAway:Movie {title:'Cast Away', released:2000, tagline:'At the edge of the world, his journey begins.'})\n    CREATE (RobertZ:Person {name:'Robert Zemeckis', born:1951})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Chuck Noland']}]->(CastAway),\n    (HelenH)-[:ACTED_IN {roles:['Kelly Frears']}]->(CastAway),\n    (RobertZ)-[:DIRECTED]->(CastAway)\n\n    CREATE (OneFlewOvertheCuckoosNest:Movie {title:\"One Flew Over the Cuckoo's Nest\", released:1975, tagline:\"If he's crazy, what does that make you?\"})\n    CREATE (MilosF:Person {name:'Milos Forman', born:1932})\n    CREATE\n    (JackN)-[:ACTED_IN {roles:['Randle McMurphy']}]->(OneFlewOvertheCuckoosNest),\n    (DannyD)-[:ACTED_IN {roles:['Martini']}]->(OneFlewOvertheCuckoosNest),\n    (MilosF)-[:DIRECTED]->(OneFlewOvertheCuckoosNest)\n\n    CREATE (SomethingsGottaGive:Movie {title:\"Something's Gotta Give\", released:2003})\n    CREATE (DianeK:Person {name:'Diane Keaton', born:1946})\n    CREATE (NancyM:Person {name:'Nancy Meyers', born:1949})\n    CREATE\n    (JackN)-[:ACTED_IN {roles:['Harry Sanborn']}]->(SomethingsGottaGive),\n    (DianeK)-[:ACTED_IN {roles:['Erica Barry']}]->(SomethingsGottaGive),\n    (Keanu)-[:ACTED_IN {roles:['Julian Mercer']}]->(SomethingsGottaGive),\n    (NancyM)-[:DIRECTED]->(SomethingsGottaGive),\n    (NancyM)-[:PRODUCED]->(SomethingsGottaGive),\n    (NancyM)-[:WROTE]->(SomethingsGottaGive)\n\n    CREATE (BicentennialMan:Movie {title:'Bicentennial Man', released:1999, tagline:\"One robot's 200 year journey to become an ordinary man.\"})\n    CREATE (ChrisC:Person {name:'Chris Columbus', born:1958})\n    CREATE\n    (Robin)-[:ACTED_IN {roles:['Andrew Marin']}]->(BicentennialMan),\n    (OliverP)-[:ACTED_IN {roles:['Rupert Burns']}]->(BicentennialMan),\n    (ChrisC)-[:DIRECTED]->(BicentennialMan)\n\n    CREATE (CharlieWilsonsWar:Movie {title:\"Charlie Wilson's War\", released:2007, tagline:\"A stiff drink. A little mascara. A lot of nerve. Who said they couldn't bring down the Soviet empire.\"})\n    CREATE (JuliaR:Person {name:'Julia Roberts', born:1967})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Rep. Charlie Wilson']}]->(CharlieWilsonsWar),\n    (JuliaR)-[:ACTED_IN {roles:['Joanne Herring']}]->(CharlieWilsonsWar),\n    (PhilipH)-[:ACTED_IN {roles:['Gust Avrakotos']}]->(CharlieWilsonsWar),\n    (MikeN)-[:DIRECTED]->(CharlieWilsonsWar)\n\n    CREATE (ThePolarExpress:Movie {title:'The Polar Express', released:2004, tagline:'This Holiday Season… Believe'})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Hero Boy', 'Father', 'Conductor', 'Hobo', 'Scrooge', 'Santa Claus']}]->(ThePolarExpress),\n    (RobertZ)-[:DIRECTED]->(ThePolarExpress)\n\n    CREATE (ALeagueofTheirOwn:Movie {title:'A League of Their Own', released:1992, tagline:'Once in a lifetime you get a chance to do something different.'})\n    CREATE (Madonna:Person {name:'Madonna', born:1954})\n    CREATE (GeenaD:Person {name:'Geena Davis', born:1956})\n    CREATE (LoriP:Person {name:'Lori Petty', born:1963})\n    CREATE (PennyM:Person {name:'Penny Marshall', born:1943})\n    CREATE\n    (TomH)-[:ACTED_IN {roles:['Jimmy Dugan']}]->(ALeagueofTheirOwn),\n    (GeenaD)-[:ACTED_IN {roles:['Dottie Hinson']}]->(ALeagueofTheirOwn),\n    (LoriP)-[:ACTED_IN {roles:['Kit Keller']}]->(ALeagueofTheirOwn),\n    (RosieO)-[:ACTED_IN {roles:['Doris Murphy']}]->(ALeagueofTheirOwn),\n    (Madonna)-[:ACTED_IN {roles:['\"All the Way\" Mae Mordabito']}]->(ALeagueofTheirOwn),\n    (BillPax)-[:ACTED_IN {roles:['Bob Hinson']}]->(ALeagueofTheirOwn),\n    (PennyM)-[:DIRECTED]->(ALeagueofTheirOwn)\n\n    CREATE (PaulBlythe:Person {name:'Paul Blythe'})\n    CREATE (AngelaScope:Person {name:'Angela Scope'})\n    CREATE (JessicaThompson:Person {name:'Jessica Thompson'})\n    CREATE (JamesThompson:Person {name:'James Thompson'})\n\n    CREATE\n    (JamesThompson)-[:FOLLOWS]->(JessicaThompson),\n    (AngelaScope)-[:FOLLOWS]->(JessicaThompson),\n    (PaulBlythe)-[:FOLLOWS]->(AngelaScope)\n\n    CREATE\n    (JessicaThompson)-[:REVIEWED {summary:'An amazing journey', rating:95}]->(CloudAtlas),\n    (JessicaThompson)-[:REVIEWED {summary:'Silly, but fun', rating:65}]->(TheReplacements),\n    (JamesThompson)-[:REVIEWED {summary:'The coolest football movie ever', rating:100}]->(TheReplacements),\n    (AngelaScope)-[:REVIEWED {summary:'Pretty funny at times', rating:62}]->(TheReplacements),\n    (JessicaThompson)-[:REVIEWED {summary:'Dark, but compelling', rating:85}]->(Unforgiven),\n    (JessicaThompson)-[:REVIEWED {summary:\"Slapstick redeemed only by the Robin Williams and Gene Hackman's stellar performances\", rating:45}]->(TheBirdcage),\n    (JessicaThompson)-[:REVIEWED {summary:'A solid romp', rating:68}]->(TheDaVinciCode),\n    (JamesThompson)-[:REVIEWED {summary:'Fun, but a little far fetched', rating:65}]->(TheDaVinciCode),\n    (JessicaThompson)-[:REVIEWED {summary:'You had me at Jerry', rating:92}]->(JerryMaguire)\n    ;\n    \"\"\"\n  end\nend\n"
  },
  {
    "path": "test/support/internal_case.ex",
    "content": "defmodule Bolt.Sips.InternalCase do\n  use ExUnit.CaseTemplate\n\n  alias Bolt.Sips.Internals.BoltProtocol\n\n  setup do\n    uri = neo4j_uri()\n    port_opts = [active: false, mode: :binary, packet: :raw]\n    {:ok, port} = :gen_tcp.connect(uri.host, uri.port, port_opts)\n    {:ok, bolt_version} = BoltProtocol.handshake(:gen_tcp, port)\n    {:ok, _} = init(:gen_tcp, port, bolt_version, uri.userinfo)\n\n    on_exit(fn ->\n      :gen_tcp.close(port)\n    end)\n\n    {:ok, port: port, is_bolt_v2: bolt_version >= 2, bolt_version: bolt_version}\n  end\n\n  defp neo4j_uri do\n    \"bolt://neo4j:test@localhost:7687\"\n    |> URI.merge(System.get_env(\"NEO4J_TEST_URL\") || \"\")\n    |> URI.parse()\n    |> Map.update!(:host, &String.to_charlist/1)\n    |> Map.update!(:userinfo, fn\n      nil ->\n        {}\n\n      userinfo ->\n        userinfo\n        |> String.split(\":\")\n        |> List.to_tuple()\n    end)\n  end\n\n  defp init(transport, port, 3, auth) do\n    BoltProtocol.hello(transport, port, 3, auth)\n  end\n\n  defp init(transport, port, bolt_version, auth) do\n    BoltProtocol.init(transport, port, bolt_version, auth)\n  end\nend\n"
  },
  {
    "path": "test/test_helper.exs",
    "content": "Logger.configure(level: :debug)\nExUnit.start(capture_log: true, assert_receive_timeout: 500, exclude: [:skip, :bench, :apoc])\nApplication.ensure_started(:porcelain)\n\nCode.require_file(\"test_support.exs\", __DIR__)\n\ndefmodule Bolt.Sips.TestHelper do\n  @doc \"\"\"\n   Read an entire file into a string.\n   Return a tuple of success and data.\n  \"\"\"\n  def read_whole_file(path) do\n    case File.read(path) do\n      {:ok, file} -> file\n      {:error, reason} -> {:error, \"Could not open #{path} #{file_error_description(reason)}\"}\n    end\n  end\n\n  @doc \"\"\"\n    Open a file stream, and join the lines into a string.\n  \"\"\"\n  def stream_file_join(filename) do\n    stream = File.stream!(filename)\n    Enum.join(stream)\n  end\n\n  defp file_error_description(:enoent), do: \"because the file does not exist.\"\n  defp file_error_description(reason), do: \"due to #{reason}.\"\nend\n\nBolt.Sips.start_link(Application.get_env(:bolt_sips, Bolt))\n\n# I am using the test db for debugging and the line below will clear *everything*\n# Bolt.Sips.query(Bolt.Sips.conn, \"MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE n,r\")\n#\n# todo: The tests should cleanup the data they create.\n\nProcess.flag(:trap_exit, true)\n"
  },
  {
    "path": "test/test_large_param_set.exs",
    "content": "defmodule Large.Param.Set.Test do\n  use ExUnit.Case\n  doctest Bolt.Sips\n\n  setup_all do\n    {:ok, [conn: Bolt.Sips.conn()]}\n  end\n\n  @doc \"\"\"\n  Boltex.Bolt.generate_chunks fails with too much data\n  test provided by @adri, for issue #16\n  \"\"\"\n  test \"executing a Cypher query, with large set of parameters\", context do\n    conn = context[:conn]\n\n    cypher = \"\"\"\n      MATCH (n:Person {bolt_sips: true})\n      FOREACH (i IN $largeRange| SET n.test = TRUE )\n    \"\"\"\n\n    case Bolt.Sips.query(conn, cypher, %{largeRange: Enum.to_list(0..1_000_000)}) do\n      {:ok, stats} ->\n        assert stats[\"properties-set\"] > 0, \"Expecting many properties set\"\n\n      {:error, reason} ->\n        IO.puts(\"Error: #{reason[\"message\"]}\")\n    end\n  end\nend\n"
  },
  {
    "path": "test/test_support.exs",
    "content": "defmodule TestSupport do\n  @moduledoc false\nend\n"
  },
  {
    "path": "test/transaction_test.exs",
    "content": "defmodule Transaction.Test do\n  use ExUnit.Case, async: true\n\n  alias Bolt.Sips.Response\n\n  setup do\n    {:ok, [main_conn: Bolt.Sips.conn()]}\n  end\n\n  test \"execute statements in transaction\", %{main_conn: main_conn} do\n    Bolt.Sips.transaction(main_conn, fn conn ->\n      book =\n        Bolt.Sips.query!(conn, \"CREATE (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n        |> Response.first()\n\n      assert %{\"b\" => g_o_t} = book\n      assert g_o_t.properties[\"title\"] == \"The Game Of Trolls\"\n      Bolt.Sips.rollback(conn, :changed_my_mind)\n    end)\n\n    books = Bolt.Sips.query!(main_conn, \"MATCH (b:Book {title: \\\"The Game Of Trolls\\\"}) return b\")\n    assert Enum.count(books) == 0\n  end\n\n  ###\n  ### NOTE:\n  ###\n  ### The labels used in these examples MUST be unique across all tests!\n  ### These tests depend on being able to expect that a node either exists\n  ### or does not, and asynchronous testing with the same names will cause\n  ### random cases where the underlying state changes.\n  ###\n\n  test \"rollback statements in transaction\", %{main_conn: main_conn} do\n    try do\n      # In case there's already a copy in our DB, count them...\n      {:ok, %Response{results: [result]}} =\n        Bolt.Sips.query(main_conn, \"MATCH (x:XactRollback) RETURN count(x)\")\n\n      original_count = result[\"count(x)\"]\n\n      Bolt.Sips.transaction(main_conn, fn conn ->\n        assert {:ok, %Response{results: [row]}} =\n                 Bolt.Sips.query(\n                   conn,\n                   \"CREATE (x:XactRollback {title:\\\"The Game Of Trolls\\\"}) return x\"\n                 )\n\n        assert row[\"x\"].properties[\"title\"] == \"The Game Of Trolls\"\n\n        # Original connection (outside the transaction) should not see this node.\n        assert {:ok, %Response{results: [result]}} =\n                 Bolt.Sips.query(main_conn, \"MATCH (x:XactRollback) RETURN count(x)\")\n\n        assert result[\"count(x)\"] == original_count,\n               \"Main connection should not be able to see transactional change\"\n\n        Bolt.Sips.rollback(conn, :changed_my_mind)\n      end)\n\n      # Original connection should still not see this node committed.\n      assert {:ok, %Response{results: [result]}} =\n               Bolt.Sips.query(main_conn, \"MATCH (x:XactRollback) RETURN count(x)\")\n\n      assert result[\"count(x)\"] == original_count\n    after\n      # Delete all XactRollback nodes in case the rollback() didn't work!\n      Bolt.Sips.query(main_conn, \"MATCH (x:XactRollback) DETACH DELETE x\")\n    end\n  end\n\n  test \"commit statements in transaction\", %{main_conn: main_conn} do\n    try do\n      Bolt.Sips.transaction(main_conn, fn conn ->\n        assert {:ok, %Response{results: books}} =\n                 Bolt.Sips.query(conn, \"CREATE (x:XactCommit {foo: 'bar'}) return x\")\n\n        # TODO: maybe we can make Entity implement Access? That will avoid the Map gets below\n        assert \"bar\" ==\n                 books\n                 |> List.first()\n                 |> Map.get(\"x\")\n                 |> Map.get(:properties)\n                 |> Map.get(\"foo\")\n\n        # Main connection should not see this new node.\n        {:ok, %Response{results: results}} =\n          Bolt.Sips.query(main_conn, \"MATCH (x:XactCommit) RETURN x\")\n\n        assert is_list(results)\n\n        assert Enum.count(results) == 0,\n               \"Main connection should not be able to see transactional changes\"\n      end)\n\n      # And we should see it now with the main connection.\n      {:ok, %Response{results: [%{\"x\" => node}]}} =\n        Bolt.Sips.query(main_conn, \"MATCH (x:XactCommit) RETURN x\")\n\n      assert node.labels == [\"XactCommit\"]\n      assert node.properties[\"foo\"] == \"bar\"\n    after\n      # Delete any XactCommit nodes that were succesfully committed!\n      Bolt.Sips.query(main_conn, \"MATCH (x:XactCommit) DETACH DELETE x\")\n    end\n  end\nend\n"
  }
]