Skip to content

Commit

Permalink
Refactor dispatch failure tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ndr-brt committed Mar 28, 2023
1 parent 684fb46 commit 753751d
Show file tree
Hide file tree
Showing 6 changed files with 243 additions and 355 deletions.
5 changes: 0 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,6 @@ if (actualVersion == "unspecified") {
buildscript {
repositories {
mavenLocal()
maven {
url = uri("https://oss.sonatype.org/content/repositories/snapshots/")
}
mavenCentral()
gradlePluginPortal()
}
dependencies {
val edcGradlePluginsVersion: String by project
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreement;
import org.eclipse.edc.connector.contract.spi.types.agreement.ContractAgreementVerificationMessage;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractOfferRequest;
import org.eclipse.edc.connector.contract.spi.types.negotiation.command.ContractNegotiationCommand;
import org.eclipse.edc.connector.contract.spi.types.offer.ContractOffer;
Expand All @@ -41,12 +42,19 @@
import org.eclipse.edc.statemachine.retry.EntityRetryProcessConfiguration;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;

import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;

import static java.util.Collections.emptyList;
import static java.util.concurrent.CompletableFuture.completedFuture;
Expand Down Expand Up @@ -79,8 +87,7 @@
class ConsumerContractNegotiationManagerImplTest {

private static final int RETRY_LIMIT = 1;
private static final int RETRIES_NOT_EXHAUSTED = RETRY_LIMIT;
private static final int RETRIES_EXHAUSTED = RETRIES_NOT_EXHAUSTED + 1;

private final ContractValidationService validationService = mock(ContractValidationService.class);
private final ContractNegotiationStore store = mock(ContractNegotiationStore.class);
private final RemoteMessageDispatcherRegistry dispatcherRegistry = mock(RemoteMessageDispatcherRegistry.class);
Expand Down Expand Up @@ -252,36 +259,6 @@ void requesting_shouldSendOfferAndTransitionRequested() {
});
}

@Test
void requesting_shouldTransitionRequestingIfSendFails_andRetriesNotExhausted() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_REQUESTING.code()).stateCount(RETRIES_NOT_EXHAUSTED).contractOffer(contractOffer()).build();
when(store.nextForState(eq(CONSUMER_REQUESTING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));
when(store.find(negotiation.getId())).thenReturn(negotiation);

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == CONSUMER_REQUESTING.code()));
verify(dispatcherRegistry, only()).send(any(), any());
});
}

@Test
void requesting_shouldTransitionTerminatingIfSendFails_andRetriesExhausted() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_REQUESTING.code()).stateCount(RETRIES_EXHAUSTED).contractOffer(contractOffer()).build();
when(store.nextForState(eq(CONSUMER_REQUESTING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));
when(store.find(negotiation.getId())).thenReturn(negotiation);

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == TERMINATING.code()));
verify(dispatcherRegistry, only()).send(any(), any());
});
}

@Test
void consumerApproving_shouldSendAgreementAndTransitionApproved() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_AGREEING.code()).contractOffer(contractOffer()).build();
Expand All @@ -298,36 +275,6 @@ void consumerApproving_shouldSendAgreementAndTransitionApproved() {
});
}

@Test
void consumerApproving_shouldTransitionApprovingIfSendFails_andRetriesNotExhausted() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_AGREEING.code()).stateCount(RETRIES_NOT_EXHAUSTED).contractOffer(contractOffer()).build();
when(store.nextForState(eq(CONSUMER_AGREEING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));
when(store.find(negotiation.getId())).thenReturn(negotiation);

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == CONSUMER_AGREEING.code()));
verify(dispatcherRegistry, only()).send(any(), any());
});
}

@Test
void consumerApproving_shouldTransitionTerminatingIfSendFails_andRetriesExhausted() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_AGREEING.code()).stateCount(RETRIES_EXHAUSTED).contractOffer(contractOffer()).build();
when(store.nextForState(eq(CONSUMER_AGREEING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));
when(store.find(negotiation.getId())).thenReturn(negotiation);

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == TERMINATING.code()));
verify(dispatcherRegistry, only()).send(any(), any());
});
}

@Test
void providerAgreed_shouldTransitionToVerifying() {
var negotiation = contractNegotiationBuilder().state(PROVIDER_AGREED.code()).build();
Expand All @@ -342,6 +289,7 @@ void providerAgreed_shouldTransitionToVerifying() {
});
}

@Deprecated(since = "milestone9")
@Test
void providerAgreed_shouldTransitionToFinalized_whenProtocolIsIdsMultipart() {
var negotiation = contractNegotiationBuilder().state(PROVIDER_AGREED.code()).protocol("ids-multipart").build();
Expand Down Expand Up @@ -372,35 +320,7 @@ void consumerVerifying_shouldSendMessageAndTransitionToVerified() {
}

@Test
void consumerVerifying_shouldKeepState_whenDispatchFails() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_VERIFYING.code()).stateCount(RETRIES_NOT_EXHAUSTED).build();
when(store.nextForState(eq(CONSUMER_VERIFYING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(store.find(negotiation.getId())).thenReturn(negotiation);
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == CONSUMER_VERIFYING.code()));
});
}

@Test
void consumerVerifying_shouldTransitionToTerminating_whenDispatchFailsAndRetriesExhausted() {
var negotiation = contractNegotiationBuilder().state(CONSUMER_VERIFYING.code()).stateCount(RETRIES_EXHAUSTED).build();
when(store.nextForState(eq(CONSUMER_VERIFYING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(store.find(negotiation.getId())).thenReturn(negotiation);
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == TERMINATING.code()));
});
}

