1ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair/*
2ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * Copyright 2007 the original author or authors.
3ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair *
4ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * Licensed under the Apache License, Version 2.0 (the "License");
5ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * you may not use this file except in compliance with the License.
6ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * You may obtain a copy of the License at
7ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair *
8ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair *      http://www.apache.org/licenses/LICENSE-2.0
9ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair *
10ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * Unless required by applicable law or agreed to in writing, software
11ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * distributed under the License is distributed on an "AS IS" BASIS,
12ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * See the License for the specific language governing permissions and
14ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * limitations under the License.
15ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair */
16ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairpackage org.mockftpserver.stub.command;
17ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
18ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.apache.log4j.Logger;
19ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.easymock.MockControl;
20ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.core.command.AbstractCommandHandler;
21ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.core.command.Command;
22ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.core.command.InvocationHistory;
23ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.core.command.InvocationRecord;
24ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.core.command.ReplyCodes;
25ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.core.session.Session;
26ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport org.mockftpserver.test.AbstractTest;
27ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
28ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport java.text.MessageFormat;
29ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport java.util.ListResourceBundle;
30ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairimport java.util.ResourceBundle;
31ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
32ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair/**
33ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * Abstract superclass for CommandHandler tests
34ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair *
35ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * @author Chris Mair
36ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair * @version $Revision$ - $Date$
37ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair */
38ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismairpublic abstract class AbstractCommandHandlerTest extends AbstractTest {
39ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
40ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    private static final Logger LOG = Logger.getLogger(AbstractCommandHandlerTest.class);
41ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
42ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    // Some common test constants
43ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected static final String DIR1 = "dir1";
44ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected static final String DIR2 = "dir2";
45ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected static final String FILENAME1 = "sample1.txt";
46ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected static final String FILENAME2 = "sample2.txt";
47ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
48ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected Session session;
49ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected ResourceBundle replyTextBundle;
50ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
51ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
52ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Test the handleCommand() method, when one or more parameter is missing or invalid
53ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
54ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param commandHandler - the CommandHandler to test
55ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param commandName    - the name for the Command
56ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param parameters     - the Command parameters
57ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
58ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected void testHandleCommand_InvalidParameters(AbstractCommandHandler commandHandler,
59ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                                                       String commandName, String[] parameters) throws Exception {
60ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        Command command = new Command(commandName, parameters);
61ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        session.sendReply(ReplyCodes.COMMAND_SYNTAX_ERROR, replyTextFor(ReplyCodes.COMMAND_SYNTAX_ERROR));
62ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        replay(session);
63ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
64ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        commandHandler.handleCommand(command, session);
65ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        verify(session);
66ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
67ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        verifyNumberOfInvocations(commandHandler, 1);
68ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        verifyNoDataElements(commandHandler.getInvocation(0));
69ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
70ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
71ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
72ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Verify that the CommandHandler contains the specified number of invocation records
73ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
74ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param commandHandler - the CommandHandler
75ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param expected       - the expected number of invocations
76ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
77ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected void verifyNumberOfInvocations(InvocationHistory commandHandler, int expected) {
78ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEquals("number of invocations", expected, commandHandler.numberOfInvocations());
79ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
80ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
81ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
82ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Verify that the InvocationRecord contains no data elements
83ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
84ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param invocationRecord - the InvocationRecord
85ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
86ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected void verifyNoDataElements(InvocationRecord invocationRecord) {
87ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        LOG.info("Verifying: " + invocationRecord);
88ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEquals("number of data elements", 0, invocationRecord.keySet().size());
89ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
90ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
91ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
92ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Verify that the InvocationRecord contains exactly one data element, with the specified key
93ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * and value.
94ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
95ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param invocationRecord - the InvocationRecord
96ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param key              - the expected key
97ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param value            - the expected value
98ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
99ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected void verifyOneDataElement(InvocationRecord invocationRecord, String key, Object value) {
100ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        LOG.info("Verifying: " + invocationRecord);
101ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEquals("number of data elements", 1, invocationRecord.keySet().size());
102ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEqualsAllTypes("value:" + value, value, invocationRecord.getObject(key));
103ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
104ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
105ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
106ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Verify that the InvocationRecord contains exactly two data element, with the specified keys
107ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * and values.
108ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
109ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param invocationRecord - the InvocationRecord
110ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param key1             - the expected key1
111ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param value1           - the expected value1
112ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param key2             - the expected key2
113ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param value2-          the expected value2
114ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
115ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected void verifyTwoDataElements(InvocationRecord invocationRecord, String key1, Object value1,
116ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                                         String key2, Object value2) {
117ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
118ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        LOG.info("Verifying: " + invocationRecord);
119ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEquals("number of data elements", 2, invocationRecord.keySet().size());
120ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEqualsAllTypes("value1:" + value1, value1, invocationRecord.getObject(key1));
121ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        assertEqualsAllTypes("value2:" + value2, value2, invocationRecord.getObject(key2));
122ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
123ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
124ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
125ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Assert that the actual is equal to the expected, using arrays equality comparison if
126ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * necessary
127ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
128ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param message  - the message, used if the comparison fails
129ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param expected - the expected value
130ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param actual   - the actual value
131ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
132ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    private void assertEqualsAllTypes(String message, Object expected, Object actual) {
133ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
134ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        if (expected instanceof byte[] || actual instanceof byte[]) {
135ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair            assertEquals(message, (byte[]) expected, (byte[]) actual);
136ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        } else if (expected instanceof Object[] || actual instanceof Object[]) {
137ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair            assertEquals(message, (Object[]) expected, (Object[]) actual);
138ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        } else {
139ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair            assertEquals(message, expected, actual);
140ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        }
141ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
142ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
143ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
144ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Perform setup before each test
145ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
146ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @see org.mockftpserver.test.AbstractTest#setUp()
147ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
148ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected void setUp() throws Exception {
149ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        super.setUp();
150ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
151ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        session = (Session) createMock(Session.class);
152ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        control(session).setDefaultMatcher(MockControl.ARRAY_MATCHER);
153ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        control(session).expectAndDefaultReturn(session.getClientHost(), DEFAULT_HOST);
154ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
155ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        replyTextBundle = new ListResourceBundle() {
156ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair            protected Object[][] getContents() {
157ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                return new Object[][]{
158ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"150", replyTextFor(150)},
159ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"200", replyTextFor(200)},
160ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"211", replyTextWithParameterFor(211)},
161ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"213", replyTextWithParameterFor(213)},
162ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"214", replyTextWithParameterFor(214)},
163ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"215", replyTextWithParameterFor(215)},
164ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"220", replyTextFor(220)},
165ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"221", replyTextFor(221)},
166ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"226", replyTextFor(226)},
167ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"226.WithFilename", replyTextWithParameterFor("226.WithFilename")},
168ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"227", replyTextWithParameterFor(227)},
169ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"230", replyTextFor(230)},
170ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"250", replyTextFor(250)},
171ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"257", replyTextWithParameterFor(257)},
172ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"331", replyTextFor(331)},
173ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"350", replyTextFor(350)},
174ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                        {"501", replyTextFor(501)},
175ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair                };
176ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair            }
177ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        };
178ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
179ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
180ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
181ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Return the test-specific reply text for the specified reply code
182ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
183ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param replyCode - the reply code
184ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @return the reply text for the specified reply code
185ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
186ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected String replyTextFor(int replyCode) {
187ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        return "Reply for " + replyCode;
188ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
189ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
190ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
191ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Return the test-specific parameterized reply text for the specified reply code
192ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
193ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param replyCode - the reply code
194ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @return the reply text for the specified reply code
195ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
196ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected String replyTextWithParameterFor(int replyCode) {
197ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        return "Reply for " + replyCode + ":{0}";
198ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
199ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
200ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
201ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Return the test-specific parameterized reply text for the specified messageKey
202ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
203ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param messageKey - the messageKey
204ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @return the reply text for the specified messageKey
205ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
206ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected String replyTextWithParameterFor(String messageKey) {
207ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        return "Reply for " + messageKey + ":{0}";
208ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
209ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
210ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
211ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Return the test-specific reply text for the specified reply code and message parameter
212ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
213ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param replyCode - the reply code
214ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param parameter - the message parameter value
215ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @return the reply text for the specified reply code
216ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
217ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected String formattedReplyTextFor(int replyCode, Object parameter) {
218ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        return MessageFormat.format(replyTextWithParameterFor(replyCode), objArray(parameter));
219ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
220ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
221ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    /**
222ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * Return the test-specific reply text for the specified message key and message parameter
223ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     *
224ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param messageKey - the messageKey
225ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @param parameter  - the message parameter value
226ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     * @return the reply text for the specified message key and parameter
227ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair     */
228ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    protected String formattedReplyTextFor(String messageKey, Object parameter) {
229ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair        return MessageFormat.format(replyTextWithParameterFor(messageKey), objArray(parameter));
230ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair    }
231ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair
232ad39334d4c363c6ada5863d0bb3184f5f4699d69chrismair}
233