Skip to content

Commit 4bfc369

Browse files
authored
Merge pull request #15142 from codeconsole/7.0.x-mongo-pre-update-dirty-fix-tests
Test Demonstrating Mongo beforeUpdate changes are not persisted
2 parents 751f49c + 96487c3 commit 4bfc369

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.grails.datastore.gorm.mongo
20+
21+
import spock.lang.PendingFeature
22+
23+
import grails.persistence.Entity
24+
import org.apache.grails.data.mongo.core.GrailsDataMongoTckManager
25+
import org.apache.grails.data.testing.tck.base.GrailsDataTckSpec
26+
import org.bson.types.ObjectId
27+
import spock.lang.Issue
28+
29+
/**
30+
* Tests that properties set in beforeUpdate() are actually persisted to MongoDB.
31+
* This specifically tests the scenario where a property is set in beforeUpdate()
32+
* but was NOT explicitly modified by the user code.
33+
*/
34+
class BeforeUpdatePropertyPersistenceSpec extends GrailsDataTckSpec<GrailsDataMongoTckManager> {
35+
36+
void setupSpec() {
37+
manager.domainClasses.addAll([UserWithBeforeUpdate, UserWithBeforeUpdateAndAutoTimestamp])
38+
}
39+
40+
@PendingFeature
41+
void "Test that properties set in beforeUpdate are persisted"() {
42+
given: "A user is created"
43+
def user = new UserWithBeforeUpdate(name: "Fred")
44+
def saved = user.save(flush: true)
45+
46+
expect: "The user was saved successfully"
47+
saved != null
48+
user.id != null
49+
user.errors.allErrors.size() == 0
50+
51+
when: "The session is cleared and user is retrieved"
52+
manager.session.clear()
53+
user = UserWithBeforeUpdate.get(user.id)
54+
55+
then: "beforeInsert was called and random was set"
56+
user != null
57+
user.name == "Fred"
58+
user.random == "Not Updated"
59+
60+
when: "The user's name is updated (but random is not explicitly modified)"
61+
def previousRandom = user.random
62+
user.name = "Bob"
63+
user.save(flush: true)
64+
manager.session.clear()
65+
user = UserWithBeforeUpdate.get(user.id)
66+
67+
then: "beforeUpdate was called and random was changed and persisted"
68+
user != null
69+
user.name == "Bob"
70+
user.random != previousRandom
71+
user.random != "Not Updated"
72+
user.random.length() == 5 // UUID substring [0..4]
73+
}
74+
75+
void "Test that beforeUpdate is called even when no properties are explicitly modified"() {
76+
given: "A user is created"
77+
def user = new UserWithBeforeUpdate(name: "Fred")
78+
user.save(flush: true)
79+
manager.session.clear()
80+
user = UserWithBeforeUpdate.get(user.id)
81+
def previousRandom = user.random
82+
83+
when: "The user is saved without any explicit property changes"
84+
user.save(flush: true)
85+
manager.session.clear()
86+
user = UserWithBeforeUpdate.get(user.id)
87+
88+
then: "beforeUpdate was called and random was updated"
89+
user != null
90+
user.random != previousRandom
91+
user.random != "Not Updated"
92+
user.random.length() == 5
93+
}
94+
95+
@PendingFeature
96+
void "Test that multiple updates continue to trigger beforeUpdate"() {
97+
given: "A user is created"
98+
def user = new UserWithBeforeUpdate(name: "Fred")
99+
user.save(flush: true)
100+
manager.session.clear()
101+
102+
when: "The user is updated multiple times"
103+
def randomValues = []
104+
3.times {
105+
user = UserWithBeforeUpdate.get(user.id)
106+
randomValues << user.random
107+
user.name = "Name${it}"
108+
user.save(flush: true)
109+
manager.session.clear()
110+
}
111+
user = UserWithBeforeUpdate.get(user.id)
112+
113+
then: "Each update generated a new random value"
114+
randomValues.size() == 3
115+
randomValues[0] == "Not Updated" // from beforeInsert
116+
randomValues[1] != "Not Updated" // first update
117+
randomValues[2] != randomValues[1] // second update
118+
user.random != randomValues[2] // third update
119+
user.random.length() == 5
120+
}
121+
122+
@PendingFeature
123+
@Issue('GPMONGODB-XXX')
124+
void "Test that properties set in beforeUpdate with AutoTimestamp are persisted"() {
125+
given: "A user with auto timestamp is created"
126+
def user = new UserWithBeforeUpdateAndAutoTimestamp(name: "Fred")
127+
user.save(flush: true)
128+
manager.session.clear()
129+
130+
when: "The user is retrieved"
131+
user = UserWithBeforeUpdateAndAutoTimestamp.get(user.id)
132+
133+
then: "beforeInsert was called and random was set"
134+
user != null
135+
user.name == "Fred"
136+
user.random == "Not Updated"
137+
user.dateCreated != null
138+
user.lastUpdated != null
139+
140+
when: "The user's name is updated"
141+
sleep 100 // ensure lastUpdated differs
142+
def previousRandom = user.random
143+
def previousLastUpdated = user.lastUpdated
144+
user.name = "Bob"
145+
user.save(flush: true)
146+
manager.session.clear()
147+
user = UserWithBeforeUpdateAndAutoTimestamp.get(user.id)
148+
149+
then: "beforeUpdate was called, random was changed, and lastUpdated was updated"
150+
user != null
151+
user.name == "Bob"
152+
user.random != previousRandom
153+
user.random != "Not Updated"
154+
user.random.length() == 5
155+
user.lastUpdated > previousLastUpdated
156+
}
157+
}
158+
159+
@Entity
160+
class UserWithBeforeUpdate {
161+
162+
ObjectId id
163+
String name
164+
String random
165+
166+
static constraints = {
167+
random nullable: true
168+
}
169+
170+
def beforeInsert() {
171+
random = "Not Updated"
172+
}
173+
174+
def beforeUpdate() {
175+
random = UUID.randomUUID().toString()[0..4]
176+
}
177+
}
178+
179+
@Entity
180+
class UserWithBeforeUpdateAndAutoTimestamp {
181+
182+
ObjectId id
183+
String name
184+
String random
185+
Date dateCreated
186+
Date lastUpdated
187+
188+
static constraints = {
189+
random nullable: true
190+
}
191+
192+
def beforeInsert() {
193+
random = "Not Updated"
194+
}
195+
196+
def beforeUpdate() {
197+
random = UUID.randomUUID().toString()[0..4]
198+
}
199+
}

0 commit comments

Comments
 (0)