Repository: OpenAEV-Platform/openaev (java)

Issue:
Status of a security platform should be “not prevented” (asset level)
## Description

We have different status for expectation in asset level and agent level.

At agent level: status are ok (detected/not detected)
![Image](https://github.com/user-attachments/assets/f768dd88-b83c-443c-b812-4c7525816fa9)

At asset level, not ok (success/failed)
![Image](https://github.com/user-attachments/assets/19f530aa-81bf-4749-9dfb-a908b1c8ca93)

## Expected Output

Have Detected, Not Detected, Prevented & Not Prevented on inject expectations (agent level, asset level, asset group level).


Discussion:

Patch:
diff --git a/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/utils/ExpectationUtils.java b/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/utils/ExpectationUtils.java
--- a/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/utils/ExpectationUtils.java
+++ b/openbas-api/src/main/java/io/openbas/collectors/expectations_expiration_manager/utils/ExpectationUtils.java
@@ -5,12 +5,15 @@
 
 import io.openbas.database.model.InjectExpectation;
 import io.openbas.database.model.InjectExpectation.EXPECTATION_TYPE;
+import io.openbas.expectation.ExpectationType;
 import jakarta.validation.constraints.NotNull;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 
 public class ExpectationUtils {
 
+  public static final String PREVENTED = "Prevented";
+
   private ExpectationUtils() {}
 
   public static boolean isExpired(@NotNull final InjectExpectation expectation) {
@@ -19,9 +22,19 @@ public static boolean isExpired(@NotNull final InjectExpectation expectation) {
     return expectation.getCreatedAt().isBefore(expirationTime);
   }
 
+  public static String computeSuccessMessage(@NotNull final EXPECTATION_TYPE expectationType) {
+    return DETECTION.equals(expectationType)
+        ? ExpectationType.DETECTION.successLabel
+        : PREVENTION.equals(expectationType)
+            ? PREVENTED
+            : ExpectationType.HUMAN_RESPONSE.successLabel;
+  }
+
   public static String computeFailedMessage(@NotNull final EXPECTATION_TYPE expectationType) {
     return DETECTION.equals(expectationType)
-        ? "Not Detected"
-        : PREVENTION.equals(expectationType) ? "Not Prevented" : "FAILED";
+        ? ExpectationType.DETECTION.failureLabel
+        : PREVENTION.equals(expectationType)
+            ? ExpectationType.PREVENTION.failureLabel
+            : ExpectationType.HUMAN_RESPONSE.failureLabel;
   }
 }
diff --git a/openbas-api/src/main/java/io/openbas/migration/V3_89__Update_Result_Label_Expectations.java b/openbas-api/src/main/java/io/openbas/migration/V3_89__Update_Result_Label_Expectations.java
new file mode 100644
--- /dev/null
+++ b/openbas-api/src/main/java/io/openbas/migration/V3_89__Update_Result_Label_Expectations.java
@@ -0,0 +1,44 @@
+package io.openbas.migration;
+
+import java.sql.Statement;
+import org.flywaydb.core.api.migration.BaseJavaMigration;
+import org.flywaydb.core.api.migration.Context;
+import org.springframework.stereotype.Component;
+
+@Component
+public class V3_89__Update_Result_Label_Expectations extends BaseJavaMigration {
+
+  @Override
+  public void migrate(Context context) throws Exception {
+    try (Statement statement = context.getConnection().createStatement()) {
+
+      String updateInjectExpectationResult =
+          """
+                UPDATE injects_expectations
+                SET inject_expectation_results = (
+                    SELECT jsonb_agg(
+                        jsonb_set(
+                            elem,
+                            '{result}',
+                            to_jsonb(
+                                CASE
+                                    WHEN elem->>'result' = 'FAILED' AND injects_expectations.inject_expectation_type = 'PREVENTION' THEN 'Not Prevented'
+                                    WHEN elem->>'result' = 'FAILED' AND injects_expectations.inject_expectation_type = 'DETECTION' THEN 'Not Detected'
+                                    WHEN elem->>'result' = 'SUCCESS' AND injects_expectations.inject_expectation_type = 'PREVENTION' THEN 'Prevented'
+                                    WHEN elem->>'result' = 'SUCCESS' AND injects_expectations.inject_expectation_type = 'DETECTION' THEN 'Detected'
+                                    ELSE elem->>'result'
+                                END
+                            )
+                        )
+                    )
+                    FROM jsonb_array_elements(injects_expectations.inject_expectation_results::jsonb) AS elem
+                )
+                WHERE jsonb_typeof(inject_expectation_results::jsonb) = 'array'
+                  AND jsonb_array_length(inject_expectation_results::jsonb) > 0
+                  AND inject_expectation_type IN ('PREVENTION', 'DETECTION');
+            """;
+
+      statement.executeUpdate(updateInjectExpectationResult);
+    }
+  }
+}
diff --git a/openbas-api/src/main/java/io/openbas/service/InjectExpectationService.java b/openbas-api/src/main/java/io/openbas/service/InjectExpectationService.java
--- a/openbas-api/src/main/java/io/openbas/service/InjectExpectationService.java
+++ b/openbas-api/src/main/java/io/openbas/service/InjectExpectationService.java
@@ -1,5 +1,7 @@
 package io.openbas.service;
 
+import static io.openbas.collectors.expectations_expiration_manager.utils.ExpectationUtils.computeFailedMessage;
+import static io.openbas.collectors.expectations_expiration_manager.utils.ExpectationUtils.computeSuccessMessage;
 import static io.openbas.database.model.InjectExpectation.EXPECTATION_TYPE.*;
 import static io.openbas.helper.StreamHelper.fromIterable;
 import static io.openbas.service.InjectExpectationUtils.*;
@@ -633,7 +635,9 @@ private void processExpectation(
         sourceId,
         sourceType,
         sourceName,
-        successScoreResult ? "SUCCESS" : "FAILED",
+        successScoreResult
+            ? computeSuccessMessage(expectation.getType())
+            : computeFailedMessage(expectation.getType()),
         successScoreResult ? finalScore : 0.0,
         null);
     expectation.setScore(finalScore);
diff --git a/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java b/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java
--- a/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java
+++ b/openbas-model/src/main/java/io/openbas/database/model/InjectExpectation.java
@@ -255,8 +255,12 @@ public boolean isUserHasAccess(User user) {
 
   @JsonProperty("target_id")
   public String getTargetId() {
-    if (team != null) {
+    if (user != null) {
+      return user.getId();
+    } else if (team != null) {
       return team.getId();
+    } else if (agent != null) {
+      return agent.getId();
     } else if (asset != null) {
       return asset.getId();
     } else if (assetGroup != null) {


Test patch:
diff --git a/openbas-api/src/test/java/io/openbas/rest/AtomicTestingApiTest.java b/openbas-api/src/test/java/io/openbas/rest/AtomicTestingApiTest.java
--- a/openbas-api/src/test/java/io/openbas/rest/AtomicTestingApiTest.java
+++ b/openbas-api/src/test/java/io/openbas/rest/AtomicTestingApiTest.java
@@ -43,7 +43,7 @@ public class AtomicTestingApiTest extends IntegrationTest {
   @Autowired private EndpointComposer endpointComposer;
   @Autowired private InjectComposer injectComposer;
   @Autowired private InjectStatusComposer injectStatusComposer;
-  @Autowired private ExpectationComposer expectationComposer;
+  @Autowired private InjectExpectationComposer injectExpectationComposer;
   @Autowired private ExecutorFixture executorFixture;
 
   @Autowired private MockMvc mvc;
@@ -297,9 +297,13 @@ public void mergedExpectationsHaveSupersetOfExpectationsAndMerged() throws Excep
                 .forInject(InjectFixture.getDefaultInject())
                 .withEndpoint(endpointWrapper)
                 .withExpectation(
-                    expectationComposer.forExpectation(detection1).withEndpoint(endpointWrapper))
+                    injectExpectationComposer
+                        .forExpectation(detection1)
+                        .withEndpoint(endpointWrapper))
                 .withExpectation(
-                    expectationComposer.forExpectation(detection2).withEndpoint(endpointWrapper));
+                    injectExpectationComposer
+                        .forExpectation(detection2)
+                        .withEndpoint(endpointWrapper));
 
         injectWrapper.persist();
 
@@ -459,13 +463,21 @@ public void mergedExpectationsAreSeparatedByType() throws Exception {
                 .forInject(InjectFixture.getDefaultInject())
                 .withEndpoint(endpointWrapper)
                 .withExpectation(
-                    expectationComposer.forExpectation(detection1).withEndpoint(endpointWrapper))
+                    injectExpectationComposer
+                        .forExpectation(detection1)
+                        .withEndpoint(endpointWrapper))
                 .withExpectation(
-                    expectationComposer.forExpectation(detection2).withEndpoint(endpointWrapper))
+                    injectExpectationComposer
+                        .forExpectation(detection2)
+                        .withEndpoint(endpointWrapper))
                 .withExpectation(
-                    expectationComposer.forExpectation(prevention1).withEndpoint(endpointWrapper))
+                    injectExpectationComposer
+                        .forExpectation(prevention1)
+                        .withEndpoint(endpointWrapper))
                 .withExpectation(
-                    expectationComposer.forExpectation(prevention2).withEndpoint(endpointWrapper));
+                    injectExpectationComposer
+                        .forExpectation(prevention2)
+                        .withEndpoint(endpointWrapper));
 
         injectWrapper.persist();
 
diff --git a/openbas-api/src/test/java/io/openbas/rest/inject/InjectTargetSearchTest.java b/openbas-api/src/test/java/io/openbas/rest/inject/InjectTargetSearchTest.java
--- a/openbas-api/src/test/java/io/openbas/rest/inject/InjectTargetSearchTest.java
+++ b/openbas-api/src/test/java/io/openbas/rest/inject/InjectTargetSearchTest.java
@@ -36,7 +36,7 @@ public class InjectTargetSearchTest extends IntegrationTest {
   @Autowired private InjectorContractComposer injectContractComposer;
   @Autowired private PayloadComposer payloadComposer;
   @Autowired private AssetGroupComposer assetGroupComposer;
-  @Autowired private ExpectationComposer expectationComposer;
+  @Autowired private InjectExpectationComposer injectExpectationComposer;
   @Autowired private EndpointComposer endpointComposer;
   @Autowired private TeamComposer teamComposer;
   @Autowired private UserComposer userComposer;
@@ -55,7 +55,7 @@ public void beforeEach() {
     injectContractComposer.reset();
     payloadComposer.reset();
     assetGroupComposer.reset();
-    expectationComposer.reset();
+    injectExpectationComposer.reset();
     endpointComposer.reset();
     agentComposer.reset();
     executorComposer.reset();
@@ -399,14 +399,14 @@ public void whenAgentBelongsToDynamicAssetInGroup_thenTargetIsDynamicAssetInGrou
         injectWrapper
             .withAssetGroup(assetGroupWrapper)
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.DETECTION,
                             InjectExpectation.EXPECTATION_STATUS.SUCCESS))
                     .withAgent(agent1Wrapper))
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.PREVENTION,
@@ -414,14 +414,14 @@ public void whenAgentBelongsToDynamicAssetInGroup_thenTargetIsDynamicAssetInGrou
                     .withAgent(agent1Wrapper))
             .withAssetGroup(assetGroupWrapper2)
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.DETECTION,
                             InjectExpectation.EXPECTATION_STATUS.SUCCESS))
                     .withAgent(agent2Wrapper))
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.PREVENTION,
@@ -1649,7 +1649,7 @@ public void whenEndpointIsDynamicAssetInGroup_thenTargetIsDynamicAssetInGroup()
         injectWrapper
             .withAssetGroup(assetGroupWrapper)
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.DETECTION,
@@ -1657,7 +1657,7 @@ public void whenEndpointIsDynamicAssetInGroup_thenTargetIsDynamicAssetInGroup()
                     .withAssetGroup(assetGroupWrapper)
                     .withEndpoint(ep1Wrapper))
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.PREVENTION,
@@ -1666,7 +1666,7 @@ public void whenEndpointIsDynamicAssetInGroup_thenTargetIsDynamicAssetInGroup()
                     .withEndpoint(ep1Wrapper))
             .withAssetGroup(assetGroupWrapper2)
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.DETECTION,
@@ -1674,7 +1674,7 @@ public void whenEndpointIsDynamicAssetInGroup_thenTargetIsDynamicAssetInGroup()
                     .withAssetGroup(assetGroupWrapper2)
                     .withEndpoint(ep2Wrapper))
             .withExpectation(
-                expectationComposer
+                injectExpectationComposer
                     .forExpectation(
                         InjectExpectationFixture.createExpectationWithTypeAndStatus(
                             InjectExpectation.EXPECTATION_TYPE.PREVENTION,
@@ -2424,9 +2424,9 @@ public void withSomeAssetGroupTargets_returnMatchingItemsInPage2() throws Except
     @Nested
     @DisplayName("With actual results")
     public class WithActualResults {
-      private ExpectationComposer.Composer getExpectationWrapperWithResult(
+      private InjectExpectationComposer.Composer getExpectationWrapperWithResult(
           InjectExpectation.EXPECTATION_TYPE type, InjectExpectation.EXPECTATION_STATUS status) {
-        return expectationComposer.forExpectation(
+        return injectExpectationComposer.forExpectation(
             InjectExpectationFixture.createExpectationWithTypeAndStatus(type, status));
       }
 
@@ -2436,17 +2436,17 @@ public void givenSpecificScores_expectationTypeResultsAreOfCorrectStatus() throw
         String searchTerm = "asset group target";
         InjectComposer.Composer injectWrapper = getInjectWrapper();
         AssetGroupComposer.Composer assetGroupWrapper = getAssetGroupComposerWithName(searchTerm);
-        ExpectationComposer.Composer expectationDetectionWrapper =
+        InjectExpectationComposer.Composer expectationDetectionWrapper =
             getExpectationWrapperWithResult(
                     InjectExpectation.EXPECTATION_TYPE.DETECTION,
                     InjectExpectation.EXPECTATION_STATUS.SUCCESS)
                 .withAssetGroup(assetGroupWrapper);
-        ExpectationComposer.Composer expectationPreventionWrapper =
+        InjectExpectationComposer.Composer expectationPreventionWrapper =
             getExpectationWrapperWithResult(
                     InjectExpectation.EXPECTATION_TYPE.PREVENTION,
                     InjectExpectation.EXPECTATION_STATUS.FAILED)
                 .withAssetGroup(assetGroupWrapper);
-        ExpectationComposer.Composer expectationHumanResponseWrapper =
+        InjectExpectationComposer.Composer expectationHumanResponseWrapper =
             getExpectationWrapperWithResult(
                     InjectExpectation.EXPECTATION_TYPE.CHALLENGE,
                     InjectExpectation.EXPECTATION_STATUS.PENDING)
@@ -2891,9 +2891,9 @@ public void withSomeTeamsTargets_returnMatchingItemsForExercisesWithInjectAllTea
     @Nested
     @DisplayName("With actual results")
     public class WithActualResults {
-      private ExpectationComposer.Composer getExpectationWrapperWithResult(
+      private InjectExpectationComposer.Composer getExpectationWrapperWithResult(
           InjectExpectation.EXPECTATION_TYPE type, InjectExpectation.EXPECTATION_STATUS status) {
-        return expectationComposer.forExpectation(
+        return injectExpectationComposer.forExpectation(
             InjectExpectationFixture.createExpectationWithTypeAndStatus(type, status));
       }
 
@@ -2903,7 +2903,7 @@ public void givenSpecificScores_expectationTypeResultsAreOfCorrectStatus() throw
         String searchTerm = "team target";
         InjectComposer.Composer injectWrapper = getInjectWrapper();
         TeamComposer.Composer teamWrapper = getTeamComposerWithName(searchTerm);
-        ExpectationComposer.Composer expectationHumanResponseWrapper =
+        InjectExpectationComposer.Composer expectationHumanResponseWrapper =
             getExpectationWrapperWithResult(
                     InjectExpectation.EXPECTATION_TYPE.CHALLENGE,
                     InjectExpectation.EXPECTATION_STATUS.PENDING)
@@ -3215,9 +3215,9 @@ public void withSomePlayersTargets_returnMatchingItemsForExercisesWithInjectWith
     @Nested
     @DisplayName("With actual results")
     public class WithActualResults {
-      private ExpectationComposer.Composer getExpectationWrapperWithResult(
+      private InjectExpectationComposer.Composer getExpectationWrapperWithResult(
           InjectExpectation.EXPECTATION_TYPE type, InjectExpectation.EXPECTATION_STATUS status) {
-        return expectationComposer.forExpectation(
+        return injectExpectationComposer.forExpectation(
             InjectExpectationFixture.createExpectationWithTypeAndStatus(type, status));
       }
 
@@ -3229,7 +3229,7 @@ public void givenSpecificScores_expectationTypeResultsAreOfCorrectStatus() throw
         TeamComposer.Composer teamWrapper = getTeamComposerWithName(searchTerm);
         UserComposer.Composer userWrapper =
             getPlayerComposerWithName(searchTerm, searchTerm, searchTerm + "@toto.fr");
-        ExpectationComposer.Composer expectationHumanResponseWrapper =
+        InjectExpectationComposer.Composer expectationHumanResponseWrapper =
             getExpectationWrapperWithResult(
                     InjectExpectation.EXPECTATION_TYPE.CHALLENGE,
                     InjectExpectation.EXPECTATION_STATUS.PENDING)
diff --git a/openbas-api/src/test/java/io/openbas/rest/inject_expectation/InjectExpectationServiceTest.java b/openbas-api/src/test/java/io/openbas/rest/inject_expectation/InjectExpectationServiceTest.java
--- a/openbas-api/src/test/java/io/openbas/rest/inject_expectation/InjectExpectationServiceTest.java
+++ b/openbas-api/src/test/java/io/openbas/rest/inject_expectation/InjectExpectationServiceTest.java
@@ -1,5 +1,6 @@
 package io.openbas.rest.inject_expectation;
 
+import static io.openbas.collectors.expectations_expiration_manager.utils.ExpectationUtils.PREVENTED;
 import static io.openbas.injectors.openbas.OpenBASInjector.OPENBAS_INJECTOR_ID;
 import static io.openbas.injectors.openbas.OpenBASInjector.OPENBAS_INJECTOR_NAME;
 import static java.util.Collections.emptyList;
@@ -10,16 +11,23 @@
 import io.openbas.database.model.*;
 import io.openbas.database.repository.*;
 import io.openbas.execution.ExecutableInject;
+import io.openbas.expectation.ExpectationType;
 import io.openbas.model.expectation.DetectionExpectation;
 import io.openbas.model.expectation.PreventionExpectation;
 import io.openbas.service.InjectExpectationService;
 import io.openbas.utils.fixtures.*;
+import io.openbas.utils.fixtures.composers.AgentComposer;
+import io.openbas.utils.fixtures.composers.EndpointComposer;
+import io.openbas.utils.fixtures.composers.InjectComposer;
+import io.openbas.utils.fixtures.composers.InjectExpectationComposer;
+import jakarta.persistence.EntityManager;
 import java.util.List;
 import java.util.Map;
 import org.junit.jupiter.api.*;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.junit.jupiter.MockitoExtension;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
 
 @TestInstance(TestInstance.Lifecycle.PER_CLASS)
 @ExtendWith(MockitoExtension.class)
@@ -30,6 +38,12 @@ class InjectExpectationServiceTest extends IntegrationTest {
   static Long EXPIRATION_TIME_SIX_HOURS = 21600L;
 
   // Saved entities for test setup
+  @Autowired private InjectComposer injectComposer;
+  @Autowired private InjectExpectationComposer injectExpectationComposer;
+  @Autowired private AgentComposer agentComposer;
+  @Autowired private EndpointComposer endpointComposer;
+  @Autowired private EntityManager entityManager;
+
   @Autowired private InjectExpectationRepository injectExpectationRepository;
   @Autowired private InjectorContractRepository injectorContractRepository;
   @Autowired private InjectorRepository injectorRepository;
@@ -37,6 +51,7 @@ class InjectExpectationServiceTest extends IntegrationTest {
   @Autowired private AssetRepository assetRepository;
   @Autowired private AssetGroupRepository assetGroupRepository;
   @Autowired private AgentRepository agentRepository;
+
   @Autowired private InjectExpectationService injectExpectationService;
 
   private static Injector savedInjector;
@@ -328,4 +343,91 @@ void expectationsForAssetGroupWithMultipleAgents() {
                 savedInject.getId(), savedAssetGroup.getId(), savedAgent1.getId())
             .size());
   }
+
+  @Transactional
+  @Nested
+  @DisplayName("Verify Result label for InjectExpectation at asset level")
+  class ResultLabelInjectExpectation {
+
+    @Test
+    @DisplayName("InjectExpectation Asset should be Prevented")
+    void
+        given_expectation_agent_prevented_when_compute_asset_expectation_then_asset_expectation_should_be_prevented() {
+      assertAssetExpectationResult(
+          InjectExpectation.EXPECTATION_TYPE.PREVENTION,
+          InjectExpectation.EXPECTATION_STATUS.PENDING,
+          InjectExpectation.EXPECTATION_STATUS.SUCCESS,
+          PREVENTED,
+          100D);
+    }
+
+    @Test
+    @DisplayName("InjectExpectation Asset should be Not Detected")
+    void
+        given_expectation_agent_not_detected_when_compute_asset_expectation_then_asset_expectation_should_be_not_detected() {
+      assertAssetExpectationResult(
+          InjectExpectation.EXPECTATION_TYPE.DETECTION,
+          InjectExpectation.EXPECTATION_STATUS.PENDING,
+          InjectExpectation.EXPECTATION_STATUS.FAILED,
+          ExpectationType.DETECTION.failureLabel,
+          0D);
+    }
+
+    private void assertAssetExpectationResult(
+        InjectExpectation.EXPECTATION_TYPE expectationType,
+        InjectExpectation.EXPECTATION_STATUS assetStatus,
+        InjectExpectation.EXPECTATION_STATUS agentStatus,
+        String expectedResult,
+        Double score) {
+      AgentComposer.Composer agent =
+          agentComposer.forAgent(AgentFixture.createDefaultAgentService());
+      EndpointComposer.Composer endpoint =
+          endpointComposer.forEndpoint(EndpointFixture.createEndpoint()).withAgent(agent);
+
+      InjectExpectation assetExpectation =
+          InjectExpectationFixture.createExpectationWithTypeAndStatus(expectationType, assetStatus);
+
+      InjectExpectation agentExpectation =
+          InjectExpectationFixture.createExpectationWithTypeAndStatus(expectationType, agentStatus);
+
+      agentExpectation.setResults(
+          List.of(
+              InjectExpectationResult.builder()
+                  .sourceType("sourceType")
+                  .sourceName("sourceName")
+                  .sourceId("sourceId")
+                  .score(score)
+                  .result(expectedResult)
+                  .build()));
+
+      Inject inject =
+          injectComposer
+              .forInject(InjectFixture.getDefaultInject())
+              .withEndpoint(endpoint)
+              .withExpectation(
+                  injectExpectationComposer
+                      .forExpectation(agentExpectation)
+                      .withAgent(agent)
+                      .withEndpoint(endpoint))
+              .withExpectation(
+                  injectExpectationComposer.forExpectation(assetExpectation).withEndpoint(endpoint))
+              .persist()
+              .get();
+
+      entityManager.flush();
+      entityManager.clear();
+
+      injectExpectationService.computeExpectationAsset(
+          assetExpectation, List.of(agentExpectation), "sourceId", "sourceType", "sourceName");
+
+      List<InjectExpectation> savedExpectations =
+          injectExpectationRepository.findAllByInjectAndAsset(
+              inject.getId(), endpoint.get().getId());
+
+      assertEquals(
+          expectedResult,
+          savedExpectations.get(0).getResults().get(0).getResult(),
+          "Asset expectation result should match expected");
+    }
+  }
 }
diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectExpectationFixture.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectExpectationFixture.java
--- a/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectExpectationFixture.java
+++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/InjectExpectationFixture.java
@@ -1,9 +1,6 @@
 package io.openbas.utils.fixtures;
 
-import io.openbas.database.model.Exercise;
-import io.openbas.database.model.Inject;
-import io.openbas.database.model.InjectExpectation;
-import io.openbas.database.model.Team;
+import io.openbas.database.model.*;
 import io.openbas.rest.inject.form.InjectExpectationUpdateInput;
 import java.util.Map;
 
diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java
--- a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java
+++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectComposer.java
@@ -24,7 +24,7 @@ public class Composer extends InnerComposerBase<Inject> {
     private final List<DocumentComposer.Composer> documentComposers = new ArrayList<>();
     private final List<TeamComposer.Composer> teamComposers = new ArrayList<>();
     private final List<AssetGroupComposer.Composer> assetGroupComposers = new ArrayList<>();
-    private final List<ExpectationComposer.Composer> expectationComposers = new ArrayList<>();
+    private final List<InjectExpectationComposer.Composer> expectationComposers = new ArrayList<>();
 
     public Composer(Inject inject) {
       this.inject = inject;
@@ -92,7 +92,7 @@ public Composer withAssetGroup(AssetGroupComposer.Composer assetGroupComposer) {
       return this;
     }
 
-    public Composer withExpectation(ExpectationComposer.Composer expectationComposer) {
+    public Composer withExpectation(InjectExpectationComposer.Composer expectationComposer) {
       expectationComposers.add(expectationComposer);
       List<InjectExpectation> tempExpectations = this.inject.getExpectations();
       tempExpectations.add(expectationComposer.get());
@@ -115,6 +115,7 @@ public Composer persist() {
       documentComposers.forEach(DocumentComposer.Composer::persist);
       injectRepository.save(inject);
       injectStatusComposers.ifPresent(InjectStatusComposer.Composer::persist);
+      expectationComposers.forEach(InjectExpectationComposer.Composer::persist);
       injectDocumentRepository.saveAll(inject.getDocuments());
       return this;
     }
@@ -129,7 +130,7 @@ public Composer delete() {
       injectStatusComposers.ifPresent(InjectStatusComposer.Composer::delete);
       teamComposers.forEach(TeamComposer.Composer::delete);
       injectorContractComposer.ifPresent(InjectorContractComposer.Composer::delete);
-      documentComposers.forEach(DocumentComposer.Composer::delete);
+      expectationComposers.forEach(InjectExpectationComposer.Composer::delete);
       return this;
     }
 
diff --git a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExpectationComposer.java b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectExpectationComposer.java
similarity index 97%
rename from openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExpectationComposer.java
rename to openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectExpectationComposer.java
--- a/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/ExpectationComposer.java
+++ b/openbas-api/src/test/java/io/openbas/utils/fixtures/composers/InjectExpectationComposer.java
@@ -7,7 +7,7 @@
 import org.springframework.stereotype.Component;
 
 @Component
-public class ExpectationComposer extends ComposerBase<InjectExpectation> {
+public class InjectExpectationComposer extends ComposerBase<InjectExpectation> {
   @Autowired private InjectExpectationRepository injectExpectationRepository;
 
   public class Composer extends InnerComposerBase<InjectExpectation> {
diff --git a/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx b/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx
--- a/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx
+++ b/openbas-front/src/admin/components/atomic_testings/atomic_testing/TargetResultsDetail.tsx
@@ -623,26 +623,26 @@ const TargetResultsDetailFlow: FunctionComponent<Props> = ({
                         <GridLegacy item={true} xs={5} sx={{ textAlign: 'end' }}>
                           {
                             injectExpectation.inject_expectation_status === 'SUCCESS' && injectExpectation.inject_expectation_type === 'PREVENTION' && (
-                              <ItemResult label="Prevented" status="Prevented" />
+                              <ItemResult label={t('Prevented')} status="Prevented" />
                             )
                           }
                           {
                             injectExpectation.inject_expectation_status === 'SUCCESS' && injectExpectation.inject_expectation_type === 'DETECTION' && (
-                              <ItemResult label="Detected" status="Detected" />
+                              <ItemResult label={t('Detected')} status="Detected" />
                             )
                           }
                           {
                             injectExpectation.inject_expectation_status === 'FAILED' && injectExpectation.inject_expectation_type === 'PREVENTION' && (
-                              <ItemResult label="Not Prevented" status="Not Prevented" />
+                              <ItemResult label={t('Not Prevented')} status="Not Prevented" />
                             )
                           }
                           {
                             injectExpectation.inject_expectation_status === 'FAILED' && injectExpectation.inject_expectation_type === 'DETECTION' && (
-                              <ItemResult label="Not Detected" status="Not Detected" />
+                              <ItemResult label={t('Not Detected')} status="Not Detected" />
                             )
                           }
                           {injectExpectation.inject_expectation_status && HUMAN_EXPECTATION.includes(injectExpectation.inject_expectation_type) && (
-                            <ItemResult label={injectExpectation.inject_expectation_status} status={injectExpectation.inject_expectation_status} />
+                            <ItemResult label={t(injectExpectation.inject_expectation_status)} status={injectExpectation.inject_expectation_status} />
                           )}
                           <Tooltip title={t('Score')}><Chip classes={{ root: classes.score }} label={injectExpectation.inject_expectation_score} /></Tooltip>
                         </GridLegacy>
@@ -789,7 +789,7 @@ const TargetResultsDetailFlow: FunctionComponent<Props> = ({
                                     </div>
                                   </TableCell>
                                   <TableCell>
-                                    <ItemResult label={expectationResult.result} status={expectationResult.result} />
+                                    <ItemResult label={t(expectationResult.result)} status={expectationResult.result} />
                                   </TableCell>
                                   <TableCell className={classes.tableFontSize}>
                                     {