PageRenderTime 26ms CodeModel.GetById 1ms RepoModel.GetById 0ms app.codeStats 0ms

/casbah-core/src/test/scala/CoreWrappersSpec.scala

http://github.com/mongodb/casbah
Scala | 457 lines | 319 code | 102 blank | 36 comment | 8 complexity | 3d1ff64c7c99db5fa7e6f90707b4cfb6 MD5 | raw file
Possible License(s): Apache-2.0
  1. /**
  2. * Copyright (c) 2010 MongoDB, Inc. <http://mongodb.com>
  3. * Copyright (c) 2009, 2010 Novus Partners, Inc. <http://novus.com>
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. * For questions and comments about this product, please see the project page at:
  18. *
  19. * http://github.com/mongodb/casbah
  20. *
  21. */
  22. package com.mongodb.casbah.test.core
  23. import scala.language.reflectiveCalls
  24. import scala.collection.mutable
  25. import scala.concurrent.duration.Duration
  26. import scala.util.{ Try, Random }
  27. import com.mongodb.InsertOptions
  28. import com.mongodb.casbah.Cursor
  29. import com.mongodb.casbah.Imports._
  30. class CoreWrappersSpec extends CasbahDBTestSpecification {
  31. "Casbah behavior between Scala and Java versions of Objects" should {
  32. lazy val javaConn = new com.mongodb.MongoClient() // Java connection
  33. "provide working .asScala methods on the Java version of the objects" in {
  34. "Connection objects" in {
  35. val scalaConn = javaConn.asScala
  36. scalaConn.underlying must beEqualTo(javaConn)
  37. }
  38. val javaDb = javaConn.getDB("test")
  39. "DB objects" in {
  40. val scalaDb = javaDb.asScala
  41. scalaDb.underlying must beEqualTo(javaDb)
  42. }
  43. val javaCollection = javaDb.getCollection("test")
  44. "Collection objects" in {
  45. val scalaCollection = javaCollection.asScala
  46. scalaCollection.underlying must beEqualTo(javaCollection)
  47. }
  48. }
  49. "be directly instantiable, with working apply methods" in {
  50. lazy val conn: MongoClient = MongoClient()
  51. lazy val db: MongoDB = conn("casbahTest")
  52. lazy val coll: MongoCollection = database("collection.in")
  53. "MongoClient" in {
  54. "direct instantiation" in {
  55. conn.underlying must haveClass[com.mongodb.MongoClient]
  56. }
  57. "the apply method works" in {
  58. database.underlying must haveClass[com.mongodb.DB]
  59. }
  60. }
  61. "MongoDB" in {
  62. "has a working apply method" in {
  63. coll.underlying must beAnInstanceOf[com.mongodb.DBCollection]
  64. }
  65. }
  66. }
  67. "allow indexes to work as expected" in {
  68. collection.drop()
  69. collection.insert(MongoDBObject("foo" -> "bar"))
  70. collection.indexInfo.length must beEqualTo(1)
  71. collection.createIndex(MongoDBObject("uid" -> 1), "user_index", unique = true)
  72. collection.indexInfo.length must beEqualTo(2)
  73. collection.indexInfo(1)("key") == MongoDBObject("uid" -> 1)
  74. }
  75. "check query failure exception" in {
  76. collection.drop()
  77. collection += MongoDBObject("loc" -> List(0, 0))
  78. val near = "loc" $near (0, 0)
  79. collection.findOne(near) must throwAn[MongoException]
  80. }
  81. "Renaming a collection successfully tracks the rename in MongoCollection" in {
  82. database("collection").drop()
  83. val coll = database("collectoin")
  84. coll.drop()
  85. coll.insert(MongoDBObject("foo" -> "bar"))
  86. coll must beAnInstanceOf[com.mongodb.casbah.MongoCollection]
  87. coll.name must beEqualTo("collectoin")
  88. val newColl = coll.rename("collection")
  89. newColl must beAnInstanceOf[com.mongodb.casbah.MongoCollection]
  90. newColl.name must beEqualTo("collection")
  91. // no mutability in the old collection
  92. coll.name must beEqualTo("collectoin")
  93. // collection should be gone so rename fails
  94. newColl.rename("collection") must throwA[MongoException]
  95. }
  96. }
  97. "findOne operations" should {
  98. "Not fail as reported by Max Afonov in CASBAH-11" in {
  99. collection.drop()
  100. collection.insert(MongoDBObject("foo" -> "bar"))
  101. val basicFind = collection.find(MongoDBObject("foo" -> "bar"))
  102. basicFind.size must beEqualTo(1)
  103. val findOne = collection.findOne()
  104. findOne must beSome
  105. val findOneMatch = collection.findOne(MongoDBObject("foo" -> "bar"))
  106. findOneMatch must beSome
  107. }
  108. }
  109. "Cursor Operations" should {
  110. "load some test data first" in {
  111. collection.drop()
  112. for (i <- 1 to 100)
  113. collection += MongoDBObject("foo" -> "bar", "x" -> Random.nextDouble())
  114. success
  115. }
  116. "Behave in chains" in {
  117. val cur = collection.find(MongoDBObject("foo" -> "bar")) skip 5
  118. cur must beAnInstanceOf[MongoCursor]
  119. val cur2 = collection.find(MongoDBObject("foo" -> "bar")) limit 25 skip 12
  120. cur2 must beAnInstanceOf[MongoCursor]
  121. }
  122. }
  123. "Distinct operations" should {
  124. "load some test data first" in {
  125. collection.drop()
  126. for (i <- 1 to 99)
  127. collection += MongoDBObject("_id" -> i, "x" -> i % 10)
  128. success
  129. }
  130. "except just a key" in {
  131. val l = collection.distinct("x")
  132. l.size must beEqualTo(10)
  133. }
  134. "except key and query" in {
  135. val l = collection.distinct("x", "_id" $gt 95)
  136. l.size must beEqualTo(4)
  137. }
  138. "except key and readPref" in {
  139. val l = collection.distinct("x", readPrefs = ReadPreference.Primary)
  140. l.size must beEqualTo(10)
  141. }
  142. "except key, query and readPref" in {
  143. val l = collection.distinct("x", "_id" $gt 95, ReadPreference.Primary)
  144. l.size must beEqualTo(4)
  145. }
  146. }
  147. "Aggregation operations" should {
  148. "load some test data first" in {
  149. collection.drop()
  150. for (i <- 1 to 99)
  151. collection += MongoDBObject("_id" -> i, "score" -> i % 10)
  152. success
  153. }
  154. "except just a single op" in {
  155. val cursor: AggregationOutput = collection.aggregate(MongoDBObject("$match" -> ("score" $gte 7)))
  156. cursor.results.size must beEqualTo(30)
  157. }
  158. "except multiple ops" in {
  159. val cursor: AggregationOutput = collection.aggregate(
  160. MongoDBObject("$match" -> ("score" $gte 7)),
  161. MongoDBObject("$project" -> MongoDBObject("score" -> 1))
  162. )
  163. cursor.results.size must beEqualTo(30)
  164. }
  165. "except list of ops" in {
  166. val cursor: AggregationOutput = collection.aggregate(
  167. List(
  168. MongoDBObject("$match" -> ("score" $gte 7)),
  169. MongoDBObject("$project" -> MongoDBObject("score" -> 1))
  170. )
  171. )
  172. cursor.results.size must beEqualTo(30)
  173. }
  174. "return a cursor when options are supplied" in {
  175. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.6")
  176. val aggregationOptions = AggregationOptions(allowDiskUse = true, outputMode = AggregationOptions.CURSOR)
  177. val cursor: CommandCursor = collection.aggregate(
  178. List(
  179. MongoDBObject("$match" -> ("score" $gte 7)),
  180. MongoDBObject("$project" -> MongoDBObject("score" -> 1))
  181. ),
  182. aggregationOptions
  183. )
  184. cursor.toList.size must beEqualTo(30)
  185. }
  186. "test allowDiskUse isn't included by default" in {
  187. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.6")
  188. val profileCollection = database("system.profile")
  189. val profileLevel = database.command(MongoDBObject("profile" -> -1)).as[Int]("was")
  190. database.command(MongoDBObject("profile" -> 0))
  191. profileCollection.drop()
  192. database.command(MongoDBObject("profile" -> 2))
  193. collection.aggregate(
  194. List(MongoDBObject("$match" -> ("score" $gte 7))),
  195. AggregationOptions(outputMode = AggregationOptions.CURSOR)
  196. )
  197. val profile = profileCollection.findOne().get.as[MongoDBObject]("command")
  198. database.command(MongoDBObject("profile" -> profileLevel))
  199. profile.contains("allowDiskUse") must beFalse
  200. }
  201. "test allowDiskUse is included if set" in {
  202. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.6")
  203. val profileCollection = database("system.profile")
  204. val profileLevel = database.command(MongoDBObject("profile" -> -1)).as[Int]("was")
  205. database.command(MongoDBObject("profile" -> 0))
  206. profileCollection.drop()
  207. database.command(MongoDBObject("profile" -> 2))
  208. collection.aggregate(
  209. List(MongoDBObject("$match" -> ("score" $gte 7))),
  210. AggregationOptions(allowDiskUse = true, outputMode = AggregationOptions.CURSOR)
  211. )
  212. val profile = profileCollection.findOne().get.as[MongoDBObject]("command")
  213. database.command(MongoDBObject("profile" -> profileLevel))
  214. profile.contains("allowDiskUse") must beTrue
  215. }
  216. "test explainAggregate" in {
  217. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.6")
  218. val aggregationOptions = AggregationOptions(AggregationOptions.CURSOR)
  219. val explaination = collection.explainAggregate(
  220. List(
  221. MongoDBObject("$match" -> ("score" $gte 7)),
  222. MongoDBObject("$project" -> MongoDBObject("score" -> 1))
  223. ),
  224. aggregationOptions
  225. )
  226. explaination("ok") must beEqualTo(1.0)
  227. explaination.keys must contain("stages")
  228. }
  229. "return a cursor when options are supplied even if inline" in {
  230. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  231. val aggregationOptions = AggregationOptions(AggregationOptions.INLINE)
  232. val cursor: CommandCursor = collection.aggregate(
  233. List(
  234. MongoDBObject("$match" -> ("score" $gte 7)),
  235. MongoDBObject("$project" -> MongoDBObject("score" -> 1))
  236. ),
  237. aggregationOptions
  238. )
  239. cursor.size must beEqualTo(30)
  240. }
  241. "handle $out in multiple ops" in {
  242. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  243. val outCollection = database("outCollection")
  244. outCollection.drop()
  245. val cursor: AggregationOutput = collection.aggregate(
  246. MongoDBObject("$match" -> ("score" $gte 7)),
  247. MongoDBObject("$project" -> MongoDBObject("score" -> 1)),
  248. MongoDBObject("$out" -> outCollection.name)
  249. )
  250. cursor.results.iterator.hasNext must beFalse
  251. outCollection.count() must beEqualTo(30)
  252. }
  253. "handle $out in list of ops" in {
  254. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  255. val outCollection = database("outCollection")
  256. outCollection.drop()
  257. val cursor: AggregationOutput = collection.aggregate(List(
  258. MongoDBObject("$match" -> ("score" $gte 7)),
  259. MongoDBObject("$project" -> MongoDBObject("score" -> 1)),
  260. MongoDBObject("$out" -> outCollection.name)
  261. ))
  262. cursor.results.iterator.hasNext must beFalse
  263. outCollection.count() must beEqualTo(30)
  264. }
  265. "handle $out with options INLINE" in {
  266. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  267. val outCollection = database("outCollection")
  268. outCollection.drop()
  269. val aggregationOptions = AggregationOptions(AggregationOptions.INLINE)
  270. val cursor: CommandCursor = collection.aggregate(
  271. List(
  272. MongoDBObject("$match" -> ("score" $gte 7)),
  273. MongoDBObject("$project" -> MongoDBObject("score" -> 1)),
  274. MongoDBObject("$out" -> outCollection.name)
  275. ),
  276. aggregationOptions
  277. )
  278. cursor.size must beEqualTo(30)
  279. outCollection.count() must beEqualTo(30)
  280. }
  281. "handle $out with options CURSOR" in {
  282. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  283. lazy val outCollection = database("outCollection")
  284. outCollection.drop()
  285. val aggregationOptions = AggregationOptions(AggregationOptions.CURSOR)
  286. val cursor: CommandCursor = collection.aggregate(
  287. List(
  288. MongoDBObject("$match" -> ("score" $gte 7)),
  289. MongoDBObject("$project" -> MongoDBObject("score" -> 1)),
  290. MongoDBObject("$out" -> outCollection.name)
  291. ),
  292. aggregationOptions
  293. )
  294. cursor.size must beEqualTo(30)
  295. outCollection.count() must beEqualTo(30)
  296. }
  297. }
  298. "Collection" should {
  299. "support parallel scan" in {
  300. serverIsAtLeastVersion(2, 5) must beTrue.orSkip("Needs server >= 2.5")
  301. isSharded must beFalse.orSkip("Currently doesn't work with mongos")
  302. collection.drop()
  303. val ids = (1 to 2000 by 1).toSet
  304. for (i <- ids) collection += MongoDBObject("_id" -> i)
  305. val numCursors = 10
  306. val cursors: mutable.Buffer[Cursor] = collection.parallelScan(ParallelScanOptions(numCursors, 1000))
  307. cursors.size must beLessThanOrEqualTo(numCursors)
  308. var cursorIds = Set[Int]()
  309. for (cursor <- cursors) {
  310. while (cursor.hasNext) {
  311. cursorIds += cursor.next().get("_id").asInstanceOf[Int]
  312. }
  313. }
  314. cursorIds must beEqualTo(ids)
  315. }
  316. "support bypass document validation" in {
  317. serverIsAtLeastVersion(3, 2) must beTrue.orSkip("Needs server >= 3.2")
  318. // given
  319. collection.drop()
  320. val createCollectionOptions = MongoDBObject("""{
  321. validator: {x: {$lte: 100}},
  322. validationLevel: "strict",
  323. validationAction: "error"}""")
  324. database.createCollection(collection.name, createCollectionOptions)
  325. val ids = (1 to 99 by 1).toSet
  326. for (i <- ids) { collection += MongoDBObject("x" -> i) }
  327. // when
  328. val findAndModify = Try(collection.findAndModify(MongoDBObject("{x: 10}"), MongoDBObject("{$inc: {x: 1000}}")))
  329. // then
  330. findAndModify should beAFailedTry
  331. // when
  332. val findAndModifyWithBypass = Try(collection.findAndModify(MongoDBObject("{x: 10}"), MongoDBObject("{}"),
  333. MongoDBObject("{}"), false, MongoDBObject("{$inc: {x: 100}}"), true, false, true, Duration(10, "seconds")))
  334. // then
  335. findAndModifyWithBypass should beASuccessfulTry
  336. // when
  337. val insert = Try(collection.insert(MongoDBObject("{x: 101}")))
  338. // then
  339. insert should beAFailedTry
  340. // when
  341. val insertWithBypass = Try(collection.insert(new InsertOptions().bypassDocumentValidation(true), MongoDBObject("{x: 101}")))
  342. // then
  343. insertWithBypass should beASuccessfulTry
  344. // when
  345. val update = Try(collection.update(MongoDBObject("{x: 1}"), MongoDBObject("{$set: {x : 101}}")))
  346. // then
  347. update should beAFailedTry
  348. // when
  349. val updateWithBypass = Try(collection.update(MongoDBObject("{x: 1}"), MongoDBObject("{$set: {x : 101}}"),
  350. bypassDocumentValidation = Some(true)))
  351. // then
  352. updateWithBypass should beASuccessfulTry
  353. }
  354. }
  355. }