1package org.codehaus.groovy.grails.orm.hibernate 2 3import org.codehaus.groovy.grails.commons.* 4import grails.validation.ValidationException 5import org.codehaus.groovy.grails.orm.hibernate.support.ClosureEventTriggeringInterceptor 6 7class SavePersistentMethodTests extends AbstractGrailsHibernateTests { 8 9 void testFlush() { 10 def bookClass = ga.getDomainClass("grails.tests.SaveBook") 11 def authorClass = ga.getDomainClass("grails.tests.SaveAuthor") 12 def addressClass = ga.getDomainClass("grails.tests.SaveAddress") 13 14 def book = bookClass.newInstance() 15 book.title = "Foo" 16 def author = authorClass.newInstance() 17 book.author = author 18 author.name = "Bar" 19 def address = addressClass.newInstance() 20 author.address = address 21 address.location = "Foo Bar" 22 assertNotNull author.save() 23 24 assertNotNull book.save(flush:true) 25 assertNotNull book.id 26 } 27 28 void testToOneCascadingValidation() { 29 def bookClass = ga.getDomainClass("grails.tests.SaveBook") 30 def authorClass = ga.getDomainClass("grails.tests.SaveAuthor") 31 def addressClass = ga.getDomainClass("grails.tests.SaveAddress") 32 33 def book = bookClass.newInstance() 34 35 assertNull book.save() 36 assertNull book.save(deepValidate:false) 37 38 book.title = "Foo" 39 40 assertNotNull book.save() 41 42 def author = authorClass.newInstance() 43 author.name = "Bar" 44 author.save() 45 46 book.author = author 47 48 // will validate book is owned by author 49 assertNotNull book.save() 50 assertNotNull book.save(deepValidate:false) 51 52 def address = addressClass.newInstance() 53 54 author.address = address 55 56 assertNull author.save() 57 58 address.location = "Foo Bar" 59 assertNotNull author.save() 60 assertNotNull author.save(deepValidate:false) 61 } 62 63 void testToManyCascadingValidation() { 64 def bookClass = ga.getDomainClass("grails.tests.SaveBook") 65 def authorClass = ga.getDomainClass("grails.tests.SaveAuthor") 66 def addressClass = ga.getDomainClass("grails.tests.SaveAddress") 67 68 def author = authorClass.newInstance() 69 70 assertNull author.save() 71 author.name = "Foo" 72 73 assertNotNull author.save() 74 75 def address = addressClass.newInstance() 76 author.address = address 77 78 assertNull author.save() 79 80 address.location = "Foo Bar" 81 assertNotNull author.save() 82 83 def book = bookClass.newInstance() 84 85 author.addToBooks(book) 86 assertNull author.save() 87 88 book.title = "TDGTG" 89 assertNotNull author.save() 90 assertNotNull author.save(deepValidate:false) 91 } 92 93 void testValidationAfterBindingErrors() { 94 def teamClass = ga.getDomainClass('grails.tests.Team') 95 def team = teamClass.newInstance() 96 team.properties = [homePage: 'invalidurl'] 97 assertNull 'validation should have failed', team.save() 98 assertEquals 'wrong number of errors found', 2, team.errors.errorCount 99 assertEquals 'wrong number of homePage errors found', 1, team.errors.getFieldErrors('homePage')?.size() 100 def homePageError = team.errors.getFieldError('homePage') 101 assertTrue 'did not find typeMismatch error', 'typeMismatch' in homePageError.codes 102 103 team.homePage = new URL('http://grails.org') 104 assertNull 'validation should have failed', team.save() 105 assertEquals 'wrong number of errors found', 1, team.errors.errorCount 106 assertEquals 'wrong number of homePage errors found', 0, team.errors.getFieldErrors('homePage')?.size() 107 } 108 109 void testFailOnErrorTrueWithValidationErrors() { 110 def teamClass = ga.getDomainClass('grails.tests.Team') 111 def team = teamClass.newInstance() 112 team.properties = [homePage: 'invalidurl'] 113 def msg = shouldFail(ValidationException) { 114 team.save(failOnError: true) 115 } 116 117 // test errors object 118 try { 119 team.save(failOnError:true) 120 } 121 catch (ValidationException e) { 122 assertNotNull "should have a reference to the errors object", e.errors 123 } 124 assertEquals '''\ 125Validation Error(s) occurred during save(): 126- Field error in object 'grails.tests.Team' on field 'homePage': rejected value [null]; codes [typeMismatch.grails.tests.Team.homePage,typeMismatch.homePage,typeMismatch.java.net.URL,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [grails.tests.Team.homePage,homePage]; arguments []; default message [homePage]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.net.URL' for property 'homePage'; nested exception is java.lang.IllegalArgumentException: Could not retrieve URL for class path resource [invalidurl]: class path resource [invalidurl] cannot be resolved to URL because it does not exist] 127- Field error in object 'grails.tests.Team' on field 'name': rejected value [null]; codes [grails.tests.Team.name.nullable.error.grails.tests.Team.name,grails.tests.Team.name.nullable.error.name,grails.tests.Team.name.nullable.error.java.lang.String,grails.tests.Team.name.nullable.error,team.name.nullable.error.grails.tests.Team.name,team.name.nullable.error.name,team.name.nullable.error.java.lang.String,team.name.nullable.error,grails.tests.Team.name.nullable.grails.tests.Team.name,grails.tests.Team.name.nullable.name,grails.tests.Team.name.nullable.java.lang.String,grails.tests.Team.name.nullable,team.name.nullable.grails.tests.Team.name,team.name.nullable.name,team.name.nullable.java.lang.String,team.name.nullable,nullable.grails.tests.Team.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class grails.tests.Team]; default message [Property [{0}] of class [{1}] cannot be null] 128''', msg 129 } 130 131 void testFailOnErrorFalseWithValidationErrors() { 132 def teamClass = ga.getDomainClass('grails.tests.Team') 133 def team = teamClass.newInstance() 134 team.properties = [homePage: 'invalidurl'] 135 assertNull 'save should have returned null', team.save(failOnError: false) 136 } 137 138 void testFailOnErrorConfigTrueWithValidationErrors() { 139 def config = new ConfigSlurper().parse("grails.gorm.failOnError = true") 140 141 ConfigurationHolder.config = config 142 def teamClass = ga.getDomainClass('grails.tests.Team') 143 def team = teamClass.newInstance() 144 team.properties = [homePage: 'invalidurl'] 145 def msg = shouldFail(ValidationException) { 146 team.save() 147 } 148 assertEquals '''\ 149Validation Error(s) occurred during save(): 150- Field error in object 'grails.tests.Team' on field 'homePage': rejected value [null]; codes [typeMismatch.grails.tests.Team.homePage,typeMismatch.homePage,typeMismatch.java.net.URL,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [grails.tests.Team.homePage,homePage]; arguments []; default message [homePage]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.net.URL' for property 'homePage'; nested exception is java.lang.IllegalArgumentException: Could not retrieve URL for class path resource [invalidurl]: class path resource [invalidurl] cannot be resolved to URL because it does not exist] 151- Field error in object 'grails.tests.Team' on field 'name': rejected value [null]; codes [grails.tests.Team.name.nullable.error.grails.tests.Team.name,grails.tests.Team.name.nullable.error.name,grails.tests.Team.name.nullable.error.java.lang.String,grails.tests.Team.name.nullable.error,team.name.nullable.error.grails.tests.Team.name,team.name.nullable.error.name,team.name.nullable.error.java.lang.String,team.name.nullable.error,grails.tests.Team.name.nullable.grails.tests.Team.name,grails.tests.Team.name.nullable.name,grails.tests.Team.name.nullable.java.lang.String,grails.tests.Team.name.nullable,team.name.nullable.grails.tests.Team.name,team.name.nullable.name,team.name.nullable.java.lang.String,team.name.nullable,nullable.grails.tests.Team.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class grails.tests.Team]; default message [Property [{0}] of class [{1}] cannot be null] 152''', msg 153 } 154 155 void testFailOnErrorConfigTrueWithValidationErrorsAndAutoFlush() { 156 def interceptor = appCtx.getBean("eventTriggeringInterceptor") 157 interceptor.failOnError=true 158 def teamClass = ga.getDomainClass('grails.tests.Team').clazz 159 def team = teamClass.newInstance(name:"Manchester United", homePage:new URL("http://www.manutd.com/")) 160 assertNotNull "should have saved", team.save(flush:true) 161 162 session.clear() 163 164 team = teamClass.get(1) 165 team.name = '' 166 shouldFail(ValidationException) { 167 session.flush() 168 } 169 } 170 171 void testFailOnErrorConfigWithPackagesAndAutoFlush() { 172 ClosureEventTriggeringInterceptor interceptor = appCtx.getBean("eventTriggeringInterceptor") 173 interceptor.failOnError=true 174 interceptor.failOnErrorPackages = ['foo.bar'] 175 def teamClass = ga.getDomainClass('grails.tests.Team').clazz 176 def team = teamClass.newInstance(name:"Manchester United", homePage:new URL("http://www.manutd.com/")) 177 assertNotNull "should have saved", team.save(flush:true) 178 179 session.clear() 180 181 team = teamClass.get(1) 182 team.name = '' 183 // should not throw exception for the grails.test package 184 session.flush() 185 186 interceptor.failOnErrorPackages = ['grails.tests'] 187 interceptor.eventListeners.clear() 188 189 session.clear() 190 team = teamClass.get(1) 191 team.name = '' 192 // should not throw exception for the grails.test package 193 shouldFail(ValidationException) { 194 session.flush() 195 } 196 } 197 198 void testFailOnErrorConfigIncludesMatchingPackageWithValidationErrors() { 199 def config = new ConfigSlurper().parse("grails.gorm.failOnError = ['com.foo', 'grails.tests', 'com.bar']") 200 201 ConfigurationHolder.config = config 202 def teamClass = ga.getDomainClass('grails.tests.Team') 203 def team = teamClass.newInstance() 204 team.properties = [homePage: 'invalidurl'] 205 def msg = shouldFail(ValidationException) { 206 team.save() 207 } 208 assertEquals '''\ 209Validation Error(s) occurred during save(): 210- Field error in object 'grails.tests.Team' on field 'homePage': rejected value [null]; codes [typeMismatch.grails.tests.Team.homePage,typeMismatch.homePage,typeMismatch.java.net.URL,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [grails.tests.Team.homePage,homePage]; arguments []; default message [homePage]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.net.URL' for property 'homePage'; nested exception is java.lang.IllegalArgumentException: Could not retrieve URL for class path resource [invalidurl]: class path resource [invalidurl] cannot be resolved to URL because it does not exist] 211- Field error in object 'grails.tests.Team' on field 'name': rejected value [null]; codes [grails.tests.Team.name.nullable.error.grails.tests.Team.name,grails.tests.Team.name.nullable.error.name,grails.tests.Team.name.nullable.error.java.lang.String,grails.tests.Team.name.nullable.error,team.name.nullable.error.grails.tests.Team.name,team.name.nullable.error.name,team.name.nullable.error.java.lang.String,team.name.nullable.error,grails.tests.Team.name.nullable.grails.tests.Team.name,grails.tests.Team.name.nullable.name,grails.tests.Team.name.nullable.java.lang.String,grails.tests.Team.name.nullable,team.name.nullable.grails.tests.Team.name,team.name.nullable.name,team.name.nullable.java.lang.String,team.name.nullable,nullable.grails.tests.Team.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class grails.tests.Team]; default message [Property [{0}] of class [{1}] cannot be null] 212''', msg 213 } 214 215 void testFailOnErrorConfigDoesNotIncludeMatchingPackageWithValidationErrors() { 216 def config = new ConfigSlurper().parse("grails.gorm.failOnError = ['com.foo', 'com.bar']") 217 218 ConfigurationHolder.config = config 219 def teamClass = ga.getDomainClass('grails.tests.Team') 220 def team = teamClass.newInstance() 221 team.properties = [homePage: 'invalidurl'] 222 assertNull 'save should have returned null', team.save() 223 } 224 225 void testFailOnErrorConfigFalseWithValidationErrors() { 226 def config = new ConfigSlurper().parse("grails.gorm.failOnError = false") 227 228 ConfigurationHolder.config = config 229 def teamClass = ga.getDomainClass('grails.tests.Team') 230 def team = teamClass.newInstance() 231 team.properties = [homePage: 'invalidurl'] 232 assertNull 'save should have returned null', team.save() 233 } 234 235 void testFailOnErrorConfigTrueArgumentFalseWithValidationErrors() { 236 def config = new ConfigSlurper().parse("grails.gorm.failOnError = true") 237 238 ConfigurationHolder.config = config 239 def teamClass = ga.getDomainClass('grails.tests.Team') 240 def team = teamClass.newInstance() 241 team.properties = [homePage: 'invalidurl'] 242 assertNull 'save should have returned null', team.save(failOnError: false) 243 } 244 245 void testFailOnErrorConfigFalseArgumentTrueWithValidationErrors() { 246 def config = new ConfigSlurper().parse("grails.gorm.failOnError = false") 247 248 ConfigurationHolder.config = config 249 def teamClass = ga.getDomainClass('grails.tests.Team') 250 def team = teamClass.newInstance() 251 team.properties = [homePage: 'invalidurl'] 252 def msg = shouldFail(ValidationException) { 253 team.save(failOnError: true) 254 } 255 assertEquals '''\ 256Validation Error(s) occurred during save(): 257- Field error in object 'grails.tests.Team' on field 'homePage': rejected value [null]; codes [typeMismatch.grails.tests.Team.homePage,typeMismatch.homePage,typeMismatch.java.net.URL,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [grails.tests.Team.homePage,homePage]; arguments []; default message [homePage]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.net.URL' for property 'homePage'; nested exception is java.lang.IllegalArgumentException: Could not retrieve URL for class path resource [invalidurl]: class path resource [invalidurl] cannot be resolved to URL because it does not exist] 258- Field error in object 'grails.tests.Team' on field 'name': rejected value [null]; codes [grails.tests.Team.name.nullable.error.grails.tests.Team.name,grails.tests.Team.name.nullable.error.name,grails.tests.Team.name.nullable.error.java.lang.String,grails.tests.Team.name.nullable.error,team.name.nullable.error.grails.tests.Team.name,team.name.nullable.error.name,team.name.nullable.error.java.lang.String,team.name.nullable.error,grails.tests.Team.name.nullable.grails.tests.Team.name,grails.tests.Team.name.nullable.name,grails.tests.Team.name.nullable.java.lang.String,grails.tests.Team.name.nullable,team.name.nullable.grails.tests.Team.name,team.name.nullable.name,team.name.nullable.java.lang.String,team.name.nullable,nullable.grails.tests.Team.name,nullable.name,nullable.java.lang.String,nullable]; arguments [name,class grails.tests.Team]; default message [Property [{0}] of class [{1}] cannot be null] 259''', msg 260 } 261 262 void testSaveWithoutValidation() { 263 def dcClass = ga.getDomainClass('grails.tests.SaveCustomValidation') 264 def dc = dcClass.newInstance() 265 dc.properties = [ title: 'Test' ] 266 267 // The custom validator for SaveCustomValidation throws an 268 // exception if it's triggered, but that shouldn't happen 269 // if we explicitly disable validation. 270 dc.save(validate: false) 271 session.flush() 272 273 // Once its attached to the session, dirty checking applies. 274 // Here we test that the validation doesn't occur even though 275 // the domain instance has been modified. 276 dc.title = "A different title" 277 dc.save(validate: false) 278 session.flush() 279 280 // Let's check that the validation kicks in if we don't disable it... 281 dc.title = "Another title" 282 shouldFail(IllegalStateException) { 283 dc.save() 284 } 285 286 // ...and make sure that this also happens with dirty-checking. 287 dc.title = "Dirty check" 288 shouldFail(IllegalStateException) { 289 session.flush() 290 } 291 } 292 293 void onSetUp() { 294 gcl.parseClass ''' 295package grails.tests 296 297import grails.persistence.* 298 299@Entity 300class Team { 301 String name 302 URL homePage 303 304 static constraints = { 305 name blank:false 306 } 307} 308 309@Entity 310class SaveBook { 311 String title 312 SaveAuthor author 313 static belongsTo = SaveAuthor 314 static constraints = { 315 title(blank:false, size:1..255) 316 author(nullable:true) 317 } 318} 319 320@Entity 321class SaveAuthor { 322 String name 323 SaveAddress address 324 static hasMany = [books:SaveBook] 325 static constraints = { 326 address(nullable:true) 327 name(size:1..255, blank:false) 328 } 329} 330 331@Entity 332class SaveAddress { 333 SaveAuthor author 334 String location 335 static belongsTo = SaveAuthor 336 static constraints = { 337 author(nullable:true) 338 location(blank:false) 339 } 340} 341 342@Entity 343class SaveCustomValidation { 344 String title 345 346 static constraints = { 347 title(validator: { val, obj -> throw new IllegalStateException() }) 348 } 349} 350''' 351 } 352 353 void onTearDown() { 354 ConfigurationHolder.config = null 355 } 356} 357