Parse and validate AT Protocol Lexicons with DTO generation for Laravel
1<?php
2
3namespace SocialDept\AtpSchema\Tests\Unit\Support;
4
5use Orchestra\Testbench\TestCase;
6use SocialDept\AtpSchema\Services\BlobHandler;
7use SocialDept\AtpSchema\Services\ModelMapper;
8use SocialDept\AtpSchema\Services\UnionResolver;
9use SocialDept\AtpSchema\Validation\Validator;
10
11class MacroableTest extends TestCase
12{
13 protected function setUp(): void
14 {
15 parent::setUp();
16
17 // Clear any existing macros
18 ModelMapper::flushMacros();
19 BlobHandler::flushMacros();
20 UnionResolver::flushMacros();
21 Validator::flushMacros();
22 }
23
24 public function test_model_mapper_supports_macros(): void
25 {
26 ModelMapper::macro('customMethod', fn () => 'custom result');
27
28 $mapper = new ModelMapper();
29
30 $this->assertEquals('custom result', $mapper->customMethod());
31 }
32
33 public function test_model_mapper_macro_can_access_instance(): void
34 {
35 ModelMapper::macro('getCount', function () {
36 return $this->count();
37 });
38
39 $mapper = new ModelMapper();
40
41 $this->assertEquals(0, $mapper->getCount());
42 }
43
44 public function test_blob_handler_supports_macros(): void
45 {
46 BlobHandler::macro('customMethod', fn () => 'blob custom');
47
48 $handler = new BlobHandler();
49
50 $this->assertEquals('blob custom', $handler->customMethod());
51 }
52
53 public function test_blob_handler_macro_can_access_instance(): void
54 {
55 BlobHandler::macro('getCurrentDisk', function () {
56 return $this->getDisk();
57 });
58
59 $handler = new BlobHandler('local');
60
61 $this->assertEquals('local', $handler->getCurrentDisk());
62 }
63
64 public function test_union_resolver_supports_macros(): void
65 {
66 UnionResolver::macro('customMethod', fn () => 'union custom');
67
68 $resolver = new UnionResolver();
69
70 $this->assertEquals('union custom', $resolver->customMethod());
71 }
72
73 public function test_validator_supports_macros(): void
74 {
75 Validator::macro('customMethod', fn () => 'validator custom');
76
77 $schemaLoader = $this->createMock(\SocialDept\AtpSchema\Parser\SchemaLoader::class);
78 $validator = new Validator($schemaLoader);
79
80 $this->assertEquals('validator custom', $validator->customMethod());
81 }
82
83 public function test_macros_can_accept_parameters(): void
84 {
85 ModelMapper::macro('multiply', fn ($a, $b) => $a * $b);
86
87 $mapper = new ModelMapper();
88
89 $this->assertEquals(15, $mapper->multiply(3, 5));
90 }
91
92 public function test_macros_work_across_multiple_instances(): void
93 {
94 ModelMapper::macro('greet', fn ($name) => "Hello, {$name}!");
95
96 $mapper1 = new ModelMapper();
97 $mapper2 = new ModelMapper();
98
99 $this->assertEquals('Hello, Alice!', $mapper1->greet('Alice'));
100 $this->assertEquals('Hello, Bob!', $mapper2->greet('Bob'));
101 }
102
103 public function test_macro_can_return_instance_for_chaining(): void
104 {
105 ModelMapper::macro('chainable', function () {
106 return $this;
107 });
108
109 $mapper = new ModelMapper();
110
111 $result = $mapper->chainable()->chainable();
112
113 $this->assertSame($mapper, $result);
114 }
115
116 public function test_has_macro_checks_existence(): void
117 {
118 ModelMapper::macro('exists', fn () => true);
119
120 $this->assertTrue(ModelMapper::hasMacro('exists'));
121 $this->assertFalse(ModelMapper::hasMacro('doesNotExist'));
122 }
123
124 public function test_flush_macros_removes_all(): void
125 {
126 ModelMapper::macro('test1', fn () => 'result1');
127 ModelMapper::macro('test2', fn () => 'result2');
128
129 $this->assertTrue(ModelMapper::hasMacro('test1'));
130 $this->assertTrue(ModelMapper::hasMacro('test2'));
131
132 ModelMapper::flushMacros();
133
134 $this->assertFalse(ModelMapper::hasMacro('test1'));
135 $this->assertFalse(ModelMapper::hasMacro('test2'));
136 }
137
138 public function test_macros_are_independent_between_classes(): void
139 {
140 ModelMapper::macro('sharedName', fn () => 'mapper');
141 BlobHandler::macro('sharedName', fn () => 'handler');
142
143 $mapper = new ModelMapper();
144 $handler = new BlobHandler();
145
146 $this->assertEquals('mapper', $mapper->sharedName());
147 $this->assertEquals('handler', $handler->sharedName());
148 }
149
150 public function test_macro_can_use_closure_variables(): void
151 {
152 $prefix = 'PREFIX';
153
154 ModelMapper::macro('withPrefix', fn ($value) => "{$prefix}: {$value}");
155
156 $mapper = new ModelMapper();
157
158 $this->assertEquals('PREFIX: test', $mapper->withPrefix('test'));
159 }
160
161 public function test_macro_can_modify_instance_state(): void
162 {
163 BlobHandler::macro('switchDisk', function ($disk) {
164 $this->setDisk($disk);
165
166 return $this;
167 });
168
169 $handler = new BlobHandler('local');
170
171 $this->assertEquals('local', $handler->getDisk());
172
173 $handler->switchDisk('s3');
174
175 $this->assertEquals('s3', $handler->getDisk());
176 }
177
178 public function test_mixin_adds_multiple_methods(): void
179 {
180 $mixin = new class () {
181 public function method1()
182 {
183 return fn () => 'method1';
184 }
185
186 public function method2()
187 {
188 return fn () => 'method2';
189 }
190 };
191
192 ModelMapper::mixin($mixin);
193
194 $mapper = new ModelMapper();
195
196 $this->assertEquals('method1', $mapper->method1());
197 $this->assertEquals('method2', $mapper->method2());
198 }
199
200 public function test_complex_macro_with_dependencies(): void
201 {
202 ModelMapper::macro('complexOperation', function ($data) {
203 // Use existing methods
204 $count = $this->count();
205
206 return [
207 'count' => $count,
208 'data' => strtoupper($data),
209 ];
210 });
211
212 $mapper = new ModelMapper();
213
214 $result = $mapper->complexOperation('test');
215
216 $this->assertEquals([
217 'count' => 0,
218 'data' => 'TEST',
219 ], $result);
220 }
221}