You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@servicecomb.apache.org by ni...@apache.org on 2018/08/23 09:12:31 UTC

[incubator-servicecomb-saga] 01/01: SCB-817 Added the unit tests of TCC related Aspect

This is an automated email from the ASF dual-hosted git repository.

ningjiang pushed a commit to branch SCB-817
in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git

commit 5e5aadef15cbd69f6c04dcb575f12ef6fce6f697
Author: Willem Jiang <ji...@huawei.com>
AuthorDate: Thu Aug 23 16:42:40 2018 +0800

    SCB-817 Added the unit tests of TCC related Aspect
---
 .../omega/transaction/tcc/TccEventService.java     |   2 -
 .../transaction/tcc/TccParticipatorAspect.java     |   8 +-
 .../tcc/TccStartAnnotationProcessor.java           |  14 +-
 .../saga/omega/transaction/tcc/TccStartAspect.java |   4 +-
 .../transaction/tcc/events/ParticipatedEvent.java  |  24 +++
 .../transaction/tcc/events/TccEndedEvent.java      |  14 +-
 .../transaction/tcc/events/TccStartedEvent.java    |   4 -
 .../transaction/tcc/TccParticipatorAspectTest.java | 169 +++++++++++++++++++++
 .../tcc/TccStartAnnotationProcessorTest.java       | 155 +++++++++++++++++++
 .../omega/transaction/tcc/TccStartAspectTest.java  | 164 ++++++++++++++++++++
 10 files changed, 534 insertions(+), 24 deletions(-)

diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccEventService.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccEventService.java
index a722f49..48ad743 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccEventService.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccEventService.java
@@ -38,7 +38,5 @@ public interface TccEventService {
   AlphaResponse TccTransactionStart(TccStartedEvent tccStartEvent);
 
   AlphaResponse TccTransactionStop(TccEndedEvent tccEndEvent);
-
-  AlphaResponse send(TxEvent event);
   
 }
diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspect.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspect.java
index e64bc2a..a78d819 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspect.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspect.java
@@ -61,14 +61,14 @@ public class TccParticipatorAspect {
     try {
       Object result = joinPoint.proceed();
       // Send the participate message back
-      tccEventService.participate(new ParticipatedEvent(context.globalTxId(), context.localTxId(), localTxId, cancelMethod, confirmMethod,
-          TransactionStatus.Succeed));
+      tccEventService.participate(new ParticipatedEvent(context.globalTxId(), context.localTxId(), localTxId, confirmMethod,
+          cancelMethod, TransactionStatus.Succeed));
       LOG.debug("Participate Transaction with context {} has finished.", context);
       return result;
     } catch (Throwable throwable) {
       // Now we don't handle the error message
-      tccEventService.participate(new ParticipatedEvent(context.globalTxId(), context.localTxId(), localTxId, cancelMethod,
-          confirmMethod, TransactionStatus.Failed));
+      tccEventService.participate(new ParticipatedEvent(context.globalTxId(), context.localTxId(), localTxId, confirmMethod,
+          cancelMethod, TransactionStatus.Failed));
       LOG.error("Participate Transaction with context {} failed.", context, throwable);
       throw throwable;
     } finally {
diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessor.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessor.java
index 26621d1..55198dd 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessor.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessor.java
@@ -21,13 +21,11 @@ import javax.transaction.TransactionalException;
 import org.apache.servicecomb.saga.common.TransactionStatus;
 import org.apache.servicecomb.saga.omega.context.OmegaContext;
 import org.apache.servicecomb.saga.omega.transaction.AlphaResponse;
-import org.apache.servicecomb.saga.omega.transaction.EventAwareInterceptor;
 import org.apache.servicecomb.saga.omega.transaction.OmegaException;
-import org.apache.servicecomb.saga.omega.transaction.TxAbortedEvent;
 import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccEndedEvent;
 import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccStartedEvent;
 
-public class TccStartAnnotationProcessor implements EventAwareInterceptor {
+public class TccStartAnnotationProcessor {
 
   private final OmegaContext omegaContext;
   private final TccEventService eventService;
@@ -37,9 +35,7 @@ public class TccStartAnnotationProcessor implements EventAwareInterceptor {
     this.eventService = eventService;
   }
 
-  @Override
-  public AlphaResponse preIntercept(String parentTxId, String compensationMethod, int timeout, String retriesMethod,
-      int retries, Object... message) {
+  public AlphaResponse preIntercept(String parentTxId, String methodName, int timeout) {
     try {
       return eventService.TccTransactionStart(new TccStartedEvent(omegaContext.globalTxId(), omegaContext.localTxId()));
     } catch (OmegaException e) {
@@ -47,14 +43,12 @@ public class TccStartAnnotationProcessor implements EventAwareInterceptor {
     }
   }
 
-  @Override
-  public void postIntercept(String parentTxId, String compensationMethod) {
+  public void postIntercept(String parentTxId, String methodName) {
     eventService.TccTransactionStop(new TccEndedEvent(omegaContext.globalTxId(), omegaContext.localTxId(),
         TransactionStatus.Succeed));
   }
 
-  @Override
-  public void onError(String parentTxId, String compensationMethod, Throwable throwable) {
+  public void onError(String parentTxId, String methodName, Throwable throwable) {
     // Send the cancel event
     // Do we need to wait for the alpha finish all the transaction
     eventService.TccTransactionStop(new TccEndedEvent(omegaContext.globalTxId(), omegaContext.localTxId(),
diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspect.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspect.java
index f6d1d77..62a2873 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspect.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspect.java
@@ -20,9 +20,7 @@ import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Method;
 
 import org.apache.servicecomb.saga.omega.context.OmegaContext;
-import org.apache.servicecomb.saga.omega.context.annotations.SagaStart;
 import org.apache.servicecomb.saga.omega.context.annotations.TccStart;
-import org.apache.servicecomb.saga.omega.transaction.MessageSender;
 import org.apache.servicecomb.saga.omega.transaction.OmegaException;
 import org.aspectj.lang.ProceedingJoinPoint;
 import org.aspectj.lang.annotation.Around;
@@ -49,7 +47,7 @@ public class TccStartAspect {
     initializeOmegaContext();
     Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
 
-    tccStartAnnotationProcessor.preIntercept(context.globalTxId(), method.toString(), tccStart.timeout(), "", 0);
+    tccStartAnnotationProcessor.preIntercept(context.globalTxId(), method.toString(), tccStart.timeout());
     LOG.debug("Initialized context {} before execution of method {}", context, method.toString());
 
     try {
diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/ParticipatedEvent.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/ParticipatedEvent.java
index 3372f8e..ac2dcb9 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/ParticipatedEvent.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/ParticipatedEvent.java
@@ -38,4 +38,28 @@ public class ParticipatedEvent {
     this.cancelMethod = cancelMethod;
     this.status = status;
   }
+
+  public String getGlobalTxId() {
+    return globalTxId;
+  }
+
+  public String getLocalTxId() {
+    return localTxId;
+  }
+
+  public String getParentTxId() {
+    return parentTxId;
+  }
+
+  public String getConfirmMethod() {
+    return confirmMethod;
+  }
+
+  public String getCancelMethod() {
+    return cancelMethod;
+  }
+
+  public TransactionStatus getStatus() {
+    return status;
+  }
 }
diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccEndedEvent.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccEndedEvent.java
index 7c666b2..31a187f 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccEndedEvent.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccEndedEvent.java
@@ -22,7 +22,19 @@ public class TccEndedEvent {
   private final String globalTxId;
   private final String localTxId;
   private final TransactionStatus status;
-   
+
+
+  public String getGlobalTxId() {
+    return globalTxId;
+  }
+
+  public String getLocalTxId() {
+    return localTxId;
+  }
+
+  public TransactionStatus getStatus() {
+    return status;
+  }
 
   public TccEndedEvent(String globalTxId, String localTxId,
       TransactionStatus status) {
diff --git a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccStartedEvent.java b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccStartedEvent.java
index edd0333..8449cb4 100644
--- a/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccStartedEvent.java
+++ b/omega/omega-transaction/src/main/java/org/apache/servicecomb/saga/omega/transaction/tcc/events/TccStartedEvent.java
@@ -20,17 +20,13 @@ public class TccStartedEvent {
   private final String globalTxId;
   private final String localTxId;
 
-
   public String getGlobalTxId() {
     return globalTxId;
   }
-
   public String getLocalTxId() {
     return localTxId;
   }
 
-
-
   public TccStartedEvent(String globalTxId, String localTxId) {
     this.globalTxId = globalTxId;
     this.localTxId = localTxId;
diff --git a/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspectTest.java b/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspectTest.java
new file mode 100644
index 0000000..8e6b36e
--- /dev/null
+++ b/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccParticipatorAspectTest.java
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.servicecomb.saga.omega.transaction.tcc;
+
+import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.servicecomb.saga.common.TransactionStatus;
+import org.apache.servicecomb.saga.omega.context.IdGenerator;
+import org.apache.servicecomb.saga.omega.context.OmegaContext;
+import org.apache.servicecomb.saga.omega.context.annotations.TccStart;
+import org.apache.servicecomb.saga.omega.transaction.AlphaResponse;
+import org.apache.servicecomb.saga.omega.transaction.annotations.Compensable;
+import org.apache.servicecomb.saga.omega.transaction.annotations.Participate;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.ParticipatedEvent;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccEndedEvent;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccStartedEvent;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TccParticipatorAspectTest {
+  private final ProceedingJoinPoint joinPoint = Mockito.mock(ProceedingJoinPoint.class);
+  private final MethodSignature methodSignature = Mockito.mock(MethodSignature.class);
+  private final String globalTxId = UUID.randomUUID().toString();
+  private final String localTxId = UUID.randomUUID().toString();
+  private final String newLocalTxId = UUID.randomUUID().toString();
+
+  private final List<ParticipatedEvent> participatedEvents = new ArrayList<>();
+  private final AlphaResponse response = new AlphaResponse(false);
+  private final TccEventService eventService = new TccEventService() {
+    @Override
+    public void onConnected() {
+
+    }
+
+    @Override
+    public void onDisconnected() {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String target() {
+      return null;
+    }
+
+    @Override
+    public AlphaResponse participate(ParticipatedEvent participateEvent) {
+      participatedEvents.add(participateEvent);
+      return response;
+    }
+
+    @Override
+    public AlphaResponse TccTransactionStart(TccStartedEvent tccStartEvent) {
+      return null;
+    }
+
+    @Override
+    public AlphaResponse TccTransactionStop(TccEndedEvent tccEndEvent) {
+      return null;
+    }
+
+  };
+
+
+  @SuppressWarnings("unchecked")
+  private final IdGenerator<String> idGenerator = Mockito.mock(IdGenerator.class);
+
+  private final OmegaContext omegaContext = new OmegaContext(idGenerator);
+  private final Participate participate = mock(Participate.class);
+
+  private final TccParticipatorAspect aspect = new TccParticipatorAspect(eventService, omegaContext);
+
+
+  @Before
+  public void setUp() throws Exception {
+    when(idGenerator.nextId()).thenReturn(newLocalTxId);
+    when(joinPoint.getSignature()).thenReturn(methodSignature);
+    when(joinPoint.getTarget()).thenReturn(this);
+
+    when(methodSignature.getMethod()).thenReturn(this.getClass().getDeclaredMethod("doNothing"));
+    when(participate.cancelMethod()).thenReturn("cancelMethod");
+    when(participate.confirmMethod()).thenReturn("confirmMethod");
+
+    omegaContext.setGlobalTxId(globalTxId);
+    omegaContext.setLocalTxId(localTxId);
+  }
+
+  @Test
+  public void participateMethodIsCalledSuccessed() throws Throwable {
+    aspect.advise(joinPoint, participate);
+
+    assertThat(participatedEvents.size(), is(1));
+    ParticipatedEvent participatedEvent = participatedEvents.get(0);
+
+    assertThat(participatedEvent.getGlobalTxId(), is(globalTxId));
+    assertThat(participatedEvent.getParentTxId(), is(localTxId));
+    assertThat(participatedEvent.getLocalTxId(), is(newLocalTxId));
+    assertThat(participatedEvent.getStatus(), is(TransactionStatus.Succeed));
+    assertThat(participatedEvent.getCancelMethod(), is("cancelMethod"));
+    assertThat(participatedEvent.getConfirmMethod(), is("confirmMethod"));
+
+    assertThat(omegaContext.globalTxId(), is(globalTxId));
+    assertThat(omegaContext.localTxId(), is(localTxId));
+  }
+
+  @Test
+  public void participateMethodIsCalledFailed()  throws Throwable {
+    RuntimeException oops = new RuntimeException("oops");
+
+    when(joinPoint.proceed()).thenThrow(oops);
+
+    try {
+      aspect.advise(joinPoint, participate);
+      expectFailing(RuntimeException.class);
+    } catch (RuntimeException e) {
+      assertThat(e, is(oops));
+    }
+
+    assertThat(participatedEvents.size(), is(1));
+    ParticipatedEvent participatedEvent = participatedEvents.get(0);
+
+    assertThat(participatedEvent.getGlobalTxId(), is(globalTxId));
+    assertThat(participatedEvent.getParentTxId(), is(localTxId));
+    assertThat(participatedEvent.getLocalTxId(), is(newLocalTxId));
+    assertThat(participatedEvent.getStatus(), is(TransactionStatus.Failed));
+    assertThat(participatedEvent.getCancelMethod(), is("cancelMethod"));
+    assertThat(participatedEvent.getConfirmMethod(), is("confirmMethod"));
+
+
+    assertThat(omegaContext.globalTxId(), is(globalTxId));
+    assertThat(omegaContext.localTxId(), is(localTxId));
+  }
+
+  private String doNothing() {
+    return "doNothing";
+  }
+
+}
diff --git a/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessorTest.java b/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessorTest.java
new file mode 100644
index 0000000..95f8137
--- /dev/null
+++ b/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAnnotationProcessorTest.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.servicecomb.saga.omega.transaction.tcc;
+
+import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import javax.transaction.TransactionalException;
+
+import org.apache.servicecomb.saga.common.TransactionStatus;
+import org.apache.servicecomb.saga.omega.context.IdGenerator;
+import org.apache.servicecomb.saga.omega.context.OmegaContext;
+import org.apache.servicecomb.saga.omega.transaction.AlphaResponse;
+import org.apache.servicecomb.saga.omega.transaction.OmegaException;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.ParticipatedEvent;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccEndedEvent;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccStartedEvent;
+import org.hamcrest.core.Is;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TccStartAnnotationProcessorTest {
+  private final String globalTxId = UUID.randomUUID().toString();
+  private final AlphaResponse response = new AlphaResponse(false);
+  private final AlphaResponse abortResponse = new AlphaResponse(true);
+  private final List<TccStartedEvent> startedEvents = new ArrayList<>();
+  private final List<TccEndedEvent> endedEvents = new ArrayList<>();
+  private boolean throwException = false;
+
+  private final IdGenerator<String> generator = mock(IdGenerator.class);
+  private final OmegaContext context = new OmegaContext(generator);
+  private final OmegaException exception = new OmegaException("exception", new RuntimeException("runtime exception"));
+  private final TccEventService eventService = new TccEventService() {
+    @Override
+    public void onConnected() {
+
+    }
+
+    @Override
+    public void onDisconnected() {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String target() {
+      return null;
+    }
+
+    @Override
+    public AlphaResponse participate(ParticipatedEvent participateEvent) {
+      return null;
+    }
+
+    @Override
+    public AlphaResponse TccTransactionStart(TccStartedEvent tccStartEvent) {
+      if (throwException) {
+        throw exception;
+      }
+      startedEvents.add(tccStartEvent);
+      return response;
+    }
+
+    @Override
+    public AlphaResponse TccTransactionStop(TccEndedEvent tccEndEvent) {
+      endedEvents.add(tccEndEvent);
+      return response;
+    }
+
+
+  };
+  private final TccStartAnnotationProcessor tccStartAnnotationProcessor = new TccStartAnnotationProcessor(context,
+      eventService);
+
+  @Before
+  public void setUp() throws Exception {
+    context.setGlobalTxId(globalTxId);
+    context.setLocalTxId(globalTxId);
+  }
+
+  @Test
+  public void testSendTccStartEvent() {
+    AlphaResponse result = tccStartAnnotationProcessor
+        .preIntercept(null, "TccStartMethod", 0);
+
+    TccStartedEvent event = startedEvents.get(0);
+
+    assertThat(event.getGlobalTxId(), is(globalTxId));
+    assertThat(event.getLocalTxId(), is(globalTxId));
+    assertThat(result, is(response));
+
+  }
+
+  @Test
+  public void testSendTccStartEventFailed() {
+    throwException = true;
+    try {
+      tccStartAnnotationProcessor
+          .preIntercept(null, "TccStartMethod", 0);
+      expectFailing(TransactionalException.class);
+    } catch (TransactionalException e) {
+      Assert.assertThat(e.getMessage(), Is.is("exception"));
+      Assert.assertThat(e.getCause(), instanceOf(RuntimeException.class));
+      Assert.assertThat(e.getCause().getMessage(), Is.is("runtime exception"));
+    }
+
+  }
+
+  @Test
+  public void testSendTccEndEventWithoutError() {
+    tccStartAnnotationProcessor.postIntercept(null, "TccStartMethod");
+
+    TccEndedEvent event = endedEvents.get(0);
+
+    assertThat(event.getGlobalTxId(), is(globalTxId));
+    assertThat(event.getLocalTxId(), is(globalTxId));
+    assertThat(event.getStatus(), is(TransactionStatus.Succeed));
+
+  }
+
+  @Test
+  public void testSendTccEndEventWithError() {
+    tccStartAnnotationProcessor.onError(null, "TccStartMethod", null);
+    TccEndedEvent event = endedEvents.get(0);
+    assertThat(event.getGlobalTxId(), is(globalTxId));
+    assertThat(event.getLocalTxId(), is(globalTxId));
+    assertThat(event.getStatus(), is(TransactionStatus.Failed));
+  }
+}
diff --git a/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspectTest.java b/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspectTest.java
new file mode 100644
index 0000000..005af67
--- /dev/null
+++ b/omega/omega-transaction/src/test/java/org/apache/servicecomb/saga/omega/transaction/tcc/TccStartAspectTest.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+package org.apache.servicecomb.saga.omega.transaction.tcc;
+
+import static com.seanyinx.github.unit.scaffolding.AssertUtils.expectFailing;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.servicecomb.saga.common.TransactionStatus;
+import org.apache.servicecomb.saga.omega.context.IdGenerator;
+import org.apache.servicecomb.saga.omega.context.OmegaContext;
+import org.apache.servicecomb.saga.omega.context.annotations.TccStart;
+import org.apache.servicecomb.saga.omega.transaction.AlphaResponse;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.ParticipatedEvent;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccEndedEvent;
+import org.apache.servicecomb.saga.omega.transaction.tcc.events.TccStartedEvent;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class TccStartAspectTest {
+  private final ProceedingJoinPoint joinPoint = Mockito.mock(ProceedingJoinPoint.class);
+  private final MethodSignature methodSignature = Mockito.mock(MethodSignature.class);
+  private final String globalTxId = UUID.randomUUID().toString();
+  private final List<TccStartedEvent> startedEvents = new ArrayList<>();
+  private final List<TccEndedEvent> endedEvents = new ArrayList<>();
+  private final AlphaResponse response = new AlphaResponse(false);
+  private final TccEventService eventService = new TccEventService() {
+    @Override
+    public void onConnected() {
+
+    }
+
+    @Override
+    public void onDisconnected() {
+
+    }
+
+    @Override
+    public void close() {
+
+    }
+
+    @Override
+    public String target() {
+      return null;
+    }
+
+    @Override
+    public AlphaResponse participate(ParticipatedEvent participateEvent) {
+      return null;
+    }
+
+    @Override
+    public AlphaResponse TccTransactionStart(TccStartedEvent tccStartEvent) {
+      startedEvents.add(tccStartEvent);
+      return response;
+    }
+
+    @Override
+    public AlphaResponse TccTransactionStop(TccEndedEvent tccEndEvent) {
+      endedEvents.add(tccEndEvent);
+      return response;
+    }
+
+  };
+
+
+  @SuppressWarnings("unchecked")
+  private final IdGenerator<String> idGenerator = Mockito.mock(IdGenerator.class);
+  private final TccStart tccStart = Mockito.mock(TccStart.class);
+
+  private final OmegaContext omegaContext = new OmegaContext(idGenerator);
+  private final TccStartAspect aspect = new TccStartAspect(eventService, omegaContext);
+
+  @Before
+  public void setUp() throws Exception {
+    when(idGenerator.nextId()).thenReturn(globalTxId);
+    when(joinPoint.getSignature()).thenReturn(methodSignature);
+
+    when(methodSignature.getMethod()).thenReturn(this.getClass().getDeclaredMethod("doNothing"));
+    omegaContext.clear();
+  }
+
+  @Test
+  public void newGlobalTxIdInTccStart() throws Throwable {
+    aspect.advise(joinPoint, tccStart);
+
+    assertThat(startedEvents.size(), is(1));
+    TccStartedEvent startedEvent = startedEvents.get(0);
+
+    assertThat(startedEvent.getGlobalTxId(), is(globalTxId));
+    assertThat(startedEvent.getLocalTxId(), is(globalTxId));
+
+    assertThat(endedEvents.size(), is(1));
+    TccEndedEvent endedEvent = endedEvents.get(0);
+
+    assertThat(endedEvent.getGlobalTxId(), is(globalTxId));
+    assertThat(endedEvent.getLocalTxId(), is(globalTxId));
+    assertThat(endedEvent.getStatus(), is(TransactionStatus.Succeed));
+
+
+    assertThat(omegaContext.globalTxId(), is(nullValue()));
+    assertThat(omegaContext.localTxId(), is(nullValue()));
+  }
+
+  @Test
+  public void clearContextOnTccStartError() throws Throwable {
+    RuntimeException oops = new RuntimeException("oops");
+
+    when(joinPoint.proceed()).thenThrow(oops);
+
+    try {
+      aspect.advise(joinPoint, tccStart);
+      expectFailing(RuntimeException.class);
+    } catch (RuntimeException e) {
+      assertThat(e, is(oops));
+    }
+
+    assertThat(startedEvents.size(), is(1));
+    TccStartedEvent event = startedEvents.get(0);
+
+    assertThat(event.getGlobalTxId(), is(globalTxId));
+    assertThat(event.getLocalTxId(), is(globalTxId));
+    
+    TccEndedEvent endedEvent = endedEvents.get(0);
+
+    assertThat(endedEvent.getGlobalTxId(), is(globalTxId));
+    assertThat(endedEvent.getLocalTxId(), is(globalTxId));
+    assertThat(endedEvent.getStatus(), is(TransactionStatus.Failed));
+
+    assertThat(omegaContext.globalTxId(), is(nullValue()));
+    assertThat(omegaContext.localTxId(), is(nullValue()));
+  }
+
+  private String doNothing() {
+    return "doNothing";
+  }
+
+
+}