···11+<?php
22+33+namespace SocialDept\Schema\Tests\Unit\Data\Types;
44+55+use Orchestra\Testbench\TestCase;
66+use SocialDept\Schema\Data\Types\BooleanType;
77+use SocialDept\Schema\Exceptions\RecordValidationException;
88+99+class BooleanTypeTest extends TestCase
1010+{
1111+ public function test_it_creates_from_array(): void
1212+ {
1313+ $type = BooleanType::fromArray([
1414+ 'type' => 'boolean',
1515+ 'description' => 'A boolean value',
1616+ 'const' => true,
1717+ ]);
1818+1919+ $this->assertSame('boolean', $type->type);
2020+ $this->assertSame('A boolean value', $type->description);
2121+ $this->assertTrue($type->const);
2222+ }
2323+2424+ public function test_it_converts_to_array(): void
2525+ {
2626+ $type = new BooleanType(
2727+ description: 'A boolean value',
2828+ const: false
2929+ );
3030+3131+ $array = $type->toArray();
3232+3333+ $this->assertSame('boolean', $array['type']);
3434+ $this->assertSame('A boolean value', $array['description']);
3535+ $this->assertFalse($array['const']);
3636+ }
3737+3838+ public function test_it_validates_boolean_type(): void
3939+ {
4040+ $type = new BooleanType();
4141+4242+ $type->validate(true, 'field');
4343+ $type->validate(false, 'field');
4444+4545+ $this->expectException(RecordValidationException::class);
4646+ $this->expectExceptionMessage("Expected type 'boolean' at 'field' but got 'string'");
4747+4848+ $type->validate('true', 'field');
4949+ }
5050+5151+ public function test_it_validates_const_true(): void
5252+ {
5353+ $type = new BooleanType(const: true);
5454+5555+ $type->validate(true, 'field');
5656+5757+ $this->expectException(RecordValidationException::class);
5858+ $this->expectExceptionMessage('Invalid value at \'field\': must equal true');
5959+6060+ $type->validate(false, 'field');
6161+ }
6262+6363+ public function test_it_validates_const_false(): void
6464+ {
6565+ $type = new BooleanType(const: false);
6666+6767+ $type->validate(false, 'field');
6868+6969+ $this->expectException(RecordValidationException::class);
7070+ $this->expectExceptionMessage('Invalid value at \'field\': must equal false');
7171+7272+ $type->validate(true, 'field');
7373+ }
7474+}
+106
tests/Unit/Data/Types/IntegerTypeTest.php
···11+<?php
22+33+namespace SocialDept\Schema\Tests\Unit\Data\Types;
44+55+use Orchestra\Testbench\TestCase;
66+use SocialDept\Schema\Data\Types\IntegerType;
77+use SocialDept\Schema\Exceptions\RecordValidationException;
88+99+class IntegerTypeTest extends TestCase
1010+{
1111+ public function test_it_creates_from_array(): void
1212+ {
1313+ $type = IntegerType::fromArray([
1414+ 'type' => 'integer',
1515+ 'description' => 'An integer value',
1616+ 'minimum' => 1,
1717+ 'maximum' => 100,
1818+ 'enum' => [1, 2, 3],
1919+ 'const' => 5,
2020+ ]);
2121+2222+ $this->assertSame('integer', $type->type);
2323+ $this->assertSame('An integer value', $type->description);
2424+ $this->assertSame(1, $type->minimum);
2525+ $this->assertSame(100, $type->maximum);
2626+ $this->assertSame([1, 2, 3], $type->enum);
2727+ $this->assertSame(5, $type->const);
2828+ }
2929+3030+ public function test_it_converts_to_array(): void
3131+ {
3232+ $type = new IntegerType(
3333+ description: 'An integer value',
3434+ minimum: 1,
3535+ maximum: 100
3636+ );
3737+3838+ $array = $type->toArray();
3939+4040+ $this->assertSame('integer', $array['type']);
4141+ $this->assertSame('An integer value', $array['description']);
4242+ $this->assertSame(1, $array['minimum']);
4343+ $this->assertSame(100, $array['maximum']);
4444+ }
4545+4646+ public function test_it_validates_integer_type(): void
4747+ {
4848+ $type = new IntegerType();
4949+5050+ $this->expectException(RecordValidationException::class);
5151+ $this->expectExceptionMessage("Expected type 'integer' at 'field' but got 'string'");
5252+5353+ $type->validate('123', 'field');
5454+ }
5555+5656+ public function test_it_validates_const(): void
5757+ {
5858+ $type = new IntegerType(const: 5);
5959+6060+ $type->validate(5, 'field');
6161+6262+ $this->expectException(RecordValidationException::class);
6363+ $this->expectExceptionMessage('Invalid value at \'field\': must equal 5');
6464+6565+ $type->validate(10, 'field');
6666+ }
6767+6868+ public function test_it_validates_enum(): void
6969+ {
7070+ $type = new IntegerType(enum: [1, 2, 3]);
7171+7272+ $type->validate(1, 'field');
7373+ $type->validate(2, 'field');
7474+7575+ $this->expectException(RecordValidationException::class);
7676+ $this->expectExceptionMessage('Invalid value at \'field\': must be one of: 1, 2, 3');
7777+7878+ $type->validate(5, 'field');
7979+ }
8080+8181+ public function test_it_validates_minimum(): void
8282+ {
8383+ $type = new IntegerType(minimum: 10);
8484+8585+ $type->validate(10, 'field');
8686+ $type->validate(20, 'field');
8787+8888+ $this->expectException(RecordValidationException::class);
8989+ $this->expectExceptionMessage('Invalid value at \'field\': must be at least 10');
9090+9191+ $type->validate(5, 'field');
9292+ }
9393+9494+ public function test_it_validates_maximum(): void
9595+ {
9696+ $type = new IntegerType(maximum: 10);
9797+9898+ $type->validate(10, 'field');
9999+ $type->validate(5, 'field');
100100+101101+ $this->expectException(RecordValidationException::class);
102102+ $this->expectExceptionMessage('Invalid value at \'field\': must be at most 10');
103103+104104+ $type->validate(20, 'field');
105105+ }
106106+}
+248
tests/Unit/Data/Types/StringTypeTest.php
···11+<?php
22+33+namespace SocialDept\Schema\Tests\Unit\Data\Types;
44+55+use Orchestra\Testbench\TestCase;
66+use SocialDept\Schema\Data\Types\StringType;
77+use SocialDept\Schema\Exceptions\RecordValidationException;
88+99+class StringTypeTest extends TestCase
1010+{
1111+ public function test_it_creates_from_array(): void
1212+ {
1313+ $type = StringType::fromArray([
1414+ 'type' => 'string',
1515+ 'description' => 'A string value',
1616+ 'minLength' => 1,
1717+ 'maxLength' => 100,
1818+ 'minGraphemes' => 1,
1919+ 'maxGraphemes' => 50,
2020+ 'format' => 'datetime',
2121+ 'enum' => ['foo', 'bar'],
2222+ 'const' => 'foo',
2323+ 'knownValues' => ['foo', 'bar', 'baz'],
2424+ ]);
2525+2626+ $this->assertSame('string', $type->type);
2727+ $this->assertSame('A string value', $type->description);
2828+ $this->assertSame(1, $type->minLength);
2929+ $this->assertSame(100, $type->maxLength);
3030+ $this->assertSame(1, $type->minGraphemes);
3131+ $this->assertSame(50, $type->maxGraphemes);
3232+ $this->assertSame('datetime', $type->format);
3333+ $this->assertSame(['foo', 'bar'], $type->enum);
3434+ $this->assertSame('foo', $type->const);
3535+ $this->assertSame(['foo', 'bar', 'baz'], $type->knownValues);
3636+ }
3737+3838+ public function test_it_converts_to_array(): void
3939+ {
4040+ $type = new StringType(
4141+ description: 'A string value',
4242+ minLength: 1,
4343+ maxLength: 100,
4444+ format: 'datetime'
4545+ );
4646+4747+ $array = $type->toArray();
4848+4949+ $this->assertSame('string', $array['type']);
5050+ $this->assertSame('A string value', $array['description']);
5151+ $this->assertSame(1, $array['minLength']);
5252+ $this->assertSame(100, $array['maxLength']);
5353+ $this->assertSame('datetime', $array['format']);
5454+ }
5555+5656+ public function test_it_validates_string_type(): void
5757+ {
5858+ $type = new StringType();
5959+6060+ $this->expectException(RecordValidationException::class);
6161+ $this->expectExceptionMessage("Expected type 'string' at 'field' but got 'integer'");
6262+6363+ $type->validate(123, 'field');
6464+ }
6565+6666+ public function test_it_validates_const(): void
6767+ {
6868+ $type = new StringType(const: 'foo');
6969+7070+ $type->validate('foo', 'field');
7171+7272+ $this->expectException(RecordValidationException::class);
7373+ $this->expectExceptionMessage("Invalid value at 'field': must equal 'foo'");
7474+7575+ $type->validate('bar', 'field');
7676+ }
7777+7878+ public function test_it_validates_enum(): void
7979+ {
8080+ $type = new StringType(enum: ['foo', 'bar']);
8181+8282+ $type->validate('foo', 'field');
8383+ $type->validate('bar', 'field');
8484+8585+ $this->expectException(RecordValidationException::class);
8686+ $this->expectExceptionMessage('Invalid value at \'field\': must be one of: foo, bar');
8787+8888+ $type->validate('baz', 'field');
8989+ }
9090+9191+ public function test_it_validates_min_length(): void
9292+ {
9393+ $type = new StringType(minLength: 5);
9494+9595+ $type->validate('hello', 'field');
9696+9797+ $this->expectException(RecordValidationException::class);
9898+ $this->expectExceptionMessage('Invalid value at \'field\': must be at least 5 bytes');
9999+100100+ $type->validate('hi', 'field');
101101+ }
102102+103103+ public function test_it_validates_max_length(): void
104104+ {
105105+ $type = new StringType(maxLength: 5);
106106+107107+ $type->validate('hello', 'field');
108108+109109+ $this->expectException(RecordValidationException::class);
110110+ $this->expectExceptionMessage('Invalid value at \'field\': must be at most 5 bytes');
111111+112112+ $type->validate('hello world', 'field');
113113+ }
114114+115115+ public function test_it_validates_min_graphemes(): void
116116+ {
117117+ $type = new StringType(minGraphemes: 3);
118118+119119+ $type->validate('abc', 'field');
120120+121121+ $this->expectException(RecordValidationException::class);
122122+ $this->expectExceptionMessage('Invalid value at \'field\': must be at least 3 graphemes');
123123+124124+ $type->validate('ab', 'field');
125125+ }
126126+127127+ public function test_it_validates_max_graphemes(): void
128128+ {
129129+ $type = new StringType(maxGraphemes: 3);
130130+131131+ $type->validate('abc', 'field');
132132+133133+ $this->expectException(RecordValidationException::class);
134134+ $this->expectExceptionMessage('Invalid value at \'field\': must be at most 3 graphemes');
135135+136136+ $type->validate('abcd', 'field');
137137+ }
138138+139139+ public function test_it_validates_datetime_format(): void
140140+ {
141141+ $type = new StringType(format: 'datetime');
142142+143143+ $type->validate('2024-01-01T00:00:00Z', 'field');
144144+145145+ $this->expectException(RecordValidationException::class);
146146+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid ISO 8601 datetime');
147147+148148+ $type->validate('not a datetime', 'field');
149149+ }
150150+151151+ public function test_it_validates_uri_format(): void
152152+ {
153153+ $type = new StringType(format: 'uri');
154154+155155+ $type->validate('https://example.com', 'field');
156156+157157+ $this->expectException(RecordValidationException::class);
158158+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid URI');
159159+160160+ $type->validate('not a uri', 'field');
161161+ }
162162+163163+ public function test_it_validates_at_uri_format(): void
164164+ {
165165+ $type = new StringType(format: 'at-uri');
166166+167167+ $type->validate('at://did:plc:123/app.bsky.feed.post/123', 'field');
168168+169169+ $this->expectException(RecordValidationException::class);
170170+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid AT URI');
171171+172172+ $type->validate('https://example.com', 'field');
173173+ }
174174+175175+ public function test_it_validates_did_format(): void
176176+ {
177177+ $type = new StringType(format: 'did');
178178+179179+ $type->validate('did:plc:123abc', 'field');
180180+181181+ $this->expectException(RecordValidationException::class);
182182+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid DID');
183183+184184+ $type->validate('not a did', 'field');
185185+ }
186186+187187+ public function test_it_validates_handle_format(): void
188188+ {
189189+ $type = new StringType(format: 'handle');
190190+191191+ $type->validate('alice.bsky.social', 'field');
192192+193193+ $this->expectException(RecordValidationException::class);
194194+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid handle');
195195+196196+ $type->validate('invalid handle!', 'field');
197197+ }
198198+199199+ public function test_it_validates_at_identifier_format(): void
200200+ {
201201+ $type = new StringType(format: 'at-identifier');
202202+203203+ $type->validate('did:plc:123abc', 'field');
204204+ $type->validate('alice.bsky.social', 'field');
205205+206206+ $this->expectException(RecordValidationException::class);
207207+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid AT identifier (DID or handle)');
208208+209209+ $type->validate('invalid!', 'field');
210210+ }
211211+212212+ public function test_it_validates_nsid_format(): void
213213+ {
214214+ $type = new StringType(format: 'nsid');
215215+216216+ $type->validate('app.bsky.feed.post', 'field');
217217+218218+ $this->expectException(RecordValidationException::class);
219219+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid NSID');
220220+221221+ $type->validate('invalid nsid!', 'field');
222222+ }
223223+224224+ public function test_it_validates_cid_format(): void
225225+ {
226226+ $type = new StringType(format: 'cid');
227227+228228+ $type->validate('bafyreihqhqv7h2gfxkj7qxvz7pxqhqvz7h2gfxkj7', 'field');
229229+230230+ $this->expectException(RecordValidationException::class);
231231+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid CID');
232232+233233+ $type->validate('invalid cid!', 'field');
234234+ }
235235+236236+ public function test_it_validates_language_format(): void
237237+ {
238238+ $type = new StringType(format: 'language');
239239+240240+ $type->validate('en', 'field');
241241+ $type->validate('en-US', 'field');
242242+243243+ $this->expectException(RecordValidationException::class);
244244+ $this->expectExceptionMessage('Invalid value at \'field\': must be a valid language tag');
245245+246246+ $type->validate('invalid', 'field');
247247+ }
248248+}
+44
tests/Unit/Data/Types/UnknownTypeTest.php
···11+<?php
22+33+namespace SocialDept\Schema\Tests\Unit\Data\Types;
44+55+use Orchestra\Testbench\TestCase;
66+use SocialDept\Schema\Data\Types\UnknownType;
77+88+class UnknownTypeTest extends TestCase
99+{
1010+ public function test_it_creates_from_array(): void
1111+ {
1212+ $type = UnknownType::fromArray([
1313+ 'type' => 'unknown',
1414+ 'description' => 'An unknown value',
1515+ ]);
1616+1717+ $this->assertSame('unknown', $type->type);
1818+ $this->assertSame('An unknown value', $type->description);
1919+ }
2020+2121+ public function test_it_converts_to_array(): void
2222+ {
2323+ $type = new UnknownType(description: 'An unknown value');
2424+2525+ $array = $type->toArray();
2626+2727+ $this->assertSame('unknown', $array['type']);
2828+ $this->assertSame('An unknown value', $array['description']);
2929+ }
3030+3131+ public function test_it_accepts_any_value(): void
3232+ {
3333+ $type = new UnknownType();
3434+3535+ // Unknown type should accept any value without throwing
3636+ $type->validate('string', 'field');
3737+ $type->validate(123, 'field');
3838+ $type->validate(true, 'field');
3939+ $type->validate(['array'], 'field');
4040+ $type->validate(null, 'field');
4141+4242+ $this->assertTrue(true); // If we get here, validation passed
4343+ }
4444+}