package com.codepoetics.octarine.matchers;
import com.codepoetics.octarine.functional.paths.Path;
import com.codepoetics.octarine.json.deserialisation.RecordDeserialiser;
import com.codepoetics.octarine.json.serialisation.RecordSerialiser;
import com.codepoetics.octarine.records.*;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.junit.Test;
import static com.codepoetics.octarine.Octarine.*;
import static com.codepoetics.octarine.json.deserialisation.Deserialisers.ofString;
import static com.codepoetics.octarine.json.serialisation.Serialisers.toInteger;
import static com.codepoetics.octarine.json.serialisation.Serialisers.toString;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.number.OrderingComparison.greaterThan;
public class ARecordTest {
public static interface Address {
static final KeySet mandatoryKeys = new KeySet();
static final ListKey<String> addressLines = mandatoryKeys.add($L("addressLines"));
static final Key<String> postcode = mandatoryKeys.add($("postcode"));
Schema<Address> schema = mandatoryKeys::accept;
RecordDeserialiser reader = RecordDeserialiser.builder()
.readList(addressLines, ofString)
.readString(postcode)
.get();
RecordSerialiser writer = RecordSerialiser.builder()
.writeList(addressLines, toString)
.writeString(postcode)
.get();
}
public static interface Person {
static final KeySet mandatoryKeys = new KeySet();
static final Key<String> name = mandatoryKeys.add($("name"));
static final Key<Integer> age = mandatoryKeys.add($("age"));
static final RecordKey address = mandatoryKeys.add($R("address"));
Schema<Person> schema = (r, v) -> {
mandatoryKeys.accept(r, v);
age.apply(r).ifPresent(a -> { if (a < 0) v.accept("Age must be 0 or greater"); });
address.apply(r).ifPresent(a -> Address.schema.accept(a, v));
};
RecordDeserialiser reader = RecordDeserialiser.builder()
.readString(name)
.readInteger(age)
.read(address, Address.reader)
.get();
RecordSerialiser writer = RecordSerialiser.builder()
.write(name, toString)
.write(age, toInteger)
.write(address, Address.writer)
.get();
}
private final Record person = $$(
Person.name.of("Arthur Putey"),
Person.age.of(42),
Person.address.of(
Address.addressLines.of("62 Acacia Avenue", "Sunderland"),
Address.postcode.of("VB6 5UX")));
@Test public void
matches_record_with_property_matchers() {
assertThat(person, ARecord.validAgainst(Person.schema)
.with(Person.name, "Arthur Putey")
.with(Person.age, 42)
// Chaining keys
.with(Person.address.join(Address.addressLines).join(Path.toIndex(0)), "62 Acacia Avenue")
// Using a sub-matcher
.with(Person.address, ARecord.validAgainst(Address.schema).with(Address.postcode, "VB6 5UX")));
}
@Test public void
applies_validations_if_schema_given() {
Record invalidPerson = person.with(Person.age.of(-1));
Matcher<Record> matcher = ARecord.validAgainst(Person.schema)
.with(Person.name, "Arthur Putey")
.with(Person.age, -1);
StringDescription description = new StringDescription();
matcher.describeMismatch(invalidPerson, description);
assertThat(description.toString(), containsString("Age must be 0 or greater"));
}
@Test public void
lists_expected_properties_in_self_description() {
Matcher<Record> matcher = ARecord.validAgainst(Person.schema)
.with(Person.name, "Hubert Cumberdale")
.with(Person.age, greaterThan(23))
.with(Person.address.join(Address.postcode), "HR9 5BH");
StringDescription description = new StringDescription();
matcher.describeTo(description);
assertThat(description.toString(), containsString("name: present and \"Hubert Cumberdale\""));
assertThat(description.toString(), containsString("age: present and a value greater than <23>"));
assertThat(description.toString(), containsString("address.postcode: present and \"HR9 5BH\""));
}
@Test public void
describes_mismatch_meaningfully() {
Matcher<Record> matcher = ARecord.instance()
.with(Person.name, "Hubert Cumberdale")
.without(Person.age);
StringDescription description = new StringDescription();
matcher.describeMismatch(person, description);
assertThat(description.toString(), containsString("name: was \"Arthur Putey\""));
assertThat(description.toString(), containsString("age: was <Optional[42]>"));
}
@Test public void
deserialise_validate_update_serialise() {
Record record = Person.reader.fromString(
"{\"name\": \"Arthur Putey\",\n" + "" +
" \"age\": 42,\n" +
" \"address\": {\n" +
" \"addressLines\": [\"59 Broad Street\", \"Cirencester\"],\n" +
" \"postcode\": \"RA8 81T\"\n" +
" }\n" +
"}");
assertThat(record, ARecord.validAgainst(Person.schema)
.with(Person.name, "Arthur Putey")
.with(Person.age, 42)
// Chaining keys
.with(Person.address.join(Address.addressLines).join(Path.toIndex(0)), "59 Broad Street")
// Using a sub-matcher
.with(Person.address, ARecord.validAgainst(Address.schema).with(Address.postcode, "RA8 81T")));
Valid<Person> validRecord = Person.schema.validate(record).get();
Record changed = Person.age.update(validRecord, age -> age.map(a -> a + 1));
assertThat(Person.writer.toString(changed), equalTo(
"{\"name\":\"Arthur Putey\"," +"" +
"\"age\":43," +
"\"address\":{" +
"\"addressLines\":[\"59 Broad Street\",\"Cirencester\"]," +
"\"postcode\":\"RA8 81T\"" +
"}}"));
}
}