/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.client.utility.validate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import mock.fedora.client.utility.validate.MockObjectSource;
import org.junit.Before;
import org.junit.Test;
import fedora.client.utility.validate.types.BasicObjectInfo;
import fedora.client.utility.validate.types.ContentModelInfo;
import fedora.client.utility.validate.types.DatastreamInfo;
import fedora.client.utility.validate.types.ObjectInfo;
import fedora.client.utility.validate.types.RelationshipInfo;
import fedora.client.utility.validate.types.ContentModelInfo.DsTypeModel;
import fedora.client.utility.validate.types.ContentModelInfo.Form;
import fedora.common.Constants;
import static junit.framework.Assert.assertEquals;
/**
* Testing to see that the {@link ObjectValidator} actually validates.
*
* @author Jim Blake
*/
public class TestObjectValidator {
/*
* Some useful shorthand constants.
*/
private static final String HAS_MODEL = Constants.MODEL.HAS_MODEL.uri;
private static final DatastreamInfo[] NO_DATASTREAMS =
new DatastreamInfo[0];
private static final RelationshipInfo[] NO_RELATIONS =
new RelationshipInfo[0];
private static final DsTypeModel[] NO_TYPE_MODELS = new DsTypeModel[0];
// Every content model must have this datastream.
private static final DatastreamInfo[] CONTENT_MODEL_DATASTREAM =
new DatastreamInfo[] {new DatastreamInfo(ContentModelInfo.DS_COMPOSITE_MODEL,
null,
ContentModelInfo.DS_COMPOSITE_MODEL_FORMAT)};
/*
*
*/
private static final String SAMPLE_PID = "throwIt";
private static final String NON_PID_URI = "not_a_pid";
/*
* A simple object and content model for use in several tests.
*/
private static final TestContentModelInfo CONTENT_MODEL_EMPTY =
contentModel("emptyContentModel", NO_TYPE_MODELS);
private static final BasicObjectInfo OBJECT_SIMPLE_SAMPLE =
basicObject("objectSimpleSample",
contentModelRelations(CONTENT_MODEL_EMPTY),
NO_DATASTREAMS);
private MockObjectSource objectSource;
private ObjectValidator validator;
/**
* Create the object source and the validator. Add some simple objects to
* the source for use in several tests.
*/
@Before
public void initializeSourceAndValidator() {
objectSource = new MockObjectSource();
addSeedsToObjectSource(CONTENT_MODEL_EMPTY, OBJECT_SIMPLE_SAMPLE);
validator = new ObjectValidator(objectSource);
}
@Test(expected = NullPointerException.class)
public void nullArgumentToConstructor() {
new ObjectValidator(null);
}
@Test(expected = NullPointerException.class)
public void nullArgumentToValidatePid() {
validator.validate((String) null);
}
@Test
public void gettingFromPidThrowsException() {
objectSource.throwObjectSourceExceptionOnPid(SAMPLE_PID);
ValidationResult expected =
expectedResult(new BasicObjectInfo(SAMPLE_PID),
ValidationResultNotation
.objectNotFound(SAMPLE_PID));
ValidationResult actual = validator.validate(SAMPLE_PID);
assertEquals("result", expected, actual);
}
@Test
public void pidReturnsNullObject() {
ValidationResult expected =
expectedResult(new BasicObjectInfo(SAMPLE_PID),
ValidationResultNotation
.objectNotFound(SAMPLE_PID));
ValidationResult actual = validator.validate(SAMPLE_PID);
assertEquals("result", expected, actual);
}
@Test
public void simpleSuccessFromPid() {
ValidationResult expected = expectedResult(OBJECT_SIMPLE_SAMPLE);
ValidationResult actual =
validator.validate(OBJECT_SIMPLE_SAMPLE.getPid());
assertEquals("result", expected, actual);
}
@Test(expected = NullPointerException.class)
public void nullArgumentToValidateObject() {
validator.validate((ObjectInfo) null);
}
@Test
public void simpleSuccessFromObject() {
validateObject(OBJECT_SIMPLE_SAMPLE);
}
@Test
public void noContentModel() {
BasicObjectInfo object =
basicObject("noContentModel", NO_RELATIONS, NO_DATASTREAMS);
validateObject(object, ValidationResultNotation.noContentModel());
}
@Test
public void contentModelUriIsNotPid() {
BasicObjectInfo object =
basicObject("unknownContentModel",
unknownContentModelRelation(),
NO_DATASTREAMS);
validateObject(object, ValidationResultNotation
.unrecognizedContentModelUri(NON_PID_URI));
}
@Test
public void gettingContentModelThrowsException() {
objectSource.throwObjectSourceException(CONTENT_MODEL_EMPTY);
validateObject(OBJECT_SIMPLE_SAMPLE,
noteErrorFetchingContentModel(CONTENT_MODEL_EMPTY));
}
@Test
public void contentModelIsInvalid() {
objectSource.throwInvalidContentModelException(CONTENT_MODEL_EMPTY);
validateObject(OBJECT_SIMPLE_SAMPLE,
noteInvalidContentModel(CONTENT_MODEL_EMPTY));
}
@Test
public void contentModelDoesntExist() {
objectSource.removeSeedModel(CONTENT_MODEL_EMPTY);
validateObject(OBJECT_SIMPLE_SAMPLE, ValidationResultNotation
.contentModelNotFound(CONTENT_MODEL_EMPTY.getPid()));
}
/**
* Content model requires a datastream, but the object doesn't have it.
*/
@Test
public void noDsToMatchTypeModel() {
TypeModel typeNoForms = new TypeModel(new HashSet<Form>(), "dsNoForms");
TestContentModelInfo model =
contentModel("oneTypeContentModel",
new DsTypeModel[] {typeNoForms});
BasicObjectInfo object =
basicObject("objectNoDsForModel",
contentModelRelations(model),
NO_DATASTREAMS);
addSeedsToObjectSource(model, object);
validateObject(object, ValidationResultNotation
.noMatchingDatastreamId(model.getPid(), typeNoForms.getId()));
}
/**
* Match to a content model with four datastreams, illustrating matches
* against an assortment of types.
*/
@Test
public void matchAnAssortmentOfTypeModels() {
TestForm formNeither = new TestForm(null, null);
TestForm formMime = new TestForm(null, "mime");
TestForm formFormat = new TestForm("format_uri", null);
TestForm formBoth = new TestForm("both_format_uri", "both_mime");
TypeModel typeNeither = typeModel("neither", formNeither);
TypeModel typeMime = typeModel("mime only", formMime);
TypeModel typeFormat = typeModel("format_uri only", formFormat);
TypeModel typeBoth = typeModel("both", formBoth);
TestContentModelInfo model =
contentModel("model", typeModels(typeNeither,
typeMime,
typeFormat,
typeBoth));
DatastreamInfo dsNeither = new DatastreamInfo("neither", null, null);
DatastreamInfo dsMime = new DatastreamInfo("mime only", "mime", null);
DatastreamInfo dsFormat =
new DatastreamInfo("format_uri only", null, "format_uri");
DatastreamInfo dsBoth =
new DatastreamInfo("both", "both_mime", "both_format_uri");
BasicObjectInfo matcher =
basicObject("severalDatastreamAllMatch",
contentModelRelations(model),
datastreams(dsNeither, dsMime, dsFormat, dsBoth));
addSeedsToObjectSource(model, matcher);
validateObject(matcher);
}
@Test
public void matchAgainstTypeWithNeither() {
// This model has one type, with neither mime nor format URI specified.
TypeModel typeNeither = typeModel("neither", new TestForm(null, null));
TestContentModelInfo model =
contentModel("model", typeModels(typeNeither));
BasicObjectInfo neither =
basicObject("neitherMatchesNeither",
contentModelRelations(model),
datastreams(new DatastreamInfo("neither",
null,
null)));
BasicObjectInfo mime =
basicObject("anyMimeMatchesNeither",
contentModelRelations(model),
datastreams(new DatastreamInfo("neither",
"wrongMime",
null)));
BasicObjectInfo formatUri =
basicObject("anyFormatMatchesNeither",
contentModelRelations(model),
datastreams(new DatastreamInfo("neither",
null,
"wrongFormat")));
addSeedsToObjectSource(model, neither, mime, formatUri);
validateObject(neither);
validateObject(mime);
validateObject(formatUri);
}
@Test
public void matchAgainstTypeWithMime() {
// This model has one type, with mime specified but not format uri.
TypeModel typeMime = typeModel("mime", new TestForm(null, "mimeType"));
TestContentModelInfo model =
contentModel("model", typeModels(typeMime));
BasicObjectInfo neither =
basicObject("neitherFailsOnMime",
contentModelRelations(model),
datastreams(new DatastreamInfo("mime", null, null)));
BasicObjectInfo mime =
basicObject("mimeMatch",
contentModelRelations(model),
datastreams(new DatastreamInfo("mime",
"mimeType",
null)));
BasicObjectInfo wrongMime =
basicObject("mimeMisMatch",
contentModelRelations(model),
datastreams(new DatastreamInfo("mime",
"wrongMime",
null)));
ValidationResultNotation note =
ValidationResultNotation.datastreamDoesNotMatchForms(model
.getPid(), "mime");
addSeedsToObjectSource(model, neither, mime, wrongMime);
validateObject(neither, note);
validateObject(mime);
validateObject(wrongMime, note);
}
@Test
public void matchAgainstTypeWithBoth() {
// This model has one type, with both mime and format uri specified.
TypeModel typeBoth =
typeModel("both", new TestForm("formatUri", "mimeType"));
TestContentModelInfo model =
contentModel("model", typeModels(typeBoth));
BasicObjectInfo neither =
basicObject("neitherFails",
contentModelRelations(model),
datastreams(new DatastreamInfo("both", null, null)));
BasicObjectInfo mimeOnly =
basicObject("mimeFails",
contentModelRelations(model),
datastreams(new DatastreamInfo("both",
"mimeType",
null)));
BasicObjectInfo fuOnly =
basicObject("fuFails",
contentModelRelations(model),
datastreams(new DatastreamInfo("both",
null,
"formatUri")));
BasicObjectInfo both =
basicObject("bothMatch",
contentModelRelations(model),
datastreams(new DatastreamInfo("both",
"mimeType",
"formatUri")));
BasicObjectInfo wrongMime =
basicObject("mimeMismatch",
contentModelRelations(model),
datastreams(new DatastreamInfo("both",
"wrongMime",
"formatUri")));
BasicObjectInfo wrongFu =
basicObject("fuMismatch",
contentModelRelations(model),
datastreams(new DatastreamInfo("both",
"mime",
"wrongFormatUri")));
ValidationResultNotation note =
ValidationResultNotation.datastreamDoesNotMatchForms(model
.getPid(), "both");
addSeedsToObjectSource(model,
neither,
mimeOnly,
fuOnly,
both,
wrongMime,
wrongFu);
validateObject(neither, note);
validateObject(mimeOnly, note);
validateObject(fuOnly, note);
validateObject(both);
validateObject(wrongMime, note);
validateObject(wrongFu, note);
}
/**
* Match against two content models - they both require one datastream, and
* they each require another.
*/
@Test
public void matchTwoContentModels() {
TestContentModelInfo model1 =
contentModel("model1",
typeModels(typeModel("dsBoth",
new TestForm(null, "mimeA"),
new TestForm(null, "mimeB")),
typeModel("ds1",
new TestForm("formatA", null))));
TestContentModelInfo model2 =
contentModel("model2",
typeModels(typeModel("ds2",
new TestForm("formatY", null)),
typeModel("dsBoth",
new TestForm("formatX", null))));
DatastreamInfo ds1Pass = new DatastreamInfo("ds1", null, "formatA");
DatastreamInfo ds1Fail = new DatastreamInfo("ds1", null, null);
DatastreamInfo ds2Pass = new DatastreamInfo("ds2", "mimeK", "formatY");
DatastreamInfo ds2Fail = new DatastreamInfo("ds2", "mimeK", "formatZ");
DatastreamInfo dsBothPass =
new DatastreamInfo("dsBoth", "mimeB", "formatX");
DatastreamInfo dsBothFail = new DatastreamInfo("dsBoth", "mimeB", null);
BasicObjectInfo success =
basicObject("success",
contentModelRelations(model1, model2),
datastreams(ds1Pass, ds2Pass, dsBothPass));
BasicObjectInfo failDs1 =
basicObject("failDs1",
contentModelRelations(model1, model2),
datastreams(ds1Fail, ds2Pass, dsBothPass));
BasicObjectInfo failDs2 =
basicObject("failDs2",
contentModelRelations(model1, model2),
datastreams(ds1Pass, ds2Fail, dsBothPass));
BasicObjectInfo failDsBoth =
basicObject("failDsBoth",
contentModelRelations(model1, model2),
datastreams(ds1Pass, ds2Pass, dsBothFail));
ValidationResultNotation note1 =
ValidationResultNotation.datastreamDoesNotMatchForms(model1
.getPid(), ds1Fail.getId());
ValidationResultNotation note2 =
ValidationResultNotation.datastreamDoesNotMatchForms(model2
.getPid(), ds2Fail.getId());
ValidationResultNotation note3 =
ValidationResultNotation.datastreamDoesNotMatchForms(model2
.getPid(), dsBothFail.getId());
addSeedsToObjectSource(model1, success, failDs1, failDs2, failDsBoth);
addSeedsToObjectSource(model2, success, failDs1, failDs2, failDsBoth);
validateObject(success);
validateObject(failDs1, note1);
validateObject(failDs2, note2);
validateObject(failDsBoth, note3);
}
/*
* ------------------------------------------------------------------------
* Helper methods
* ------------------------------------------------------------------------
*/
/**
* Create a basic object from these specifications.
*/
private static BasicObjectInfo basicObject(String pid,
RelationshipInfo[] relations,
DatastreamInfo[] datastreams) {
return new BasicObjectInfo(pid, Arrays.asList(relations), Arrays
.asList(datastreams));
}
/**
* Create a content model from these specifications.
*/
private static TestContentModelInfo contentModel(String pid,
DsTypeModel[] typeModels) {
BasicObjectInfo base =
basicObject(pid, NO_RELATIONS, CONTENT_MODEL_DATASTREAM);
return new TestContentModelInfo(base, typeModels);
}
/**
* Create relationships to these content models.
*/
private static RelationshipInfo[] contentModelRelations(ContentModelInfo... models) {
RelationshipInfo[] relations = new RelationshipInfo[models.length];
for (int i = 0; i < models.length; i++) {
String objectUri = "info:fedora/" + models[i].getPid();
relations[i] = new RelationshipInfo(HAS_MODEL, objectUri);
}
return relations;
}
/**
* Create a type model from these specifications.
*/
private static TypeModel typeModel(String id, Form... forms) {
return new TypeModel(Arrays.asList(forms), id);
}
/**
* Create a relationship to a content model whose URI is not recognized as a
* PID.
*/
private static RelationshipInfo[] unknownContentModelRelation() {
return new RelationshipInfo[] {new RelationshipInfo(HAS_MODEL,
NON_PID_URI)};
}
private static DatastreamInfo[] datastreams(DatastreamInfo... dsInfos) {
return dsInfos;
}
private static DsTypeModel[] typeModels(DsTypeModel... types) {
return types;
}
/**
* Put the content model and the objects that comply to it into the mock
* object source.
*/
private void addSeedsToObjectSource(TestContentModelInfo model,
BasicObjectInfo... objects) {
for (BasicObjectInfo object : objects) {
objectSource.addSeedObject(object);
}
objectSource.addSeedModel(model.getBasicObject(), model);
}
/**
* If I validate this object, I should get these notes.
*/
private void validateObject(BasicObjectInfo object,
ValidationResultNotation... expectedNotes) {
ValidationResult expected = expectedResult(object, expectedNotes);
ValidationResult actual = validator.validate(object);
assertEquals("result", expected, actual);
}
/**
* Assemble the expected {@link ValidationResult}.
*/
private ValidationResult expectedResult(BasicObjectInfo object,
ValidationResultNotation... notes) {
ValidationResult result = new ValidationResult(object);
for (ValidationResultNotation note : notes) {
result.addNote(note);
}
return result;
}
/**
* Convenience method: create a notation saying that we couldn't fetch this
* content model.
*/
private ValidationResultNotation noteErrorFetchingContentModel(ContentModelInfo model) {
String pid = model.getPid();
ObjectSourceException e = objectSource.createObjectSourceException(pid);
return ValidationResultNotation.errorFetchingContentModel(pid, e);
}
/**
* Convenience method: create a notation saying that the content model is
* invalid.
*/
private ValidationResultNotation noteInvalidContentModel(ContentModelInfo model) {
String pid = model.getPid();
InvalidContentModelException e =
objectSource.createInvalidContentModelException(pid);
return ValidationResultNotation.contentModelNotValid(e);
}
/*
* ------------------------------------------------------------------------
* Helper classes - simple implementations of the interfaces we use.
* ------------------------------------------------------------------------
*/
private static class TypeModel
implements DsTypeModel {
private final Collection<Form> forms;
private final String id;
public TypeModel(Collection<Form> forms, String id) {
this.forms = forms;
this.id = id;
}
public Collection<Form> getForms() {
return forms;
}
public String getId() {
return id;
}
}
private static class TestContentModelInfo
implements ContentModelInfo {
private final BasicObjectInfo basicObject;
private final List<DsTypeModel> typeModels;
public TestContentModelInfo(BasicObjectInfo basicObject,
DsTypeModel[] typeModels) {
this.basicObject = basicObject;
this.typeModels =
new ArrayList<DsTypeModel>(Arrays.asList(typeModels));
}
public String getPid() {
return basicObject.getPid();
}
public Collection<DsTypeModel> getTypeModels() {
return new HashSet<DsTypeModel>(typeModels);
}
public BasicObjectInfo getBasicObject() {
return basicObject;
}
}
private static class TestForm
implements Form {
private final String formatUri;
private final String mimeType;
public TestForm(String formatUri, String mimeType) {
super();
this.formatUri = formatUri;
this.mimeType = mimeType;
}
public String getFormatUri() {
return formatUri;
}
public String getMimeType() {
return mimeType;
}
}
}