{"action":"created","comment":{"url":"https://api.github.com/repos/caskdata/tephra/pulls/comments/26102612","id":26102612,"diff_hunk":"@@ -0,0 +1,195 @@\n+/*\n+ * Copyright © 2015 Cask Data, Inc.\n+ *  \n+ * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n+ * use this file except in compliance with the License. You may obtain a copy of\n+ * the License at\n+ *  \n+ * http://www.apache.org/licenses/LICENSE-2.0\n+ *  \n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n+ * License for the specific language governing permissions and limitations under\n+ * the License.\n+ */\n+\n+package co.cask.tephra;\n+\n+import co.cask.tephra.distributed.TransactionService;\n+import co.cask.tephra.persist.InMemoryTransactionStateStorage;\n+import co.cask.tephra.persist.TransactionStateStorage;\n+import co.cask.tephra.runtime.ConfigModule;\n+import co.cask.tephra.runtime.DiscoveryModules;\n+import co.cask.tephra.runtime.TransactionClientModule;\n+import co.cask.tephra.runtime.TransactionModules;\n+import co.cask.tephra.runtime.ZKModule;\n+import com.google.common.base.Joiner;\n+import com.google.common.collect.ImmutableList;\n+import com.google.inject.AbstractModule;\n+import com.google.inject.Guice;\n+import com.google.inject.Injector;\n+import com.google.inject.Scopes;\n+import com.google.inject.util.Modules;\n+import org.apache.hadoop.conf.Configuration;\n+import org.apache.hadoop.hbase.HBaseConfiguration;\n+import org.apache.hadoop.hbase.util.Bytes;\n+import org.apache.twill.internal.zookeeper.InMemoryZKServer;\n+import org.apache.twill.zookeeper.ZKClientService;\n+import org.junit.AfterClass;\n+import org.junit.Assert;\n+import org.junit.Before;\n+import org.junit.BeforeClass;\n+import org.junit.ClassRule;\n+import org.junit.Test;\n+import org.junit.rules.TemporaryFolder;\n+import org.slf4j.Logger;\n+import org.slf4j.LoggerFactory;\n+\n+import java.io.ByteArrayOutputStream;\n+import java.io.PrintStream;\n+import java.util.List;\n+import java.util.concurrent.TimeUnit;\n+\n+public class TransactionAdminTest {\n+  private static final Logger LOG = LoggerFactory.getLogger(TransactionAdminTest.class);\n+  \n+  private static Configuration conf;\n+  private static InMemoryZKServer zkServer;\n+  private static ZKClientService zkClientService;\n+  private static TransactionService txService;\n+  private static TransactionSystemClient txClient;\n+\n+  @ClassRule\n+  public static TemporaryFolder tmpFolder = new TemporaryFolder();\n+\n+  @BeforeClass\n+  public static void start() throws Exception {\n+    zkServer = InMemoryZKServer.builder().setDataDir(tmpFolder.newFolder()).build();\n+    zkServer.startAndWait();\n+\n+    conf = HBaseConfiguration.create();\n+    conf.setBoolean(TxConstants.Manager.CFG_DO_PERSIST, false);\n+    conf.set(TxConstants.Service.CFG_DATA_TX_ZOOKEEPER_QUORUM, zkServer.getConnectionStr());\n+    conf.set(TxConstants.Service.CFG_DATA_TX_CLIENT_RETRY_STRATEGY, \"n-times\");\n+    conf.setInt(TxConstants.Service.CFG_DATA_TX_CLIENT_ATTEMPTS, 1);\n+\n+    Injector injector = Guice.createInjector(\n+      new ConfigModule(conf),\n+      new ZKModule(),\n+      new DiscoveryModules().getDistributedModules(),\n+      Modules.override(new TransactionModules().getDistributedModules())\n+        .with(new AbstractModule() {\n+          @Override\n+          protected void configure() {\n+            bind(TransactionStateStorage.class).to(InMemoryTransactionStateStorage.class).in(Scopes.SINGLETON);\n+          }\n+        }),\n+      new TransactionClientModule()\n+    );\n+\n+    zkClientService = injector.getInstance(ZKClientService.class);\n+    zkClientService.startAndWait();\n+\n+    // start a tx server\n+    txService = injector.getInstance(TransactionService.class);\n+    txClient = injector.getInstance(TransactionSystemClient.class);\n+    try {\n+      LOG.info(\"Starting transaction service\");\n+      txService.startAndWait();\n+    } catch (Exception e) {\n+      LOG.error(\"Failed to start service: \", e);\n+    }\n+  }\n+\n+  @Before\n+  public void reset() throws Exception {\n+    txClient.resetState();\n+  }\n+\n+  @AfterClass\n+  public static void stop() throws Exception {\n+    txService.stopAndWait();\n+    zkClientService.stopAndWait();\n+    zkServer.stopAndWait();\n+  }\n+\n+  @Test\n+  public void testPrintUsage() throws Exception {\n+    ByteArrayOutputStream out = new ByteArrayOutputStream();\n+    ByteArrayOutputStream err = new ByteArrayOutputStream();\n+    TransactionAdmin txAdmin = new TransactionAdmin(new PrintStream(out), new PrintStream(err));\n+    txAdmin.doMain(new String[0], conf);\n+    //noinspection ConstantConditions\n+    Assert.assertTrue(Bytes.toString(err.toByteArray()).startsWith(\"Usage:\"));\n+    Assert.assertEquals(0, out.toByteArray().length);\n+  }\n+\n+  @Test\n+  public void testTruncateInvalidTx() throws Exception {\n+    Transaction tx1 = txClient.startLong();\n+    Transaction tx2 = txClient.startShort();\n+    txClient.invalidate(tx1.getWritePointer());\n+    txClient.invalidate(tx2.getWritePointer());\n+    \n+    ByteArrayOutputStream out = new ByteArrayOutputStream();\n+    ByteArrayOutputStream err = new ByteArrayOutputStream();\n+    TransactionAdmin txAdmin = new TransactionAdmin(new PrintStream(out), new PrintStream(err));\n+    txAdmin.doMain(new String[]{\"--truncate-invalid-tx\", String.valueOf(tx2.getWritePointer())}, conf);\n+    List<String> expected = ImmutableList.of(\"Invalid list size before truncation: 2\",","path":"tephra-core/src/test/java/co/cask/tephra/TransactionAdminTest.java","position":139,"original_position":139,"commit_id":"d0559e6143c1f15de0a077aab28e5974a108e5c7","original_commit_id":"d0559e6143c1f15de0a077aab28e5974a108e5c7","user":{"login":"ghelmling","id":88435,"avatar_url":"https://avatars.githubusercontent.com/u/88435?v=3","gravatar_id":"","url":"https://api.github.com/users/ghelmling","html_url":"https://github.com/ghelmling","followers_url":"https://api.github.com/users/ghelmling/followers","following_url":"https://api.github.com/users/ghelmling/following{/other_user}","gists_url":"https://api.github.com/users/ghelmling/gists{/gist_id}","starred_url":"https://api.github.com/users/ghelmling/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ghelmling/subscriptions","organizations_url":"https://api.github.com/users/ghelmling/orgs","repos_url":"https://api.github.com/users/ghelmling/repos","events_url":"https://api.github.com/users/ghelmling/events{/privacy}","received_events_url":"https://api.github.com/users/ghelmling/received_events","type":"User","site_admin":false},"body":"I think that tests should validate behavior, not copy (text).  Asserting on output or log messages just doesn't seem like the right thing to do and doesn't add much value.  It works until someone else wants to improve the output, say to add the time elapsed for the call.  Then the output becomes less deterministic and has to be updated in any tests asserting on it.  In my opinion, we should test that the tool correctly calls the function it needs to, not what the form of its output is.","created_at":"2015-03-10T07:23:09Z","updated_at":"2015-03-10T07:23:09Z","html_url":"https://github.com/caskdata/tephra/pull/36#discussion_r26102612","pull_request_url":"https://api.github.com/repos/caskdata/tephra/pulls/36","_links":{"self":{"href":"https://api.github.com/repos/caskdata/tephra/pulls/comments/26102612"},"html":{"href":"https://github.com/caskdata/tephra/pull/36#discussion_r26102612"},"pull_request":{"href":"https://api.github.com/repos/caskdata/tephra/pulls/36"}}},"pull_request":{"url":"https://api.github.com/repos/caskdata/tephra/pulls/36","id":30699827,"html_url":"https://github.com/caskdata/tephra/pull/36","diff_url":"https://github.com/caskdata/tephra/pull/36.diff","patch_url":"https://github.com/caskdata/tephra/pull/36.patch","issue_url":"https://api.github.com/repos/caskdata/tephra/issues/36","number":36,"state":"open","locked":false,"title":"Truncate invalid transaction list functionality and TransactionAdmin tool","user":{"login":"poornachandra","id":3181212,"avatar_url":"https://avatars.githubusercontent.com/u/3181212?v=3","gravatar_id":"","url":"https://api.github.com/users/poornachandra","html_url":"https://github.com/poornachandra","followers_url":"https://api.github.com/users/poornachandra/followers","following_url":"https://api.github.com/users/poornachandra/following{/other_user}","gists_url":"https://api.github.com/users/poornachandra/gists{/gist_id}","starred_url":"https://api.github.com/users/poornachandra/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/poornachandra/subscriptions","organizations_url":"https://api.github.com/users/poornachandra/orgs","repos_url":"https://api.github.com/users/poornachandra/repos","events_url":"https://api.github.com/users/poornachandra/events{/privacy}","received_events_url":"https://api.github.com/users/poornachandra/received_events","type":"User","site_admin":false},"body":"Addresses https://issues.cask.co/browse/TEPHRA-55","created_at":"2015-03-07T08:37:34Z","updated_at":"2015-03-10T07:23:09Z","closed_at":null,"merged_at":null,"merge_commit_sha":"78a866baa83971e5e2a8d1266539f77c592c8a99","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/caskdata/tephra/pulls/36/commits","review_comments_url":"https://api.github.com/repos/caskdata/tephra/pulls/36/comments","review_comment_url":"https://api.github.com/repos/caskdata/tephra/pulls/comments{/number}","comments_url":"https://api.github.com/repos/caskdata/tephra/issues/36/comments","statuses_url":"https://api.github.com/repos/caskdata/tephra/statuses/d0559e6143c1f15de0a077aab28e5974a108e5c7","head":{"label":"caskdata:feature/truncate-invalid-tx-list","ref":"feature/truncate-invalid-tx-list","sha":"d0559e6143c1f15de0a077aab28e5974a108e5c7","user":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"repo":{"id":21894018,"name":"tephra","full_name":"caskdata/tephra","owner":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/caskdata/tephra","description":"Transactions for HBase","fork":false,"url":"https://api.github.com/repos/caskdata/tephra","forks_url":"https://api.github.com/repos/caskdata/tephra/forks","keys_url":"https://api.github.com/repos/caskdata/tephra/keys{/key_id}","collaborators_url":"https://api.github.com/repos/caskdata/tephra/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/caskdata/tephra/teams","hooks_url":"https://api.github.com/repos/caskdata/tephra/hooks","issue_events_url":"https://api.github.com/repos/caskdata/tephra/issues/events{/number}","events_url":"https://api.github.com/repos/caskdata/tephra/events","assignees_url":"https://api.github.com/repos/caskdata/tephra/assignees{/user}","branches_url":"https://api.github.com/repos/caskdata/tephra/branches{/branch}","tags_url":"https://api.github.com/repos/caskdata/tephra/tags","blobs_url":"https://api.github.com/repos/caskdata/tephra/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/caskdata/tephra/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/caskdata/tephra/git/refs{/sha}","trees_url":"https://api.github.com/repos/caskdata/tephra/git/trees{/sha}","statuses_url":"https://api.github.com/repos/caskdata/tephra/statuses/{sha}","languages_url":"https://api.github.com/repos/caskdata/tephra/languages","stargazers_url":"https://api.github.com/repos/caskdata/tephra/stargazers","contributors_url":"https://api.github.com/repos/caskdata/tephra/contributors","subscribers_url":"https://api.github.com/repos/caskdata/tephra/subscribers","subscription_url":"https://api.github.com/repos/caskdata/tephra/subscription","commits_url":"https://api.github.com/repos/caskdata/tephra/commits{/sha}","git_commits_url":"https://api.github.com/repos/caskdata/tephra/git/commits{/sha}","comments_url":"https://api.github.com/repos/caskdata/tephra/comments{/number}","issue_comment_url":"https://api.github.com/repos/caskdata/tephra/issues/comments{/number}","contents_url":"https://api.github.com/repos/caskdata/tephra/contents/{+path}","compare_url":"https://api.github.com/repos/caskdata/tephra/compare/{base}...{head}","merges_url":"https://api.github.com/repos/caskdata/tephra/merges","archive_url":"https://api.github.com/repos/caskdata/tephra/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/caskdata/tephra/downloads","issues_url":"https://api.github.com/repos/caskdata/tephra/issues{/number}","pulls_url":"https://api.github.com/repos/caskdata/tephra/pulls{/number}","milestones_url":"https://api.github.com/repos/caskdata/tephra/milestones{/number}","notifications_url":"https://api.github.com/repos/caskdata/tephra/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/caskdata/tephra/labels{/name}","releases_url":"https://api.github.com/repos/caskdata/tephra/releases{/id}","created_at":"2014-07-16T09:07:44Z","updated_at":"2015-03-04T20:45:06Z","pushed_at":"2015-03-10T05:04:25Z","git_url":"git://github.com/caskdata/tephra.git","ssh_url":"git@github.com:caskdata/tephra.git","clone_url":"https://github.com/caskdata/tephra.git","svn_url":"https://github.com/caskdata/tephra","homepage":"http://tephra.io","size":1877,"stargazers_count":69,"watchers_count":69,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":15,"mirror_url":null,"open_issues_count":5,"forks":15,"open_issues":5,"watchers":69,"default_branch":"develop"}},"base":{"label":"caskdata:develop","ref":"develop","sha":"9be9c8b4faf85f3e8e66e408d66bdecf2deed92a","user":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"repo":{"id":21894018,"name":"tephra","full_name":"caskdata/tephra","owner":{"login":"caskdata","id":8123023,"avatar_url":"https://avatars.githubusercontent.com/u/8123023?v=3","gravatar_id":"","url":"https://api.github.com/users/caskdata","html_url":"https://github.com/caskdata","followers_url":"https://api.github.com/users/caskdata/followers","following_url":"https://api.github.com/users/caskdata/following{/other_user}","gists_url":"https://api.github.com/users/caskdata/gists{/gist_id}","starred_url":"https://api.github.com/users/caskdata/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/caskdata/subscriptions","organizations_url":"https://api.github.com/users/caskdata/orgs","repos_url":"https://api.github.com/users/caskdata/repos","events_url":"https://api.github.com/users/caskdata/events{/privacy}","received_events_url":"https://api.github.com/users/caskdata/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/caskdata/tephra","description":"Transactions for HBase","fork":false,"url":"https://api.github.com/repos/caskdata/tephra","forks_url":"https://api.github.com/repos/caskdata/tephra/forks","keys_url":"https://api.github.com/repos/caskdata/tephra/keys{/key_id}","collaborators_url":"https://api.github.com/repos/caskdata/tephra/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/caskdata/tephra/teams","hooks_url":"https://api.github.com/repos/caskdata/tephra/hooks","issue_events_url":"https://api.github.com/repos/caskdata/tephra/issues/events{/number}","events_url":"https://api.github.com/repos/caskdata/tephra/events","assignees_url":"https://api.github.com/repos/caskdata/tephra/assignees{/user}","branches_url":"https://api.github.com/repos/caskdata/tephra/branches{/branch}","tags_url":"https://api.github.com/repos/caskdata/tephra/tags","blobs_url":"https://api.github.com/repos/caskdata/tephra/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/caskdata/tephra/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/caskdata/tephra/git/refs{/sha}","trees_url":"https://api.github.com/repos/caskdata/tephra/git/trees{/sha}","statuses_url":"https://api.github.com/repos/caskdata/tephra/statuses/{sha}","languages_url":"https://api.github.com/repos/caskdata/tephra/languages","stargazers_url":"https://api.github.com/repos/caskdata/tephra/stargazers","contributors_url":"https://api.github.com/repos/caskdata/tephra/contributors","subscribers_url":"https://api.github.com/repos/caskdata/tephra/subscribers","subscription_url":"https://api.github.com/repos/caskdata/tephra/subscription","commits_url":"https://api.github.com/repos/caskdata/tephra/commits{/sha}","git_commits_url":"https://api.github.com/repos/caskdata/tephra/git/commits{/sha}","comments_url":"https://api.github.com/repos/caskdata/tephra/comments{/number}","issue_comment_url":"https://api.github.com/repos/caskdata/tephra/issues/comments{/number}","contents_url":"https://api.github.com/repos/caskdata/tephra/contents/{+path}","compare_url":"https://api.github.com/repos/caskdata/tephra/compare/{base}...{head}","merges_url":"https://api.github.com/repos/caskdata/tephra/merges","archive_url":"https://api.github.com/repos/caskdata/tephra/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/caskdata/tephra/downloads","issues_url":"https://api.github.com/repos/caskdata/tephra/issues{/number}","pulls_url":"https://api.github.com/repos/caskdata/tephra/pulls{/number}","milestones_url":"https://api.github.com/repos/caskdata/tephra/milestones{/number}","notifications_url":"https://api.github.com/repos/caskdata/tephra/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/caskdata/tephra/labels{/name}","releases_url":"https://api.github.com/repos/caskdata/tephra/releases{/id}","created_at":"2014-07-16T09:07:44Z","updated_at":"2015-03-04T20:45:06Z","pushed_at":"2015-03-10T05:04:25Z","git_url":"git://github.com/caskdata/tephra.git","ssh_url":"git@github.com:caskdata/tephra.git","clone_url":"https://github.com/caskdata/tephra.git","svn_url":"https://github.com/caskdata/tephra","homepage":"http://tephra.io","size":1877,"stargazers_count":69,"watchers_count":69,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":15,"mirror_url":null,"open_issues_count":5,"forks":15,"open_issues":5,"watchers":69,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/caskdata/tephra/pulls/36"},"html":{"href":"https://github.com/caskdata/tephra/pull/36"},"issue":{"href":"https://api.github.com/repos/caskdata/tephra/issues/36"},"comments":{"href":"https://api.github.com/repos/caskdata/tephra/issues/36/comments"},"review_comments":{"href":"https://api.github.com/repos/caskdata/tephra/pulls/36/comments"},"review_comment":{"href":"https://api.github.com/repos/caskdata/tephra/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/caskdata/tephra/pulls/36/commits"},"statuses":{"href":"https://api.github.com/repos/caskdata/tephra/statuses/d0559e6143c1f15de0a077aab28e5974a108e5c7"}}}}