@Test
void terminating_shouldSendRejectionAndTransitionTerminated() {
void terminating_shouldSendRejectionAndTransitionToTerminated() {
var negotiation = contractNegotiationBuilder().state(TERMINATING.code()).contractOffer(contractOffer()).build();
negotiation.setErrorDetail("an error");
when(store.nextForState(eq(TERMINATING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
Expand All @@ -416,36 +336,51 @@ void terminating_shouldSendRejectionAndTransitionTerminated() {
});
}

@Test
void terminating_shouldTransitionTerminatingIfSendFails_andRetriesNotExhausted() {
var negotiation = contractNegotiationBuilder().state(TERMINATING.code()).stateCount(RETRIES_NOT_EXHAUSTED).contractOffer(contractOffer()).build();
negotiation.setErrorDetail("an error");
when(store.nextForState(eq(TERMINATING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
@ParameterizedTest
@ArgumentsSource(DispatchFailureArguments.class)
void dispatchFailure(ContractNegotiationStates starting, ContractNegotiationStates ending, UnaryOperator<ContractNegotiation.Builder> builderEnricher) {
var negotiation = builderEnricher.apply(contractNegotiationBuilder().state(starting.code())).build();
when(store.nextForState(eq(starting.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));
when(store.find(negotiation.getId())).thenReturn(negotiation);

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == TERMINATING.code()));
verify(store).save(argThat(p -> p.getState() == ending.code()));
verify(dispatcherRegistry, only()).send(any(), any());
});
}

@Test
void terminating_shouldTransitionToTerminatedIfSendFails_andRetriesExhausted() {
var negotiation = contractNegotiationBuilder().state(TERMINATING.code()).stateCount(RETRIES_EXHAUSTED).contractOffer(contractOffer()).build();
negotiation.setErrorDetail("an error");
when(store.nextForState(eq(TERMINATING.code()), anyInt())).thenReturn(List.of(negotiation)).thenReturn(emptyList());
when(dispatcherRegistry.send(any(), any())).thenReturn(failedFuture(new EdcException("error")));
when(store.find(negotiation.getId())).thenReturn(negotiation);

negotiationManager.start();

await().untilAsserted(() -> {
verify(store).save(argThat(p -> p.getState() == TERMINATED.code()));
verify(dispatcherRegistry, only()).send(any(), any());
});
private static class DispatchFailureArguments implements ArgumentsProvider {

private static final int RETRIES_NOT_EXHAUSTED = RETRY_LIMIT;
private static final int RETRIES_EXHAUSTED = RETRIES_NOT_EXHAUSTED + 1;

@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) {
return Stream.of(
// retries not exhausted
new DispatchFailure(CONSUMER_REQUESTING, CONSUMER_REQUESTING, b -> b.stateCount(RETRIES_NOT_EXHAUSTED).contractOffer(contractOffer())),
new DispatchFailure(CONSUMER_AGREEING, CONSUMER_AGREEING, b -> b.stateCount(RETRIES_NOT_EXHAUSTED).contractOffer(contractOffer())),
new DispatchFailure(CONSUMER_VERIFYING, CONSUMER_VERIFYING, b -> b.stateCount(RETRIES_NOT_EXHAUSTED)),
new DispatchFailure(TERMINATING, TERMINATING, b -> b.stateCount(RETRIES_NOT_EXHAUSTED).errorDetail("an error")),
// retries exhausted
new DispatchFailure(CONSUMER_REQUESTING, TERMINATING, b -> b.stateCount(RETRIES_EXHAUSTED).contractOffer(contractOffer())),
new DispatchFailure(CONSUMER_AGREEING, TERMINATING, b -> b.stateCount(RETRIES_EXHAUSTED).contractOffer(contractOffer())),
new DispatchFailure(CONSUMER_VERIFYING, TERMINATING, b -> b.stateCount(RETRIES_EXHAUSTED)),
new DispatchFailure(TERMINATING, TERMINATED, b -> b.stateCount(RETRIES_EXHAUSTED).errorDetail("an error"))
);
}

private ContractOffer contractOffer() {
return ContractOffer.Builder.newInstance().id("id:id")
.policy(Policy.Builder.newInstance().build())
.asset(Asset.Builder.newInstance().id("assetId").build())
.contractStart(ZonedDateTime.now())
.contractEnd(ZonedDateTime.now())
.build();
}
}

private ContractNegotiation createContractNegotiationConsumerRequested() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG)
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0
*
* SPDX-License-Identifier: Apache-2.0
*
* Contributors:
* Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - initial API and implementation
*
*/

package org.eclipse.edc.connector.contract.negotiation;

import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiation;
import org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates;
import org.junit.jupiter.params.provider.Arguments;

import java.util.function.UnaryOperator;

import static org.eclipse.edc.connector.contract.spi.types.negotiation.ContractNegotiationStates.INITIAL;

public class DispatchFailure implements Arguments {

private final ContractNegotiationStates starting;
private final ContractNegotiationStates ending;
private final UnaryOperator<ContractNegotiation.Builder> builderEnricher;

public DispatchFailure() {
this(INITIAL, INITIAL, it -> it);
}

public DispatchFailure(ContractNegotiationStates starting, ContractNegotiationStates ending, UnaryOperator<ContractNegotiation.Builder> builderEnricher) {
this.starting = starting;
this.ending = ending;
this.builderEnricher = builderEnricher;
}

@Override
public Object[] get() {
return new Object[]{ starting, ending, builderEnricher };
}
}
Loading

0 comments on commit 753751d

Please sign in to comment.