Skip to content

Commit 1e10cae

Browse files
committed
Add audit exclusion/inclusion feature tests
Introduces feature tests for verifying audit log exclusion and inclusion behavior on User and Post models. Tests cover model-level and global exclusions, auditInclude arrays, disabled auditing, and edge cases for field changes.
1 parent a3ad3bb commit 1e10cae

File tree

1 file changed

+399
-0
lines changed

1 file changed

+399
-0
lines changed
Lines changed: 399 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,399 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace iamfarhad\LaravelAuditLog\Tests\Feature;
6+
7+
use iamfarhad\LaravelAuditLog\Tests\Mocks\Post;
8+
use iamfarhad\LaravelAuditLog\Tests\Mocks\User;
9+
use iamfarhad\LaravelAuditLog\Tests\TestCase;
10+
use Illuminate\Support\Facades\DB;
11+
use Illuminate\Support\Facades\Schema;
12+
13+
final class UserModelAuditExclusionInclusionTest extends TestCase
14+
{
15+
protected function setUp(): void
16+
{
17+
parent::setUp();
18+
19+
// Clear any existing audit logs before each test
20+
DB::table('audit_users_logs')->delete();
21+
}
22+
23+
public function test_user_model_audit_exclusions_verification(): void
24+
{
25+
// Test the actual User model to verify audit exclusions work
26+
$user = User::create([
27+
'name' => 'Test User',
28+
'email' => '[email protected]',
29+
'password' => 'secret-password',
30+
'is_active' => true,
31+
]);
32+
33+
// Verify user audit table exists
34+
$this->assertTrue(Schema::hasTable('audit_users_logs'));
35+
36+
// Get the audit log
37+
$auditLog = DB::table('audit_users_logs')
38+
->where('entity_id', $user->id)
39+
->where('action', 'created')
40+
->first();
41+
42+
$this->assertNotNull($auditLog, 'Audit log should be created for user creation');
43+
44+
$newValues = json_decode($auditLog->new_values, true);
45+
46+
// These fields should be logged
47+
$this->assertArrayHasKey('name', $newValues);
48+
$this->assertArrayHasKey('email', $newValues);
49+
$this->assertArrayHasKey('is_active', $newValues);
50+
$this->assertEquals('Test User', $newValues['name']);
51+
$this->assertEquals('[email protected]', $newValues['email']);
52+
$this->assertTrue($newValues['is_active']);
53+
54+
// These sensitive fields should be excluded
55+
$this->assertArrayNotHasKey(
56+
'password',
57+
$newValues,
58+
'Password field should be excluded from audit logs but it appears in: ' . json_encode(array_keys($newValues))
59+
);
60+
61+
$this->assertArrayNotHasKey(
62+
'remember_token',
63+
$newValues,
64+
'Remember token should be excluded from audit logs but it appears in: ' . json_encode(array_keys($newValues))
65+
);
66+
}
67+
68+
public function test_user_model_audit_exclusions_on_update(): void
69+
{
70+
// Create user first
71+
$user = User::create([
72+
'name' => 'Initial Name',
73+
'email' => '[email protected]',
74+
'password' => 'initial-password',
75+
'is_active' => false,
76+
]);
77+
78+
// Clear the creation audit log
79+
DB::table('audit_users_logs')->delete();
80+
81+
// Update user with both allowed and excluded fields
82+
$user->update([
83+
'name' => 'Updated Name',
84+
'password' => 'updated-password',
85+
'is_active' => true,
86+
]);
87+
88+
// Get the update audit log
89+
$auditLog = DB::table('audit_users_logs')
90+
->where('entity_id', $user->id)
91+
->where('action', 'updated')
92+
->first();
93+
94+
$this->assertNotNull($auditLog, 'Audit log should be created for user update');
95+
96+
$oldValues = json_decode($auditLog->old_values, true);
97+
$newValues = json_decode($auditLog->new_values, true);
98+
99+
// Allowed fields should be logged
100+
$this->assertArrayHasKey('name', $newValues);
101+
$this->assertArrayHasKey('is_active', $newValues);
102+
$this->assertEquals('Updated Name', $newValues['name']);
103+
$this->assertTrue($newValues['is_active']);
104+
105+
$this->assertArrayHasKey('name', $oldValues);
106+
$this->assertArrayHasKey('is_active', $oldValues);
107+
$this->assertEquals('Initial Name', $oldValues['name']);
108+
$this->assertFalse($oldValues['is_active']);
109+
110+
// Excluded fields should not be logged even if changed
111+
$this->assertArrayNotHasKey(
112+
'password',
113+
$newValues,
114+
'Password field should be excluded from audit logs in new values'
115+
);
116+
117+
$this->assertArrayNotHasKey(
118+
'password',
119+
$oldValues,
120+
'Password field should be excluded from audit logs in old values'
121+
);
122+
}
123+
124+
public function test_user_model_respects_global_and_model_exclusions(): void
125+
{
126+
// Create user with all possible fields that might be excluded
127+
$user = new User();
128+
$user->name = 'Test User';
129+
$user->email = '[email protected]';
130+
$user->password = 'secret-password';
131+
$user->is_active = true;
132+
$user->remember_token = 'test-token';
133+
$user->save();
134+
135+
// Get the audit log
136+
$auditLog = DB::table('audit_users_logs')
137+
->where('entity_id', $user->id)
138+
->where('action', 'created')
139+
->first();
140+
141+
$this->assertNotNull($auditLog);
142+
143+
$newValues = json_decode($auditLog->new_values, true);
144+
145+
// Verify allowed fields are present
146+
$this->assertArrayHasKey('name', $newValues);
147+
$this->assertArrayHasKey('email', $newValues);
148+
$this->assertArrayHasKey('is_active', $newValues);
149+
150+
// Verify model-level exclusions (from User model's $auditExclude)
151+
$this->assertArrayNotHasKey('password', $newValues);
152+
$this->assertArrayNotHasKey('remember_token', $newValues);
153+
154+
// Verify global exclusions (from config) - timestamps should be excluded by default
155+
$this->assertArrayNotHasKey('created_at', $newValues);
156+
$this->assertArrayNotHasKey('updated_at', $newValues);
157+
}
158+
159+
public function test_user_model_audit_exclusions_with_disabled_auditing(): void
160+
{
161+
// Create user with auditing disabled
162+
$user = new User();
163+
$user->disableAuditing();
164+
$user->name = 'Test User';
165+
$user->email = '[email protected]';
166+
$user->password = 'secret-password';
167+
$user->is_active = true;
168+
$user->save();
169+
170+
// Verify no audit log was created
171+
$auditLogCount = DB::table('audit_users_logs')
172+
->where('entity_id', $user->id)
173+
->count();
174+
175+
$this->assertEquals(0, $auditLogCount, 'No audit logs should be created when auditing is disabled');
176+
}
177+
178+
public function test_post_model_audit_inclusions_verification(): void
179+
{
180+
// Clear any existing audit logs
181+
DB::table('audit_posts_logs')->delete();
182+
183+
// Create a user first (required for posts)
184+
$user = User::create([
185+
'name' => 'Post Author',
186+
'email' => '[email protected]',
187+
'password' => 'password',
188+
'is_active' => true,
189+
]);
190+
191+
// Test the Post model which has auditInclude = ['title', 'status', 'published_at']
192+
$post = Post::create([
193+
'user_id' => $user->id,
194+
'title' => 'Test Post Title',
195+
'content' => 'This content should NOT be audited',
196+
'status' => 'draft',
197+
'published_at' => null,
198+
]);
199+
200+
// Verify post audit table exists
201+
$this->assertTrue(Schema::hasTable('audit_posts_logs'));
202+
203+
// Get the audit log
204+
$auditLog = DB::table('audit_posts_logs')
205+
->where('entity_id', $post->id)
206+
->where('action', 'created')
207+
->first();
208+
209+
$this->assertNotNull($auditLog, 'Audit log should be created for post creation');
210+
211+
$newValues = json_decode($auditLog->new_values, true);
212+
213+
// These fields should be logged (from auditInclude array)
214+
$this->assertArrayHasKey('title', $newValues);
215+
$this->assertArrayHasKey('status', $newValues);
216+
$this->assertArrayHasKey('published_at', $newValues);
217+
$this->assertEquals('Test Post Title', $newValues['title']);
218+
$this->assertEquals('draft', $newValues['status']);
219+
$this->assertNull($newValues['published_at']);
220+
221+
// These fields should NOT be logged (not in auditInclude array)
222+
$this->assertArrayNotHasKey(
223+
'content',
224+
$newValues,
225+
'Content field should not be audited because it is not in auditInclude array. Found fields: ' . json_encode(array_keys($newValues))
226+
);
227+
228+
$this->assertArrayNotHasKey(
229+
'user_id',
230+
$newValues,
231+
'User ID field should not be audited because it is not in auditInclude array. Found fields: ' . json_encode(array_keys($newValues))
232+
);
233+
234+
// Global exclusions should still apply (timestamps should be excluded)
235+
$this->assertArrayNotHasKey(
236+
'created_at',
237+
$newValues,
238+
'Created at timestamp should be excluded by global config. Found fields: ' . json_encode(array_keys($newValues))
239+
);
240+
241+
$this->assertArrayNotHasKey(
242+
'updated_at',
243+
$newValues,
244+
'Updated at timestamp should be excluded by global config. Found fields: ' . json_encode(array_keys($newValues))
245+
);
246+
}
247+
248+
public function test_post_model_audit_inclusions_on_update(): void
249+
{
250+
// Create a user first
251+
$user = User::create([
252+
'name' => 'Post Author',
253+
'email' => '[email protected]',
254+
'password' => 'password',
255+
'is_active' => true,
256+
]);
257+
258+
// Create a post
259+
$post = Post::create([
260+
'user_id' => $user->id,
261+
'title' => 'Original Title',
262+
'content' => 'Original content that should not be audited',
263+
'status' => 'draft',
264+
'published_at' => null,
265+
]);
266+
267+
// Clear the creation audit log
268+
DB::table('audit_posts_logs')->delete();
269+
270+
// Update the post with both included and excluded fields
271+
$post->update([
272+
'title' => 'Updated Title',
273+
'content' => 'Updated content that should still not be audited',
274+
'status' => 'published',
275+
'published_at' => now(),
276+
]);
277+
278+
// Get the update audit log
279+
$auditLog = DB::table('audit_posts_logs')
280+
->where('entity_id', $post->id)
281+
->where('action', 'updated')
282+
->first();
283+
284+
$this->assertNotNull($auditLog, 'Audit log should be created for post update');
285+
286+
$oldValues = json_decode($auditLog->old_values, true);
287+
$newValues = json_decode($auditLog->new_values, true);
288+
289+
// Included fields should be logged in both old and new values
290+
$this->assertArrayHasKey('title', $newValues);
291+
$this->assertArrayHasKey('status', $newValues);
292+
$this->assertArrayHasKey('published_at', $newValues);
293+
$this->assertEquals('Updated Title', $newValues['title']);
294+
$this->assertEquals('published', $newValues['status']);
295+
$this->assertNotNull($newValues['published_at']);
296+
297+
$this->assertArrayHasKey('title', $oldValues);
298+
$this->assertArrayHasKey('status', $oldValues);
299+
$this->assertArrayHasKey('published_at', $oldValues);
300+
$this->assertEquals('Original Title', $oldValues['title']);
301+
$this->assertEquals('draft', $oldValues['status']);
302+
$this->assertNull($oldValues['published_at']);
303+
304+
// Excluded fields should not be logged even if changed
305+
$this->assertArrayNotHasKey(
306+
'content',
307+
$newValues,
308+
'Content field should not be audited in new values'
309+
);
310+
311+
$this->assertArrayNotHasKey(
312+
'content',
313+
$oldValues,
314+
'Content field should not be audited in old values'
315+
);
316+
}
317+
318+
public function test_post_model_inclusion_with_partial_changes(): void
319+
{
320+
// Create a user first
321+
$user = User::create([
322+
'name' => 'Post Author',
323+
'email' => '[email protected]',
324+
'password' => 'password',
325+
'is_active' => true,
326+
]);
327+
328+
// Create a post
329+
$post = Post::create([
330+
'user_id' => $user->id,
331+
'title' => 'Test Title',
332+
'content' => 'Test content',
333+
'status' => 'draft',
334+
'published_at' => null,
335+
]);
336+
337+
// Clear the creation audit log
338+
DB::table('audit_posts_logs')->delete();
339+
340+
// Update only non-included fields (should not create audit log if no included fields change)
341+
$post->content = 'Updated content that is not audited';
342+
$post->save();
343+
344+
// Check if an audit log was created
345+
$auditLogCount = DB::table('audit_posts_logs')
346+
->where('entity_id', $post->id)
347+
->where('action', 'updated')
348+
->count();
349+
350+
// Since only non-included fields changed, no audit log should be created
351+
$this->assertEquals(0, $auditLogCount, 'No audit log should be created when only non-included fields change');
352+
353+
// Now update an included field
354+
$post->status = 'published';
355+
$post->save();
356+
357+
// Now an audit log should be created
358+
$auditLogCount = DB::table('audit_posts_logs')
359+
->where('entity_id', $post->id)
360+
->where('action', 'updated')
361+
->count();
362+
363+
$this->assertEquals(1, $auditLogCount, 'Audit log should be created when included fields change');
364+
}
365+
366+
public function test_model_with_wildcard_include_respects_exclusions(): void
367+
{
368+
// Test that a model without explicit auditInclude (defaults to ['*']) still respects exclusions
369+
// The User model doesn't have auditInclude, so it should include all fields except excluded ones
370+
371+
$user = User::create([
372+
'name' => 'Wildcard Test User',
373+
'email' => '[email protected]',
374+
'password' => 'secret-password',
375+
'is_active' => true,
376+
]);
377+
378+
// Get the audit log
379+
$auditLog = DB::table('audit_users_logs')
380+
->where('entity_id', $user->id)
381+
->where('action', 'created')
382+
->first();
383+
384+
$this->assertNotNull($auditLog);
385+
386+
$newValues = json_decode($auditLog->new_values, true);
387+
388+
// With wildcard include (['*']), all fields should be included except excluded ones
389+
$this->assertArrayHasKey('name', $newValues);
390+
$this->assertArrayHasKey('email', $newValues);
391+
$this->assertArrayHasKey('is_active', $newValues);
392+
393+
// But excluded fields should still be excluded
394+
$this->assertArrayNotHasKey('password', $newValues);
395+
$this->assertArrayNotHasKey('remember_token', $newValues);
396+
$this->assertArrayNotHasKey('created_at', $newValues); // Global exclusion
397+
$this->assertArrayNotHasKey('updated_at', $newValues); // Global exclusion
398+
}
399+
}

0 commit comments

Comments
 (0)