Hello, I'm doing consumer driven contract testing....
# pact-jvm
r
Hello, I'm doing consumer driven contract testing. I have finished the consumer class and now I am working on the provider. My provider is able to verify that the message is correct or what is missing but when I try to Publish the Verification results I get 403. So it can access Pactflow to verify the contract/message but it cannot punish the results, weird. 😞
Copy code
@Provider("myProvider")
@Consumer("myConsumer")
@PactBroker(scheme = "https", host = "<http://mypactflow.pactflow.io|mypactflow.pactflow.io>",
        authentication = @PactBrokerAuth(token = "mytoken"))
public class ProviderUCDLambdaTest {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    public void setUp(PactVerificationContext context) {
        context.setTarget(new MessageTestTarget(List.of("com.myPackage")));
        System.setProperty("pact.provider.version", System.getenv("GIT_COMMIT") == null ? "" : System.getenv("GIT_COMMIT"));
        System.setProperty("pact.provider.tag", System.getenv("GIT_BRANCH") == null ? "" : System.getenv("GIT_BRANCH"));
        System.setProperty("pact.provider.branch", System.getenv("GIT_BRANCH") == null ? "" : System.getenv("GIT_BRANCH"));
        System.setProperty("pact.verifier.publishResults", System.getenv("PACT_BROKER_PUBLISH_VERIFICATION_RESULTS") == null ? "true" : "true");
    }

    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    void testTemplate(PactVerificationContext context) {
        context.verifyInteraction();
    }

    @PactVerifyProvider("a message with user signal information")
    public MessageAndMetadata provideMessage() throws Exception {

        // Main Message Simplified For Example
        Map<String, Object> mainMessage = new HashMap<>();
        mainMessage.put("messageId", "test");
        mainMessage.put("receiptHandle", "MessageReceiptHandle");

        Map<String, Object> metadata = new HashMap<>();

        return new MessageAndMetadata(objectMapper.writeValueAsBytes(mainMessage), metadata);
    }

}
I'm using "au.com.dius.pact" version "4.3.15"
Copy code
testImplementation("au.com.dius.pact.consumer:junit5:${project.pactVersion}")
testImplementation("au.com.dius.pact.provider:junit5:${project.pactVersion}")
Messager I get:
Task user changed details lambdaproviderContractTest
Verifying a pact between payments-fraud-risk-lambda (fce57cb70c7fe8b9a40ab47787aaef10f4b4dddc) and user-change-details-signal Notices: 1) The pact at https://xxxxxxx being verified because the pact content belongs to the consumer version matching the following criterion: * latest version of awslambdaxxx from the main branch 'main' (fce57cb70c7fe8) [from Pact Broker https://xxxxxxxx a message with user signal information 2023-08-22 142945 WARN Metrics - Please note: we are tracking events anonymously to gather important usage statistics like JVM version and operating system. To disable tracking, set the 'pact_do_not_track' system property or environment variable to 'true'. generates a message which has a matching body (OK) has matching metadata (OK) 2023-08-22 142945 WARN ProviderVersion - Provider version not set, defaulting to '0.0.0' 2023-08-22 142947 ERROR HalClient - PUT JSON request failed with status 403 Forbidden 2023-08-22 142947 ERROR PactBrokerClient - Publishing verification results failed with an exception au.com.dius.pact.core.pactbroker.RequestFailedException: Request failed with 403
y
are you using a token that doesn’t have write perms (ie read only will allow you to retrieve, but you wouldn’t be expected to publish results from your local machine)
r
Yes it was read only.
y
that is the expected behaviour then
🙌 1
r
Is it possible to run my class without:
Copy code
@PactBroker(scheme = "https", host = "<http://mypactflow.pactflow.io|mypactflow.pactflow.io>",
        authentication = @PactBrokerAuth(token = "mytoken"))
