Search code examples
grailsgrails-orm

Issue with integration testing "composite unique constraint" in Grails/GORM?


I am trying to make sure a user can vote a given project only once (no duplicate voting allowed). After some search, looks like there is a composite unique constraint to achieve this. However, I am having some issue in the integration testing. My domain classes are as follows.

package grailstuts

class User {
    String name
    static hasMany = [ projects: Project, votes: Vote ]

    static constraints = { name(nullable: true) }

    static mapping = { projects cascade: "all-delete-orphan" }
}

.

package grailstuts

class Project {
    String title
    Set votes = []
    static belongsTo = [ user: User ]
    static hasMany = [ votes: Vote ]

    static constraints = { title(nullable: true) }
}

.

package grailstuts

class Vote {
    static belongsTo = [ user: User, project: Project ]

    static constraints = { user(unique: 'project') }
}

Please note the unique constraint in the Vote class. Then my integration test looks like this:

package grailstuts

import spock.lang.*

class VoteIntegrationSpec extends Specification {

    void "Testing uniqueness"() {

        given: "A user and a project"
        def user = new User();
        def project = new Project();
        user.addToProjects(project)
        user.save(failOnError: true)

        when: "The user votes the project"
        def vote = new Vote()
        user.addToVotes(vote)
        project.addToVotes(vote)

        then: "The user should not be able to vote the same project again"
        def vote2 = new Vote()
        user.addToVotes(vote2)
        project.addToVotes(vote2)
    }
}

This integration test passes without any error. But I was thinking this test should fail, because the user is voting again for the same project (in the then: block), which should not be possible based on the static constraints = { user(unique: 'project') } in the Vote class. Cannot seem to figure this out, can anyone please point out what I am doing wrong?

Thank you for your help.


Solution

  • The reason for this behavior is that the test is not validated.

    Since voting is per user and per project (My assumption) as you said you need a composite uniqueness:

    package grailstuts
    class Vote {
        static belongsTo = [ user: User, project: Project ]
        static constraints = { 
               user unique: 'project'
        }
    }
    

    Your spec would be:

    void "Testing uniqueness"() {
    
        given: "A user and a project"
            def user = new User()
            def project = new Project()
            user.addToProjects(project)
            user.save(failOnError: true)
    
        when: "The user votes the project"      
            def vote = new Vote(user:user,project:project).save()
    
        then: "The user should not be able to vote the same project again"      
           // def vote2 = new Vote(user:user,project:project).save(flush:true,failOnError:true)
           // or 
           new Vote(user:user,project:project).validate() == false
        }