/*
* (C) 2007-2012 Alibaba Group Holding Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* Authors:
* wuhua <wq163@163.com> , boyan <killme2008@gmail.com>
*/
package com.taobao.metamorphosis.server.network;
import javax.transaction.xa.XAResource;
import org.easymock.classextension.EasyMock;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.taobao.gecko.service.Connection;
import com.taobao.gecko.service.exception.NotifyRemotingException;
import com.taobao.metamorphosis.network.BooleanCommand;
import com.taobao.metamorphosis.network.HttpStatus;
import com.taobao.metamorphosis.network.TransactionCommand;
import com.taobao.metamorphosis.server.CommandProcessor;
import com.taobao.metamorphosis.server.utils.XIDGenerator;
import com.taobao.metamorphosis.transaction.LocalTransactionId;
import com.taobao.metamorphosis.transaction.TransactionId;
import com.taobao.metamorphosis.transaction.TransactionInfo;
import com.taobao.metamorphosis.transaction.TransactionInfo.TransactionType;
public class TransactionProcessorUnitTest {
private TransactionProcessor transactionProcessor;
private CommandProcessor commandProcessor;
private Connection conn;
@Before
public void setUp() {
this.conn = EasyMock.createMock(Connection.class);
this.commandProcessor = EasyMock.createMock(CommandProcessor.class);
this.transactionProcessor = new TransactionProcessor(this.commandProcessor, null);
}
@After
public void tearDown() {
EasyMock.verify(this.conn, this.commandProcessor);
}
private void mockSessionContext(final String key, final String sessionId) {
EasyMock.expect(this.conn.getAttribute(key)).andReturn(new SessionContextImpl(sessionId, this.conn));
}
private void replay() {
EasyMock.replay(this.conn, this.commandProcessor);
}
@Test
public void testHandleBeginTransaction() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.BEGIN);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.commandProcessor.beginTransaction(new SessionContextImpl(sessionId, this.conn), transactionId, 0);
EasyMock.expectLastCall();
this.mockSessionContext(sessionId, sessionId);
this.mockResponseOK();
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testHandleEndTransaction() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.END);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.mockSessionContext(sessionId, sessionId);
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testHandlePrepare() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.PREPARE);
final TransactionCommand tc = new TransactionCommand(info, 1);
EasyMock.expect(
this.commandProcessor.prepareTransaction(new SessionContextImpl(sessionId, this.conn), transactionId))
.andReturn(XAResource.XA_OK);
this.conn.response(new BooleanCommand(HttpStatus.Success, String.valueOf(XAResource.XA_OK), 1));
this.mockSessionContext(sessionId, sessionId);
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testCommitOnePhase() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.COMMIT_ONE_PHASE);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.commandProcessor.commitTransaction(new SessionContextImpl(sessionId, this.conn), transactionId, true);
EasyMock.expectLastCall();
this.mockResponseOK();
this.mockSessionContext(sessionId, sessionId);
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testCommitTwoPhase() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.COMMIT_TWO_PHASE);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.commandProcessor.commitTransaction(new SessionContextImpl(sessionId, this.conn), transactionId, false);
EasyMock.expectLastCall();
this.mockResponseOK();
this.mockSessionContext(sessionId, sessionId);
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testForgetTransaction() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.FORGET);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.commandProcessor.forgetTransaction(new SessionContextImpl(sessionId, this.conn), transactionId);
EasyMock.expectLastCall();
this.mockResponseOK();
this.mockSessionContext(sessionId, sessionId);
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testRollback() throws Exception {
final String sessionId = "test";
final LocalTransactionId transactionId = new LocalTransactionId(sessionId, 1);
final TransactionInfo info = new TransactionInfo(transactionId, sessionId, TransactionType.ROLLBACK);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.commandProcessor.rollbackTransaction(new SessionContextImpl(sessionId, this.conn), transactionId);
EasyMock.expectLastCall();
this.mockResponseOK();
this.mockSessionContext(sessionId, sessionId);
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
private TransactionId[] generateIds() {
final TransactionId[] ids = new TransactionId[10];
for (int i = 0; i < ids.length; i++) {
ids[i] = XIDGenerator.createXID(i);
}
return ids;
}
private String generateIdsString(final TransactionId[] ids) {
final StringBuilder sb = new StringBuilder();
boolean wasFirst = true;
for (final TransactionId id : ids) {
if (wasFirst) {
sb.append(id.getTransactionKey());
wasFirst = false;
}
else {
sb.append("\r\n").append(id.getTransactionKey());
}
}
return sb.toString();
}
@Test
public void testRecoverTransaction() throws Exception {
final String sessionId = "test";
String uniqueQualifier = "unique-qualifier";
final TransactionInfo info = new TransactionInfo(null, sessionId, TransactionType.RECOVER, uniqueQualifier);
final TransactionCommand tc = new TransactionCommand(info, 1);
final TransactionId[] ids = this.generateIds();
final String resultString = this.generateIdsString(ids);
this.mockSessionContext(SessionContextHolder.GLOBAL_SESSION_KEY, sessionId);
EasyMock.expect(
this.commandProcessor.getPreparedTransactions(new SessionContextImpl("test", this.conn), uniqueQualifier))
.andReturn(ids);
this.conn.response(new BooleanCommand(HttpStatus.Success, resultString, 1));
EasyMock.expectLastCall();
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
@Test
public void testHandleException() throws Exception {
final String sessionId = "test";
String uniqueQualifier = "unique-qualifier";
final TransactionInfo info = new TransactionInfo(null, sessionId, TransactionType.RECOVER, uniqueQualifier);
final TransactionCommand tc = new TransactionCommand(info, 1);
this.mockSessionContext(SessionContextHolder.GLOBAL_SESSION_KEY, sessionId);
EasyMock.expect(
this.commandProcessor.getPreparedTransactions(new SessionContextImpl("test", this.conn), uniqueQualifier))
.andThrow(new RuntimeException("just for test"));
this.conn.response(new BooleanCommand(HttpStatus.InternalServerError, "just for test", 1));
EasyMock.expectLastCall();
this.replay();
this.transactionProcessor.handleRequest(tc, this.conn);
}
private void mockResponseOK() throws NotifyRemotingException {
this.conn.response(new BooleanCommand(HttpStatus.Success, null, 1));
}
}