and perhaps use like a command like here:
Copy code
- label: ":pact: Publish Consumer Contract"
  key: pact-publish-consumer-contract
  depends_on:
    - gradle:build
  agents:
    queue: java-build
  plugins:
    - seek-oss/aws-sm#v2.3.1:
        json-to-env:
          - secret-id: "/buildkite/common/pactflow"
            json-key: ".Variables"
    - docker#v3.5.0:
        volumes:
          - "/shared/data:/shared/data"
        image: ${ECR_REGISTRY}pactfoundation/pact-cli:0.50.0.31
        command:
          - pact-broker
          - publish
          - /shared/data/mypact.json
          - --consumer-app-version=$BUILDKITE_COMMIT
          - --branch=$BUILDKITE_BRANCH
          - --tag=${PACT_TAG}
          - --tag=PR-${BUILDKITE_PULL_REQUEST}

        environment:
          - "PACT_BROKER_BASE_URL"
          - "PACT_BROKER_TOKEN"
          - "BUILDKITE_BRANCH"
          - "BUILDKITE_COMMIT"
          - "SERVICE"
I'm trying to get the environment variables (Base_Url and Token) to the @PactBroker annotation but no luck 😞
y
If all you want to do is not publish verification results with a read only token, set this as default to false that will allow you to pull down contracts for local verification and then publish them back.
Copy code
System.setProperty("pact.verifier.publishResults", System.getenv("PACT_BROKER_PUBLISH_VERIFICATION_RESULTS") == null ? "true" : "true");
👆 this is saying always publish results, which probably isn’t what you want, if you are going to run verifications locally the above snippet you’ve shown is for publishing pacts, and the first snippet is for verifying. Pact-JVM uses its own core for consumer/provider testing, whereas the pact cli tooling contains the ruby command line implementation of a verifier. There is also a rust one, which supports v3/v4 features. https://docs.pact.io/implementation_guides/cli#provider-verifier you would probably want the native verifier from that list, if you are going to try and verify pacts in a language agnostic fashion via a cli. I suspect you don’t need this at all tho
🙌 1
r
Is there a way to pass the token from env variables to this annotation?
Copy code
@PactBroker(scheme = "https", host = "<http://myTest.pactflow.io|myTest.pactflow.io>",
        authentication = @PactBrokerAuth(token = "myToken"))
@Provider("user-change-details-signal")
@Consumer("payments-fraud-risk-lambda")
_______
Copy code
@PactBroker(scheme = "https", host = "<http://myTest.pactflow.io|myTest.pactflow.io>",
        authentication = @PactBrokerAuth(token = "myToken"))
@Provider("user-change-details-signal")
@Consumer("payments-fraud-risk-lambda")
public class ProviderUCDLambdaTest {

    private final ObjectMapper objectMapper = new ObjectMapper();

    @BeforeEach
    public void setUp(PactVerificationContext context) {
        context.setTarget(new MessageTestTarget(List.of("com.myTest.payments.user_change_details")));
        System.setProperty("pact.provider.version", System.getenv("GIT_COMMIT") == null ? "" : System.getenv("GIT_COMMIT"));
        System.setProperty("pact.provider.tag", System.getenv("GIT_BRANCH") == null ? "" : System.getenv("GIT_BRANCH"));
        System.setProperty("pact.provider.branch", System.getenv("GIT_BRANCH") == null ? "" : System.getenv("GIT_BRANCH"));
        System.setProperty("pact.verifier.publishResults", System.getenv("PACT_BROKER_PUBLISH_VERIFICATION_RESULTS") == null ? "true" : "true");

        System.setProperty("pact.broker.url", "<https://fanduel.pactflow.io>");
        System.setProperty("pact.broker.token", System.getenv("Pact_Broker_Token"));
    }
m
wouldn’t be better to prepare your Maven/Gradle to inject that credentials? ex gradle:
Copy code
test {
    useJUnitPlatform()

    if (System.getenv("CI") != null) {
        println("[contract-tests] CI run")
        systemProperty 'pact.verifier.publishResults', 'true'
    } else {
        println("[contract-tests] Local run")
        systemProperty 'pact.verifier.publishResults', 'false'
    }
    environment 'pactbroker.enablePending', 'true'
    environment 'pactbroker.url', 'your-url'
    environment 'pactbroker.auth.token', 'token'
}
then you do not need to pass any token to annotation at all
m