1<?php
2
3namespace Sabre\VObject\Component;
4
5use Sabre\VObject;
6
7class VCardTest extends \PHPUnit_Framework_TestCase {
8
9    /**
10     * @dataProvider validateData
11     */
12    function testValidate($input, $expectedWarnings, $expectedRepairedOutput) {
13
14        $vcard = VObject\Reader::read($input);
15
16        $warnings = $vcard->validate();
17
18        $warnMsg = array();
19        foreach($warnings as $warning) {
20            $warnMsg[] = $warning['message'];
21        }
22
23        $this->assertEquals($expectedWarnings, $warnMsg);
24
25        $vcard->validate(VObject\Component::REPAIR);
26
27        $this->assertEquals(
28            $expectedRepairedOutput,
29            $vcard->serialize()
30        );
31
32    }
33
34    public function validateData() {
35
36        $tests = array();
37
38        // Correct
39        $tests[] = array(
40            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
41            array(),
42            "BEGIN:VCARD\r\nVERSION:4.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
43        );
44
45        // No VERSION
46        $tests[] = array(
47            "BEGIN:VCARD\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
48            array(
49                'VERSION MUST appear exactly once in a VCARD component',
50            ),
51            "BEGIN:VCARD\r\nVERSION:3.0\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
52        );
53
54        // Unknown version
55        $tests[] = array(
56            "BEGIN:VCARD\r\nVERSION:2.2\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
57            array(
58                'Only vcard version 4.0 (RFC6350), version 3.0 (RFC2426) or version 2.1 (icm-vcard-2.1) are supported.',
59            ),
60            "BEGIN:VCARD\r\nVERSION:2.1\r\nFN:John Doe\r\nUID:foo\r\nEND:VCARD\r\n",
61        );
62
63        // No FN
64        $tests[] = array(
65            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n",
66            array(
67                'The FN property must appear in the VCARD component exactly 1 time',
68            ),
69            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nEND:VCARD\r\n",
70        );
71        // No FN, N fallback
72        $tests[] = array(
73            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nEND:VCARD\r\n",
74            array(
75                'The FN property must appear in the VCARD component exactly 1 time',
76            ),
77            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;John;;;;;\r\nFN:John Doe\r\nEND:VCARD\r\n",
78        );
79        // No FN, N fallback, no first name
80        $tests[] = array(
81            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nEND:VCARD\r\n",
82            array(
83                'The FN property must appear in the VCARD component exactly 1 time',
84            ),
85            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nN:Doe;;;;;;\r\nFN:Doe\r\nEND:VCARD\r\n",
86        );
87
88        // No FN, ORG fallback
89        $tests[] = array(
90            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nEND:VCARD\r\n",
91            array(
92                'The FN property must appear in the VCARD component exactly 1 time',
93            ),
94            "BEGIN:VCARD\r\nVERSION:4.0\r\nUID:foo\r\nORG:Acme Co.\r\nFN:Acme Co.\r\nEND:VCARD\r\n",
95        );
96        return $tests;
97
98    }
99
100    function testGetDocumentType() {
101
102        $vcard = new VCard(array(), false);
103        $vcard->VERSION = '2.1';
104        $this->assertEquals(VCard::VCARD21, $vcard->getDocumentType());
105
106        $vcard = new VCard(array(), false);
107        $vcard->VERSION = '3.0';
108        $this->assertEquals(VCard::VCARD30, $vcard->getDocumentType());
109
110        $vcard = new VCard(array(), false);
111        $vcard->VERSION = '4.0';
112        $this->assertEquals(VCard::VCARD40, $vcard->getDocumentType());
113
114        $vcard = new VCard(array(), false);
115        $this->assertEquals(VCard::UNKNOWN, $vcard->getDocumentType());
116    }
117
118    function testPreferredNoPref() {
119
120        $vcard = <<<VCF
121BEGIN:VCARD
122VERSION:3.0
123EMAIL:1@example.org
124EMAIL:2@example.org
125END:VCARD
126VCF;
127
128        $vcard = VObject\Reader::read($vcard);
129        $this->assertEquals('1@example.org', $vcard->preferred('EMAIL')->getValue());
130
131    }
132
133    function testPreferredWithPref() {
134
135        $vcard = <<<VCF
136BEGIN:VCARD
137VERSION:3.0
138EMAIL:1@example.org
139EMAIL;TYPE=PREF:2@example.org
140END:VCARD
141VCF;
142
143        $vcard = VObject\Reader::read($vcard);
144        $this->assertEquals('2@example.org', $vcard->preferred('EMAIL')->getValue());
145
146    }
147
148    function testPreferredWith40Pref() {
149
150        $vcard = <<<VCF
151BEGIN:VCARD
152VERSION:4.0
153EMAIL:1@example.org
154EMAIL;PREF=3:2@example.org
155EMAIL;PREF=2:3@example.org
156END:VCARD
157VCF;
158
159        $vcard = VObject\Reader::read($vcard);
160        $this->assertEquals('3@example.org', $vcard->preferred('EMAIL')->getValue());
161
162    }
163
164    function testPreferredNotFound() {
165
166        $vcard = <<<VCF
167BEGIN:VCARD
168VERSION:4.0
169END:VCARD
170VCF;
171
172        $vcard = VObject\Reader::read($vcard);
173        $this->assertNull($vcard->preferred('EMAIL'));
174
175    }
176
177    function testNoUIDCardDAV() {
178
179        $vcard = <<<VCF
180BEGIN:VCARD
181VERSION:4.0
182FN:John Doe
183END:VCARD
184VCF;
185        $this->assertValidate(
186            $vcard,
187            VCARD::PROFILE_CARDDAV,
188            3,
189            'vCards on CardDAV servers MUST have a UID property.'
190        );
191
192    }
193
194    function testNoUIDNoCardDAV() {
195
196        $vcard = <<<VCF
197BEGIN:VCARD
198VERSION:4.0
199FN:John Doe
200END:VCARD
201VCF;
202        $this->assertValidate(
203            $vcard,
204            0,
205            2,
206            'Adding a UID to a vCard property is recommended.'
207        );
208
209    }
210    function testNoUIDNoCardDAVRepair() {
211
212        $vcard = <<<VCF
213BEGIN:VCARD
214VERSION:4.0
215FN:John Doe
216END:VCARD
217VCF;
218        $this->assertValidate(
219            $vcard,
220            VCARD::REPAIR,
221            1,
222            'Adding a UID to a vCard property is recommended.'
223        );
224
225    }
226
227    function testVCard21CardDAV() {
228
229        $vcard = <<<VCF
230BEGIN:VCARD
231VERSION:2.1
232FN:John Doe
233UID:foo
234END:VCARD
235VCF;
236        $this->assertValidate(
237            $vcard,
238            VCARD::PROFILE_CARDDAV,
239            3,
240            'CardDAV servers are not allowed to accept vCard 2.1.'
241        );
242
243    }
244
245    function testVCard21NoCardDAV() {
246
247        $vcard = <<<VCF
248BEGIN:VCARD
249VERSION:2.1
250FN:John Doe
251UID:foo
252END:VCARD
253VCF;
254        $this->assertValidate(
255            $vcard,
256            0,
257            0
258        );
259
260    }
261
262    function assertValidate($vcf, $options, $expectedLevel, $expectedMessage = null) {
263
264        $vcal = VObject\Reader::read($vcf);
265        $result = $vcal->validate($options);
266
267        $this->assertValidateResult($result, $expectedLevel, $expectedMessage);
268
269    }
270
271    function assertValidateResult($input, $expectedLevel, $expectedMessage = null) {
272
273        $messages = array();
274        foreach($input as $warning) {
275            $messages[] = $warning['message'];
276        }
277
278        if ($expectedLevel === 0) {
279            $this->assertEquals(0, count($input), 'No validation messages were expected. We got: ' . implode(', ', $messages));
280        } else {
281            $this->assertEquals(1, count($input), 'We expected exactly 1 validation message, We got: ' . implode(', ', $messages));
282
283            $this->assertEquals($expectedMessage, $input[0]['message']);
284            $this->assertEquals($expectedLevel, $input[0]['level']);
285        }
286
287    }
288}
289