diff --git a/provider/gradle/src/main/kotlin/au/com/dius/pact/provider/gradle/PactBrokerConsumerConfig.kt b/provider/gradle/src/main/kotlin/au/com/dius/pact/provider/gradle/PactBrokerConsumerConfig.kt index 90e94c0993..e266f9e5b6 100644 --- a/provider/gradle/src/main/kotlin/au/com/dius/pact/provider/gradle/PactBrokerConsumerConfig.kt +++ b/provider/gradle/src/main/kotlin/au/com/dius/pact/provider/gradle/PactBrokerConsumerConfig.kt @@ -1,6 +1,7 @@ package au.com.dius.pact.provider.gradle import au.com.dius.pact.core.pactbroker.ConsumerVersionSelectors +import au.com.dius.pact.provider.junitsupport.loader.SelectorBuilder import mu.KLogging import org.gradle.api.Action import org.gradle.api.model.ObjectFactory @@ -44,95 +45,4 @@ open class PactBrokerConsumerConfig @Inject constructor( } } -open class ConsumerVersionSelectorConfig { - val selectors: MutableList = mutableListOf() - - /** - * The latest version from the main branch of each consumer, as specified by the consumer's mainBranch property. - */ - fun mainBranch() { - selectors.add(ConsumerVersionSelectors.MainBranch) - } - - /** - * The latest version from a particular branch of each consumer, or for a particular consumer if the second - * parameter is provided. If fallback is provided, falling back to the fallback branch if none is found from the - * specified branch. - * - * @param name - Branch name - * @param consumer - Consumer name (optional) - * @param fallback - Fall back to this branch if none is found from the specified branch (optional) - */ - @JvmOverloads - fun branch(name: String, consumer: String? = null, fallback: String? = null) { - selectors.add(ConsumerVersionSelectors.Branch(name, consumer, fallback)) - } - - /** - * All the currently deployed and currently released and supported versions of each consumer. - */ - fun deployedOrReleased() { - selectors.add(ConsumerVersionSelectors.DeployedOrReleased) - } - - /** - * The latest version from any branch of the consumer that has the same name as the current branch of the provider. - * Used for coordinated development between consumer and provider teams using matching feature branch names. - */ - fun matchingBranch() { - selectors.add(ConsumerVersionSelectors.MatchingBranch) - } - - /** - * Any versions currently deployed to the specified environment - */ - fun deployedTo(environment: String) { - selectors.add(ConsumerVersionSelectors.DeployedTo(environment)) - } - - /** - * Any versions currently released and supported in the specified environment - */ - fun releasedTo(environment: String) { - selectors.add(ConsumerVersionSelectors.ReleasedTo(environment)) - } - - /** - * any versions currently deployed or released and supported in the specified environment - */ - fun environment(environment: String) { - selectors.add(ConsumerVersionSelectors.Environment(environment)) - } - - /** - * All versions with the specified tag - */ - @Deprecated("Tags are deprecated in favor of branches", ReplaceWith("branch")) - fun tag(name: String) { - selectors.add(ConsumerVersionSelectors.Tag(name)) - } - - /** - * The latest version for each consumer with the specified tag - */ - @Deprecated("Tags are deprecated in favor of branches", ReplaceWith("branch")) - fun latestTag(name: String) { - selectors.add(ConsumerVersionSelectors.LatestTag(name)) - } - - /** - * Generic selector. - * - * * With just the tag name, returns all versions with the specified tag. - * * With latest, returns the latest version for each consumer with the specified tag. - * * With a fallback tag, returns the latest version for each consumer with the specified tag, falling back to the - * fallbackTag if non is found with the specified tag. - * * With a consumer name, returns the latest version for a specified consumer with the specified tag. - * * With only latest, returns the latest version for each consumer. NOT RECOMMENDED as it suffers from race - * conditions when pacts are published from multiple branches. - */ - @Deprecated("Tags are deprecated in favor of branches", ReplaceWith("branch")) - fun selector(tagName: String?, latest: Boolean?, fallbackTag: String?, consumer: String?) { - selectors.add(ConsumerVersionSelectors.Selector(tagName, latest, consumer, fallbackTag)) - } -} +open class ConsumerVersionSelectorConfig: SelectorBuilder() diff --git a/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/PactRunner.kt b/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/PactRunner.kt index b6476350d1..325d0cfcb7 100644 --- a/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/PactRunner.kt +++ b/provider/junit/src/main/kotlin/au/com/dius/pact/provider/junit/PactRunner.kt @@ -1,6 +1,5 @@ package au.com.dius.pact.provider.junit -import au.com.dius.pact.core.model.Interaction import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.support.expressions.SystemPropertyResolver import au.com.dius.pact.core.support.json.JsonException @@ -171,7 +170,7 @@ open class PactRunner(private val clazz: Class<*>) : ParentRunner> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) notThrown(NoPactsFoundException) list.empty } @@ -104,13 +107,13 @@ class PactBrokerLoaderSpec extends Specification { def result = pactBrokerLoader(false).load('test') then: - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) result == [] } def 'Throws any Exception On Execution Exception'() { given: - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Err(new InvalidHalResponse('message')) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Err(new InvalidHalResponse('message')) when: pactBrokerLoader().load('test') @@ -159,7 +162,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } @RestoreSystemProperties @@ -183,7 +186,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } @RestoreSystemProperties @@ -207,7 +210,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } @RestoreSystemProperties @@ -279,16 +282,16 @@ class PactBrokerLoaderSpec extends Specification { then: noExceptionThrown() - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } def 'Loads pacts for each provided tag'() { given: tags = ['a', 'b', 'c'] def selectors = [ - new ConsumerVersionSelector('a', true, null, null), - new ConsumerVersionSelector('b', true, null, null), - new ConsumerVersionSelector('c', true, null, null) + new ConsumerVersionSelectors.Selector('a', true, null, null), + new ConsumerVersionSelectors.Selector('b', true, null, null), + new ConsumerVersionSelectors.Selector('c', true, null, null) ] def expected = [ new PactBrokerResult('test', 'a', '', [], [], false, null, false, true), @@ -301,7 +304,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -314,9 +317,9 @@ class PactBrokerLoaderSpec extends Specification { createVersionSelector(tag: 'c', latest: 'true') ] def selectors = [ - new ConsumerVersionSelector('a', true, null, null), - new ConsumerVersionSelector('b', false, null, null), - new ConsumerVersionSelector('c', true, null, null) + new ConsumerVersionSelectors.Selector('a', true, null, null), + new ConsumerVersionSelectors.Selector('b', false, null, null), + new ConsumerVersionSelectors.Selector('c', true, null, null) ] def expected = [ new PactBrokerResult('test', 'a', '', [], [], false, null, false, true), @@ -329,7 +332,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -341,8 +344,8 @@ class PactBrokerLoaderSpec extends Specification { System.setProperty('composite', "one${VALUES_SEPARATOR}two") tags = ['${composite}'] def selectors = [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('two', true, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('two', true, null, null) ] def expected = [ new PactBrokerResult('test', 'one', '', [], [], false, null, false, true), @@ -353,7 +356,7 @@ class PactBrokerLoaderSpec extends Specification { def result = pactBrokerLoader().load('test') then: - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) result.size() == 2 } @@ -365,8 +368,8 @@ class PactBrokerLoaderSpec extends Specification { System.setProperty('compositeLatest', "true${VALUES_SEPARATOR}false") consumerVersionSelectors = [createVersionSelector(tag: '${compositeTag}', latest: '${compositeLatest}')] def selectors = [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('two', false, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('two', false, null, null) ] def expected = [ new PactBrokerResult('test', 'one', '', [], [], false, null, false, true), @@ -377,7 +380,7 @@ class PactBrokerLoaderSpec extends Specification { def result = pactBrokerLoader().load('test') then: - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) result.size() == 2 } @@ -391,7 +394,7 @@ class PactBrokerLoaderSpec extends Specification { then: result.size() == 1 - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok(expected) } @SuppressWarnings('GStringExpressionWithinString') @@ -402,9 +405,9 @@ class PactBrokerLoaderSpec extends Specification { loader.valueResolver = [resolveValue: { val -> 'X' } ] as ValueResolver def expected = [ new PactBrokerResult('test', 'a', '', [], [], false, null, false, true) ] def selectors = [ - new ConsumerVersionSelector('X', true, null, null), - new ConsumerVersionSelector('X', true, null, null), - new ConsumerVersionSelector('X', true, null, null) + new ConsumerVersionSelectors.Selector('X', true, null, null), + new ConsumerVersionSelectors.Selector('X', true, null, null), + new ConsumerVersionSelectors.Selector('X', true, null, null) ] when: @@ -412,7 +415,7 @@ class PactBrokerLoaderSpec extends Specification { then: 1 * brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 1 } @@ -430,9 +433,9 @@ class PactBrokerLoaderSpec extends Specification { newLoader.valueResolver = [resolveValue: { val -> val == 'd' ? 'D' : 'X' }] as ValueResolver def expected = [ new PactBrokerResult('test', 'a', '', [], [], false, null, false, true) ] def selectors = [ - new ConsumerVersionSelector('X', true, null, null), - new ConsumerVersionSelector('X', false, null, null), - new ConsumerVersionSelector('X', true, null, 'D') + new ConsumerVersionSelectors.Selector('X', true, null, null), + new ConsumerVersionSelectors.Selector('X', false, null, null), + new ConsumerVersionSelectors.Selector('X', true, null, 'D') ] when: @@ -440,7 +443,7 @@ class PactBrokerLoaderSpec extends Specification { then: 1 * brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 1 } @@ -452,9 +455,9 @@ class PactBrokerLoaderSpec extends Specification { System.setProperty('compositeTag', "one${VALUES_SEPARATOR}two${VALUES_SEPARATOR}three") consumerVersionSelectors = [createVersionSelector(tag: '${compositeTag}')] def selectors = [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('two', true, null, null), - new ConsumerVersionSelector('three', true, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('two', true, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, null) ] def expected = [ new PactBrokerResult('test', 'one', '', [], [], false, null, false, true), @@ -465,7 +468,7 @@ class PactBrokerLoaderSpec extends Specification { def result = pactBrokerLoader().load('test') then: - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) result.size() == 2 } @@ -488,9 +491,9 @@ class PactBrokerLoaderSpec extends Specification { given: consumers = ['a', 'b', 'c'] def selectors = [ - new ConsumerVersionSelector(null, true, 'a', null), - new ConsumerVersionSelector(null, true, 'b', null), - new ConsumerVersionSelector(null, true, 'c', null) + new ConsumerVersionSelectors.Selector(null, true, 'a', null), + new ConsumerVersionSelectors.Selector(null, true, 'b', null), + new ConsumerVersionSelectors.Selector(null, true, 'c', null) ] def expected = [ new PactBrokerResult('a', '', '', [], [], false, null, false, false), @@ -504,7 +507,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -523,9 +526,9 @@ class PactBrokerLoaderSpec extends Specification { new PactBrokerResult('d', '', '', [], [], false, null, false, true) ] def selectors = [ - new ConsumerVersionSelector(null, true, 'a', null), - new ConsumerVersionSelector(null, true, 'b', null), - new ConsumerVersionSelector(null, true, 'c', null) + new ConsumerVersionSelectors.Selector(null, true, 'a', null), + new ConsumerVersionSelectors.Selector(null, true, 'b', null), + new ConsumerVersionSelectors.Selector(null, true, 'c', null) ] when: @@ -533,7 +536,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 4 } @@ -545,9 +548,9 @@ class PactBrokerLoaderSpec extends Specification { System.setProperty('composite', "a${VALUES_SEPARATOR}b${VALUES_SEPARATOR}c") consumers = ['${composite}'] def selectors = [ - new ConsumerVersionSelector(null, true, 'a', null), - new ConsumerVersionSelector(null, true, 'b', null), - new ConsumerVersionSelector(null, true, 'c', null) + new ConsumerVersionSelectors.Selector(null, true, 'a', null), + new ConsumerVersionSelectors.Selector(null, true, 'b', null), + new ConsumerVersionSelectors.Selector(null, true, 'c', null) ] def expected = [ new PactBrokerResult('a', '', '', [], [], false, null, false, false), @@ -561,7 +564,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -581,7 +584,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 4 } @@ -603,7 +606,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 4 } @@ -619,9 +622,9 @@ class PactBrokerLoaderSpec extends Specification { new PactBrokerResult('d', '', '', [], [], false, 'demo', false, false) ] def selectors = [ - new ConsumerVersionSelector('demo', true, 'a', null), - new ConsumerVersionSelector('demo', true, 'b', null), - new ConsumerVersionSelector('demo', true, 'c', null) + new ConsumerVersionSelectors.Selector('demo', true, 'a', null), + new ConsumerVersionSelectors.Selector('demo', true, 'b', null), + new ConsumerVersionSelectors.Selector('demo', true, 'c', null) ] when: @@ -629,7 +632,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -644,14 +647,14 @@ class PactBrokerLoaderSpec extends Specification { new PactBrokerResult('c', '', '', [], [], false, 'demo', false, false), new PactBrokerResult('d', '', '', [], [], false, 'demo', false, false) ] - def selectors = [ new ConsumerVersionSelector('demo', true, null, null) ] + def selectors = [ new ConsumerVersionSelectors.Selector('demo', true, null, null) ] when: def result = pactBrokerLoader().load('test') then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -669,7 +672,7 @@ class PactBrokerLoaderSpec extends Specification { } } def selectors = [ - new ConsumerVersionSelector('master', true, null, null) + new ConsumerVersionSelectors.Selector('master', true, null, null) ] when: @@ -677,7 +680,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok([]) } @Issue('#1208') @@ -687,9 +690,9 @@ class PactBrokerLoaderSpec extends Specification { valueResolver = Mock(ValueResolver) valueResolver.propertyDefined(_) >> false def selectors = [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('two', true, null, null), - new ConsumerVersionSelector('three', true, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('two', true, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, null) ] def expected = [ new PactBrokerResult('d', '', '', [], [], false, 'one', false, false) @@ -704,7 +707,7 @@ class PactBrokerLoaderSpec extends Specification { then: valueResolver.propertyDefined('pactbroker.consumerversionselectors.tags') >> true valueResolver.resolveValue('pactbroker.consumerversionselectors.tags') >> 'one,two,three' - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) } def 'Loads pacts with consumer version selectors when consumer version selectors and tags are both present'() { @@ -717,14 +720,14 @@ class PactBrokerLoaderSpec extends Specification { new PactBrokerResult('c', '', '', [], [], false, 'demo', false, false), new PactBrokerResult('d', '', '', [], [], false, 'demo', false, false) ] - def selectors = [ new ConsumerVersionSelector('demo', true, null, null) ] + def selectors = [ new ConsumerVersionSelectors.Selector('demo', true, null, null) ] when: def result = pactBrokerLoader().load('test') then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 4 } @@ -747,7 +750,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok([]) } def 'Does not loads wip pacts when pending is false'() { @@ -758,8 +761,8 @@ class PactBrokerLoaderSpec extends Specification { ] includeWipPactsSince = '2020-06-25' def selectors = [ - new ConsumerVersionSelector('a', true, null, null), - new ConsumerVersionSelector('b', false, null, null), + new ConsumerVersionSelectors.Selector('a', true, null, null), + new ConsumerVersionSelectors.Selector('b', false, null, null), ] def expected = [ new PactBrokerResult('test', 'a', '', [], [], false, null, false, false), @@ -772,7 +775,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, [], '', false, '') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, [], '', false, '') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -787,8 +790,8 @@ class PactBrokerLoaderSpec extends Specification { providerTags = ['dev'] includeWipPactsSince = '2020-06-25' def selectors = [ - new ConsumerVersionSelector('a', true, null, null), - new ConsumerVersionSelector('b', false, null, null), + new ConsumerVersionSelectors.Selector('a', true, null, null), + new ConsumerVersionSelectors.Selector('b', false, null, null), ] def expected = [ new PactBrokerResult('test', 'a', '', [], [], false, null, false, false), @@ -801,7 +804,7 @@ class PactBrokerLoaderSpec extends Specification { then: brokerClient.getOptions() >> [:] - 1 * brokerClient.fetchConsumersWithSelectors('test', selectors, ['dev'], '', true, '2020-06-25') >> new Ok(expected) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', selectors, ['dev'], '', true, '2020-06-25') >> new Ok(expected) 0 * brokerClient._ result.size() == 3 } @@ -828,7 +831,7 @@ class PactBrokerLoaderSpec extends Specification { pactBrokerLoader().load('test') then: - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok([]) noExceptionThrown() } @@ -852,7 +855,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok([]) } def 'configured from annotation with https and no port'() { @@ -877,7 +880,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> new Ok([]) } def 'Auth: Uses no auth if no auth is provided'() { @@ -1022,10 +1025,10 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('2', true, null, null), - new ConsumerVersionSelector('3', true, null, null), - new ConsumerVersionSelector('three', true, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('2', true, null, null), + new ConsumerVersionSelectors.Selector('3', true, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, null) ] } @@ -1041,12 +1044,12 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, 'bob', null), - new ConsumerVersionSelector('one', true, 'fred', null), - new ConsumerVersionSelector('two', true, 'bob', null), - new ConsumerVersionSelector('two', true, 'fred', null), - new ConsumerVersionSelector('three', true, 'bob', null), - new ConsumerVersionSelector('three', true, 'fred', null) + new ConsumerVersionSelectors.Selector('one', true, 'bob', null), + new ConsumerVersionSelectors.Selector('one', true, 'fred', null), + new ConsumerVersionSelectors.Selector('two', true, 'bob', null), + new ConsumerVersionSelectors.Selector('two', true, 'fred', null), + new ConsumerVersionSelectors.Selector('three', true, 'bob', null), + new ConsumerVersionSelectors.Selector('three', true, 'fred', null) ] } @@ -1073,9 +1076,9 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('two', true, null, null), - new ConsumerVersionSelector('three', true, null, 'four') + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('two', true, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, 'four') ] } @@ -1104,10 +1107,10 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('2', true, null, null), - new ConsumerVersionSelector('3', true, null, null), - new ConsumerVersionSelector('three', true, null, 'Y') + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('2', true, null, null), + new ConsumerVersionSelectors.Selector('3', true, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, 'Y') ] } @@ -1136,10 +1139,10 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('2', true, null, null), - new ConsumerVersionSelector('3', false, null, null), - new ConsumerVersionSelector('three', true, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('2', true, null, null), + new ConsumerVersionSelectors.Selector('3', false, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, null) ] } @@ -1168,10 +1171,10 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('2', false, null, null), - new ConsumerVersionSelector('3', false, null, null), - new ConsumerVersionSelector('three', true, null, null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('2', false, null, null), + new ConsumerVersionSelectors.Selector('3', false, null, null), + new ConsumerVersionSelectors.Selector('three', true, null, null) ] } @@ -1189,9 +1192,9 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('one', true, null, null), - new ConsumerVersionSelector('two', false, null, null), - new ConsumerVersionSelector('three', true, 'test', null) + new ConsumerVersionSelectors.Selector('one', true, null, null), + new ConsumerVersionSelectors.Selector('two', false, null, null), + new ConsumerVersionSelectors.Selector('three', true, 'test', null) ] } @@ -1207,7 +1210,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector(null, true, 'test', null) + new ConsumerVersionSelectors.Selector(null, true, 'test', null) ] } @@ -1234,8 +1237,8 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector(null, true, '2', null), - new ConsumerVersionSelector(null, true, '3', null) + new ConsumerVersionSelectors.Selector(null, true, '2', null), + new ConsumerVersionSelectors.Selector(null, true, '3', null) ] } @@ -1262,12 +1265,12 @@ class PactBrokerLoaderSpec extends Specification { then: result == [ - new ConsumerVersionSelector('Y', true, '2', null), - new ConsumerVersionSelector('Y', true, '3', null), - new ConsumerVersionSelector('Y', true, '4', null), - new ConsumerVersionSelector('Z', true, '2', null), - new ConsumerVersionSelector('Z', true, '3', null), - new ConsumerVersionSelector('Z', true, '4', null) + new ConsumerVersionSelectors.Selector('Y', true, '2', null), + new ConsumerVersionSelectors.Selector('Y', true, '3', null), + new ConsumerVersionSelectors.Selector('Y', true, '4', null), + new ConsumerVersionSelectors.Selector('Z', true, '2', null), + new ConsumerVersionSelectors.Selector('Z', true, '3', null), + new ConsumerVersionSelectors.Selector('Z', true, '4', null) ] } @@ -1285,12 +1288,12 @@ class PactBrokerLoaderSpec extends Specification { } } def selectors = [ - new ConsumerVersionSelector('1', true, 'A', ''), - new ConsumerVersionSelector('1', true, 'B', ''), - new ConsumerVersionSelector('2', true, 'A', ''), - new ConsumerVersionSelector('2', true, 'B', ''), - new ConsumerVersionSelector('3', true, 'A', ''), - new ConsumerVersionSelector('3', true, 'B', '') + new ConsumerVersionSelectors.Selector('1', true, 'A', ''), + new ConsumerVersionSelectors.Selector('1', true, 'B', ''), + new ConsumerVersionSelectors.Selector('2', true, 'A', ''), + new ConsumerVersionSelectors.Selector('2', true, 'B', ''), + new ConsumerVersionSelectors.Selector('3', true, 'A', ''), + new ConsumerVersionSelectors.Selector('3', true, 'B', '') ] when: @@ -1298,7 +1301,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors(_, selectors, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2(_, selectors, _, _, _, _) >> new Ok([]) } @Unroll @@ -1354,7 +1357,7 @@ class PactBrokerLoaderSpec extends Specification { pactBrokerLoader().load('test') then: - 1 * brokerClient.fetchConsumersWithSelectors('test', [], [], '', false, '') >> { + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', [], [], '', false, '') >> { throw new InvalidNavigationRequest('PKIX path building failed', new SSLHandshakeException('PKIX path building failed')) } thrown(InvalidNavigationRequest) @@ -1377,7 +1380,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } void 'Enables insecure TLS from explicit PactBroker annotation setting'() { @@ -1397,7 +1400,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } @RestoreSystemProperties @@ -1421,7 +1424,7 @@ class PactBrokerLoaderSpec extends Specification { then: result == [] - 1 * brokerClient.fetchConsumersWithSelectors('test', _, _, _, _, _) >> new Ok([]) + 1 * brokerClient.fetchConsumersWithSelectorsV2('test', _, _, _, _, _) >> new Ok([]) } def 'Uses the insecure TlS setting when creating the PactBrokerClient'() { @@ -1438,6 +1441,38 @@ class PactBrokerLoaderSpec extends Specification { pactBrokerClient.config.insecureTLS == true } + @Unroll + def 'test Class Has Selectors Method'() { + expect: + (PactBrokerLoader.testClassHasSelectorsMethod(clazz) != null) == result + + where: + + clazz | result + null | false + PactBrokerLoaderSpec | false + FullPactBrokerAnnotation | false + IncorrectTypesOnSelectorMethod | false + IncorrectTypesOnSelectorMethod2 | false + IncorrectScopeOnSelectorMethod | false + CorrectSelectorMethod | true + CorrectSelectorMethod2 | true + CorrectSelectorMethod3 | true + } + + @Unroll + def 'Invoke Selectors Method'() { + expect: + PactBrokerLoader.invokeSelectorsMethod(instance, PactBrokerLoader.testClassHasSelectorsMethod(clazz)) == result + + where: + + clazz | instance | result + CorrectSelectorMethod | new CorrectSelectorMethod() | [new ConsumerVersionSelectors.Environment('CorrectSelectorMethod')] + CorrectSelectorMethod2 | new CorrectSelectorMethod2() | [new ConsumerVersionSelectors.Environment('CorrectSelectorMethod2')] + CorrectSelectorMethod3 | null | [new ConsumerVersionSelectors.Environment('CorrectSelectorMethod3')] + } + private static VersionSelector createVersionSelector(Map args = [:]) { new VersionSelector() { @Override @@ -1532,4 +1567,39 @@ class PactBrokerLoaderSpec extends Specification { } + static class IncorrectTypesOnSelectorMethod { + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + void consumerVersionSelectors(int i) {} + } + + static class IncorrectTypesOnSelectorMethod2 { + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + int consumerVersionSelectors(SelectorBuilder builder) { 0 } + } + + static class IncorrectScopeOnSelectorMethod { + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + private SelectorBuilder consumerVersionSelectors(SelectorBuilder builder) { null } + } + + static class CorrectSelectorMethod implements IConsumerVersionSelectors { + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + SelectorBuilder consumerVersionSelectors(SelectorBuilder builder) { + builder.environment('CorrectSelectorMethod') + } + } + + static class CorrectSelectorMethod2 { + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + List consumerVersionSelectors(SelectorBuilder builder) { + builder.environment('CorrectSelectorMethod2').build() + } + } + + static class CorrectSelectorMethod3 { + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + static List consumerVersionSelectors(SelectorBuilder builder) { + builder.environment('CorrectSelectorMethod3').build() + } + } } diff --git a/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactJUnit5VerificationProvider.kt b/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactJUnit5VerificationProvider.kt index 6d0ed05ebc..51cc1aa770 100644 --- a/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactJUnit5VerificationProvider.kt +++ b/provider/junit5/src/main/kotlin/au/com/dius/pact/provider/junit5/PactJUnit5VerificationProvider.kt @@ -145,7 +145,7 @@ open class PactVerificationInvocationContextProvider : TestTemplateInvocationCon logger.debug { "Pact sources on test class:\n ${pactSources.joinToString("\n") { it.first.toString() }}" } return pactSources.map { (pactSource, annotation) -> - instantiatePactLoader(pactSource, context.requiredTestClass, annotation) + instantiatePactLoader(pactSource, context.requiredTestClass, context.testInstance, annotation) }.map { checkForOverriddenPactUrl(it, context.requiredTestClass.getAnnotation(AllowOverridePactUrl::class.java), diff --git a/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/ConsumerVersionSelectors.java b/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/ConsumerVersionSelectors.java new file mode 100644 index 0000000000..bbc9e47169 --- /dev/null +++ b/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/ConsumerVersionSelectors.java @@ -0,0 +1,11 @@ +package au.com.dius.pact.provider.junitsupport.loader; + +import java.lang.annotation.*; + +/** + * Used to mark a method that will set up any consumer version selectors required for a Pact verification test + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@Inherited +public @interface ConsumerVersionSelectors { } diff --git a/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/IConsumerVersionSelectors.java b/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/IConsumerVersionSelectors.java new file mode 100644 index 0000000000..b234a4e0ea --- /dev/null +++ b/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/IConsumerVersionSelectors.java @@ -0,0 +1,12 @@ +package au.com.dius.pact.provider.junitsupport.loader; + +/** + * Interface which defines a consumer version selector method with the correct signature + */ +public interface IConsumerVersionSelectors { + /** + * Return the consumer version selectors to use in the test + */ + @au.com.dius.pact.provider.junitsupport.loader.ConsumerVersionSelectors + SelectorBuilder consumerVersionSelectors(SelectorBuilder builder); +} diff --git a/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/PactLoader.java b/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/PactLoader.java index 45b0c334de..298e3b5dd1 100644 --- a/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/PactLoader.java +++ b/provider/src/main/java/au/com/dius/pact/provider/junitsupport/loader/PactLoader.java @@ -44,5 +44,5 @@ default void setValueResolver(ValueResolver valueResolver) { } /** * Supports additional initialisation using the test class */ - default void initLoader(Class testClass) { }; + default void initLoader(Class testClass, Object testInstance) { }; } diff --git a/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderUtils.kt b/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderUtils.kt index 8a4021168b..035d7f7c16 100644 --- a/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderUtils.kt +++ b/provider/src/main/kotlin/au/com/dius/pact/provider/ProviderUtils.kt @@ -133,7 +133,7 @@ object ProviderUtils : KLogging() { return result } - fun instantiatePactLoader(pactSource: PactSource, testClass: Class<*>, annotation: Annotation?): PactLoader { + fun instantiatePactLoader(pactSource: PactSource, testClass: Class<*>, testInstance: Any?, annotation: Annotation?): PactLoader { val pactLoaderClass = pactSource.value val pactLoader = try { // Checks if there is a constructor with one argument of type Class. @@ -168,7 +168,7 @@ object ProviderUtils : KLogging() { pactLoaderClass.createInstance() } } - pactLoader.initLoader(testClass) + pactLoader.initLoader(testClass, testInstance) return pactLoader } } diff --git a/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt b/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt index 04c5b66fdc..1959f82eea 100644 --- a/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt +++ b/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/PactBrokerLoader.kt @@ -8,7 +8,7 @@ import au.com.dius.pact.core.model.Pact import au.com.dius.pact.core.model.PactBrokerSource import au.com.dius.pact.core.model.PactReader import au.com.dius.pact.core.model.PactSource -import au.com.dius.pact.core.pactbroker.ConsumerVersionSelector +import au.com.dius.pact.core.pactbroker.ConsumerVersionSelectors import au.com.dius.pact.core.pactbroker.IPactBrokerClient import au.com.dius.pact.core.pactbroker.PactBrokerClient import au.com.dius.pact.core.pactbroker.PactBrokerClientConfig @@ -25,12 +25,12 @@ import org.apache.hc.core5.net.URIBuilder import java.io.IOException import java.net.URI import java.net.URISyntaxException +import kotlin.reflect.KCallable import kotlin.reflect.KClass - -data class SelectorResult( - val selectors: List, - val filtered: Boolean -) +import kotlin.reflect.KVisibility +import kotlin.reflect.full.findAnnotation +import kotlin.reflect.full.isSubtypeOf +import kotlin.reflect.full.starProjectedType /** * Out-of-the-box implementation of {@link PactLoader} that downloads pacts from Pact broker @@ -43,9 +43,9 @@ open class PactBrokerLoader( val pactBrokerPort: String?, @Deprecated("Use pactBrokerUrl") val pactBrokerScheme: String?, - @Deprecated(message = "Use Consumer version selectors instead", - replaceWith = ReplaceWith("pactBrokerConsumerVersionSelectors")) - val pactBrokerTags: List? = listOf("latest"), + @Deprecated(message = "use consumerVersionSelectors method or pactbroker.consumerversionselectors property") + val pactBrokerTags: List? = emptyList(), + @Deprecated(message = "use consumerVersionSelectors method or pactbroker.consumerversionselectors property") val pactBrokerConsumerVersionSelectors: List, val pactBrokerConsumers: List = emptyList(), var failIfNoPactsFound: Boolean = true, @@ -61,6 +61,8 @@ open class PactBrokerLoader( val ep: ExpressionParser = ExpressionParser() ) : OverrideablePactLoader { + private var testClass: Class<*>? = null + private var testInstance: Any? = null private var resolver: ValueResolver? = valueResolver private var overriddenPactUrl: String? = null private var overriddenConsumer: String? = null @@ -128,11 +130,14 @@ open class PactBrokerLoader( } } - fun buildConsumerVersionSelectors(resolver: ValueResolver): List { + fun buildConsumerVersionSelectors(resolver: ValueResolver): List { val tags = pactBrokerTags.orEmpty().flatMap { ep.parseListExpression(it, resolver) } - return if (shouldFallBackToTags(tags, pactBrokerConsumerVersionSelectors, resolver)) { + val selectorsMethod = testClassHasSelectorsMethod(this.testClass) + return if (selectorsMethod != null) { + invokeSelectorsMethod(this.testInstance, selectorsMethod) + } else if (shouldFallBackToTags(tags, pactBrokerConsumerVersionSelectors, resolver)) { permutations(tags, pactBrokerConsumers.flatMap { ep.parseListExpression(it, resolver) }) - .map { ConsumerVersionSelector(it.first, consumer = it.second) } + .map { ConsumerVersionSelectors.Selector(it.first, true, it.second) } } else { pactBrokerConsumerVersionSelectors.flatMap { val tags = ep.parseListExpression(it.tag, resolver) @@ -153,19 +158,17 @@ open class PactBrokerLoader( when { tags.isNotEmpty() && consumers.isNotEmpty() -> { permutations(tags.mapIndexed { index, tag -> tag to index }, consumers).map { (tag, consumer) -> - ConsumerVersionSelector(tag!!.first, latest[tag.second].toBoolean(), fallbackTag = fallbackTag, - consumer = consumer) + ConsumerVersionSelectors.Selector(tag!!.first, latest[tag.second].toBoolean(), consumer, fallbackTag) } } tags.isNotEmpty() -> { tags.mapIndexed { index, tag -> - ConsumerVersionSelector(tag, latest[index].toBoolean(), fallbackTag = fallbackTag, - consumer = consumers.firstOrNull()) + ConsumerVersionSelectors.Selector(tag, latest[index].toBoolean(), consumers.firstOrNull(), fallbackTag) } } consumers.isNotEmpty() -> { consumers.map { name -> - ConsumerVersionSelector(null, true, fallbackTag = fallbackTag, consumer = name) + ConsumerVersionSelectors.Selector(null, true, name, fallbackTag) } } else -> listOf() @@ -212,16 +215,16 @@ open class PactBrokerLoader( @Suppress("ThrowsCount") private fun loadPactsForProvider( providerName: String, - selectors: List, + selectors: List, resolver: ValueResolver ): List { logger.debug { "Loading pacts from pact broker for provider $providerName and consumer version selectors " + "$selectors" } val pending = ep.parseExpression(enablePendingPacts, DataType.BOOLEAN, resolver) as Boolean val providerTags = providerTags.flatMap { ep.parseListExpression(it, resolver) }.filter { it.isNotEmpty() } - val providerBranch = ep.parseExpression(providerBranch, DataType.STRING, resolver) as String + val providerBranch = ep.parseExpression(providerBranch, DataType.STRING, resolver) as String? - if (pending && providerTags.none { it.isNotEmpty() } && providerBranch.isNullOrBlank() ) { + if (pending && providerTags.none { it.isNotEmpty() } && providerBranch.isNullOrBlank()) { throw IllegalArgumentException("Pending pacts feature has been enabled, but no provider tags or branch have" + " been specified. To use the pending pacts feature, you need to provide the list of provider names for the" + " provider application version with the providerTags or providerBranch property that will be published with" + @@ -235,7 +238,7 @@ open class PactBrokerLoader( try { val pactBrokerClient = newPactBrokerClient(uriBuilder.build(), resolver) - val result = pactBrokerClient.fetchConsumersWithSelectors(providerName, selectors, providerTags, + val result = pactBrokerClient.fetchConsumersWithSelectorsV2(providerName, selectors, providerTags, providerBranch, pending, wipSinceDate) var consumers = when (result) { is Ok -> result.value @@ -340,5 +343,47 @@ open class PactBrokerLoader( return PactBrokerClient(url.toString(), options.toMutableMap(), config) } - companion object : KLogging() + override fun initLoader(testClass: Class<*>?, testInstance: Any?) { + this.testClass = testClass + this.testInstance = testInstance + } + + companion object : KLogging() { + @JvmStatic + fun invokeSelectorsMethod(testInstance: Any?, selectorsMethod: KCallable<*>): List { + val projectedType = SelectorBuilder::class.starProjectedType + return when (selectorsMethod.parameters.size) { + 1 -> if (selectorsMethod.returnType.isSubtypeOf(projectedType)) { + val builder = selectorsMethod.call(SelectorBuilder()) as SelectorBuilder + builder.build() + } else { + selectorsMethod.call(SelectorBuilder()) as List + } + 2 -> if (selectorsMethod.returnType.isSubtypeOf(projectedType)) { + val builder = selectorsMethod.call(testInstance, SelectorBuilder()) as SelectorBuilder + builder.build() + } else { + selectorsMethod.call(testInstance, SelectorBuilder()) as List + } + else -> throw java.lang.IllegalArgumentException( + "Consumer version selector method should take one parameter of type SelectorBuilder") + } + } + + @JvmStatic + fun testClassHasSelectorsMethod(testClass: Class<*>?): KCallable<*>? { + val projectedType = SelectorBuilder::class.starProjectedType + return testClass?.kotlin?.members?.firstOrNull { method -> + method.findAnnotation() != null + && ( + // static method + (method.parameters.size == 1 && method.parameters[0].type.isSubtypeOf(projectedType)) + // instance method + || (method.parameters.size == 2 && method.parameters[1].type.isSubtypeOf(projectedType)) + ) + && method.visibility == KVisibility.PUBLIC + && (method.returnType.isSubtypeOf(projectedType) || method.returnType.isSubtypeOf(List::class.starProjectedType)) + } + } + } } diff --git a/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/SelectorBuilder.kt b/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/SelectorBuilder.kt new file mode 100644 index 0000000000..e7b120f4cc --- /dev/null +++ b/provider/src/main/kotlin/au/com/dius/pact/provider/junitsupport/loader/SelectorBuilder.kt @@ -0,0 +1,115 @@ +package au.com.dius.pact.provider.junitsupport.loader + +import au.com.dius.pact.core.pactbroker.ConsumerVersionSelectors + +/** + * Builder for setting up consumer version selectors in provider JUnit tests. + * See https://docs.pact.io/pact_broker/advanced_topics/consumer_version_selectors + */ +open class SelectorBuilder { + val selectors: MutableList = mutableListOf() + + /** + * The latest version from the main branch of each consumer, as specified by the consumer's mainBranch property. + */ + fun mainBranch(): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.MainBranch) + return this + } + + /** + * The latest version from a particular branch of each consumer, or for a particular consumer if the second + * parameter is provided. If fallback is provided, falling back to the fallback branch if none is found from the + * specified branch. + * + * @param name - Branch name + * @param consumer - Consumer name (optional) + * @param fallback - Fall back to this branch if none is found from the specified branch (optional) + */ + @JvmOverloads + fun branch(name: String, consumer: String? = null, fallback: String? = null): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.Branch(name, consumer, fallback)) + return this + } + + /** + * All the currently deployed and currently released and supported versions of each consumer. + */ + fun deployedOrReleased(): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.DeployedOrReleased) + return this + } + + /** + * The latest version from any branch of the consumer that has the same name as the current branch of the provider. + * Used for coordinated development between consumer and provider teams using matching feature branch names. + */ + fun matchingBranch(): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.MatchingBranch) + return this + } + + /** + * Any versions currently deployed to the specified environment + */ + fun deployedTo(environment: String): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.DeployedTo(environment)) + return this + } + + /** + * Any versions currently released and supported in the specified environment + */ + fun releasedTo(environment: String): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.ReleasedTo(environment)) + return this + } + + /** + * any versions currently deployed or released and supported in the specified environment + */ + fun environment(environment: String): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.Environment(environment)) + return this + } + + /** + * All versions with the specified tag + */ + @Deprecated("Tags are deprecated in favor of branches", ReplaceWith("branch")) + fun tag(name: String): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.Tag(name)) + return this + } + + /** + * The latest version for each consumer with the specified tag + */ + @Deprecated("Tags are deprecated in favor of branches", ReplaceWith("branch")) + fun latestTag(name: String): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.LatestTag(name)) + return this + } + + /** + * Generic selector. + * + * * With just the tag name, returns all versions with the specified tag. + * * With latest, returns the latest version for each consumer with the specified tag. + * * With a fallback tag, returns the latest version for each consumer with the specified tag, falling back to the + * fallbackTag if non is found with the specified tag. + * * With a consumer name, returns the latest version for a specified consumer with the specified tag. + * * With only latest, returns the latest version for each consumer. NOT RECOMMENDED as it suffers from race + * conditions when pacts are published from multiple branches. + */ + @Deprecated("Tags are deprecated in favor of branches", ReplaceWith("branch")) + fun selector(tagName: String?, latest: Boolean?, fallbackTag: String?, consumer: String?): SelectorBuilder { + selectors.add(ConsumerVersionSelectors.Selector(tagName, latest, consumer, fallbackTag)) + return this + } + + /** + * Construct the final list of consumer version selectors + */ + fun build(): List = selectors +}