Maintain local ⭤ remote in sync with automatic AT Protocol parity for Laravel (alpha & unstable)
1<?php
2
3namespace SocialDept\AtpParity\Tests\Unit\Sync;
4
5use Carbon\Carbon;
6use SocialDept\AtpParity\Sync\ConflictDetector;
7use SocialDept\AtpParity\Tests\Fixtures\SyncableModel;
8use SocialDept\AtpParity\Tests\Fixtures\TestModel;
9use SocialDept\AtpParity\Tests\Fixtures\TestRecord;
10use SocialDept\AtpParity\Tests\TestCase;
11
12class ConflictDetectorTest extends TestCase
13{
14 private ConflictDetector $detector;
15
16 protected function setUp(): void
17 {
18 parent::setUp();
19 $this->detector = new ConflictDetector();
20 }
21
22 public function test_no_conflict_when_cid_matches(): void
23 {
24 $model = new TestModel([
25 'atp_cid' => 'samecid123',
26 'atp_synced_at' => Carbon::parse('2024-01-15 12:00:00'),
27 'updated_at' => Carbon::parse('2024-01-15 13:00:00'), // local changes
28 ]);
29 $record = new TestRecord(text: 'Remote content');
30
31 $hasConflict = $this->detector->hasConflict($model, $record, 'samecid123');
32
33 $this->assertFalse($hasConflict);
34 }
35
36 public function test_no_conflict_when_no_local_changes(): void
37 {
38 $model = new TestModel([
39 'atp_cid' => 'oldcid',
40 'atp_synced_at' => Carbon::parse('2024-01-15 13:00:00'),
41 'updated_at' => Carbon::parse('2024-01-15 12:00:00'), // updated before sync
42 ]);
43 $record = new TestRecord(text: 'Remote content');
44
45 $hasConflict = $this->detector->hasConflict($model, $record, 'newcid');
46
47 $this->assertFalse($hasConflict);
48 }
49
50 public function test_conflict_when_cid_differs_and_local_changes(): void
51 {
52 $model = new TestModel([
53 'atp_cid' => 'oldcid',
54 'atp_synced_at' => Carbon::parse('2024-01-15 12:00:00'),
55 'updated_at' => Carbon::parse('2024-01-15 13:00:00'), // local changes
56 ]);
57 $record = new TestRecord(text: 'Remote content');
58
59 $hasConflict = $this->detector->hasConflict($model, $record, 'newcid');
60
61 $this->assertTrue($hasConflict);
62 }
63
64 public function test_conflict_when_never_synced(): void
65 {
66 $model = new TestModel([
67 'atp_cid' => 'cid',
68 // No atp_synced_at means never synced, which implies local changes
69 ]);
70 $record = new TestRecord(text: 'Remote');
71
72 $hasConflict = $this->detector->hasConflict($model, $record, 'differentcid');
73
74 $this->assertTrue($hasConflict);
75 }
76
77 public function test_uses_syncs_with_atp_trait_method(): void
78 {
79 $model = new SyncableModel([
80 'atp_cid' => 'oldcid',
81 'atp_synced_at' => Carbon::parse('2024-01-15 12:00:00'),
82 'updated_at' => Carbon::parse('2024-01-15 13:00:00'),
83 ]);
84 $record = new TestRecord(text: 'Remote');
85
86 $hasConflict = $this->detector->hasConflict($model, $record, 'newcid');
87
88 $this->assertTrue($hasConflict);
89 }
90
91 public function test_no_conflict_when_synced_after_update_with_trait(): void
92 {
93 $model = new SyncableModel([
94 'atp_cid' => 'oldcid',
95 'updated_at' => Carbon::parse('2024-01-15 12:00:00'),
96 'atp_synced_at' => Carbon::parse('2024-01-15 13:00:00'),
97 ]);
98 $record = new TestRecord(text: 'Remote');
99
100 $hasConflict = $this->detector->hasConflict($model, $record, 'newcid');
101
102 $this->assertFalse($hasConflict);
103 }
104
105 public function test_no_conflict_without_updated_at(): void
106 {
107 $model = new TestModel([
108 'atp_cid' => 'cid',
109 'atp_synced_at' => Carbon::now(),
110 // No updated_at
111 ]);
112 $record = new TestRecord(text: 'Remote');
113
114 $hasConflict = $this->detector->hasConflict($model, $record, 'newcid');
115
116 $this->assertFalse($hasConflict);
117 }
118}