Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

End-to-end tests.

Add tests that hit a running mongo to verify that the queries Rogue
formulates actually do what we expect.

 * Factored test models from QueryTest into TestModels.scala
 * Made EndToEndTest with some simple query tests that I lifted from
   QueryTest.
 * Added a start-test-mongo.sh script to fetch a mongo binary if
   necessary, make a mongo-testdb directory for its data, and start it.
 * Added a RogueTestMongo mongo identifier and made all of the test
   models use it. It connects to localhost:37648 (EROGU), the same port
   that start-test-mongo.sh uses.
 * Added some stuff to gitignore too.
  • Loading branch information...
commit 75a7d4c7a8eba61ab7dabb4e11c917249d44cbd8 1 parent f40d13a
@nsanch authored
View
5 .gitignore
@@ -9,3 +9,8 @@ sbtlib
target
out
*~
+mongo-testdb
+dependencies
+mongo.log
+tags
+tags.old
View
2  sbt
@@ -1,3 +1,5 @@
+#!/bin/bash
+
# Internal options, always specified
INTERNAL_OPTS="-Dfile.encoding=UTF-8 -Xss8M -Xmx1G -noverify -XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGC -XX:MaxPermSize=512M"
View
162 src/test/scala/com/foursquare/rogue/EndToEndTest.scala
@@ -0,0 +1,162 @@
+// Copyright 2011 Foursquare Labs Inc. All Rights Reserved.
+package com.foursquare.rogue
+
+import com.foursquare.rogue.Rogue._
+
+import java.util.regex.Pattern
+import net.liftweb.common.{Box, Empty, Full}
+import org.bson.types.ObjectId
+import org.junit.{Before, After, Ignore, Test}
+import org.specs.SpecsMatchers
+
+/**
+ * Contains tests that test the interaction of Rogue with a real mongo.
+ */
+class EndToEndTest extends SpecsMatchers {
@jliszka
jliszka added a note

awesome! modify and delete tests can be done the same way i guess?

@nsanch Owner
nsanch added a note

Yeah, absolutely. I just didn't want to spend another day yak-shaving, so I only migrated a few of the QueryTest's over.

@jliszka
jliszka added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ def baseTestVenue(): Venue = {
+ Venue.createRecord
+ .legacyid(123)
+ .userid(456)
+ .venuename("test venue")
+ .mayor(789)
+ .mayor_count(3)
+ .closed(false)
+ .popularity(List(1L, 2L, 3L))
+ .categories(List(new ObjectId()))
+ .geolatlng(LatLong(40.73, -73.98))
+ .status(VenueStatus.open)
+ .claims(List(VenueClaimBson.createRecord.userid(1234).status(ClaimStatus.pending),
+ VenueClaimBson.createRecord.userid(5678).status(ClaimStatus.approved)))
+ .lastClaim(VenueClaimBson.createRecord.userid(5678).status(ClaimStatus.approved))
+ }
+
+ def baseTestVenueClaim(vid: ObjectId): VenueClaim = {
+ VenueClaim.createRecord
+ .venueid(vid)
+ .userid(123)
+ .status(ClaimStatus.approved)
+ }
+
+ def baseTestTip(): Tip = {
+ Tip.createRecord
+ .legacyid(234)
+ .counts(Map("foo" -> 1L,
+ "bar" -> 2L))
+ }
+
+ @Before
+ def setupMongoConnection: Unit = {
+ RogueTestMongo.connectToMongo
+ }
+
+ @After
+ def cleanupTestData: Unit = {
+ Venue.bulkDelete_!!()
+ Venue.count() must_== 0
+
+ VenueClaim.bulkDelete_!!()
+ VenueClaim.count() must_== 0
+
+ RogueTestMongo.disconnectFromMongo
+ }
+
+ @Test
+ def eqsTests: Unit = {
+ val v = baseTestVenue().save
+ val vc = baseTestVenueClaim(v.id).save
+
+ // eqs
+ Venue.where(_._id eqs v.id).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor eqs v.mayor.value).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor eqs v.mayor.value).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.venuename eqs v.venuename.value).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.closed eqs false).fetch().map(_.id) must_== List(v.id)
+
+ Venue.where(_.mayor eqs 432432).fetch().map(_.id) must_== Nil
+ Venue.where(_.closed eqs true).fetch().map(_.id) must_== Nil
+
+ VenueClaim.where(_.status eqs ClaimStatus.approved).fetch().map(_.id) must_== List(vc.id)
+ VenueClaim.where(_.venueid eqs v.id).fetch().map(_.id) must_== List(vc.id)
+ VenueClaim.where(_.venueid eqs v).fetch().map(_.id) must_== List(vc.id)
+ }
+
+ @Test
+ def testInequalityQueries: Unit = {
+ val v = baseTestVenue().save
+ val vc = baseTestVenueClaim(v.id).save
+
+ // neq,lt,gt, where the lone Venue has mayor_count=3, and the only
+ // VenueClaim has status approved.
+ Venue.where(_.mayor_count neqs 5).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor_count < 5).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor_count lt 5).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor_count <= 5).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor_count lte 5).fetch().map(_.id) must_== List(v.id)
+ Venue.where(_.mayor_count > 5).fetch().map(_.id) must_== Nil
+ Venue.where(_.mayor_count gt 5).fetch().map(_.id) must_== Nil
+ Venue.where(_.mayor_count >= 5).fetch().map(_.id) must_== Nil
+ Venue.where(_.mayor_count gte 5).fetch().map(_.id) must_== Nil
+ Venue.where(_.mayor_count between (3, 5)).fetch().map(_.id) must_== List(v.id)
+ VenueClaim.where (_.status neqs ClaimStatus.approved).fetch().map(_.id) must_== Nil
+ VenueClaim.where (_.status neqs ClaimStatus.pending).fetch().map(_.id) must_== List(vc.id)
+ }
+
+ @Test
+ def selectQueries: Unit = {
+ val v = baseTestVenue().save
+
+ val base = Venue.where(_._id eqs v.id)
+ base.select(_.legacyid).fetch() must_== List(v.legacyid.value)
+ base.select(_.legacyid, _.userid).fetch() must_== List((v.legacyid.value, v.userid.value))
+ base.select(_.legacyid, _.userid, _.mayor).fetch() must_== List((v.legacyid.value, v.userid.value, v.mayor.value))
+ base.select(_.legacyid, _.userid, _.mayor, _.mayor_count).fetch() must_== List((v.legacyid.value, v.userid.value, v.mayor.value, v.mayor_count.value))
+ base.select(_.legacyid, _.userid, _.mayor, _.mayor_count, _.closed).fetch() must_== List((v.legacyid.value, v.userid.value, v.mayor.value, v.mayor_count.value, v.closed.value))
+ base.select(_.legacyid, _.userid, _.mayor, _.mayor_count, _.closed, _.tags).fetch() must_== List((v.legacyid.value, v.userid.value, v.mayor.value, v.mayor_count.value, v.closed.value, v.tags.value))
+ }
+
+ @Test
+ def selectEnum: Unit = {
+ val v = baseTestVenue().save
+ Venue.where(_._id eqs v.id).select(_.status).fetch() must_== List(VenueStatus.open)
+ }
+
+ @Test
+ def selectCaseQueries: Unit = {
+ val v = baseTestVenue().save
+
+ val base = Venue.where(_._id eqs v.id)
+ base.selectCase(_.legacyid, V1).fetch() must_== List(V1(v.legacyid.value))
+ base.selectCase(_.legacyid, _.userid, V2).fetch() must_== List(V2(v.legacyid.value, v.userid.value))
+ base.selectCase(_.legacyid, _.userid, _.mayor, V3).fetch() must_== List(V3(v.legacyid.value, v.userid.value, v.mayor.value))
+ base.selectCase(_.legacyid, _.userid, _.mayor, _.mayor_count, V4).fetch() must_== List(V4(v.legacyid.value, v.userid.value, v.mayor.value, v.mayor_count.value))
+ base.selectCase(_.legacyid, _.userid, _.mayor, _.mayor_count, _.closed, V5).fetch() must_== List(V5(v.legacyid.value, v.userid.value, v.mayor.value, v.mayor_count.value, v.closed.value))
+ base.selectCase(_.legacyid, _.userid, _.mayor, _.mayor_count, _.closed, _.tags, V6).fetch() must_== List(V6(v.legacyid.value, v.userid.value, v.mayor.value, v.mayor_count.value, v.closed.value, v.tags.value))
+ }
+
+ @Test
+ def selectSubfieldQueries: Unit = {
+ val v = baseTestVenue().save
+ val t = baseTestTip().save
+
+ // select subfields
+ Tip.where(_._id eqs t.id).select(_.counts at "foo").fetch() must_== List(Full(1L))
+
+ Venue.where(_._id eqs v.id).select(_.geolatlng.unsafeField[Double]("lat")).fetch() must_== List(Full(40.73))
+ }
+
+ @Ignore("These tests are broken because DummyField doesn't know how to convert a String to an Enum")
+ def testSelectEnumSubfield: Unit = {
+ val v = baseTestVenue().save
+
+ // This behavior is broken because we get a String back from mongo, and at
+ // that point we only have a DummyField for the subfield, and that doesn't
+ // know how to convert the String to an Enum.
+
+ val statuses: List[Box[VenueClaimBson.status.MyType]] =
+ Venue.where(_._id eqs v.id).select(_.lastClaim.subfield(_.status)) .fetch()
+ // This assertion works.
+ statuses must_== List(Full("Approved"))
+ // This assertion is what we want, and it fails.
+ // statuses must_== List(Full(ClaimStatus.approved))
+ }
+}
View
101 src/test/scala/com/foursquare/rogue/QueryTest.scala
@@ -14,107 +14,6 @@ import org.joda.time.{DateTime, DateTimeZone}
import org.junit._
import org.specs.SpecsMatchers
-/////////////////////////////////////////////////
-// Sample records for testing
-/////////////////////////////////////////////////
-object VenueStatus extends Enumeration {
- val open = Value("Open")
- val closed = Value("Closed")
-}
-
-class Venue extends MongoRecord[Venue] with MongoId[Venue] {
- def meta = Venue
- object legacyid extends LongField(this) { override def name = "legid" }
- object userid extends LongField(this)
- object venuename extends StringField(this, 255)
- object mayor extends LongField(this)
- object mayor_count extends LongField(this)
- object closed extends BooleanField(this)
- object tags extends MongoListField[Venue, String](this)
- object popularity extends MongoListField[Venue, Long](this)
- object categories extends MongoListField[Venue, ObjectId](this)
- object geolatlng extends MongoCaseClassField[Venue, LatLong](this) { override def name = "latlng" }
- object last_updated extends DateTimeField(this)
- object status extends EnumNameField(this, VenueStatus) { override def name = "status" }
- object claims extends BsonRecordListField(this, VenueClaimBson)
- object lastClaim extends BsonRecordField(this, VenueClaimBson)
-}
-object Venue extends Venue with MongoMetaRecord[Venue] {
- object CustomIndex extends IndexModifier("custom")
- val idIdx = Venue.index(_._id, Asc)
- val legIdx = Venue.index(_.legacyid, Desc)
- val geoIdx = Venue.index(_.geolatlng, TwoD)
- val geoCustomIdx = Venue.index(_.geolatlng, CustomIndex, _.tags, Asc)
-
- trait FK[T <: FK[T]] extends MongoRecord[T] {
- self: T=>
- object venueid extends ObjectIdField[T](this) with HasMongoForeignObjectId[Venue] {
- override def name = "vid"
- }
- }
-}
-
-object ClaimStatus extends Enumeration {
- val pending = Value("Pending approval")
- val approved = Value("Approved")
-}
-
-class VenueClaim extends MongoRecord[VenueClaim] with MongoId[VenueClaim] with Venue.FK[VenueClaim] {
- def meta = VenueClaim
- object userid extends LongField(this) { override def name = "uid" }
- object status extends EnumNameField(this, ClaimStatus)
-}
-object VenueClaim extends VenueClaim with MongoMetaRecord[VenueClaim] {
- override def fieldOrder = List(status, _id, userid, venueid)
-}
-
-class VenueClaimBson extends BsonRecord[VenueClaimBson] {
- def meta = VenueClaimBson
- object userid extends LongField(this) { override def name = "uid" }
- object status extends EnumNameField(this, ClaimStatus)
-}
-object VenueClaimBson extends VenueClaimBson with BsonMetaRecord[VenueClaimBson] {
- override def fieldOrder = List(status, userid)
-}
-
-
-case class OneComment(timestamp: String, userid: Long, comment: String)
-class Comment extends MongoRecord[Comment] with MongoId[Comment] {
- def meta = Comment
- object comments extends MongoCaseClassListField[Comment, OneComment](this)
-}
-object Comment extends Comment with MongoMetaRecord[Comment] {
- val idx1 = Comment.index(_._id, Asc)
-}
-
-class Tip extends MongoRecord[Tip] with MongoId[Tip] {
- def meta = Tip
- object legacyid extends LongField(this) { override def name = "legid" }
- object counts extends MongoMapField[Tip, Long](this)
-}
-object Tip extends Tip with MongoMetaRecord[Tip]
-
-object ConsumerPrivilege extends Enumeration {
- val awardBadges = Value("Award badges")
-}
-
-class OAuthConsumer extends MongoRecord[OAuthConsumer] with MongoId[OAuthConsumer] {
- def meta = OAuthConsumer
- object privileges extends MongoListField[OAuthConsumer, ConsumerPrivilege.Value](this)
-}
-object OAuthConsumer extends OAuthConsumer with MongoMetaRecord[OAuthConsumer]
-
-case class V1(legacyid: Long)
-case class V2(legacyid: Long, userid: Long)
-case class V3(legacyid: Long, userid: Long, mayor: Long)
-case class V4(legacyid: Long, userid: Long, mayor: Long, mayor_count: Long)
-case class V5(legacyid: Long, userid: Long, mayor: Long, mayor_count: Long, closed: Boolean)
-case class V6(legacyid: Long, userid: Long, mayor: Long, mayor_count: Long, closed: Boolean, tags: List[String])
-
-/////////////////////////////////////////////////
-// Actual tests
-/////////////////////////////////////////////////
-
class QueryTest extends SpecsMatchers {
@Test
View
143 src/test/scala/com/foursquare/rogue/TestModels.scala
@@ -0,0 +1,143 @@
+// Copyright 2011 Foursquare Labs Inc. All Rights Reserved.
+package com.foursquare.rogue
+
+import com.foursquare.rogue.Rogue._
+
+import com.mongodb.{Mongo, ServerAddress}
+import net.liftweb.mongodb.{MongoDB, MongoIdentifier}
+import net.liftweb.mongodb.record._
+import net.liftweb.mongodb.record.field._
+import net.liftweb.record.field._
+import net.liftweb.record._
+import org.bson.types.ObjectId
+
+/////////////////////////////////////////////////
+// Sample records for testing
+/////////////////////////////////////////////////
+
+object RogueTestMongo extends MongoIdentifier {
+ override def jndiName = "rogue_mongo"
+
+ private var mongo: Option[Mongo] = None
+
+ def connectToMongo = {
+ val MongoPort = 37648
+ mongo = Some(new Mongo(new ServerAddress("localhost", MongoPort)))
+ MongoDB.defineDb(RogueTestMongo, mongo.get, "rogue-test")
+ }
+
+ def disconnectFromMongo = {
+ mongo.foreach(_.close)
+ MongoDB.close
+ mongo = None
+ }
+}
+
+object VenueStatus extends Enumeration {
+ val open = Value("Open")
+ val closed = Value("Closed")
+}
+
+class Venue extends MongoRecord[Venue] with MongoId[Venue] {
+ def meta = Venue
+ object legacyid extends LongField(this) { override def name = "legid" }
+ object userid extends LongField(this)
+ object venuename extends StringField(this, 255)
+ object mayor extends LongField(this)
+ object mayor_count extends LongField(this)
+ object closed extends BooleanField(this)
+ object tags extends MongoListField[Venue, String](this)
+ object popularity extends MongoListField[Venue, Long](this)
+ object categories extends MongoListField[Venue, ObjectId](this)
+ object geolatlng extends MongoCaseClassField[Venue, LatLong](this) { override def name = "latlng" }
+ object last_updated extends DateTimeField(this)
+ object status extends EnumNameField(this, VenueStatus) { override def name = "status" }
+ object claims extends BsonRecordListField(this, VenueClaimBson)
+ object lastClaim extends BsonRecordField(this, VenueClaimBson)
+}
+object Venue extends Venue with MongoMetaRecord[Venue] {
+ override def collectionName = "venues"
+ override def mongoIdentifier = RogueTestMongo
+
+ object CustomIndex extends IndexModifier("custom")
+ val idIdx = Venue.index(_._id, Asc)
+ val legIdx = Venue.index(_.legacyid, Desc)
+ val geoIdx = Venue.index(_.geolatlng, TwoD)
+ val geoCustomIdx = Venue.index(_.geolatlng, CustomIndex, _.tags, Asc)
+
+ trait FK[T <: FK[T]] extends MongoRecord[T] {
+ self: T=>
+ object venueid extends ObjectIdField[T](this) with HasMongoForeignObjectId[Venue] {
+ override def name = "vid"
+ }
+ }
+}
+
+object ClaimStatus extends Enumeration {
+ val pending = Value("Pending approval")
+ val approved = Value("Approved")
+}
+
+class VenueClaim extends MongoRecord[VenueClaim] with MongoId[VenueClaim] with Venue.FK[VenueClaim] {
+ def meta = VenueClaim
+ object userid extends LongField(this) { override def name = "uid" }
+ object status extends EnumNameField(this, ClaimStatus)
+}
+object VenueClaim extends VenueClaim with MongoMetaRecord[VenueClaim] {
+ override def fieldOrder = List(status, _id, userid, venueid)
+ override def collectionName = "venueclaims"
+ override def mongoIdentifier = RogueTestMongo
+}
+
+class VenueClaimBson extends BsonRecord[VenueClaimBson] {
+ def meta = VenueClaimBson
+ object userid extends LongField(this) { override def name = "uid" }
+ object status extends EnumNameField(this, ClaimStatus)
+}
+object VenueClaimBson extends VenueClaimBson with BsonMetaRecord[VenueClaimBson] {
+ override def fieldOrder = List(status, userid)
+}
+
+
+case class OneComment(timestamp: String, userid: Long, comment: String)
+class Comment extends MongoRecord[Comment] with MongoId[Comment] {
+ def meta = Comment
+ object comments extends MongoCaseClassListField[Comment, OneComment](this)
+}
+object Comment extends Comment with MongoMetaRecord[Comment] {
+ override def collectionName = "comments"
+ override def mongoIdentifier = RogueTestMongo
+
+ val idx1 = Comment.index(_._id, Asc)
+}
+
+class Tip extends MongoRecord[Tip] with MongoId[Tip] {
+ def meta = Tip
+ object legacyid extends LongField(this) { override def name = "legid" }
+ object counts extends MongoMapField[Tip, Long](this)
+}
+object Tip extends Tip with MongoMetaRecord[Tip] {
+ override def collectionName = "tips"
+ override def mongoIdentifier = RogueTestMongo
+}
+
+object ConsumerPrivilege extends Enumeration {
+ val awardBadges = Value("Award badges")
+}
+
+class OAuthConsumer extends MongoRecord[OAuthConsumer] with MongoId[OAuthConsumer] {
+ def meta = OAuthConsumer
+ object privileges extends MongoListField[OAuthConsumer, ConsumerPrivilege.Value](this)
+}
+object OAuthConsumer extends OAuthConsumer with MongoMetaRecord[OAuthConsumer] {
+ override def collectionName = "oauthconsumers"
+ override def mongoIdentifier = RogueTestMongo
+}
+
+// Used for selectCase tests.
+case class V1(legacyid: Long)
+case class V2(legacyid: Long, userid: Long)
+case class V3(legacyid: Long, userid: Long, mayor: Long)
+case class V4(legacyid: Long, userid: Long, mayor: Long, mayor_count: Long)
+case class V5(legacyid: Long, userid: Long, mayor: Long, mayor_count: Long, closed: Boolean)
+case class V6(legacyid: Long, userid: Long, mayor: Long, mayor_count: Long, closed: Boolean, tags: List[String])
View
40 start-test-mongo.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+# Usage: ./start-test-mongo.sh
+
+# EROGU
+MONGO_PORT=37648
+
+MY_DIR=$(dirname $0)
+
+# download mongodb if necessary and place it inside the dependencies/ subdirectory.
+MONGO_DIR=osx
+MONGO_VERSION=1.8.4
+MONGO=mongodb-osx-x86_64-$MONGO_VERSION
+
+if [ $(uname) = 'Linux' ]; then
+ MONGO_DIR=linux
+ MONGO=mongodb-linux-x86_64-$MONGO_VERSION
+fi
+
+mkdir -p dependencies
+
+if [ ! -e dependencies/$MONGO ]; then
+ echo "Fetching MongoDB: $MONGO"
+ curl -# http://fastdl.mongodb.org/$MONGO_DIR/$MONGO.tgz | tar -xz -C dependencies/
+fi
+
+# ln -sf doesn't work on mac os x, so we need to rm and recreate.
+rm -f dependencies/mongodb
+ln -sf $MONGO dependencies/mongodb
+
+if ! [ -d mongo-testdb ]; then
+ echo "creating mongo-testdb directly for tests that use mongo"
+ mkdir mongo-testdb
+fi
+
+if ! ./dependencies/mongodb/bin/mongo --port $MONGO_PORT --eval "db.serverStatus()" 2>&1 > /dev/null; then
+ echo "automatically starting up local mongo on $MONGO_PORT so we can use it for tests"
+ ./dependencies/mongodb/bin/mongod --dbpath mongo-testdb --maxConns 800 --port $MONGO_PORT $@ 2>&1 | tee mongo.log
+else
+ echo "great, you have a local mongo running for tests already"
+fi
Please sign in to comment.
Something went wrong with that request. Please try again.