Skip to main content

Property Validation Test Matrix

This document defines a comprehensive test matrix for all property validation scenarios in OpenRegister, based on SchemaPropertyValidatorService.php.

Read the vocabulary, do not retype it

Every list below is a snapshot. The live one is published:

GET /api/schemas/property-vocabulary

It returns every property type the layer accepts, the constraint keys each type takes, the string formats, whether converting a populated property to a type is supported, and a sentence per entry. Generate your property editor from that read instead of hand-writing a shorter list, because a hand-written list is how an app ends up offering eight of the types the layer validates.

A key the vocabulary does not hold fails the schema save with a 422 naming it. A key starting with x- is a vendor extension and passes through.

The two prose keys may be written once per language, as title:nl and description:en. Only those two take a suffix, and only a language tag may follow the colon, so title:englisch and order:en are refused rather than quietly stored. The payload publishes the rule under localisation, so an editor reads which keys take a suffix and what may follow it instead of guessing.

An app whose own form lets an administrator author properties declares which vocabulary keys that form forwards, with x-openregister-extends-form. Read the declared narrowings, and what each form leaves out, at GET /api/schemas/extending-forms.

Test Categories​

1. String Type Tests​

1.1 String Formats (Valid)​

FormatValid ExampleTest Case
text"Plain text content"testStringFormatText
markdown"# Heading\n\nBold"testStringFormatMarkdown
html"

HTML content

"
testStringFormatHtml
date-time"2024-01-15T10:30:00Z"testStringFormatDateTime
date"2024-01-15"testStringFormatDate
time"10:30:00"testStringFormatTime
email"user@example.com"testStringFormatEmail
idn-email"用户@例え.jp"testStringFormatIdnEmail
hostname"example.com"testStringFormatHostname
ipv4"192.168.1.1"testStringFormatIpv4
ipv6"2001:0db8::1"testStringFormatIpv6
uri"https://example.com/path"testStringFormatUri
uuid"550e8400-e29b-41d4-a716-446655440000"testStringFormatUuid
url"https://www.example.com"testStringFormatUrl
color"#FF5733"testStringFormatColor
color-hex"#FF5733"testStringFormatColorHex
color-rgb"rgb(255, 87, 51)"testStringFormatColorRgb
color-rgba"rgba(255, 87, 51, 0.5)"testStringFormatColorRgba
semver"1.2.3"testStringFormatSemver

1.2 String Constraints​

ConstraintValidInvalidTest Case
minLength: 5"12345""1234"testStringMinLength
maxLength: 10"1234567890""12345678901"testStringMaxLength
pattern: "^[A-Z]""ABC""abc"testStringPattern
enum: ["a","b"]"a""c"testStringEnum

1.3 String Format Errors (Should Fail)​

FormatInvalid ExampleExpected ErrorTest Case
date"15/01/2024"400 Bad RequesttestStringDateFormatError
email"not-an-email"400 Bad RequesttestStringEmailFormatError
uuid"not-a-uuid"400 Bad RequesttestStringUuidFormatError
url"not a url"400 Bad RequesttestStringUrlFormatError
ipv4"999.999.999.999"400 Bad RequesttestStringIpv4FormatError

2. Number/Integer Type Tests​

2.1 Number Constraints​

TypeConstraintValidInvalidTest Case
integerminimum: 00, 5-1testIntegerMinimum
integermaximum: 10050, 100101testIntegerMaximum
integerminimum: 18, maximum: 652517, 66testIntegerRange
numberminimum: 0.00.5-0.1testNumberMinimum
numbermaximum: 1.00.51.1testNumberMaximum
numbermultipleOf: 0.51.51.3testNumberMultipleOf

2.2 Number Type Errors​

TypeInvalid ValueExpected ErrorTest Case
integer"not a number"400testIntegerTypeError
integer3.14400testIntegerFloatError
number"string"400testNumberTypeError

3. Boolean Type Tests​

