package net.sourceforge.stripes.validation; import net.sourceforge.stripes.FilterEnabledTestBase; import net.sourceforge.stripes.action.ActionBean; import net.sourceforge.stripes.action.ActionBeanContext; import net.sourceforge.stripes.action.DefaultHandler; import net.sourceforge.stripes.action.HandlesEvent; import net.sourceforge.stripes.action.Resolution; import net.sourceforge.stripes.action.UrlBinding; import net.sourceforge.stripes.mock.MockRoundtrip; import net.sourceforge.stripes.StripesTestFixture; import net.sourceforge.stripes.mock.MockServletContext; import org.testng.annotations.AfterClass; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; import org.testng.Assert; /** * Test out various aspects of the validation subsystem in Stripes with regard to optional * application, ordering and flow control. Each of the individual test methods has javadoc * explaining why the expected results are as they are. * * @author Tim Fennell */ @UrlBinding("/test/ValidationFlowTest.action") public class ValidationFlowTest extends FilterEnabledTestBase implements ActionBean { int counter=1, validateAlwaysRan, validateOneRan, validateTwoRan; private ActionBeanContext context; public ActionBeanContext getContext() { return context; } public void setContext(ActionBeanContext context) { this.context = context;} @Validate(required=true) private int numberZero; public int getNumberZero() { return numberZero; } public void setNumberZero(int numberZero) { this.numberZero = numberZero; } @Validate(required=true, on="eventOne", minvalue=0) private int numberOne; public int getNumberOne() { return numberOne; } public void setNumberOne(int numberOne) { this.numberOne = numberOne; } @Validate(required=true,on="eventTwo") private int numberTwo; public int getNumberTwo() { return numberTwo; } public void setNumberTwo(int numberTwo) { this.numberTwo = numberTwo; } @ValidationMethod(priority=0) public void validateAlways(ValidationErrors errors) { if (errors == null) throw new RuntimeException("errors must not be null"); validateAlwaysRan = counter++; } @ValidationMethod(priority=1, on="eventOne", when=ValidationState.NO_ERRORS) public void validateOne() { validateOneRan = counter++; } @ValidationMethod(priority=1, on={"!eventZero", "!eventOne"}, when=ValidationState.ALWAYS) public void validateTwo(ValidationErrors errors) { validateTwoRan = counter++; } @HandlesEvent("eventZero") public Resolution zero() { return null; } @DefaultHandler @HandlesEvent("eventOne") public Resolution one() { return null; } @HandlesEvent("eventTwo") public Resolution two() { return null; } // Test methods begin here! /** * numberZero is the only required field for eventZero, so there should be no validation * errors generated. The only validation method that should be run is validateAlways() because * the others are tied to specific events. */ @Test(groups="fast") public void testEventZeroNoErrors() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", "99"); trip.execute("eventZero"); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(1, test.validateAlwaysRan); Assert.assertEquals(0, test.validateOneRan); Assert.assertEquals(0, test.validateTwoRan); Assert.assertEquals(0, test.getContext().getValidationErrors().size()); } /** * Generates an error by providing an invalid value for numberOne (which has a minimum * value of 0). Validations other than required are still applied even though that @Validate * has a on="one". The single validaiton error should prevent validateAlways() and * validateOne from running. */ @Test(groups="fast") public void testEventZeroWithErrors() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", "99"); trip.addParameter("numberOne", "-100"); trip.execute("eventZero"); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(0, test.validateAlwaysRan); Assert.assertEquals(0, test.validateOneRan); Assert.assertEquals(0, test.validateTwoRan); Assert.assertEquals(test.numberZero, 99); Assert.assertEquals(1, test.getContext().getValidationErrors().size()); } /** * Number one is a required field also for this event, so we supply it. This event should * cause both validateAlways and validateOne to run. */ @Test(groups="fast") public void testEventOneNoErrors() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", "100"); trip.addParameter("numberOne", "101"); trip.execute("eventOne"); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(1, test.validateAlwaysRan); Assert.assertEquals(2, test.validateOneRan); Assert.assertEquals(0, test.validateTwoRan); Assert.assertEquals(test.numberZero, 100); Assert.assertEquals(test.numberOne, 101); Assert.assertEquals(0, test.getContext().getValidationErrors().size()); } /** * Tests that a required field error is raised this time for numberOne which is only * required for this event. Again this single error should prevent both validateAlways * and validateOne from running. */ @Test(groups="fast") public void testEventOneWithErrors() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", "100"); trip.addParameter("numberOne", ""); // required field for event one trip.execute("eventOne"); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(0, test.validateAlwaysRan); Assert.assertEquals(0, test.validateOneRan); Assert.assertEquals(0, test.validateTwoRan); Assert.assertEquals(test.numberZero, 100); Assert.assertEquals(1, test.getContext().getValidationErrors().size()); } /** * Almost identical to testEventOneWithNoErrors except that we invoke the 'default' event. * Tests to make sure that event-specific validations are still applied correctly when the * event name isn't present in the request. */ @Test(groups="fast") public void testEventOneAsDefault() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", "100"); trip.addParameter("numberOne", "101"); trip.execute(); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(1, test.validateAlwaysRan); Assert.assertEquals(2, test.validateOneRan); Assert.assertEquals(0, test.validateTwoRan); Assert.assertEquals(test.numberZero, 100); Assert.assertEquals(test.numberOne, 101); Assert.assertEquals(0, test.getContext().getValidationErrors().size()); } /** * Straightforward test for event two that makes sure its validations run. This time * numberTwo should be required (and is supplied) and validateAlways and validateTwo should * run but not validateOne. */ @Test(groups="fast") public void testEventTwoNoErrors() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", "100"); trip.addParameter("numberTwo", "102"); trip.execute("eventTwo"); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(1, test.validateAlwaysRan); Assert.assertEquals(0, test.validateOneRan); Assert.assertEquals(2, test.validateTwoRan); Assert.assertEquals(test.numberZero, 100); Assert.assertEquals(test.numberTwo, 102); Assert.assertEquals(0, test.getContext().getValidationErrors().size()); } /** * Tests that validateTwo is run event though there are errors and valiateAlways did not run, * because validateTwo is marked to run always. */ @Test(groups="fast") public void testEventTwoWithErrors() throws Exception { MockRoundtrip trip = new MockRoundtrip(getMockServletContext(), getClass()); trip.addParameter("numberZero", ""); // required field always trip.addParameter("numberTwo", ""); // required field for event one trip.execute("eventTwo"); ValidationFlowTest test = trip.getActionBean(getClass()); Assert.assertEquals(0, test.validateAlwaysRan); Assert.assertEquals(0, test.validateOneRan); Assert.assertEquals(1, test.validateTwoRan); Assert.assertEquals(2, test.getContext().getValidationErrors().size()); } }