Skip to content

Commit

Permalink
Merge pull request #209 from ankhers/specification_tests
Browse files Browse the repository at this point in the history
Specification Tests
  • Loading branch information
Justin Wood committed Jul 16, 2018
2 parents 65bd68a + 38b6bea commit 3aaa114
Show file tree
Hide file tree
Showing 18 changed files with 856 additions and 25 deletions.
7 changes: 3 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ before_script:

env:
matrix:
- MONGOVERSION=2.4.14 TRAVIS_NODE_VERSION=4
- MONGOVERSION=2.6.12 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.0.15 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.2.13 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.4.4 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.6.0 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.2.20 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.4.15 TRAVIS_NODE_VERSION=4
- MONGOVERSION=3.6.5 TRAVIS_NODE_VERSION=4
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## Features

* Supports MongoDB versions 2.4, 2.6, 3.0, 3.2, 3.4, 3.6
* Supports MongoDB versions 2.6, 3.0, 3.2, 3.4, 3.6
* Connection pooling (through db_connection)
* Streaming cursors
* Performant ObjectID generation
Expand Down
78 changes: 64 additions & 14 deletions lib/mongo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ defmodule Mongo do
## Options
* `:allow_disk_use` - Enables writing to temporary files (Default: false)
* `:collation` - Optionally specifies a collation to use in MongoDB 3.4 and
* `:max_time` - Specifies a time limit in milliseconds
* `:use_cursor` - Use a cursor for a batched response (Default: true)
"""
Expand All @@ -153,6 +154,7 @@ defmodule Mongo do
aggregate: coll,
pipeline: pipeline,
allowDiskUse: opts[:allow_disk_use],
collation: opts[:collation],
maxTimeMS: opts[:max_time]
] |> filter_nils
wv_query = %Query{action: :wire_version}
Expand Down Expand Up @@ -186,6 +188,7 @@ defmodule Mongo do
selects multiple documents.
* `:upsert` - Create a document if no document matches the query or updates
the document.
* `:collation` - Optionally specifies a collation to use in MongoDB 3.4 and
"""
@spec find_one_and_update(GenServer.server, collection, BSON.document, BSON.document, Keyword.t) :: result(BSON.document)
def find_one_and_update(topology_pid, coll, filter, update, opts \\ []) do
Expand Down Expand Up @@ -280,54 +283,101 @@ defmodule Mongo do
{:ok, doc} <- direct_command(conn, query, opts), do: {:ok, doc["value"]}
end

@doc """
Returns the count of documents that would match a `find/4` query.
## Options
* `:limit` - Maximum number of documents to fetch with the cursor
* `:skip` - Number of documents to skip before returning the first
* `:hint` - Hint which index to use for the query
"""
@doc false
@spec count(GenServer.server, collection, BSON.document, Keyword.t) :: result(non_neg_integer)
def count(topology_pid, coll, filter, opts \\ []) do
query = [
count: coll,
query: filter,
limit: opts[:limit],
skip: opts[:skip],
hint: opts[:hint]
hint: opts[:hint],
collation: opts[:collation]
] |> filter_nils

opts = Keyword.drop(opts, ~w(limit skip hint)a)
opts = Keyword.drop(opts, ~w(limit skip hint collation)a)

# Mongo 2.4 and 2.6 returns a float
with {:ok, conn, _, _} <- select_server(topology_pid, :read, opts),
{:ok, doc} <- direct_command(conn, query, opts),
do: {:ok, trunc(doc["n"])}
end

@doc """
Similar to `count/4` but unwraps the result and raises on error.
"""
@doc false
@spec count!(GenServer.server, collection, BSON.document, Keyword.t) :: result!(non_neg_integer)
def count!(topology_pid, coll, filter, opts \\ []) do
bangify(count(topology_pid, coll, filter, opts))
end

@doc """
Returns the count of documents that would match a find/4 query.
## Options
* `:limit` - Maximum number of documents to fetch with the cursor
* `:skip` - Number of documents to skip before returning the first
"""
@spec count_documents(GenServer.server, collection, BSON.document, Keyword.t) :: result(non_neg_integer)
def count_documents(topology_pid, coll, filter, opts \\ []) do
pipeline = [
{"$match", filter},
{"$skip", opts[:skip]},
{"$limit", opts[:limit]},
{"$group", %{"_id" => nil, "n" => %{"$sum" => 1}}}
] |> filter_nils |> Enum.map(&List.wrap/1)

documents =
topology_pid
|> Mongo.aggregate(coll, pipeline, opts)
|> Enum.to_list

case documents do
[%{"n" => count}] -> {:ok, count}
[] -> {:error, :nothing_returned}
_ -> {:error, :too_many_documents_returned}
end
end

@doc """
Similar to `count_documents/4` but unwraps the result and raises on error.
"""
@spec count_documents!(GenServer.server, collection, BSON.document, Keyword.t) :: result!(non_neg_integer)
def count_documents!(topology_pid, coll, filter, opts \\ []) do
bangify(count_documents(topology_pid, coll, filter, opts))
end