ValueValidTest Case
true✅testBooleanTrue
false✅testBooleanFalse
"true" (string)❌testBooleanStringError
1 (number)❌testBooleanNumberError
null❌testBooleanNullError

4. Array Type Tests​

4.1 Array Constraints​

ConstraintValidInvalidTest Case
items: {type: "string"}["a","b"]["a",1]testArrayItemType
minItems: 1["a"][]testArrayMinItems
maxItems: 5["a","b"]["a","b","c","d","e","f"]testArrayMaxItems
uniqueItems: true["a","b"]["a","a"]testArrayUniqueItems

4.2 Nested Array Types​

Items DefinitionValidInvalidTest Case
{type: "integer", minimum: 0}[1,2,3][1,-1,3]testArrayNestedConstraints
{type: "object", properties: {...}}[{name:"A"}][{invalid}]testArrayOfObjects

5. Object Type Tests​

5.1 Object Structure​

SchemaValidInvalidTest Case
properties: {name: {type: "string"}}{name: "John"}{name: 123}testObjectPropertyType
required: ["name"]{name: "John"}{}testObjectRequired
additionalProperties: false{name: "John"}{name: "John", extra: "x"}testObjectNoAdditional

5.2 Nested Objects​

SchemaValidTest Case
Nested object with required fields{person: {name: "John"}}testNestedObjectRequired
Multiple levels deep{a: {b: {c: "value"}}}testDeepNestedObject

6. File Type Tests​

6.1 File Constraints​

ConstraintValidInvalidTest Case
allowedTypes: ["application/pdf"]PDF fileJPEG filetestFileAllowedTypes
maxSize: 5242880 (5MB)4MB file6MB filetestFileMaxSize
allowedTags: ["public","internal"]with "public" tagwith "secret" tagtestFileAllowedTags
autoTags: ["uploaded"]auto-tagged-testFileAutoTags

6.2 File Upload Methods​

MethodFile TypeTest Case
MultipartPDF, JPEG, PNGtestFileMultipart
Base64Data URItestFileBase64
URLExternal URLtestFileUrl

7. Combined Constraints Tests​

7.1 Multiple Constraints​

Property ConfigValidInvalidTest Case
String: minLength=5, maxLength=10, pattern="^[A-Z]""ABCDE""abc" / "A" / "ABCDEFGHIJK"testStringMultipleConstraints
Integer: minimum=0, maximum=100, multipleOf=50, 5, 100-5, 3, 105testIntegerMultipleConstraints

8. Special Property Flags​

FlagTypeValid ValuesTest Case
visiblebooleantrue, falsetestPropertyVisible
hideOnCollectionbooleantrue, falsetestPropertyHideOnCollection
hideOnFormbooleantrue, falsetestPropertyHideOnForm
readOnlybooleantrue, falsetestPropertyReadOnly
writeOnlybooleantrue, falsetestPropertyWriteOnly
authorizationobject{"read": ["authenticated"], "update": ["admin"]}testAPropertyAuthorizationSaves
tableobject{"default": true}testTheKeysTheFleetAlreadyWritesAreHeld
widgetstring"switch", "icon"testTheKeysTheFleetAlreadyWritesAreHeld
defaultBehaviorstring"always", "falsy"testTheKeysTheFleetAlreadyWritesAreHeld
title:<lang>stringtitle:nl, title:pt-BRtestAProseKeyTakesALanguageSuffixAndNothingElseDoes
description:<lang>stringdescription:entestAProseKeyTakesALanguageSuffixAndNothingElseDoes

authorization is the one this layer acts on itself: Schema::hasPropertyAuthorization() reads it to decide whether a property carries its own read and write rules. The other three are read by the apps that write them, and are held here so a shipped schema imports.

9. Schema-Level Tests​

9.1 Required Fields​

Schema ConfigObject DataShould PassTest Case
required: ["name"]{name: "John"}✅testRequiredFieldPresent
required: ["name"]{}❌testRequiredFieldMissing
required: ["name", "email"]{name: "John"}❌testMultipleRequiredPartial

