Something went wrong. Try again.
Parse and validate AT Protocol Lexicons with DTO generation for Laravel
Something went wrong. Try again.
7.3 kB · 264 lines
PHP
at dev
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265<?php
namespace SocialDept\AtpSchema\Tests\Integration;
use Orchestra\Testbench\TestCase;use SocialDept\AtpSchema\Contracts\Transformer;use SocialDept\AtpSchema\Data\LexiconDocument;use SocialDept\AtpSchema\Parser\SchemaLoader;use SocialDept\AtpSchema\Services\ModelMapper;use SocialDept\AtpSchema\Validation\Validator;
class ModelMappingIntegrationTest extends TestCase{ protected ModelMapper $mapper;
protected Validator $validator;
protected SchemaLoader $schemaLoader;
protected function setUp(): void { parent::setUp();
$this->mapper = new ModelMapper(); $this->schemaLoader = new SchemaLoader([]); $this->validator = new Validator($this->schemaLoader); }
public function test_it_transforms_and_validates_complete_workflow(): void { // Register transformer $this->mapper->register('app.bsky.feed.post', new PostTransformer());
// Load schema $schema = LexiconDocument::fromArray([ 'lexicon' => 1, 'id' => 'app.bsky.feed.post', 'defs' => [ 'main' => [ 'type' => 'record', 'key' => 'tid', 'record' => [ 'type' => 'object', 'required' => ['text', 'createdAt'], 'properties' => [ 'text' => ['type' => 'string', 'maxLength' => 300], 'createdAt' => ['type' => 'string', 'format' => 'datetime'], ], ], ], ], ]);
// Transform to model $model = $this->mapper->fromArray('app.bsky.feed.post', [ 'text' => 'Hello, world!', 'createdAt' => '2024-01-01T00:00:00Z', ]);
// Verify model $this->assertInstanceOf(Post::class, $model); $this->assertEquals('Hello, world!', $model->text);
// Transform back to array $data = $this->mapper->toArray('app.bsky.feed.post', $model);
// Validate transformed data $result = $this->validator->validate($data, $schema);
$this->assertTrue($result); }
public function test_it_handles_multiple_model_transformations(): void { $this->mapper->registerMany([ 'app.bsky.feed.post' => new PostTransformer(), 'app.bsky.feed.repost' => new RepostTransformer(), ]);
$posts = [ ['text' => 'First post', 'createdAt' => '2024-01-01T00:00:00Z'], ['text' => 'Second post', 'createdAt' => '2024-01-02T00:00:00Z'], ];
$models = $this->mapper->fromArrayMany('app.bsky.feed.post', $posts);
$this->assertCount(2, $models); $this->assertContainsOnlyInstancesOf(Post::class, $models);
$arrays = $this->mapper->toArrayMany('app.bsky.feed.post', $models);
$this->assertEquals($posts, $arrays); }
public function test_it_extends_mapper_with_macros(): void { ModelMapper::macro('validateAndTransform', function ($type, $data, $schema) { // Validate first if (! $this->validator->validate($data, $schema)) { return null; }
// Then transform return $this->fromArray($type, $data); });
$this->mapper->validator = $this->validator; $this->mapper->register('app.bsky.feed.post', new PostTransformer());
$schema = LexiconDocument::fromArray([ 'lexicon' => 1, 'id' => 'app.bsky.feed.post', 'defs' => [ 'main' => [ 'type' => 'record', 'key' => 'tid', 'record' => [ 'type' => 'object', 'required' => ['text'], 'properties' => [ 'text' => ['type' => 'string'], 'createdAt' => ['type' => 'string'], ], ], ], ], ]);
$validData = ['text' => 'Hello', 'createdAt' => '2024-01-01T00:00:00Z'];
$result = $this->mapper->validateAndTransform('app.bsky.feed.post', $validData, $schema);
$this->assertInstanceOf(Post::class, $result);
ModelMapper::flushMacros(); }
public function test_it_handles_nested_transformations(): void { $this->mapper->register('app.bsky.actor.profile', new ProfileTransformer());
$data = [ 'displayName' => 'Alice', 'description' => 'Developer', 'avatar' => [ 'url' => 'https://example.com/avatar.jpg', 'size' => 12345, ], ];
$model = $this->mapper->fromArray('app.bsky.actor.profile', $data);
$this->assertInstanceOf(Profile::class, $model); $this->assertEquals('Alice', $model->displayName); $this->assertIsArray($model->avatar);
$transformed = $this->mapper->toArray('app.bsky.actor.profile', $model);
$this->assertEquals($data, $transformed); }}
// Test models and transformersclass Post{ public function __construct( public string $text, public string $createdAt ) { }}
class PostTransformer implements Transformer{ public function fromArray(array $data): Post { return new Post( text: $data['text'], createdAt: $data['createdAt'] ); }
public function toArray(mixed $model): array { return [ 'text' => $model->text, 'createdAt' => $model->createdAt, ]; }
public function supports(string $type): bool { return $type === 'app.bsky.feed.post'; }}
class Repost{ public function __construct( public string $uri, public string $createdAt ) { }}
class RepostTransformer implements Transformer{ public function fromArray(array $data): Repost { return new Repost( uri: $data['uri'], createdAt: $data['createdAt'] ); }
public function toArray(mixed $model): array { return [ 'uri' => $model->uri, 'createdAt' => $model->createdAt, ]; }
public function supports(string $type): bool { return $type === 'app.bsky.feed.repost'; }}
class Profile{ public function __construct( public string $displayName, public string $description, public array $avatar ) { }}
class ProfileTransformer implements Transformer{ public function fromArray(array $data): Profile { return new Profile( displayName: $data['displayName'], description: $data['description'], avatar: $data['avatar'] ); }
public function toArray(mixed $model): array { return [ 'displayName' => $model->displayName, 'description' => $model->description, 'avatar' => $model->avatar, ]; }
public function supports(string $type): bool { return $type === 'app.bsky.actor.profile'; }}