From f710aad4ed098f23b7d4fb3dbbf0bd7745c03721 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Istv=C3=A1n=20R=C3=A1tkai?=
Date: Thu, 22 Jun 2023 15:00:34 +0100
Subject: [PATCH 1/2] Add scheme validation for every URI in the model
---
xapi-model/src/main/java/dev/learning/xapi/model/Account.java | 2 ++
xapi-model/src/main/java/dev/learning/xapi/model/Activity.java | 2 ++
.../main/java/dev/learning/xapi/model/ActivityDefinition.java | 2 ++
xapi-model/src/main/java/dev/learning/xapi/model/Actor.java | 2 ++
.../src/main/java/dev/learning/xapi/model/Attachment.java | 3 +++
5 files changed, 11 insertions(+)
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Account.java b/xapi-model/src/main/java/dev/learning/xapi/model/Account.java
index 65515cd2..759bf261 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Account.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Account.java
@@ -6,6 +6,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import dev.learning.xapi.model.validation.constraints.HasScheme;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.net.URI;
@@ -30,6 +31,7 @@ public class Account {
* The canonical home page for the system the account is on.
*/
@NotNull
+ @HasScheme
private URI homePage;
/**
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java b/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java
index 76de8aaf..f8bff72e 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Activity.java
@@ -5,6 +5,7 @@
package dev.learning.xapi.model;
import com.fasterxml.jackson.annotation.JsonMerge;
+import dev.learning.xapi.model.validation.constraints.HasScheme;
import dev.learning.xapi.model.validation.constraints.ValidActivityDefinition;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
@@ -34,6 +35,7 @@ public class Activity implements StatementObject, SubStatementObject {
* An identifier for a single unique Activity.
*/
@NotNull
+ @HasScheme
private URI id;
/**
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java
index 93d25acd..4c7dd03c 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/ActivityDefinition.java
@@ -57,12 +57,14 @@ public class ActivityDefinition {
/**
* The type of Activity.
*/
+ @HasScheme
private URI type;
/**
* Resolves to a document with human-readable information about the Activity, which could include
* a way to launch the activity.
*/
+ @HasScheme
private URI moreInfo;
/**
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java b/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java
index d522bac3..14daa3e8 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Actor.java
@@ -11,6 +11,7 @@
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeInfo.As;
+import dev.learning.xapi.model.validation.constraints.HasScheme;
import dev.learning.xapi.model.validation.constraints.Mbox;
import jakarta.validation.Valid;
import java.net.URI;
@@ -63,6 +64,7 @@ public abstract class Actor implements StatementObject, SubStatementObject {
/**
* An openID.
*/
+ @HasScheme
URI openid;
/**
diff --git a/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java b/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java
index 0e558bfc..44481a12 100644
--- a/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java
+++ b/xapi-model/src/main/java/dev/learning/xapi/model/Attachment.java
@@ -7,6 +7,7 @@
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
+import dev.learning.xapi.model.validation.constraints.HasScheme;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.valueextraction.Unwrapping;
import java.net.URI;
@@ -37,6 +38,7 @@ public class Attachment {
* Identifies the usage of this Attachment.
*/
@NotNull
+ @HasScheme
private URI usageType;
/**
@@ -75,6 +77,7 @@ public class Attachment {
* performs DNS lookups when calling equals and hashcode.
*
*/
+ @HasScheme
private URI fileUrl;
/**
From 99b80daff929f128b00c9dd789ed1fa363a3be80 Mon Sep 17 00:00:00 2001
From: Thomas Turrell-Croft
Date: Thu, 22 Jun 2023 17:19:38 +0100
Subject: [PATCH 2/2] Add tests
---
.../dev/learning/xapi/model/AccountTests.java | 19 +
.../xapi/model/ActivityDefinitionTests.java | 734 ++++++++++--------
.../learning/xapi/model/ActivityTests.java | 22 +
.../dev/learning/xapi/model/AgentTests.java | 23 +
.../learning/xapi/model/AttachmentTests.java | 60 ++
5 files changed, 521 insertions(+), 337 deletions(-)
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/AccountTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/AccountTests.java
index fe5964e6..26369469 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/AccountTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/AccountTests.java
@@ -185,4 +185,23 @@ void whenValidatingAccountWithEmptyNameThenConstraintViolationsSizeIsOne() {
}
+ @Test
+ void whenValidatingAccountWithHomepageWithNoSchemeThenConstraintViolationsSizeIsOne() {
+
+ final var account = Account.builder()
+
+ .name("Example")
+
+ .homePage(URI.create("www.example.com"))
+
+ .build();
+
+ // When Validating Account With Homepage With No Scheme
+ final Set> constraintViolations = validator.validate(account);
+
+ // Then ConstraintViolations Size Is One
+ assertThat(constraintViolations, hasSize(1));
+
+ }
+
}
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java
index 490e2521..6548d05a 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityDefinitionTests.java
@@ -13,6 +13,8 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.learning.xapi.model.validation.constraints.HasScheme;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
@@ -34,522 +36,580 @@
@DisplayName("ActivityDefinition tests")
class ActivityDefinitionTests {
- private final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
+ private final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
- @Test
- void whenDeserializingActivityDefinitionThenResultIsInstanceOfActivityDefinition()
- throws Exception {
+ private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenResultIsInstanceOfActivityDefinition()
+ throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Result Is Instance Of Activity Definition
- assertThat(result, instanceOf(ActivityDefinition.class));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Result Is Instance Of Activity Definition
+ assertThat(result, instanceOf(ActivityDefinition.class));
- @Test
- void whenDeserializingActivityDefinitionThenMoreInfoIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenMoreInfoIsExpected() throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then MoreInfo Is Expected
- assertThat(result.getMoreInfo(), is(URI.create("http://example.com/more")));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then MoreInfo Is Expected
+ assertThat(result.getMoreInfo(), is(URI.create("http://example.com/more")));
- @Test
- void whenDeserializingActivityDefinitionThenInteractionTypeIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenInteractionTypeIsExpected() throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then InteractionType Is Expected
- assertThat(result.getInteractionType(), is(InteractionType.TRUE_FALSE));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then InteractionType Is Expected
+ assertThat(result.getInteractionType(), is(InteractionType.TRUE_FALSE));
- @Test
- void whenDeserializingActivityDefinitionThenCorrectResponsesPatternIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenCorrectResponsesPatternIsExpected()
+ throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then CorrectResponsesPattern Is Expected
- assertThat(result.getCorrectResponsesPattern(), is(Collections.singletonList(
- "{case_matters=false}{lang=en}To store and provide access to learning experiences.")));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then CorrectResponsesPattern Is Expected
+ assertThat(result.getCorrectResponsesPattern(), is(Collections.singletonList(
+ "{case_matters=false}{lang=en}To store and provide access to learning experiences.")));
- @Test
- void whenDeserializingActivityDefinitionThenExtensionsAreExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenExtensionsAreExpected() throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Extensions Are Expected
- assertThat(result.getExtensions().get(URI.create("http://url")), is("www.example.com"));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Extensions Are Expected
+ assertThat(result.getExtensions().get(URI.create("http://url")), is("www.example.com"));
- @Test
- void whenDeserializingActivityDefinitionThenNameIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenNameIsExpected() throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Name Is Expected
- assertThat(result.getName().get(Locale.US),
- is("Does the xAPI include the concept of statements?"));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Name Is Expected
+ assertThat(result.getName().get(Locale.US),
+ is("Does the xAPI include the concept of statements?"));
- @Test
- void whenDeserializingActivityDefinitionThenDescriptionIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenDescriptionIsExpected() throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Description Is Expected
- assertThat(result.getDescription().get(Locale.US), is("pong[.]1:[,]dg[.]:10[,]lunch[.]"));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Description Is Expected
+ assertThat(result.getDescription().get(Locale.US), is("pong[.]1:[,]dg[.]:10[,]lunch[.]"));
- @Test
- void whenDeserializingActivityDefinitionThenTypeIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionThenTypeIsExpected() throws Exception {
- // When Deserializing ActivityDefinition
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Type Is Expected
- assertThat(result.getType(),
- is(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction")));
+ // When Deserializing ActivityDefinition
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Type Is Expected
+ assertThat(result.getType(),
+ is(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction")));
- @Test
- void whenDeserializingActivityDefinitionWithoutTypeWhenDeserializedThenTypeIsNull()
- throws Exception {
+ }
- final var file = ResourceUtils
- .getFile("classpath:activity_definition/activity_definition_without_type.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithoutTypeWhenDeserializedThenTypeIsNull()
+ throws Exception {
- // When Deserializing Activity Definition Without Type
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file = ResourceUtils
+ .getFile("classpath:activity_definition/activity_definition_without_type.json");
- // Then Type Is Null
- assertThat(result.getType(), nullValue());
+ // When Deserializing Activity Definition Without Type
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Type Is Null
+ assertThat(result.getType(), nullValue());
- @Test
- void whenDeserializingActivityDefinitionWithChoicesThenChoicesIDIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithChoicesThenChoicesIDIsExpected() throws Exception {
- // When Deserializing ActivityDefinition With Choices
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Choices ID Is Expected
- assertThat(result.getChoices().get(0).getId(), is("1"));
+ // When Deserializing ActivityDefinition With Choices
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Choices ID Is Expected
+ assertThat(result.getChoices().get(0).getId(), is("1"));
- @Test
- void whenDeserializingActivityDefinitionWithChoicesThenChoicesDescriptionIsExpected()
- throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithChoicesThenChoicesDescriptionIsExpected()
+ throws Exception {
- // When Deserializing ActivityDefinition With Choices
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Choices Description Is Expected
- assertThat(result.getChoices().get(0).getDescription().get(Locale.US),
- is("Does the xAPI include the concept of statements?"));
+ // When Deserializing ActivityDefinition With Choices
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Choices Description Is Expected
+ assertThat(result.getChoices().get(0).getDescription().get(Locale.US),
+ is("Does the xAPI include the concept of statements?"));
- @Test
- void whenDeserializingActivityDefinitionWithScaledThenScaleIDIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithScaledThenScaleIDIsExpected() throws Exception {
- // When Deserializing ActivityDefinition With Scaled
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Scale ID Is Expected
- assertThat(result.getScale().get(0).getId(), is("1"));
+ // When Deserializing ActivityDefinition With Scaled
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Scale ID Is Expected
+ assertThat(result.getScale().get(0).getId(), is("1"));
- @Test
- void whenDeserializingActivityDefinitionWithScaledThenScaleDescriptionIsExpected()
- throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithScaledThenScaleDescriptionIsExpected()
+ throws Exception {
- // When Deserializing Activity Definition With Scaled
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Scale Description Is Expected
- assertThat(result.getScale().get(0).getDescription().get(Locale.US),
- is("Does the xAPI include the concept of statements?"));
+ // When Deserializing Activity Definition With Scaled
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Scale Description Is Expected
+ assertThat(result.getScale().get(0).getDescription().get(Locale.US),
+ is("Does the xAPI include the concept of statements?"));
- @Test
- void whenDeserializingActivityDefinitionWithSourceThenSourceIDIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithSourceThenSourceIDIsExpected() throws Exception {
- // When Deserializing Activity Definition With Source
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Source ID Is Expected
- assertThat(result.getSource().get(0).getId(), is("1"));
+ // When Deserializing Activity Definition With Source
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Source ID Is Expected
+ assertThat(result.getSource().get(0).getId(), is("1"));
- @Test
- void whenDeserializingActivityDefinitionWithSourceThenSourceDescriptionIsExpected()
- throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithSourceThenSourceDescriptionIsExpected()
+ throws Exception {
- // When Deserializing Activity Definition With Source
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Source Description Is Expected
- assertThat(result.getSource().get(0).getDescription().get(Locale.US),
- is("Does the xAPI include the concept of statements?"));
+ // When Deserializing Activity Definition With Source
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Source Description Is Expected
+ assertThat(result.getSource().get(0).getDescription().get(Locale.US),
+ is("Does the xAPI include the concept of statements?"));
- @Test
- void whenDeserializingActivityDefinitionWithTargetThenTargetIDIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithTargetThenTargetIDIsExpected() throws Exception {
- // When Deserializing Activity Definition With Target
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Target ID Is Expected
- assertThat(result.getTarget().get(0).getId(), is("1"));
+ // When Deserializing Activity Definition With Target
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Target ID Is Expected
+ assertThat(result.getTarget().get(0).getId(), is("1"));
- @Test
- void whenDeserializingActivityDefinitionWithTargetThenTargetDescriptionIsExpected()
- throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithTargetThenTargetDescriptionIsExpected()
+ throws Exception {
- // When Deserializing Activity Definition With Target
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Target Description Is Expected
- assertThat(result.getTarget().get(0).getDescription().get(Locale.US),
- is("Does the xAPI include the concept of statements?"));
+ // When Deserializing Activity Definition With Target
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Target Description Is Expected
+ assertThat(result.getTarget().get(0).getDescription().get(Locale.US),
+ is("Does the xAPI include the concept of statements?"));
- @Test
- void whenDeserializingActivityDefinitionWithStepsThenStepsIDIsExpected() throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithStepsThenStepsIDIsExpected() throws Exception {
- // When Deserializing Activity Definition With Steps
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Steps ID Is Expected
- assertThat(result.getSteps().get(0).getId(), is("1"));
+ // When Deserializing Activity Definition With Steps
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Steps ID Is Expected
+ assertThat(result.getSteps().get(0).getId(), is("1"));
- @Test
- void whenDeserializingActivityDefinitionWithStepsThenStepsDescriptionIsExpected()
- throws Exception {
+ }
- final var file =
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
+ @Test
+ void whenDeserializingActivityDefinitionWithStepsThenStepsDescriptionIsExpected()
+ throws Exception {
- // When Deserializing Activity Definition With Steps
- final var result = objectMapper.readValue(file, ActivityDefinition.class);
+ final var file =
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json");
- // Then Steps Description Is Expected
- assertThat(result.getSteps().get(0).getDescription().get(Locale.US),
- is("Does the xAPI include the concept of statements?"));
+ // When Deserializing Activity Definition With Steps
+ final var result = objectMapper.readValue(file, ActivityDefinition.class);
- }
+ // Then Steps Description Is Expected
+ assertThat(result.getSteps().get(0).getDescription().get(Locale.US),
+ is("Does the xAPI include the concept of statements?"));
- @Test
- void whenSerializingActivityDefinitionOfInteractionTypeTrueFalseThenResultIsEqualToExpectedJson()
- throws IOException {
+ }
- final var activityDefinition = ActivityDefinition.builder()
+ @Test
+ void whenSerializingActivityDefinitionOfInteractionTypeTrueFalseThenResultIsEqualToExpectedJson()
+ throws IOException {
- .addName(Locale.US, "True false question")
+ final var activityDefinition = ActivityDefinition.builder()
- .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
+ .addName(Locale.US, "True false question")
- .interactionType(InteractionType.TRUE_FALSE)
+ .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
- .correctResponsesPattern(Collections.singletonList("true"))
+ .interactionType(InteractionType.TRUE_FALSE)
- .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
+ .correctResponsesPattern(Collections.singletonList("true"))
- .build();
+ .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
- // When Serializing Activity Definition Of InteractionType True False
- final var result = objectMapper.readTree(objectMapper.writeValueAsString(activityDefinition));
+ .build();
- // Then Result Is Equal To Expected Json
- assertThat(result, is(objectMapper
- .readTree(ResourceUtils.getFile("classpath:activity_definition/true_false.json"))));
+ // When Serializing Activity Definition Of InteractionType True False
+ final var result =
+ objectMapper.readTree(objectMapper.writeValueAsString(activityDefinition));
- }
+ // Then Result Is Equal To Expected Json
+ assertThat(result, is(objectMapper
+ .readTree(ResourceUtils.getFile("classpath:activity_definition/true_false.json"))));
- @Test
- void whenSerializingActivityDefinitionOfInteractionTypeChoiceThenResultIsEqualToExpectedJson()
- throws IOException {
+ }
- final var activityDefinition = ActivityDefinition.builder()
+ @Test
+ void whenSerializingActivityDefinitionOfInteractionTypeChoiceThenResultIsEqualToExpectedJson()
+ throws IOException {
- .addName(Locale.US, "Choice")
+ final var activityDefinition = ActivityDefinition.builder()
- .addDescription(Locale.US, "Which of these prototypes are available at the beta site?")
+ .addName(Locale.US, "Choice")
- .interactionType(InteractionType.CHOICE)
+ .addDescription(Locale.US,
+ "Which of these prototypes are available at the beta site?")
- .correctResponsesPattern(Collections.singletonList("golf[,]tetris"))
+ .interactionType(InteractionType.CHOICE)
- .addChoice(c -> c.id("golf").addDescription(Locale.US, "Golf Example"))
+ .correctResponsesPattern(Collections.singletonList("golf[,]tetris"))
- .addChoice(c -> c.id("facebook").addDescription(Locale.US, "Facebook App"))
+ .addChoice(c -> c.id("golf").addDescription(Locale.US, "Golf Example"))
- .addChoice(c -> c.id("tetris").addDescription(Locale.US, "Tetris Example"))
+ .addChoice(c -> c.id("facebook").addDescription(Locale.US, "Facebook App"))
- .addChoice(c -> c.id("scrabble").addDescription(Locale.US, "Scrabble Example"))
+ .addChoice(c -> c.id("tetris").addDescription(Locale.US, "Tetris Example"))
- .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
+ .addChoice(c -> c.id("scrabble").addDescription(Locale.US, "Scrabble Example"))
- .build();
+ .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
- // When Serializing Activity Definition Of InteractionType Choice
- final var result = objectMapper.readTree(objectMapper.writeValueAsString(activityDefinition));
+ .build();
- // Then Result Is Equal To Expected Json
- assertThat(result, is(
- objectMapper.readTree(ResourceUtils.getFile("classpath:activity_definition/choice.json"))));
+ // When Serializing Activity Definition Of InteractionType Choice
+ final var result =
+ objectMapper.readTree(objectMapper.writeValueAsString(activityDefinition));
- }
+ // Then Result Is Equal To Expected Json
+ assertThat(result, is(objectMapper
+ .readTree(ResourceUtils.getFile("classpath:activity_definition/choice.json"))));
- @Test
- void whenCallingToStringThenResultIsExpected() throws Exception {
+ }
- final var activityDefinition = objectMapper.readValue(
- ResourceUtils.getFile("classpath:activity_definition/activity_definition.json"),
- ActivityDefinition.class);
+ @Test
+ void whenCallingToStringThenResultIsExpected() throws Exception {
- // When Calling ToString
- final var result = activityDefinition.toString();
+ final var activityDefinition = objectMapper.readValue(
+ ResourceUtils.getFile("classpath:activity_definition/activity_definition.json"),
+ ActivityDefinition.class);
- // Then Result Is Expected
- assertThat(result, is(
- "ActivityDefinition(name={en_US=Does the xAPI include the concept of statements?}, description={en_US=pong[.]1:[,]dg[.]:10[,]lunch[.]}, type=http://adlnet.gov/expapi/activities/cmi.interaction, moreInfo=http://example.com/more, interactionType=TRUE_FALSE, correctResponsesPattern=[{case_matters=false}{lang=en}To store and provide access to learning experiences.], choices=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], scale=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], source=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], target=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], steps=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], extensions={http://url=www.example.com})"));
- }
+ // When Calling ToString
+ final var result = activityDefinition.toString();
- @Test
- void whenBuildingActivityDefinitionWithTwoNameValuesThenNameLanguageMapHasTwoEntries() {
+ // Then Result Is Expected
+ assertThat(result, is(
+ "ActivityDefinition(name={en_US=Does the xAPI include the concept of statements?}, description={en_US=pong[.]1:[,]dg[.]:10[,]lunch[.]}, type=http://adlnet.gov/expapi/activities/cmi.interaction, moreInfo=http://example.com/more, interactionType=TRUE_FALSE, correctResponsesPattern=[{case_matters=false}{lang=en}To store and provide access to learning experiences.], choices=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], scale=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], source=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], target=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], steps=[InteractionComponent(id=1, description={en_US=Does the xAPI include the concept of statements?})], extensions={http://url=www.example.com})"));
+ }
- // When Building ActivityDefinition With Two Name Values
- final var activityDefinition = ActivityDefinition.builder()
+ @Test
+ void whenBuildingActivityDefinitionWithTwoNameValuesThenNameLanguageMapHasTwoEntries() {
- .addName(Locale.US, "True false question")
+ // When Building ActivityDefinition With Two Name Values
+ final var activityDefinition = ActivityDefinition.builder()
- .addName(Locale.GERMAN, "Richtig / Falsch-Frage")
+ .addName(Locale.US, "True false question")
- .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
+ .addName(Locale.GERMAN, "Richtig / Falsch-Frage")
- .interactionType(InteractionType.TRUE_FALSE)
+ .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
- .correctResponsesPattern(Collections.singletonList("true"))
+ .interactionType(InteractionType.TRUE_FALSE)
- .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
+ .correctResponsesPattern(Collections.singletonList("true"))
- .build();
+ .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
- // Then Name Language Map Has Two Entries
- assertThat(activityDefinition.getName(), aMapWithSize(2));
+ .build();
- }
+ // Then Name Language Map Has Two Entries
+ assertThat(activityDefinition.getName(), aMapWithSize(2));
- @Test
- void whenBuildingActivityDefinitionWithTwoDescriptionValuesThenDescriptionLanguageMapHasTwoEntries() {
+ }
- // When Building ActivityDefinition With Two Description Values
- final var activityDefinition = ActivityDefinition.builder()
+ @Test
+ void whenBuildingActivityDefinitionWithTwoDescriptionValuesThenDescriptionLanguageMapHasTwoEntries() {
- .addName(Locale.US, "True false question")
+ // When Building ActivityDefinition With Two Description Values
+ final var activityDefinition = ActivityDefinition.builder()
- .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
+ .addName(Locale.US, "True false question")
- .addDescription(Locale.GERMAN, "Enthält die xAPI das Konzept von Anweisungen?")
+ .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
- .interactionType(InteractionType.TRUE_FALSE)
+ .addDescription(Locale.GERMAN, "Enthält die xAPI das Konzept von Anweisungen?")
- .correctResponsesPattern(Collections.singletonList("true"))
+ .interactionType(InteractionType.TRUE_FALSE)
- .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
+ .correctResponsesPattern(Collections.singletonList("true"))
- .build();
+ .type(URI.create("http://adlnet.gov/expapi/activities/cmi.interaction"))
- // Then Description Language Map Has Two Entries
- assertThat(activityDefinition.getDescription(), aMapWithSize(2));
+ .build();
- }
+ // Then Description Language Map Has Two Entries
+ assertThat(activityDefinition.getDescription(), aMapWithSize(2));
- @Test
- void whenMergingActivityDefinitionsWithNamesThenMergedNameIsExpected() throws IOException {
+ }
- final var activityDefinition1 =
- ActivityDefinition.builder().addName(Locale.UK, "Colour").build();
+ @Test
+ void whenMergingActivityDefinitionsWithNamesThenMergedNameIsExpected() throws IOException {
- final var x =
- objectMapper.valueToTree(ActivityDefinition.builder().addName(Locale.US, "Color").build());
+ final var activityDefinition1 =
+ ActivityDefinition.builder().addName(Locale.UK, "Colour").build();
- final var expected = new LanguageMap();
- expected.put(Locale.UK, "Colour");
- expected.put(Locale.US, "Color");
+ final var x = objectMapper
+ .valueToTree(ActivityDefinition.builder().addName(Locale.US, "Color").build());
- // When Merging ActivityDefinitions With Names
- final var merged =
- (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1).readValue(x);
+ final var expected = new LanguageMap();
+ expected.put(Locale.UK, "Colour");
+ expected.put(Locale.US, "Color");
- // Then Merged Name Is Expected
- assertThat(merged.getName(), hasAllEntries(expected));
+ // When Merging ActivityDefinitions With Names
+ final var merged = (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1)
+ .readValue(x);
- }
+ // Then Merged Name Is Expected
+ assertThat(merged.getName(), hasAllEntries(expected));
- @Test
- void whenMergingActivityDefinitionsWithDescriptionsThenMergedDescriptionIsExpected()
- throws IOException {
+ }
- final var activityDefinition1 =
- ActivityDefinition.builder().addDescription(Locale.UK, "flavour").build();
+ @Test
+ void whenMergingActivityDefinitionsWithDescriptionsThenMergedDescriptionIsExpected()
+ throws IOException {
- final var x = objectMapper
- .valueToTree(ActivityDefinition.builder().addDescription(Locale.US, "flavor").build());
+ final var activityDefinition1 =
+ ActivityDefinition.builder().addDescription(Locale.UK, "flavour").build();
- final var expected = new LanguageMap();
- expected.put(Locale.UK, "flavour");
- expected.put(Locale.US, "flavor");
+ final var x = objectMapper.valueToTree(
+ ActivityDefinition.builder().addDescription(Locale.US, "flavor").build());
- // When Merging ActivityDefinitions With Descriptions
- final var merged =
- (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1).readValue(x);
+ final var expected = new LanguageMap();
+ expected.put(Locale.UK, "flavour");
+ expected.put(Locale.US, "flavor");
- // Then Merged Description Is Expected
- assertThat(merged.getDescription(), hasAllEntries(expected));
+ // When Merging ActivityDefinitions With Descriptions
+ final var merged = (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1)
+ .readValue(x);
- }
+ // Then Merged Description Is Expected
+ assertThat(merged.getDescription(), hasAllEntries(expected));
- @Test
- void whenMergingActivityDefinitionsWithExtensionsThenMergedExtensionsAreExpected()
- throws IOException {
+ }
- final Map<@HasScheme URI, Object> extensions1 = new HashMap<>();
- extensions1.put(URI.create("https://example.com/extensions/1"), "1");
+ @Test
+ void whenMergingActivityDefinitionsWithExtensionsThenMergedExtensionsAreExpected()
+ throws IOException {
- final var activityDefinition1 = ActivityDefinition.builder().addName(Locale.UK, "Colour")
- .addDescription(Locale.UK, "flavour").extensions(extensions1).build();
+ final Map<@HasScheme URI, Object> extensions1 = new HashMap<>();
+ extensions1.put(URI.create("https://example.com/extensions/1"), "1");
- final Map<@HasScheme URI, Object> extensions2 = new HashMap<>();
- extensions2.put(URI.create("https://example.com/extensions/2"), "2");
+ final var activityDefinition1 = ActivityDefinition.builder().addName(Locale.UK, "Colour")
+ .addDescription(Locale.UK, "flavour").extensions(extensions1).build();
- final var x = objectMapper.valueToTree(ActivityDefinition.builder().addName(Locale.US, "Color")
- .addDescription(Locale.US, "flavor").extensions(extensions2).build());
+ final Map<@HasScheme URI, Object> extensions2 = new HashMap<>();
+ extensions2.put(URI.create("https://example.com/extensions/2"), "2");
- final Map<@HasScheme URI, Object> expected = new HashMap<>();
- expected.put(URI.create("https://example.com/extensions/1"), "1");
- expected.put(URI.create("https://example.com/extensions/2"), "2");
+ final var x =
+ objectMapper.valueToTree(ActivityDefinition.builder().addName(Locale.US, "Color")
+ .addDescription(Locale.US, "flavor").extensions(extensions2).build());
- // When Merging ActivityDefinitions With Extensions
- final var merged =
- (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1).readValue(x);
+ final Map<@HasScheme URI, Object> expected = new HashMap<>();
+ expected.put(URI.create("https://example.com/extensions/1"), "1");
+ expected.put(URI.create("https://example.com/extensions/2"), "2");
- // Then Merged Extensions Are Expected
- assertThat(merged.getExtensions(), hasAllEntries(expected));
+ // When Merging ActivityDefinitions With Extensions
+ final var merged = (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1)
+ .readValue(x);
- }
+ // Then Merged Extensions Are Expected
+ assertThat(merged.getExtensions(), hasAllEntries(expected));
- @Test
- void whenMergingActivityDefinitionsWithNestedExtensionsThenMergedExtensionsAreExpected()
- throws IOException {
+ }
- final Map<@HasScheme URI, Object> extensions1 = new HashMap<>();
- extensions1.put(URI.create("https://example.com/extensions/map"),
- new HashMap<>(Collections.singletonMap("a", "y")));
+ @Test
+ void whenMergingActivityDefinitionsWithNestedExtensionsThenMergedExtensionsAreExpected()
+ throws IOException {
- final var activityDefinition1 = ActivityDefinition.builder().extensions(extensions1).build();
+ final Map<@HasScheme URI, Object> extensions1 = new HashMap<>();
+ extensions1.put(URI.create("https://example.com/extensions/map"),
+ new HashMap<>(Collections.singletonMap("a", "y")));
- final Map<@HasScheme URI, Object> extensions2 = new HashMap<>();
- extensions2.put(URI.create("https://example.com/extensions/map"),
- new HashMap<>(Collections.singletonMap("b", "z")));
+ final var activityDefinition1 =
+ ActivityDefinition.builder().extensions(extensions1).build();
- final var x =
- objectMapper.valueToTree(ActivityDefinition.builder().extensions(extensions2).build());
+ final Map<@HasScheme URI, Object> extensions2 = new HashMap<>();
+ extensions2.put(URI.create("https://example.com/extensions/map"),
+ new HashMap<>(Collections.singletonMap("b", "z")));
- final Map expected = new HashMap<>();
- expected.put("a", "y");
- expected.put("b", "z");
+ final var x = objectMapper
+ .valueToTree(ActivityDefinition.builder().extensions(extensions2).build());
- // When Merging ActivityDefinitions With Nested Extensions
- final var merged =
- (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1).readValue(x);
+ final Map expected = new HashMap<>();
+ expected.put("a", "y");
+ expected.put("b", "z");
- @SuppressWarnings("unchecked")
- final var po = (Map) merged.getExtensions()
- .get(URI.create("https://example.com/extensions/map"));
+ // When Merging ActivityDefinitions With Nested Extensions
+ final var merged = (ActivityDefinition) objectMapper.readerForUpdating(activityDefinition1)
+ .readValue(x);
- // Then Merged Extensions Are Expected
- assertThat(po, hasAllEntries(expected));
+ @SuppressWarnings("unchecked")
+ final var po = (Map) merged.getExtensions()
+ .get(URI.create("https://example.com/extensions/map"));
- }
+ // Then Merged Extensions Are Expected
+ assertThat(po, hasAllEntries(expected));
+
+ }
+
+ @Test
+ void whenValidatingActivityDefinitionWithTypeWithNoSchemeThenConstraintViolationsSizeIsOne() {
+
+ final var activityDefinition = ActivityDefinition.builder()
+
+ .addName(Locale.US, "True false question")
+
+ .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
+
+ .interactionType(InteractionType.TRUE_FALSE)
+
+ .correctResponsesPattern(Collections.singletonList("true"))
+
+ .type(URI.create("cmi.interaction"))
+
+ .build();
+
+ // When Validating ActivityDefinition With Type With No Scheme
+ final var violations = validator.validate(activityDefinition);
+
+ // Then Constraint Violations Size Is One
+ assertThat(violations.size(), is(1));
+
+ }
+
+ @Test
+ void whenValidatingActivityDefinitionWithMoreInfoWithNoSchemeThenConstraintViolationsSizeIsOne() {
+
+ final var activityDefinition = ActivityDefinition.builder()
+
+ .addName(Locale.US, "True false question")
+
+ .addDescription(Locale.US, "Does the xAPI include the concept of statements?")
+
+ .interactionType(InteractionType.TRUE_FALSE)
+
+ .correctResponsesPattern(Collections.singletonList("true"))
+
+ .moreInfo(URI.create("example.com/moreInfo"))
+
+ .build();
+
+ // When Validating ActivityDefinition With MoreInfo With No Scheme
+ final var violations = validator.validate(activityDefinition);
+
+ // Then Constraint Violations Size Is One
+ assertThat(violations.size(), is(1));
+
+ }
}
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java
index f48998a7..15a2746c 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/ActivityTests.java
@@ -13,12 +13,16 @@
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import dev.learning.xapi.jackson.XapiStrictLocaleModule;
import dev.learning.xapi.model.validation.constraints.HasScheme;
+import jakarta.validation.ConstraintViolation;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.Set;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
@@ -35,6 +39,8 @@ class ActivityTests {
private final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
+ private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+
@Test
void whenDeserializingActivityThenResultIsInstanceOfActivity() throws Exception {
@@ -233,5 +239,21 @@ void whenMergingActivitiesWithActivityDefinitionsWithExtensionsThenMergedExtensi
}
+ @Test
+ void whenValidatingActivityWithIdWithNoSchemeThenConstraintViolationsSizeIsOne() {
+
+ final var activity = Activity.builder()
+
+ .id(URI.create("www.example.com"))
+
+ .build();
+
+ // When Validating Activity With Id With No Scheme
+ final Set> constraintViolations = validator.validate(activity);
+
+ // Then Constraint Violations Size Is One
+ assertThat(constraintViolations.size(), is(1));
+
+ }
}
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java
index a72b9e16..659641eb 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/AgentTests.java
@@ -9,6 +9,8 @@
import static org.hamcrest.Matchers.is;
import com.fasterxml.jackson.databind.ObjectMapper;
+import jakarta.validation.Validation;
+import jakarta.validation.Validator;
import java.io.IOException;
import java.net.URI;
import org.junit.jupiter.api.DisplayName;
@@ -29,6 +31,8 @@ class AgentTests {
private final ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules();
+ private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
+
@ParameterizedTest
@ValueSource(
strings = {"classpath:agent/agent.json", "classpath:agent/agent_without_object_type.json",
@@ -210,4 +214,23 @@ void whenSerializingAgentWithAccountThenResultIsEqualToExpectedJson() throws IOE
}
+ @Test
+ void whenValidatingAgentWithOpenIdWithNoSchemeThenConstraintViolationsSizeIsOne() {
+
+ final Agent agent = Agent.builder()
+
+ .name("A N Other")
+
+ .openid(URI.create("1234"))
+
+ .build();
+
+ // When Validating Agent With OpenId With No Scheme
+ final var result = validator.validate(agent);
+
+ // Then Constraint Violations Size Is One
+ assertThat(result.size(), is(1));
+
+ }
+
}
diff --git a/xapi-model/src/test/java/dev/learning/xapi/model/AttachmentTests.java b/xapi-model/src/test/java/dev/learning/xapi/model/AttachmentTests.java
index 88825e6e..3b6c9a19 100644
--- a/xapi-model/src/test/java/dev/learning/xapi/model/AttachmentTests.java
+++ b/xapi-model/src/test/java/dev/learning/xapi/model/AttachmentTests.java
@@ -581,4 +581,64 @@ void whenValidatingAttachmentWithoutLengthThenConstraintViolationsSizeIsOne() {
}
+ @Test
+ void whenValidatingAttachmentWithUsageTypeWithoutSchemeThenConstraintViolationsSizeIsOne() {
+
+ final var attachment = Attachment.builder()
+
+ .usageType(URI.create("signature"))
+
+ .addDisplay(Locale.US, "Signature")
+
+ .addDescription(Locale.US, "A test signature")
+
+ .contentType("application/octet-stream")
+
+ .length(4235)
+
+ .sha2("672fa5fa658017f1b72d65036f13379c6ab05d4ab3b6664908d8acf0b6a0c634")
+
+ .fileUrl(URI.create("https://example.com"))
+
+ .build();
+
+ // When Validating Attachment With UsageType Without Scheme
+ final Set> constraintViolations =
+ validator.validate(attachment);
+
+ // Then ConstraintViolations Size Is One
+ assertThat(constraintViolations, hasSize(1));
+
+ }
+
+ @Test
+ void whenValidatingAttachmentWithFileUrlWithoutSchemeThenConstraintViolationsSizeIsOne() {
+
+ final var attachment = Attachment.builder()
+
+ .usageType(URI.create("http://adlnet.gov/expapi/attachments/signature"))
+
+ .addDisplay(Locale.US, "Signature")
+
+ .addDescription(Locale.US, "A test signature")
+
+ .contentType("application/octet-stream")
+
+ .length(4235)
+
+ .sha2("672fa5fa658017f1b72d65036f13379c6ab05d4ab3b6664908d8acf0b6a0c634")
+
+ .fileUrl(URI.create("example.com"))
+
+ .build();
+
+ // When Validating Attachment With FileUrl Without Scheme
+ final Set> constraintViolations =
+ validator.validate(attachment);
+
+ // Then ConstraintViolations Size Is One
+ assertThat(constraintViolations, hasSize(1));
+
+ }
+
}