Skip to content

Commit

Permalink
Fixes #325: Add tests for findOneAndUpdate() with Projection (#326)
Browse files Browse the repository at this point in the history
  • Loading branch information
tatu-at-datastax authored Mar 31, 2023
1 parent ea14bd1 commit 0056503
Show file tree
Hide file tree
Showing 3 changed files with 297 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
* various {@code find} commands.
*/
public class DocumentProjector {
/** Pseudo-projector that makes no modifications to documents */
private static final DocumentProjector IDENTITY_PROJECTOR = new DocumentProjector(null, true);
/**
* No-op projector that does not modify documents. Considered "exclusion" projector since "no
* exclusions" is conceptually what happens ("no inclusions" would drop all content)
*/
private static final DocumentProjector IDENTITY_PROJECTOR = new DocumentProjector(null, false);

private final ProjectionLayer rootLayer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,27 +140,15 @@ public void emptyOptionsAllowed() {

@Test
public void byIdReturnDocumentAfter() {
String document =
insertDoc(
"""
{
"_id": "afterDoc3",
"username": "afterUser3",
"active_user" : true
}
""";
insertDoc(document);

String json =
"""
{
"findOneAndUpdate": {
"filter" : {"_id" : "afterDoc3"},
"update" : {"$set" : {"active_user": false}},
"options" : {"returnDocument" : "after"}
}
}
""";
String expected =
""");
final String expected =
"""
{
"_id":"afterDoc3",
Expand All @@ -171,7 +159,16 @@ public void byIdReturnDocumentAfter() {
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(json)
.body(
"""
{
"findOneAndUpdate": {
"filter" : {"_id" : "afterDoc3"},
"update" : {"$set" : {"active_user": false}},
"options" : {"returnDocument" : "after"}
}
}
""")
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
Expand All @@ -182,23 +179,88 @@ public void byIdReturnDocumentAfter() {
.body("errors", is(nullValue()));

// assert state after update
json =
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(
"""
{
"find": {
"filter" : {"_id" : "afterDoc3"}
}
}
""")
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("errors", is(nullValue()))
.body("data.docs[0]", jsonEquals(expected));
}

@Test
public void byIdReturnDocumentBefore() {
final String docBefore =
"""
{
"find": {
"filter" : {"_id" : "afterDoc3"}
}
"_id": "beforeDoc3",
"username": "beforeUser3",
"active_user": true
}
""";
insertDoc(docBefore);
final String docAfter =
"""
{
"_id":"beforeDoc3",
"username":"beforeUser3",
"active_user":false,
"hits": 1
}
""";
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(json)
.body(
"""
{
"findOneAndUpdate": {
"filter" : {"_id" : "beforeDoc3"},
"update" : {
"$set" : {"active_user": false},
"$inc" : {"hits": 1}
},
"options" : {"returnDocument" : "before"}
}
}
""")
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("data.docs[0]", jsonEquals(expected));
.body("data.docs[0]", jsonEquals(docBefore))
.body("status.matchedCount", is(1))
.body("status.modifiedCount", is(1))
.body("errors", is(nullValue()));

// assert state after update
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(
"""
{
"find": {
"filter" : {"_id" : "beforeDoc3"}
}
}
""")
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("errors", is(nullValue()))
.body("data.docs[0]", jsonEquals(docAfter));
}

@Test
Expand Down Expand Up @@ -1156,6 +1218,190 @@ public void byIdUpsertAndAddOnInsert() {
}
}

@Nested
class FindOneAndUpdateWithProjection {
@Test
public void projectionAfterUpdate() {
String document =
"""
{
"_id": "update_doc_projection_after",
"x": 1,
"y": 2,
"z": 3,
"subdoc": {
"a": 4,
"b": 5,
"c": 6
}
}
""";
insertDoc(document);

String updateQuery =
"""
{
"findOneAndUpdate": {
"filter" : {"_id" : "update_doc_projection_after"},
"options" : {"returnDocument" : "after"},
"projection" : { "x":0, "subdoc.c":0 },
"update" : {
"$unset" : {
"subdoc.a": 1,
"z": 1
}
}
}
}
""";
// assert that returned document shows doc AFTER update WITH given projection
String expectedFiltered =
"""
{
"_id": "update_doc_projection_after",
"y": 2,
"subdoc": {
"b": 5
}
}
""";
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(updateQuery)
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("status.matchedCount", is(1))
.body("status.modifiedCount", is(1))
.body("errors", is(nullValue()))
.body("data.docs[0]", jsonEquals(expectedFiltered));

// But also that update itself worked ($unset "z" and "subdoc.a")
String expectedUpdated =
"""
{
"_id": "update_doc_projection_after",
"x": 1,
"y": 2,
"subdoc": {
"b": 5,
"c": 6
}
}
""";

given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(
"""
{
"find": {
"filter" : {"_id" : "update_doc_projection_after"}
}
}
""")
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("data.docs[0]", jsonEquals(expectedUpdated));
}

@Test
public void projectionBeforeUpdate() {
String document =
"""
{
"_id": "update_doc_projection_before",
"a": 1,
"b": 2,
"c": 3,
"subdoc": {
"x": 4,
"y": 5,
"z": 6
}
}
""";
insertDoc(document);

String updateQuery =
"""
{
"findOneAndUpdate": {
"filter" : {"_id" : "update_doc_projection_before"},
"options" : {"returnDocument" : "before"},
"projection" : { "a":0, "subdoc.z":0 },
"update" : {
"$unset" : {
"subdoc.x": 1,
"c": 1
}
}
}
}
""";
// assert state before update, with given projection (so unsets not visible)
String expectedFiltered =
"""
{
"_id": "update_doc_projection_before",
"b": 2,
"c": 3,
"subdoc": {
"x": 4,
"y": 5
}
}
""";
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(updateQuery)
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("status.matchedCount", is(1))
.body("status.modifiedCount", is(1))
.body("errors", is(nullValue()))
.body("data.docs[0]", jsonEquals(expectedFiltered));

// And with updates $unset of c and subdoc.x, but no Projection
String expectedUpdated =
"""
{
"_id": "update_doc_projection_before",
"a": 1,
"b": 2,
"subdoc": {
"y": 5,
"z": 6
}
}
""";
given()
.header(HttpConstants.AUTHENTICATION_TOKEN_HEADER_NAME, getAuthToken())
.contentType(ContentType.JSON)
.body(
"""
{
"find": {
"filter" : {"_id" : "update_doc_projection_before"}
}
}
""")
.when()
.post(CollectionResource.BASE_PATH, keyspaceId.asInternal(), collectionName)
.then()
.statusCode(200)
.body("data.docs[0]", jsonEquals(expectedUpdated));
}
}

@AfterEach
public void cleanUpData() {
deleteAllDocuments();
Expand Down
Loading

0 comments on commit 0056503

Please sign in to comment.