From f6575793413932cff8a2346863247989dc98cd9a Mon Sep 17 00:00:00 2001 From: Joaquim Alvino de Mesquita Neto Date: Tue, 13 Jul 2021 13:13:06 -0300 Subject: [PATCH] Improved the sturdyness of repository deletion (#3) Refactoring of the extension, changing from a inheritance based structure, to a composition based one. This helps a lot with testing, and makes the code easier to understand, and it didn't changed any public interfaces, so it is entirely retro compatible. The main points of the refactor are: Repository creation behavior isolated in the RepositoryFactory class The GithubRepository/SharedGithubRepository/GithubRepositoryFeature interceptors now inherits directly from AbstractMethodInterceptor. The underlying operations on Field-based interceptors are now in the RepositoryFieldOperations which is a slightly-changed, and has similar methods to the old GithubRepositoryFieldInterceptor. The slightly more complex object creation process is isolated in static factory methods in the interceptors. Besides there were several some in the deletion code to make it more sturdy, the main ones being: deletions now are retried in case of non FileNotFoundException exceptions, and also checks if the repository still exists before trying deletion Catch blocks that ignored Exception exceptions in the deletion process, now are only ignoring FileNotFoundException (404) exceptions, to make more clear which errors are happening. Added tests for the specific situation of creating new repositories with the same name of existing ones. The refactoring is isolated in the refactor/interceptors branch, for ease of comparison, and if so wished, a new PR with only the refactored code can be open as well. Co-authored-by: Joaquim Neto --- .../github/GithubRepositoryExtension.groovy | 17 +- .../spock/extensions/github/Repository.groovy | 24 +- ...ceptor.groovy => RepositoryFactory.groovy} | 299 +++++++++--------- .../interceptor/FieldInterceptor.groovy | 7 + .../GithubRepositoryFeatureInterceptor.groovy | 57 ++-- .../GithubRepositoryInterceptor.groovy | 25 +- ...roovy => RepositoryFieldOperations.groovy} | 30 +- .../SharedGithubRepositoryInterceptor.groovy | 33 +- .../github/GithubRepositorySpec.groovy | 1 + .../github/RepositoryFactorySpec.groovy | 71 +++++ 10 files changed, 327 insertions(+), 237 deletions(-) rename src/main/groovy/com/wooga/spock/extensions/github/{interceptor/GithubRepositoryManagingInterceptor.groovy => RepositoryFactory.groovy} (52%) create mode 100644 src/main/groovy/com/wooga/spock/extensions/github/interceptor/FieldInterceptor.groovy rename src/main/groovy/com/wooga/spock/extensions/github/interceptor/{GithubRepositoryFieldInterceptor.groovy => RepositoryFieldOperations.groovy} (79%) create mode 100644 src/test/groovy/com/wooga/spock/extensions/github/RepositoryFactorySpec.groovy diff --git a/src/main/groovy/com/wooga/spock/extensions/github/GithubRepositoryExtension.groovy b/src/main/groovy/com/wooga/spock/extensions/github/GithubRepositoryExtension.groovy index 6457cf6..ffd3cb0 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/GithubRepositoryExtension.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/GithubRepositoryExtension.groovy @@ -17,7 +17,7 @@ package com.wooga.spock.extensions.github - +import com.wooga.spock.extensions.github.interceptor.FieldInterceptor import com.wooga.spock.extensions.github.interceptor.GithubRepositoryFeatureInterceptor import com.wooga.spock.extensions.github.interceptor.GithubRepositoryInterceptor import com.wooga.spock.extensions.github.interceptor.SharedGithubRepositoryInterceptor @@ -27,23 +27,18 @@ import org.spockframework.runtime.model.FieldInfo class GithubRepositoryExtension extends AbstractAnnotationDrivenExtension { + @Override void visitFeatureAnnotation(GithubRepository annotation, FeatureInfo feature) { - def interceptor - - interceptor = new GithubRepositoryFeatureInterceptor(annotation) + def interceptor = GithubRepositoryFeatureInterceptor.withMetadata(annotation) interceptor.install(feature) } @Override void visitFieldAnnotation(GithubRepository annotation, FieldInfo field) { - def interceptor - - if (field.isShared()) { - interceptor = new SharedGithubRepositoryInterceptor(annotation) - } else { - interceptor = new GithubRepositoryInterceptor(annotation) - } + FieldInterceptor interceptor = field.isShared()? + SharedGithubRepositoryInterceptor.withMetadata(annotation, field) : + GithubRepositoryInterceptor.withMetadata(annotation, field) interceptor.install(field) } diff --git a/src/main/groovy/com/wooga/spock/extensions/github/Repository.groovy b/src/main/groovy/com/wooga/spock/extensions/github/Repository.groovy index 7605144..9eca52a 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/Repository.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/Repository.groovy @@ -32,6 +32,7 @@ class Repository { private interface GHRepositoryDelegateExcludes { String getDefaultBranch() + void delete() } @Delegate(excludeTypes = [GHRepositoryDelegateExcludes.class]) @@ -61,9 +62,9 @@ class Repository { boolean exists() { try { - client.getRepository(fullName) + client.getRepositoryById(repository.id) return true - } catch (ignored) { + } catch (FileNotFoundException) { return false } } @@ -129,6 +130,23 @@ class Repository { findTag(tagName).orElse(null) } + void delete(int retries=3) { + try { + if(this.exists()) { + repository.delete() + } + } catch (FileNotFoundException _) { + } catch (Exception e) { + if(retries > 0) { + sleep(500) + delete(retries-1) + } else { + throw e + } + } + + } + Optional findTag(String tagName) { //there is no other way at the moment to load a GHTag Optional.ofNullable(listTags().find { it.name == tagName } as GHTag) @@ -235,6 +253,8 @@ class Repository { } } + + private static void tryToDelete(GHRef ref) { try { ref.delete() diff --git a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryManagingInterceptor.groovy b/src/main/groovy/com/wooga/spock/extensions/github/RepositoryFactory.groovy similarity index 52% rename from src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryManagingInterceptor.groovy rename to src/main/groovy/com/wooga/spock/extensions/github/RepositoryFactory.groovy index 176c06d..b29ecd2 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryManagingInterceptor.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/RepositoryFactory.groovy @@ -1,159 +1,142 @@ -/* - * Copyright 2019 Wooga GmbH - * - * 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. - * - */ - -package com.wooga.spock.extensions.github.interceptor - -import com.wooga.spock.extensions.github.GithubRepository -import com.wooga.spock.extensions.github.Repository -import com.wooga.spock.extensions.github.api.RepositoryPostFix -import groovy.transform.InheritConstructors -import org.kohsuke.github.GHRepository -import org.kohsuke.github.GitHub -import org.kohsuke.github.GitHubBuilder -import org.spockframework.runtime.extension.AbstractMethodInterceptor -import org.spockframework.runtime.extension.IMethodInvocation -import org.spockframework.runtime.model.NodeInfo - -@InheritConstructors -abstract class GithubRepositoryManagingInterceptor extends AbstractMethodInterceptor { - - protected final GithubRepository metadata - - private GitHub client - private String repositoryName - private String repositoryOwner - private String token - private T info - - T getInfo() { - this.info - } - - GithubRepositoryManagingInterceptor(GithubRepository metadata) { - super() - this.metadata = metadata - } - - void install(T info) { - this.info = info - } - - void maybeDelete(String repoName) { - try { - def repository = getClient().getRepository(repoName) - repository.delete() - } - catch (Exception e) { - } - } - - String getRepositoryBaseName() { - info.name.replaceAll(/\s|\[|\]|\(|\)|\{|\}/, '-') - } - - String getRepositoryName() { - if (!repositoryName) { - - String prefix = metadata.repositoryNamePrefix() - List postFixProvider = metadata.repositoryPostFixProvider().collect { - it.getDeclaredConstructor().newInstance() - } as List - - String postfix = postFixProvider.inject("") { String postFix, provider -> - String post = provider.postFix - if (post && !post.empty) { - postFix += "-${provider.postFix}" - } - postFix - } - - repositoryName = "${getRepositoryBaseName()}${postfix}" - - if(prefix) { - repositoryName = "${prefix}-${repositoryName}" - } - } - - repositoryName - } - - String getRepositoryFullName() { - "${getRepositoryOwner()}/${getRepositoryName()}" - } - - String getRepositoryOwner() { - if (!repositoryOwner) { - repositoryOwner = System.getenv(metadata.usernameEnv()) - } - - repositoryOwner - } - - String getToken() { - if (!token) { - token = System.getenv(metadata.tokenEnv()) - } - - token - } - - GitHub getClient() { - if (!client) { - def builder = new GitHubBuilder() - client = builder.withOAuthToken(System.getenv()[metadata.tokenEnv()]) - .withEndpoint(metadata.endpoint()) - .withRateLimitHandler(metadata.rateLimitHandler().getDeclaredConstructor().newInstance()) - .build() - } - - client - } - - Repository setupRepository(IMethodInvocation invocation) { - maybeDelete(getRepositoryFullName()) - - //create github repo - def builder = getClient().createRepository(getRepositoryName()) - builder.description(metadata.repositoryDescription()) - builder.autoInit(metadata.autoInit()) - - if (metadata.autoInit()) { - builder.licenseTemplate(metadata.licenseTemplate()) - if (metadata.gitignoreTemplate()) { - builder.gitignoreTemplate(metadata.gitignoreTemplate()) - } - } - - builder.private_(metadata.createPrivateRepository()) - builder.issues(metadata.setupIssues()) - builder.wiki(metadata.setupWiki()) - builder.allowSquashMerge(metadata.allowSquashMerge()) - builder.allowRebaseMerge(metadata.allowRebaseMerge()) - builder.allowMergeCommit(metadata.allowMergeCommit()) - builder.downloads(metadata.enableDownloads()) - - - GHRepository repository = builder.create() - new Repository(repository, getClient(), getRepositoryOwner(), getToken()) - } - - abstract void destroyRepository(IMethodInvocation invocation) - - abstract void resetRepository(IMethodInvocation invocation) - - abstract void captureResetRef(IMethodInvocation invocation) +/* + * Copyright 2019 Wooga GmbH + * + * 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. + * + */ + +package com.wooga.spock.extensions.github + + +import com.wooga.spock.extensions.github.api.RepositoryPostFix +import groovy.transform.InheritConstructors +import org.kohsuke.github.GHRepository +import org.kohsuke.github.GitHub +import org.kohsuke.github.GitHubBuilder +import org.spockframework.runtime.model.NodeInfo + +@InheritConstructors +class RepositoryFactory { + + protected final GithubRepository metadata + + private GitHub client + private String repositoryOwner + private String token + + static String getRepositoryBaseName(NodeInfo info) { + info.name.replaceAll(/\s|\[|\]|\(|\)|\{|\}/, '-') + } + + RepositoryFactory(GithubRepository metadata) { + this.metadata = metadata + } + + Repository setupRepository(String repositoryBaseName) { + def repositoryName = createRepositoryName(repositoryBaseName) + deleteRepoIfExists(getRepositoryFullName(repositoryName)) + + //create github repo + def builder = getClient().createRepository(repositoryName) + builder.description(metadata.repositoryDescription()) + builder.autoInit(metadata.autoInit()) + + if (metadata.autoInit()) { + builder.licenseTemplate(metadata.licenseTemplate()) + if (metadata.gitignoreTemplate()) { + builder.gitignoreTemplate(metadata.gitignoreTemplate()) + } + } + + builder.private_(metadata.createPrivateRepository()) + builder.issues(metadata.setupIssues()) + builder.wiki(metadata.setupWiki()) + builder.allowSquashMerge(metadata.allowSquashMerge()) + builder.allowRebaseMerge(metadata.allowRebaseMerge()) + builder.allowMergeCommit(metadata.allowMergeCommit()) + builder.downloads(metadata.enableDownloads()) + + + GHRepository repository = builder.create() + new Repository(repository, getClient(), getRepositoryOwner(), getToken()) + } + + protected void deleteRepoIfExists(String repoName) { + try { + def repository = getClient().getRepository(repoName) + repository?.delete() + } + catch (FileNotFoundException e) { + } + } + + protected String createRepositoryName(String repositoryBaseName) { + String prefix = metadata.repositoryNamePrefix() + List postFixProvider = metadata.repositoryPostFixProvider().collect { + it.getDeclaredConstructor().newInstance() + } as List + + String postfix = postFixProvider.inject("") { String postFix, provider -> + String post = provider.postFix + if (post && !post.empty) { + postFix += "-${provider.postFix}" + } + return postFix + } + + def repositoryName = "${repositoryBaseName}${postfix}" + if(prefix) { + repositoryName = "${prefix}-${repositoryName}" + } + return repositoryName + } + + protected String getRepositoryFullName(String repositoryName) { + return "${getRepositoryOwner()}/${repositoryName}" + } + + protected String getRepositoryOwner() { + if (!repositoryOwner) { + repositoryOwner = getClient().myself.login + } + repositoryOwner + } + + protected String getToken() { + if (!token) { + token = mustGetEnvVar(metadata.tokenEnv()) + } + token + } + + protected GitHub getClient() { + if (!client) { + def builder = new GitHubBuilder() + client = builder.withOAuthToken(getToken()) + .withEndpoint(metadata.endpoint()) + .withRateLimitHandler(metadata.rateLimitHandler().getDeclaredConstructor().newInstance()) + .build() + } + return client + } + + private static String mustGetEnvVar(String envVar){ + def value = System.getenv(envVar) + if (!value) { + throw new IllegalArgumentException("Couldn't find environment variable ${envVar}") + } + return value + } + + } \ No newline at end of file diff --git a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/FieldInterceptor.groovy b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/FieldInterceptor.groovy new file mode 100644 index 0000000..0372a54 --- /dev/null +++ b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/FieldInterceptor.groovy @@ -0,0 +1,7 @@ +package com.wooga.spock.extensions.github.interceptor + +import org.spockframework.runtime.model.FieldInfo + +interface FieldInterceptor { + void install(FieldInfo info) +} diff --git a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFeatureInterceptor.groovy b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFeatureInterceptor.groovy index 01b77c6..670d6bb 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFeatureInterceptor.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFeatureInterceptor.groovy @@ -19,23 +19,29 @@ package com.wooga.spock.extensions.github.interceptor import com.wooga.spock.extensions.github.GithubRepository import com.wooga.spock.extensions.github.Repository +import com.wooga.spock.extensions.github.RepositoryFactory import groovy.transform.InheritConstructors +import org.spockframework.runtime.extension.AbstractMethodInterceptor import org.spockframework.runtime.extension.IMethodInvocation import org.spockframework.runtime.model.FeatureInfo import java.lang.reflect.Parameter @InheritConstructors -class GithubRepositoryFeatureInterceptor extends GithubRepositoryManagingInterceptor { +class GithubRepositoryFeatureInterceptor extends AbstractMethodInterceptor { - GithubRepositoryFeatureInterceptor(GithubRepository metadata) { - super(metadata) - } + private RepositoryFactory factory + private GithubRepository metadata + Repository repo; - Repository repo + static GithubRepositoryFeatureInterceptor withMetadata(GithubRepository metadata) { + def repoFactory = new RepositoryFactory(metadata) + return new GithubRepositoryFeatureInterceptor(metadata, repoFactory) + } - boolean getResetAfterTestCase() { - this.metadata.resetAfterTestCase() + GithubRepositoryFeatureInterceptor(GithubRepository metadata, RepositoryFactory factory) { + this.metadata = metadata + this.factory = factory } private static void injectRepository(IMethodInvocation invocation, Repository repo) { @@ -78,8 +84,8 @@ class GithubRepositoryFeatureInterceptor extends GithubRepositoryManagingInterce invocation.proceed() } finally { - if (resetAfterTestCase) { - resetRepository(invocation) + if (metadata.resetAfterTestCase()) { + repo.resetRepository() } } } @@ -87,8 +93,7 @@ class GithubRepositoryFeatureInterceptor extends GithubRepositoryManagingInterce @Override void interceptSetupMethod(IMethodInvocation invocation) throws Throwable { invocation.proceed() - setupRepository(invocation) - + setupRepository(invocation.feature) invocation.spec.setupInterceptors.remove(this) } @@ -96,42 +101,24 @@ class GithubRepositoryFeatureInterceptor extends GithubRepositoryManagingInterce @Override void interceptFeatureExecution(IMethodInvocation invocation) throws Throwable { invocation.spec.addSetupInterceptor(this) - try { invocation.proceed() } finally { - destroyRepository(invocation) + repo.delete() } } - @Override void install(FeatureInfo info) { - super.install(info) info.addInterceptor(this) info.addIterationInterceptor(this) info.featureMethod.addInterceptor(this) } - @Override - Repository setupRepository(IMethodInvocation invocation) { - repo = super.setupRepository(invocation) - captureResetRef(invocation) - repo - } - - @Override - void destroyRepository(IMethodInvocation invocation) { - repo.delete() - } - - @Override - void resetRepository(IMethodInvocation invocation) { - repo.resetRepository() - } - - @Override - void captureResetRef(IMethodInvocation invocation) { + Repository setupRepository(FeatureInfo info) { + def repoBaseName = RepositoryFactory.getRepositoryBaseName(info) + this.repo = factory.setupRepository(repoBaseName) repo.captureResetRefs() + return repo } -} \ No newline at end of file +} diff --git a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryInterceptor.groovy b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryInterceptor.groovy index f6dbb4b..a2ba0cd 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryInterceptor.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryInterceptor.groovy @@ -17,18 +17,31 @@ package com.wooga.spock.extensions.github.interceptor - +import com.wooga.spock.extensions.github.GithubRepository +import com.wooga.spock.extensions.github.RepositoryFactory import groovy.transform.InheritConstructors +import org.spockframework.runtime.extension.AbstractMethodInterceptor import org.spockframework.runtime.extension.IMethodInvocation import org.spockframework.runtime.model.FieldInfo -import org.spockframework.runtime.model.SpecInfo @InheritConstructors -class GithubRepositoryInterceptor extends GithubRepositoryFieldInterceptor { +class GithubRepositoryInterceptor extends AbstractMethodInterceptor implements FieldInterceptor { + + private RepositoryFieldOperations ops; + + static GithubRepositoryInterceptor withMetadata(GithubRepository metadata, FieldInfo field) { + def repoFactory = new RepositoryFactory(metadata) + def repoOps = new RepositoryFieldOperations(field, repoFactory) + return new GithubRepositoryInterceptor(repoOps) + } + + GithubRepositoryInterceptor(RepositoryFieldOperations ops) { + this.ops = ops + } @Override void interceptSetupMethod(IMethodInvocation invocation) { - setupRepository(invocation) + ops.setupRepository(invocation) invocation.proceed() } @@ -37,14 +50,12 @@ class GithubRepositoryInterceptor extends GithubRepositoryFieldInterceptor { try { invocation.proceed() } finally { - destroyRepository(invocation) + ops.destroyRepository(invocation) } } @Override void install(FieldInfo info) { - super.install(info) - final spec = info.parent.getTopSpec() spec.setupInterceptors.add(this) spec.cleanupInterceptors.add(this) diff --git a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFieldInterceptor.groovy b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/RepositoryFieldOperations.groovy similarity index 79% rename from src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFieldInterceptor.groovy rename to src/main/groovy/com/wooga/spock/extensions/github/interceptor/RepositoryFieldOperations.groovy index c61cc85..fdad634 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/GithubRepositoryFieldInterceptor.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/RepositoryFieldOperations.groovy @@ -19,46 +19,52 @@ package com.wooga.spock.extensions.github.interceptor import com.wooga.spock.extensions.github.Repository +import com.wooga.spock.extensions.github.RepositoryFactory import groovy.transform.InheritConstructors import org.spockframework.runtime.extension.IMethodInvocation import org.spockframework.runtime.model.FieldInfo import spock.lang.Specification @InheritConstructors -abstract class GithubRepositoryFieldInterceptor extends GithubRepositoryManagingInterceptor { +class RepositoryFieldOperations { + + RepositoryFactory repoFactory + FieldInfo info + + RepositoryFieldOperations(FieldInfo info, RepositoryFactory repoFactory) { + this.info = info + this.repoFactory = repoFactory + } - @Override Repository setupRepository(IMethodInvocation invocation) { - def repo = super.setupRepository(invocation) + def repoBaseName = RepositoryFactory.getRepositoryBaseName(info) + def repo = repoFactory.setupRepository(repoBaseName) def spec = getSpec(invocation) info.writeValue(spec, repo) - repo + return repo } - @Override void destroyRepository(IMethodInvocation invocation) { Repository repository = getRepository(invocation) - repository.repository.delete() + repository.delete() } - @Override void resetRepository(IMethodInvocation invocation) { Repository repository = getRepository(invocation) repository.resetRepository() } - @Override void captureResetRef(IMethodInvocation invocation) { Repository repository = getRepository(invocation) repository.captureResetRefs() } - protected Specification getSpec(IMethodInvocation invocation) { - ((info.shared) ? invocation.sharedInstance : invocation.instance) as Specification - } - protected Repository getRepository(IMethodInvocation invocation) { final specInstance = getSpec(invocation) info.readValue(specInstance) as Repository } + + protected Specification getSpec(IMethodInvocation invocation) { + ((info.shared) ? invocation.sharedInstance : invocation.instance) as Specification + } } diff --git a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/SharedGithubRepositoryInterceptor.groovy b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/SharedGithubRepositoryInterceptor.groovy index 05eec40..98a2456 100644 --- a/src/main/groovy/com/wooga/spock/extensions/github/interceptor/SharedGithubRepositoryInterceptor.groovy +++ b/src/main/groovy/com/wooga/spock/extensions/github/interceptor/SharedGithubRepositoryInterceptor.groovy @@ -17,24 +17,35 @@ package com.wooga.spock.extensions.github.interceptor - +import com.wooga.spock.extensions.github.GithubRepository +import com.wooga.spock.extensions.github.RepositoryFactory import groovy.transform.InheritConstructors +import org.spockframework.runtime.extension.AbstractMethodInterceptor import org.spockframework.runtime.extension.IMethodInvocation import org.spockframework.runtime.model.FieldInfo -import org.spockframework.runtime.model.SpecInfo @InheritConstructors -class SharedGithubRepositoryInterceptor extends GithubRepositoryFieldInterceptor { +class SharedGithubRepositoryInterceptor extends AbstractMethodInterceptor implements FieldInterceptor { + + GithubRepository metadata; + RepositoryFieldOperations ops; - boolean getResetAfterTestCase() { - this.metadata.resetAfterTestCase() + static SharedGithubRepositoryInterceptor withMetadata(GithubRepository metadata, FieldInfo field) { + def repoFactory = new RepositoryFactory(metadata) + def repoOps = new RepositoryFieldOperations(field, repoFactory) + return new SharedGithubRepositoryInterceptor(metadata, repoOps) + } + + SharedGithubRepositoryInterceptor(GithubRepository metadata, RepositoryFieldOperations ops) { + this.metadata = metadata + this.ops = ops } @Override void interceptSetupSpecMethod(IMethodInvocation invocation) { - setupRepository(invocation) + ops.setupRepository(invocation) invocation.proceed() - captureResetRef(invocation) + ops.captureResetRef(invocation) } @Override @@ -42,7 +53,7 @@ class SharedGithubRepositoryInterceptor extends GithubRepositoryFieldInterceptor try { invocation.proceed() } finally { - destroyRepository(invocation) + ops.destroyRepository(invocation) } } @@ -51,19 +62,17 @@ class SharedGithubRepositoryInterceptor extends GithubRepositoryFieldInterceptor try { invocation.proceed() } finally { - resetRepository(invocation) + ops.resetRepository(invocation) } } @Override void install(FieldInfo info) { - super.install(info) - final spec = info.getParent().getTopSpec() spec.setupSpecInterceptors.add(this) spec.cleanupSpecInterceptors.add(this) - if (resetAfterTestCase) { + if (metadata.resetAfterTestCase()) { spec.cleanupInterceptors.add(this) } } diff --git a/src/test/groovy/com/wooga/spock/extensions/github/GithubRepositorySpec.groovy b/src/test/groovy/com/wooga/spock/extensions/github/GithubRepositorySpec.groovy index 221d434..97d24e5 100644 --- a/src/test/groovy/com/wooga/spock/extensions/github/GithubRepositorySpec.groovy +++ b/src/test/groovy/com/wooga/spock/extensions/github/GithubRepositorySpec.groovy @@ -51,4 +51,5 @@ class GithubRepositorySpec extends BaseGithubRepositorySpec { expect: testRepository != null } + } diff --git a/src/test/groovy/com/wooga/spock/extensions/github/RepositoryFactorySpec.groovy b/src/test/groovy/com/wooga/spock/extensions/github/RepositoryFactorySpec.groovy new file mode 100644 index 0000000..9a62cdd --- /dev/null +++ b/src/test/groovy/com/wooga/spock/extensions/github/RepositoryFactorySpec.groovy @@ -0,0 +1,71 @@ +package com.wooga.spock.extensions.github + +import spock.lang.Specification + +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Method +import java.lang.reflect.Proxy + +class RepositoryFactorySpec extends Specification{ + + def "creates new repository"() { + given: "some repository metadata" + def baseName = "basereponame" + def defaultMetadata = proxyDefaultMetadata(usernameEnv: "ATLAS_GITHUB_INTEGRATION_USER", + tokenEnv: "ATLAS_GITHUB_INTEGRATION_PASSWORD") + + when: "creating new repo with same name as existing repository" + def newRepo = new RepositoryFactory(defaultMetadata).setupRepository(baseName) + + then: "repository is created successfully" + newRepo.exists() + + cleanup: + tryToDelete(newRepo) + } + + + def "deletes existing repository when creating new repository with existing name"() { + given: "some repository metadata" + def baseName = "basereponame" + def defaultMetadata = proxyDefaultMetadata(usernameEnv: "ATLAS_GITHUB_INTEGRATION_USER", + tokenEnv: "ATLAS_GITHUB_INTEGRATION_PASSWORD") + and: "a existing repository" + def existingRepo = new RepositoryFactory(defaultMetadata).setupRepository(baseName) + + when: "creating new repo with same name as existing repository" + def newRepo = new RepositoryFactory(defaultMetadata).setupRepository(baseName) + + then: "repository is created successfully" + newRepo.exists() + and: "previous existing repository is deleted" + !existingRepo.exists() + + cleanup: + tryToDelete(existingRepo) + tryToDelete(newRepo) + } + + boolean tryToDelete(Repository repo) { + try { + repo?.delete() + return true + } catch(FileNotFoundException) { + return false + } + } + + GithubRepository proxyDefaultMetadata(Map overrides = new HashMap<>()) { + Class[] proxyInterface = [GithubRepository.class] + GithubRepository repo = Proxy.newProxyInstance(getClass().classLoader, proxyInterface, new InvocationHandler() { + @Override + Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if(overrides.containsKey(method.name)) { + return overrides[method.name] + } + return method.defaultValue + } + }) as GithubRepository + return repo + } +}