Coverage Report - com.jcabi.github.mock.MkContents
 
Classes in this File Line Coverage Branch Coverage Complexity
MkContents
93%
89/95
34%
15/44
1.562
MkContents$AjcClosure1
100%
1/1
N/A
1.562
MkContents$AjcClosure11
100%
1/1
N/A
1.562
MkContents$AjcClosure13
100%
1/1
N/A
1.562
MkContents$AjcClosure15
100%
1/1
N/A
1.562
MkContents$AjcClosure17
100%
1/1
N/A
1.562
MkContents$AjcClosure19
100%
1/1
N/A
1.562
MkContents$AjcClosure3
100%
1/1
N/A
1.562
MkContents$AjcClosure5
100%
1/1
N/A
1.562
MkContents$AjcClosure7
100%
1/1
N/A
1.562
MkContents$AjcClosure9
100%
1/1
N/A
1.562
 
 1  6
 /**
 2  
  * Copyright (c) 2013-2017, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.github.mock;
 31  
 
 32  
 import com.jcabi.aspects.Immutable;
 33  
 import com.jcabi.aspects.Loggable;
 34  
 import com.jcabi.github.Content;
 35  
 import com.jcabi.github.Contents;
 36  
 import com.jcabi.github.Coordinates;
 37  
 import com.jcabi.github.Repo;
 38  
 import com.jcabi.github.RepoCommit;
 39  
 import com.jcabi.xml.XML;
 40  
 import java.io.IOException;
 41  
 import java.util.ArrayList;
 42  
 import java.util.Collection;
 43  
 import javax.json.JsonObject;
 44  
 import lombok.EqualsAndHashCode;
 45  
 import lombok.ToString;
 46  
 import org.apache.commons.lang3.RandomStringUtils;
 47  
 import org.xembly.Directives;
 48  
 
 49  
 /**
 50  
  * Mock Github contents.
 51  
  *
 52  
  * @author Andres Candal (andres.candal@rollasolution.com)
 53  
  * @version $Id: dd7d58dcd2e259617bde3c723d1e36be422cf582 $
 54  
  * @since 0.8
 55  
  */
 56  
 @Immutable
 57  
 @Loggable(Loggable.DEBUG)
 58  0
 @ToString
 59  0
 @EqualsAndHashCode(of = { "storage", "self", "coords" })
 60  
 @SuppressWarnings({ "PMD.AvoidDuplicateLiterals", "PMD.TooManyMethods" })
 61  
 final class MkContents implements Contents {
 62  
 
 63  
     /**
 64  
      * Storage.
 65  
      */
 66  
     private final transient MkStorage storage;
 67  
 
 68  
     /**
 69  
      * Login of the user logged in.
 70  
      */
 71  
     private final transient String self;
 72  
 
 73  
     /**
 74  
      * Repo name.
 75  
      */
 76  
     private final transient Coordinates coords;
 77  
 
 78  
     /**
 79  
      * Public ctor.
 80  
      * @param stg Storage
 81  
      * @param login User to login
 82  
      * @param rep Repo
 83  
      * @throws IOException If there is any I/O problem
 84  
      */
 85  
     public MkContents(
 86  
         final MkStorage stg,
 87  
         final String login,
 88  
         final Coordinates rep
 89  22
     ) throws IOException {
 90  22
         this.storage = stg;
 91  22
         this.self = login;
 92  22
         this.coords = rep;
 93  44
         this.storage.apply(
 94  22
             new Directives().xpath(
 95  22
                 String.format("/github/repos/repo[@coords='%s']", this.coords)
 96  22
             ).addIf("contents").up().addIf("commits")
 97  
         );
 98  22
     }
 99  
 
 100  
     @Override
 101  
     public Repo repo() {
 102  48
         return new MkRepo(this.storage, this.self, this.coords);
 103  
     }
 104  
 
 105  
     @Override
 106  
     public Content readme() throws IOException {
 107  
         // @checkstyle MultipleStringLiterals (1 line)
 108  2
         return this.readme("master");
 109  
     }
 110  
 
 111  
     @Override
 112  
     public Content readme(
 113  
         final String branch
 114  
     ) throws IOException {
 115  4
         return new MkContent(
 116  
             this.storage, this.self, this.coords, "README.md", branch
 117  
         );
 118  
     }
 119  
 
 120  
     @Override
 121  
     public Content create(
 122  
         final JsonObject json
 123  
     ) throws IOException {
 124  38
         this.storage.lock();
 125  
         // @checkstyle MultipleStringLiterals (20 lines)
 126  
         final String branch;
 127  
         try {
 128  19
             if (json.containsKey("ref")) {
 129  8
                 branch = json.getString("ref");
 130  
             } else {
 131  11
                 branch = "master";
 132  
             }
 133  38
             this.storage.apply(
 134  19
                 new Directives().xpath(this.xpath()).add("content")
 135  19
                     .attr("ref", branch)
 136  19
                     .add("name").set(json.getString("path")).up()
 137  19
                     .add("path").set(json.getString("path")).up()
 138  19
                     .add("content").set(json.getString("content")).up()
 139  19
                     .add("type").set("file").up()
 140  19
                     .add("encoding").set("base64").up()
 141  19
                     .add("sha").set(fakeSha()).up()
 142  19
                     .add("url").set("http://localhost/1").up()
 143  19
                     .add("git_url").set("http://localhost/2").up()
 144  19
                     .add("html_url").set("http://localhost/3").up()
 145  
             );
 146  19
             this.commit(json);
 147  
         } finally {
 148  19
             this.storage.unlock();
 149  19
         }
 150  38
         return new MkContent(
 151  19
             this.storage, this.self, this.coords, json.getString("path"), branch
 152  
         );
 153  
     }
 154  
 
 155  
     @Override
 156  
     public Content get(
 157  
         final String path,
 158  
         final String ref
 159  
     ) throws IOException {
 160  6
         return new MkContent(this.storage, this.self, this.coords, path, ref);
 161  
     }
 162  
 
 163  
     @Override
 164  
     public Content get(
 165  
         final String path
 166  
     ) throws IOException {
 167  2
         return new MkContent(
 168  
             this.storage, this.self, this.coords, path, "master"
 169  
         );
 170  
     }
 171  
 
 172  
     @Override
 173  
     public Iterable<Content> iterate(
 174  
         final String pattern,
 175  
         final String ref
 176  
     ) throws IOException {
 177  3
         final Collection<XML> nodes = this.storage.xml().nodes(
 178  1
             String.format("%s/content[@ref='%s']", this.xpath(), ref)
 179  
         );
 180  1
         final Collection<Content> result = new ArrayList<Content>(nodes.size());
 181  1
         for (final XML node : nodes) {
 182  4
             final String path = node.xpath("path/text()").get(0);
 183  4
             if (path.startsWith(pattern)) {
 184  4
                 result.add(
 185  2
                     this.mkContent(ref, path)
 186  
                 );
 187  
             }
 188  4
         }
 189  1
         return result;
 190  
     }
 191  
 
 192  
     @Override
 193  
     public RepoCommit remove(
 194  
         final JsonObject content
 195  
     ) throws IOException {
 196  4
         this.storage.lock();
 197  2
         final String path = content.getString("path");
 198  
         // @checkstyle MultipleStringLiterals (20 lines)
 199  
         final String branch;
 200  
         try {
 201  2
             if (content.containsKey("ref")) {
 202  1
                 branch = content.getString("ref");
 203  
             } else {
 204  1
                 branch = "master";
 205  
             }
 206  4
             this.storage.apply(
 207  
                 new Directives()
 208  2
                     .xpath(this.xpath())
 209  2
                     .xpath(String.format("content[path='%s']", path))
 210  2
                     .attr("ref", branch)
 211  2
                     .remove()
 212  
             );
 213  4
             return this.commit(content);
 214  
         } finally {
 215  2
             this.storage.unlock();
 216  
         }
 217  
     }
 218  
 
 219  
     /**
 220  
      * Updates a file.
 221  
      * @param path The content path.
 222  
      * @param json JSON object containing updates to the content.
 223  
      * @return Commit related to this update.
 224  
      * @throws IOException If any I/O problem occurs.
 225  
      */
 226  
     @Override
 227  
     public RepoCommit update(
 228  
         final String path,
 229  
         final JsonObject json
 230  
     ) throws IOException {
 231  6
         this.storage.lock();
 232  
         try {
 233  3
             final String ref = "ref";
 234  
             final String branch;
 235  3
             if (json.containsKey(ref)) {
 236  1
                 branch = json.getString(ref);
 237  
             } else {
 238  2
                 branch = "master";
 239  
             }
 240  3
             final String xpath = String.format(
 241  
                 // @checkstyle LineLengthCheck (1 line)
 242  
                 "/github/repos/repo[@coords='%s']/contents/content[path='%s' and @ref='%s']",
 243  
                 this.coords, path, branch
 244  
             );
 245  3
             new JsonPatch(this.storage).patch(xpath, json);
 246  6
             return this.commit(json);
 247  
         } finally {
 248  3
             this.storage.unlock();
 249  
         }
 250  
     }
 251  
 
 252  
     @Override
 253  
     public boolean exists(final String path, final String ref)
 254  
         throws IOException {
 255  8
         return this.storage.xml().nodes(
 256  2
             String.format("%s/content[path='%s']", this.xpath(), path)
 257  2
         ).size() > 0;
 258  
     }
 259  
 
 260  
     /**
 261  
      * Builder method for MkContent.
 262  
      * @param ref Branch name.
 263  
      * @param path Path to MkContent.
 264  
      * @return MkContent instance.
 265  
      * @throws IOException if any I/O error occurs.
 266  
      */
 267  
     private MkContent mkContent(final String ref, final String path)
 268  
         throws IOException {
 269  2
         return new MkContent(this.storage, this.self, this.coords, path, ref);
 270  
     }
 271  
 
 272  
     /**
 273  
      * XPath of this element in XML tree.
 274  
      * @return The XPath
 275  
      */
 276  
     private String xpath() {
 277  24
         return String.format(
 278  
             "/github/repos/repo[@coords='%s']/contents",
 279  
             this.coords
 280  
         );
 281  
     }
 282  
 
 283  
     /**
 284  
      * Xpath of the commits element in XML tree.
 285  
      * @return Xpath
 286  
      */
 287  
     private String commitXpath() {
 288  24
         return String.format(
 289  
             "/github/repos/repo[@coords='%s']/commits",
 290  
             this.coords
 291  
         );
 292  
     }
 293  
 
 294  
     /**
 295  
      * XML Directives for commit creation.
 296  
      * @param json Source
 297  
      * @return SHA string
 298  
      * @throws IOException If an IO Exception occurs
 299  
      */
 300  
     private MkRepoCommit commit(
 301  
         final JsonObject json
 302  
     ) throws IOException {
 303  24
         final String sha = fakeSha();
 304  
         // @checkstyle MultipleStringLiterals (40 lines)
 305  24
         final Directives commit = new Directives().xpath(this.commitXpath())
 306  24
             .add("commit")
 307  24
             .add("sha").set(sha).up()
 308  24
             .add("url").set("http://localhost/4").up()
 309  24
             .add("html_url").set("http://localhost/5").up()
 310  24
             .add("message").set(json.getString("message")).up();
 311  24
         if (json.containsKey("committer")) {
 312  6
             final JsonObject committer = json.getJsonObject("committer");
 313  6
             commit.add("committer")
 314  6
                 .add("email").set(committer.getString("email")).up()
 315  6
                 .add("name").set(committer.getString("name")).up();
 316  
         }
 317  24
         if (json.containsKey("author")) {
 318  0
             final JsonObject author = json.getJsonObject("author");
 319  0
             commit.add("author")
 320  0
                 .add("email").set(author.getString("email")).up()
 321  0
                 .add("name").set(author.getString("name")).up();
 322  
         }
 323  24
         this.storage.apply(commit);
 324  24
         return new MkRepoCommit(this.storage, this.repo(), sha);
 325  
     }
 326  
 
 327  
     /**
 328  
      * Generate a random fake SHA hex string.
 329  
      *
 330  
      * @return Fake SHA string.
 331  
      */
 332  
     private static String fakeSha() {
 333  
         // @checkstyle MagicNumberCheck (1 line)
 334  43
         return RandomStringUtils.random(40, "0123456789abcdef");
 335  
     }
 336  
 }