diff --git a/xapi-model/pom.xml b/xapi-model/pom.xml
index 2a473e5e..f5cfe6b4 100644
--- a/xapi-model/pom.xml
+++ b/xapi-model/pom.xml
@@ -31,7 +31,7 @@
org.hibernate.validator
hibernate-validator
true
-
+
org.springframework.boot
spring-boot-starter-test
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Result.java b/xapi-model/src/main/java/dev/learning/xapi/model/Result.java
index 4772f353..f6d63ebd 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Result.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Result.java
@@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import dev.learning.xapi.model.validation.constraints.HasScheme;
+import dev.learning.xapi.model.validation.constraints.VaildScore;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
import java.net.URI;
@@ -32,6 +33,7 @@ public class Result {
* The score of the Agent in relation to the success or quality of the experience.
*/
@Valid
+ @VaildScore
private Score score;
/**
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/VaildScore.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/VaildScore.java
new file mode 100644
index 00000000..65d15240
--- /dev/null
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/constraints/VaildScore.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2016-2019 Berry Cloud Ltd. All rights reserved.
+ */
+
+package dev.learning.xapi.model.validation.constraints;
+
+import static java.lang.annotation.ElementType.FIELD;
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * The annotated element must be a valid score.
+ *
+ * @author Thomas Turrell-Croft
+ *
+ * @see Score
+ */
+@Documented
+@Constraint(validatedBy = {})
+@Target({METHOD, FIELD})
+@Retention(RUNTIME)
+public @interface VaildScore {
+
+ /**
+ * Error Message.
+ */
+ String message() default "must be a valid score";
+
+ /**
+ * Groups.
+ */
+ Class>[] groups() default {};
+
+ /**
+ * Payload.
+ */
+ Class extends Payload>[] payload() default {};
+}
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScoreValidator.java b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScoreValidator.java
new file mode 100644
index 00000000..746d6915
--- /dev/null
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/validation/internal/validators/ScoreValidator.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+
+package dev.learning.xapi.model.validation.internal.validators;
+
+import dev.learning.xapi.model.Score;
+import dev.learning.xapi.model.validation.constraints.VaildScore;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+/**
+ * The raw score must be greater or equal to min and less or equal to max.
+ *
+ * @author István Rátkai (Selindek)
+ */
+public class ScoreValidator implements ConstraintValidator {
+
+ @Override
+ public boolean isValid(Score value, ConstraintValidatorContext context) {
+
+ if (value == null) {
+ return true;
+ }
+
+ return (value.getMax() == null || value.getMax() >= value.getRaw())
+ && (value.getMin() == null || value.getMin() <= value.getRaw());
+ }
+
+}
diff --git a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator
index 77c1c051..fd019e08 100644
--- a/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator
+++ b/xapi-model/src/main/resources/META-INF/services/jakarta.validation.ConstraintValidator
@@ -10,3 +10,4 @@ dev.learning.xapi.model.validation.internal.validators.StatementRevisionValidato
dev.learning.xapi.model.validation.internal.validators.StatementPlatformValidator
dev.learning.xapi.model.validation.internal.validators.StatementVerbValidator
dev.learning.xapi.model.validation.internal.validators.StatementsValidator
+dev.learning.xapi.model.validation.internal.validators.ScoreValidator
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java
index 76378e17..9f6a36df 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScaledScoreValidatorTests.java
@@ -15,7 +15,7 @@
*
* @author István Rátkai (Selindek)
*/
-@DisplayName("ScaledSoreValidator tests")
+@DisplayName("ScaledScoreValidator tests")
class ScaledScoreValidatorTests {
private static final ScaledScoreValidator validator = new ScaledScoreValidator();
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScoreValidatorTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScoreValidatorTests.java
new file mode 100644
index 00000000..41b7b854
--- /dev/null
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/validation/internal/validators/ScoreValidatorTests.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2016-2023 Berry Cloud Ltd. All rights reserved.
+ */
+
+package dev.learning.xapi.model.validation.internal.validators;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import dev.learning.xapi.model.Score;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+
+/**
+ * ScoreValidator Tests.
+ *
+ * @author Thomas Turrell-Croft
+ */
+@DisplayName("ScoreValidator tests")
+class ScoreValidatorTests {
+
+ private static final ScoreValidator validator = new ScoreValidator();
+
+ @Test
+ void whenValueIsNullThenResultIsTrue() {
+
+ // When Value Is Null
+ final var result = validator.isValid(null, null);
+
+ // Then Result Is True
+ assertTrue(result);
+ }
+
+ @ParameterizedTest
+ @CsvSource({"0,100,50", "0,100,0", "0,100,100", "-100,0,-50"})
+ void whenCallingIsValidWithValidScoreThenResultIsTrue(float min, float max, float raw) {
+
+ final var s = Score.builder().min(min).max(max).raw(raw).build();
+
+ // When Calling Is Valid With Valid Score
+ final var result = validator.isValid(s, null);
+
+ // Then Result Is True
+ assertTrue(result);
+ }
+
+ @ParameterizedTest
+ @CsvSource({"0,100,101", "0,100,-1"})
+ void whenCallingIsValidWithInvalidScoreThenResultIsFalse(float min, float max, float raw) {
+
+ final var s = Score.builder().min(min).max(max).raw(raw).build();
+
+ // When Calling Is Valid With Invalid Score
+ final var result = validator.isValid(s, null);
+
+ // Then Result Is False
+ assertFalse(result);
+ }
+
+ @Test
+ void whenCallingIsValidWithAScoreWithMinNullThenResultIsTrue() {
+
+ final var s = Score.builder().min(null).max(100F).raw(50F).build();
+
+ // When Calling Is Valid With A Score With Min Null
+ final var result = validator.isValid(s, null);
+
+ // Then Result Is True
+ assertTrue(result);
+ }
+
+ @Test
+ void whenCallingIsValidWithAScoreWithMaxNullThenResultIsTrue() {
+
+ final var s = Score.builder().min(0F).max(null).raw(50F).build();
+
+ // When Calling Is Valid With A Score With Max Null
+ final var result = validator.isValid(s, null);
+
+ // Then Result Is True
+ assertTrue(result);
+ }
+
+
+
+}