1 package com.atlassian.sal.core.rdbms;
2
3 import com.atlassian.sal.api.rdbms.RdbmsException;
4 import org.hamcrest.Description;
5 import org.hamcrest.Matcher;
6 import org.hamcrest.TypeSafeMatcher;
7 import org.junit.Test;
8 import org.junit.runner.RunWith;
9 import org.junit.runners.Parameterized;
10
11 import java.sql.SQLException;
12 import java.util.Arrays;
13 import java.util.Collection;
14
15 import static org.hamcrest.Matchers.nullValue;
16 import static org.hamcrest.core.Is.is;
17 import static org.hamcrest.core.Is.isA;
18 import static org.junit.Assert.assertThat;
19 import static org.mockito.Matchers.any;
20 import static org.mockito.Mockito.doThrow;
21 import static org.mockito.Mockito.never;
22 import static org.mockito.Mockito.verify;
23
24 @RunWith(Parameterized.class)
25 public class TestDefaultTransactionalExecutorExceptions extends TestDefaultTransactionalExecutorBase {
26 @Parameterized.Parameters(name = "{index}: throwing {0}")
27 public static Collection data() {
28 return Arrays.asList(new Object[][]{
29 {new SQLException("horrible commit exception")},
30 {new LinkageError("horrible linkage error")},
31 });
32 }
33
34 @Parameterized.Parameter(0)
35 public Throwable exception;
36
37 @Test
38 public void executeCommitFails() throws SQLException {
39 doThrow(exception).when(connection).commit();
40
41 expectedException.expect(RdbmsException.class);
42 expectedException.expectCause(isA(exception.getClass()));
43
44 defaultTransactionalExecutor.executeInternal(connection, callback);
45 assertThat(wrappedConnection.connection, nullValue());
46
47 verify(callback).execute(any(WrappedConnection.class));
48 verify(connection).commit();
49 verify(connection, never()).rollback();
50 }
51
52 @Test
53 public void executeRollbackFails() throws SQLException {
54 callbackThrows = new TestDefaultTransactionalExecutorBase.CallbackException("epic fail");
55
56 doThrow(exception).when(connection).rollback();
57
58 expectedException.expect(CallbackException.class);
59 expectedException.expectMessage("epic fail");
60 expectedException.expect(new SuppressedMatches(is(exception)));
61
62 defaultTransactionalExecutor.executeInternal(connection, callback);
63 assertThat(wrappedConnection.connection, nullValue());
64
65 verify(callback).execute(any(WrappedConnection.class));
66 verify(connection, never()).commit();
67 verify(connection).rollback();
68 }
69
70 private static class SuppressedMatches<T extends Throwable> extends TypeSafeMatcher<T> {
71 private Matcher<T> expectedSuppressed;
72
73 public SuppressedMatches(Matcher<T> expectedSuppressed) {
74 this.expectedSuppressed = expectedSuppressed;
75 }
76
77 @Override
78 protected void describeMismatchSafely(final T item, final Description mismatchDescription) {
79 mismatchDescription.appendText("was not found in ").appendValue(item.getSuppressed());
80 }
81
82 @Override
83 public void describeTo(final Description description) {
84 description.appendText("a suppressed exception matching(");
85 expectedSuppressed.describeTo(description);
86 description.appendText(")");
87 }
88
89 @Override
90 public boolean matchesSafely(final Throwable t) {
91 for (Throwable s : t.getSuppressed()) {
92 if (expectedSuppressed.matches(s)) {
93 return true;
94 }
95 }
96
97 return false;
98 }
99 }
100 }