9.2 Optional Fields​

Schema ConfigObject DataShould PassTest Case
properties: {name, email} (no required){name: "John"}✅testOptionalFieldPartial
properties: {name, email} (no required){}✅testOptionalFieldEmpty

10. OneOf Tests​

Schema ConfigValidInvalidTest Case
oneOf: [{type: "string"}, {type: "integer"}]"text" OR 42truetestOneOfValid
oneOf: [{type: "string"}, {type: "integer"}]{object}❌testOneOfInvalid

11. Edge Cases​

11.1 Boundary Values​

TestValueExpectedTest Case
String maxLength exactly"1234567890" (len=10, max=10)✅testStringMaxLengthExact
Integer minimum exactly0 (min=0)✅testIntegerMinimumExact
Integer maximum exactly100 (max=100)✅testIntegerMaximumExact
Array minItems exactly["a"] (len=1, min=1)✅testArrayMinItemsExact

11.2 Empty/Null Values​

Property TypeValueRequiredShould PassTest Case
string""yes❌testStringEmptyRequired
string""no✅testStringEmptyOptional
stringnullno✅testStringNullOptional
array[]no✅testArrayEmptyOptional
objectno✅testObjectEmptyOptional

11.3 Type Coercion​

Property TypeInput ValueShould PassTest Case
integer"123" (string)❓testIntegerStringCoercion
boolean1 (number)❌testBooleanNumberNoCoercion
number"3.14" (string)❓testNumberStringCoercion

12. Error Response Format Tests​

All invalid inputs should return consistent error responses:

{
"error": "Validation failed",
"message": "Property 'age' failed validation: value must be >= 18",
"property": "age",
"constraint": "minimum",
"value": 15
}

Test cases:

  • testErrorResponseFormat - Verify error structure
  • testErrorMessageClarity - Verify error messages are clear
  • testMultipleErrors - Multiple validation errors returned
  • testNestedPropertyErrors - Errors in nested properties have correct path

Implementation Priority​

Phase 1: Basic Types (Complete ✅)​

  • String (basic)
  • Number/Integer min/max
  • Boolean
  • Required vs Optional
  • Enum
  • Pattern

Phase 2: Advanced String Formats (Next)​

  • All string formats from validStringFormats
  • Format validation errors

Phase 3: Complex Types​

  • Array with all constraints
  • Nested objects
  • OneOf

Phase 4: File Properties​

  • All file constraints
  • File upload methods
  • File validation errors

Phase 5: Edge Cases​

  • Boundary values
  • Empty/null values
  • Type coercion
  • Error response format

Test Organization​

Tests should be organized in the following structure:

class CoreIntegrationTest extends TestCase
{
// File Upload Tests (1-12) ✅

// Basic Property Validation (13-21) ✅

// String Format Matrix (22-40)
public function testStringFormatDate(): void
public function testStringFormatDateTime(): void
public function testStringFormatEmail(): void
// ...

// Number/Integer Matrix (41-50)
public function testIntegerMinimum(): void
public function testNumberMultipleOf(): void
// ...

// Array Tests (51-60)
public function testArrayItemType(): void
public function testArrayMinItems(): void
// ...

// File Property Tests (61-70)
public function testFileAllowedTags(): void
public function testFileAutoTags(): void
// ...

// Edge Cases (71-80)
public function testStringMaxLengthExact(): void
public function testEmptyValues(): void
// ...

// Error Response Tests (81-90)
public function testErrorResponseFormat(): void
public function testMultipleValidationErrors(): void
// ...
}

Expected Test Count​

  • File Upload Tests: 12
  • Basic Validation: 9 (13-21)
  • String Format Tests: ~20
  • Number/Integer Tests: ~10
  • Array Tests: ~10
  • Object Tests: ~8
  • File Property Tests: ~10
  • Edge Cases: ~10
  • Error Response Tests: ~5

Total: ~94 comprehensive integration tests

This ensures complete coverage of all validation scenarios in OpenRegister.