@doc """
Estimate the number of documents in a collection using collection metadata.
"""
@spec estimated_document_count(GenServer.server, collection, Keyword.t) :: result(non_neg_integer)
def estimated_document_count(topology_pid, coll, opts) do
opts = Keyword.drop(opts, [:skip, :limit, :hint, :collation])
count(topology_pid, coll, %{}, opts)
end

@doc """
Similar to `estimated_document_count/3` but unwraps the result and raises on
error.
"""
@spec estimated_document_count!(GenServer.server, collection, Keyword.t) :: result!(non_neg_integer)
def estimated_document_count!(topology_pid, coll, opts) do
bangify(estimated_document_count(topology_pid, coll, opts))
end

@doc """
Finds the distinct values for a specified field across a collection.
## Options
* `:max_time` - Specifies a time limit in milliseconds
* `:collation` - Optionally specifies a collation to use in MongoDB 3.4 and
"""
@spec distinct(GenServer.server, collection, String.t | atom, BSON.document, Keyword.t) :: result([BSON.t])
def distinct(topology_pid, coll, field, filter, opts \\ []) do
query = [
distinct: coll,
key: field,
query: filter,
collation: opts[:collation],
maxTimeMS: opts[:max_time]
] |> filter_nils

Expand Down
15 changes: 9 additions & 6 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,15 @@ defmodule Mongodb.Mixfile do
def applications(_), do: [:logger, :connection, :db_connection]

defp deps do
[{:connection, "~> 1.0"},
{:db_connection, "~> 1.1"},
{:poolboy, ">= 0.0.0", only: :test},
{:ex_doc, ">= 0.0.0", only: :dev},
{:earmark, ">= 0.0.0", only: :dev},
{:dialyxir, "~> 0.5.1", only: :dev}]
[
{:connection, "~> 1.0"},
{:db_connection, "~> 1.1"},
{:poolboy, ">= 0.0.0", only: :test},
{:jason, "~> 1.0.0", only: :test},
{:ex_doc, ">= 0.0.0", only: :dev},
{:earmark, ">= 0.0.0", only: :dev},
{:dialyxir, "~> 0.5.1", only: :dev}
]
end

defp docs do
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.4", "99b637c62a4d65a20a9fb674b8cffb8baa771c04605a80c911c4418c69b75439", [:mix], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [:mix], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], [], "hexpm"}}
41 changes: 41 additions & 0 deletions test/specification_tests/crud_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule Mongo.SpecificationTests.CRUDTest do
use Mongo.SpecificationCase
import Mongo.Specification.CRUD.Helpers
require Mongo.Specification.CRUD

def min_server_version?(nil), do: true
def min_server_version?(number) do
min_server_version =
number <> ".0"
|> String.split(".")
|> Enum.map(&elem(Integer.parse(&1), 0))
|> List.to_tuple()

mongo_version() >= min_server_version
end

setup_all do
{:ok, pid} = Mongo.start_link(database: "mongodb_test")

%{mongo: pid}
end

Enum.map(@crud_tests, fn file ->
json = file |> File.read!() |> Jason.decode!()
[file_no_suffix, _suffix] =
file
|> String.split("/")
|> List.last()
|> String.split(".")

describe file do
setup %{mongo: mongo} do
collection = unquote(Macro.escape(file_no_suffix))
Mongo.delete_many!(mongo, collection, %{})
%{collection: collection}
end

Mongo.Specification.CRUD.create_tests(json)
end
end)
end
38 changes: 38 additions & 0 deletions test/support/crud_tests/read/aggregate-collation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"data": [
{
"_id": 1,
"x": "ping"
}
],
"minServerVersion": "3.4",
"tests": [
{
"description": "Aggregate with collation",
"operation": {
"name": "aggregate",
"arguments": {
"pipeline": [
{
"$match": {
"x": "PING"
}
}
],
"collation": {
"locale": "en_US",
"strength": 2
}
}
},
"outcome": {
"result": [
{
"_id": 1,
"x": "ping"
}
]
}
}
]
}
121 changes: 121 additions & 0 deletions test/support/crud_tests/read/aggregate-out.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
{
"data": [
{
"_id": 1,
"x": 11
},
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"minServerVersion": "2.6",
"tests": [
{
"description": "Aggregate with $out",
"operation": {
"name": "aggregate",
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"batchSize": 2
}
},
"outcome": {
"result": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
},
{
"description": "Aggregate with $out and batch size of 0",
"operation": {
"name": "aggregate",
"arguments": {
"pipeline": [
{
"$sort": {
"x": 1
}
},
{
"$match": {
"_id": {
"$gt": 1
}
}
},
{
"$out": "other_test_collection"
}
],
"batchSize": 0
}
},
"outcome": {
"result": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
],
"collection": {
"name": "other_test_collection",
"data": [
{
"_id": 2,
"x": 22
},
{
"_id": 3,
"x": 33
}
]
}
}
}
]
}
Loading

0 comments on commit 3aaa114

Please sign in to comment.