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)); + + } + }