Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F117914198
D3989.1775402885.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
Unknown
Size
26 KB
Referenced Files
None
Subscribers
None
D3989.1775402885.diff
View Options
diff --git a/docker/proxy/rootfs/etc/nginx/nginx.conf b/docker/proxy/rootfs/etc/nginx/nginx.conf
--- a/docker/proxy/rootfs/etc/nginx/nginx.conf
+++ b/docker/proxy/rootfs/etc/nginx/nginx.conf
@@ -140,7 +140,9 @@
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
- rewrite ^/\\.well-known/(caldav|carddav) https://\$server_name/iRony/ redirect;
+ location ~ ^/\\.well-known/(caldav|carddav)(.*)$ {
+ return 301 /iRony/$2;
+ }
location /iRony {
auth_request /auth;
diff --git a/src/config/activesync.php b/src/config/activesync.php
new file mode 100644
--- /dev/null
+++ b/src/config/activesync.php
@@ -0,0 +1,5 @@
+<?php
+
+return [
+ 'uri' => env('ACTIVESYNC_URI', 'https://proxy/Microsoft-Server-ActiveSync'),
+];
diff --git a/src/config/dav.php b/src/config/dav.php
--- a/src/config/dav.php
+++ b/src/config/dav.php
@@ -1,5 +1,5 @@
<?php
return [
- 'uri' => env('DAV_URI', 'http://kolab/dav'),
+ 'uri' => env('DAV_URI', 'https://proxy/'),
];
diff --git a/src/tests/Infrastructure/ActivesyncTest.php b/src/tests/Infrastructure/ActivesyncTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/ActivesyncTest.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+use App\Backends\IMAP;
+use App\Backends\LDAP;
+use App\User;
+
+class ActivesyncTest extends TestCase
+{
+ private ?\GuzzleHttp\Client $client = null;
+ private ?User $user = null;
+
+ private static function toWbxml($xml)
+ {
+ $outputStream = fopen("php://temp", 'r+');
+ $encoder = new \Syncroton_Wbxml_Encoder($outputStream, 'UTF-8', 3);
+ $dom = new \DOMDocument();
+ $dom->loadXML($xml);
+ $encoder->encode($dom);
+ rewind($outputStream);
+ return stream_get_contents($outputStream);
+ }
+
+ private static function fromWbxml($binary)
+ {
+ $stream = fopen('php://memory', 'r+');
+ fwrite($stream, $binary);
+ rewind($stream);
+ $decoder = new \Syncroton_Wbxml_Decoder($stream);
+ return $decoder->decode();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!$this->user) {
+ $user = $this->getTestUser('jane@kolab.org');
+ $user->password = "simple123";
+ $user->save();
+ IMAP::createUser($user);
+ LDAP::createUser($user);
+ $this->user = $user;
+ }
+
+ if (!$this->client) {
+ $this->client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => \config("activesync.uri"),
+ 'verify' => false,
+ 'auth' => [$this->user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ 'headers' => [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public static function tearDownAfterClass(): void
+ {
+ // $user = User::withTrashed()->where('email', 'jane@kolab.org')->first();
+ // IMAP::deleteUser($user);
+ // LDAP::deleteUser($user);
+ // $user->forceDelete();
+ }
+
+ public function testOptions()
+ {
+ $response = $this->client->request('OPTIONS', '');
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertStringContainsString('14', $response->getHeader('MS-Server-ActiveSync')[0]);
+ $this->assertStringContainsString('14.1', $response->getHeader('MS-ASProtocolVersions')[0]);
+ $this->assertStringContainsString('FolderSync', $response->getHeader('MS-ASProtocolCommands')[0]);
+ }
+
+ public function testFolderList()
+ {
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <FolderSync xmlns="uri:FolderHierarchy">
+ <SyncKey>0</SyncKey>
+ </FolderSync>
+ EOF;
+ $body = self::toWbxml($request);
+ $response = $this->client->request(
+ 'POST',
+ "?Cmd=FolderSync&User={$this->user->email}&DeviceId=v140Device&DeviceType=iphone",
+ [
+ 'headers' => [
+ "Content-Type" => "application/vnd.ms-sync.wbxml",
+ 'MS-ASProtocolVersion' => "14.0"
+ ],
+ 'body' => $body
+ ]
+ );
+ $this->assertEquals(200, $response->getStatusCode());
+ $result = self::fromWbxml($response->getBody())->saveXML();
+
+ print($result);
+ $this->assertStringContainsString('INBOX', $result);
+ //TODO for this to work we need to create the default folders in IMAP::createUser
+ // $this->assertStringContainsString('Drafts', $result);
+ // $this->assertStringContainsString('Sent', $result);
+ // $this->assertStringContainsString('Trash', $result);
+ // $this->assertStringContainsString('Calendar', $result);
+ // $this->assertStringContainsString('Contacts', $result);
+
+ //TODO extract the collectionid for the next step
+ }
+
+ public function testInitialSync()
+ {
+ $request = <<<EOF
+ <?xml version="1.0" encoding="utf-8"?>
+ <!DOCTYPE AirSync PUBLIC "-//AIRSYNC//DTD AirSync//EN" "http://www.microsoft.com/">
+ <Sync xmlns="uri:AirSync" xmlns:AirSyncBase="uri:AirSyncBase">
+ <Collections>
+ <Collection>
+ <SyncKey>0</SyncKey>
+ <CollectionId>38b950ebd62cd9a66929c89615d0fc04</CollectionId>
+ <DeletesAsMoves>0</DeletesAsMoves>
+ <GetChanges>0</GetChanges>
+ <WindowSize>512</WindowSize>
+ <Options>
+ <FilterType>0</FilterType>
+ <BodyPreference xmlns="uri:AirSyncBase">
+ <Type>1</Type>
+ <AllOrNone>1</AllOrNone>
+ </BodyPreference>
+ </Options>
+ </Collection>
+ </Collections>
+ <WindowSize>16</WindowSize>
+ </Sync>
+ EOF;
+ $body = self::toWbxml($request);
+ $response = $this->client->request(
+ 'POST',
+ "?Cmd=Sync&User={$this->user->email}&DeviceId=v140Device&DeviceType=iphone",
+ [
+ 'headers' => [
+ "Content-Type" => "application/vnd.ms-sync.wbxml",
+ 'MS-ASProtocolVersion' => "14.0"
+ ],
+ 'body' => $body
+ ]
+ );
+ $this->assertEquals(200, $response->getStatusCode());
+ $dom = self::fromWbxml($response->getBody());
+ $collections = $dom->getElementsByTagName('Collection');
+ $this->assertEquals(1, $collections->length);
+ $collection = $collections->item(0);
+ $this->assertEquals("Class", $collection->childNodes->item(0)->nodeName);
+ $this->assertEquals("Email", $collection->childNodes->item(0)->nodeValue);
+ $this->assertEquals("SyncKey", $collection->childNodes->item(1)->nodeName);
+ $this->assertEquals("1", $collection->childNodes->item(1)->nodeValue);
+ $this->assertEquals("Status", $collection->childNodes->item(3)->nodeName);
+ $this->assertEquals("1", $collection->childNodes->item(3)->nodeValue);
+ }
+}
diff --git a/src/tests/Infrastructure/AutodiscoverTest.php b/src/tests/Infrastructure/AutodiscoverTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/AutodiscoverTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class AutodiscoverTest extends TestCase
+{
+ private \GuzzleHttp\Client $client;
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => "http://roundcube/",
+ 'verify' => false,
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ ]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ }
+
+ public function testWellKnownOutlook()
+ {
+ $body = <<<EOF
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006">
+ <Request>
+ <EMailAddress>admin@example.local</EMailAddress>
+ <AcceptableResponseSchema>
+ http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a
+ </AcceptableResponseSchema>
+ </Request>
+ </Autodiscover>
+ EOF;
+ $response = $this->client->request('POST', 'autodiscover/autodiscover.xml', [
+ 'headers' => [
+ "Content-Type" => "text/xml; charset=utf-8"
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $data = $response->getBody();
+ $this->assertTrue(str_contains($data, '<Server>example.local</Server>'));
+ $this->assertTrue(str_contains($data, 'admin@example.local'));
+ }
+
+ public function testWellKnownActivesync()
+ {
+ $body = <<<EOF
+ <Autodiscover xmlns="http://schemas.microsoft.com/exchange/autodiscover/mobilesync/requestschema/2006">
+ <Request>
+ <EMailAddress>admin@example.local</EMailAddress>
+ <AcceptableResponseSchema>
+ http://schemas.microsoft.com/exchange/autodiscover/mobilesync/responseschema/2006
+ </AcceptableResponseSchema>
+ </Request>
+ </Autodiscover>
+ EOF;
+ $response = $this->client->request('POST', 'autodiscover/autodiscover.xml', [
+ 'headers' => [
+ "Content-Type" => "text/xml; charset=utf-8"
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $data = $response->getBody();
+ $this->assertTrue(str_contains($data, '<Url>https://example.local/Microsoft-Server-ActiveSync</Url>'));
+ $this->assertTrue(str_contains($data, 'admin@example.local'));
+ }
+
+ public function testWellKnownMail()
+ {
+ $response = $this->client->request(
+ 'GET',
+ '.well-known/autoconfig/mail/config-v1.1.xml?emailaddress=fred@example.com'
+ );
+ $this->assertEquals($response->getStatusCode(), 200);
+ }
+}
diff --git a/src/tests/Infrastructure/ChwalaTest.php b/src/tests/Infrastructure/ChwalaTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/ChwalaTest.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+
+class ChwalaTest extends TestCase
+{
+ private \GuzzleHttp\Client $client;
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ //FIXME test users are probably not created in imap,
+ //which is why this fails. So we're using john@kolab.org for now.
+ // $user = $this->getTestUser('jane@kolabnow.com');
+ // $user->password = "simple123";
+ // $user->save();
+
+ $this->client = new \GuzzleHttp\Client([
+ 'base_uri' => "http://roundcube/chwala/",
+ 'verify' => false,
+ // 'auth' => [$user->email, $user->password],
+ 'auth' => ['john@kolab.org', 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10
+ ]);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ // $this->deleteTestUser('jane@kolabnow.com');
+ parent::tearDown();
+ }
+
+ public function testAccess()
+ {
+ $response = $this->client->request('GET', 'api/?method=authenticate&version=4');
+ $this->assertEquals($response->getStatusCode(), 200);
+ $json = json_decode($response->getBody(), true);
+
+ $this->assertEquals('OK', $json['status']);
+ $token = $json['result']['token'];
+ $this->assertTrue(!empty($token));
+
+ //FIXME the session token doesn't seem to be required here?
+ $response = $this->client->request('GET', 'api/?method=mimetypes', [
+ 'headers' => [
+ 'X-Session_token' => $token
+ ]
+ ]);
+ $this->assertEquals($response->getStatusCode(), 200);
+ $json = json_decode($response->getBody(), true);
+ $this->assertEquals('OK', $json['status']);
+ $this->assertEquals('OK', $json['status']);
+ $this->assertContains('image/png', $json['result']['view']);
+ $this->assertArrayHasKey('text/plain', $json['result']['edit']);
+ }
+}
diff --git a/src/tests/Infrastructure/DavTest.php b/src/tests/Infrastructure/DavTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/DavTest.php
@@ -0,0 +1,304 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\TestCase;
+use App\Backends\IMAP;
+use App\Backends\LDAP;
+use App\User;
+
+class DavTest extends TestCase
+{
+ private ?\GuzzleHttp\Client $client = null;
+ private ?User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+
+ if (!$this->user) {
+ $user = $this->getTestUser('jane@kolab.org');
+ $user->password = "simple123";
+ $user->save();
+ IMAP::createUser($user);
+ LDAP::createUser($user);
+ $this->user = $user;
+ }
+
+ if (!$this->client) {
+ $this->client = new \GuzzleHttp\Client([
+ 'http_errors' => false, // No exceptions
+ 'base_uri' => \config("dav.uri"),
+ 'verify' => false,
+ 'auth' => [$this->user->email, 'simple123'],
+ 'connect_timeout' => 10,
+ 'timeout' => 10,
+ 'headers' => [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ ]
+ ]);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public static function tearDownAfterClass(): void
+ {
+ // $user = User::withTrashed()->where('email', 'jane@kolab.org')->first();
+ // IMAP::deleteUser($user);
+ // LDAP::deleteUser($user);
+ // $user->forceDelete();
+ }
+
+ public function testDiscoverPrincipal()
+ {
+ $user = $this->user;
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $response = $this->client->request('PROPFIND', '/iRony/', ['body' => $body]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/principals/{$user->email}/</d:href>", $data);
+ $this->assertStringContainsString('<d:href>/iRony/calendars/</d:href>', $data);
+ $this->assertStringContainsString('<d:href>/iRony/addressbooks/</d:href>', $data);
+ }
+
+ /**
+ * This codepath is triggerd by MacOS CalDAV when it tries to login.
+ * Verify we don't crash and end up with a 500 status code.
+ */
+ public function testFailingLogin()
+ {
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $headers = [
+ "Content-Type" => "application/xml; charset=utf-8",
+ "Depth" => "1",
+ 'body' => $body,
+ 'auth' => ['invaliduser@kolab.org', 'invalid']
+ ];
+
+ $response = $this->client->request('PROPFIND', '/iRony/', $headers);
+ $this->assertEquals(403, $response->getStatusCode());
+ }
+
+ /**
+ * This codepath is triggerd by MacOS CardDAV when it tries to login.
+ * NOTE: This depends on the username_domain roundcube config option.
+ */
+ public function testShortlogin()
+ {
+ $this->markTestSkipped(
+ 'Shortlogins dont work with the nginx proxy.'
+ );
+ $body = "<d:propfind xmlns:d='DAV:'><d:prop><d:current-user-principal/></d:prop></d:propfind>";
+ $response = $this->client->request('PROPFIND', '/iRony/', [
+ 'body' => $body,
+ 'auth' => ['jane', 'simple123']
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ }
+
+ public function testDiscoverCalendarHomeset()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <c:calendar-home-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', '/iRony/', ['body' => $body]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+ }
+
+ public function testDiscoverCalendars()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', "/iRony/calendars/{$user->email}", [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+
+ $doc = new \DOMDocument('1.0', 'UTF-8');
+ $doc->loadXML($data);
+ $response = $doc->getElementsByTagName('response')->item(1);
+ $doc->getElementsByTagName('href')->item(0);
+
+ $this->assertEquals("d:href", $response->childNodes->item(0)->nodeName);
+ $href = $response->childNodes->item(0)->nodeValue;
+ return $href;
+ }
+
+ /**
+ * @depends testDiscoverCalendars
+ */
+ public function testPropfindCalendar($href)
+ {
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:owner/>
+ <d:current-user-principal/>
+ <d:current-user-privilege-set/>
+ <d:supported-report-set/>
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', $href, [
+ 'headers' => [
+ "Depth" => "0",
+ ],
+ 'body' => $body,
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>$href</d:href>", $data);
+ }
+
+ /**
+ * Thunderbird does this and relies on the WWW-Authenticate header response to
+ * start sending authenticated requests.
+ *
+ * @depends testDiscoverCalendars
+ */
+ public function testPropfindCalendarWithoutAuth($href)
+ {
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:owner/>
+ <d:current-user-principal/>
+ <d:current-user-privilege-set/>
+ <d:supported-report-set/>
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('PROPFIND', $href, [
+ 'headers' => [
+ "Depth" => "0",
+ ],
+ 'body' => $body,
+ 'auth' => []
+ ]);
+ $this->assertEquals(401, $response->getStatusCode());
+ $this->assertStringContainsString('Basic realm=', $response->getHeader('WWW-Authenticate')[0]);
+ $data = $response->getBody();
+ $this->assertStringContainsString("<s:exception>Sabre\DAV\Exception\NotAuthenticated</s:exception>", $data);
+ }
+
+ /**
+ * Required for MacOS autoconfig
+ */
+ public function testOptions()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ $response = $this->client->request('OPTIONS', "/iRony/principals/{$user->email}/", ['body' => $body]);
+ $this->assertEquals(200, $response->getStatusCode());
+ $this->assertStringContainsString('PROPFIND', $response->getHeader('Allow')[0]);
+ }
+
+ public function testWellKnown()
+ {
+ $user = $this->user;
+ $body = <<<EOF
+ <d:propfind xmlns:d="DAV:" xmlns:cs="http://calendarserver.org/ns/" xmlns:c="urn:ietf:params:xml:ns:caldav">
+ <d:prop>
+ <d:resourcetype />
+ <d:displayname />
+ <cs:getctag />
+ <c:supported-calendar-component-set />
+ </d:prop>
+ </d:propfind>
+ EOF;
+
+ // The base URL needs to work as a redirect
+ $response = $this->client->request('PROPFIND', '/.well-known/caldav', [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(301, $response->getStatusCode());
+ $redirectTarget = $response->getHeader('location')[0];
+ $this->assertEquals(\config('dav.uri') . "iRony/", $redirectTarget);
+
+ // Follow the redirect
+ $response = $this->client->request('PROPFIND', $redirectTarget, [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+
+ // Any URL should result in a redirect to the same path
+ $response = $this->client->request('PROPFIND', "/.well-known/caldav/calendars/{$user->email}", [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(301, $response->getStatusCode());
+ $redirectTarget = $response->getHeader('location')[0];
+ //FIXME we have an extra slash that we don't technically want here
+ $this->assertEquals(\config('dav.uri') . "iRony//calendars/{$user->email}", $redirectTarget);
+
+ // Follow the redirect
+ $response = $this->client->request('PROPFIND', $redirectTarget, [
+ 'headers' => [
+ "Depth" => "infinity",
+ ],
+ 'body' => $body,
+ 'allow_redirects' => false
+ ]);
+ $this->assertEquals(207, $response->getStatusCode());
+ $data = $response->getBody();
+ $this->assertStringContainsString("<d:href>/iRony/calendars/{$user->email}/</d:href>", $data);
+ }
+}
diff --git a/src/tests/Infrastructure/RoundcubeTest.php b/src/tests/Infrastructure/RoundcubeTest.php
new file mode 100644
--- /dev/null
+++ b/src/tests/Infrastructure/RoundcubeTest.php
@@ -0,0 +1,81 @@
+<?php
+
+namespace Tests\Infrastructure;
+
+use Tests\Browser;
+use Tests\TestCaseDusk;
+use App\Backends\IMAP;
+use App\Backends\LDAP;
+use App\User;
+
+class RoundcubeTest extends TestCaseDusk
+{
+ private ?User $user = null;
+
+ /**
+ * {@inheritDoc}
+ */
+ public function setUp(): void
+ {
+ parent::setUp();
+ Browser::$baseUrl = "http://roundcube/roundcubemail/";
+
+ if (!$this->user) {
+ $user = $this->getTestUser('jane@kolab.org');
+ $user->password = "simple123";
+ $user->save();
+ IMAP::createUser($user);
+ LDAP::createUser($user);
+ $this->user = $user;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function tearDown(): void
+ {
+ parent::tearDown();
+ }
+
+ public function testLogin()
+ {
+ $this->browse(function (Browser $browser) {
+ $browser->visit('/')
+ ->type('#rcmloginuser', $this->user->email)
+ ->type('#rcmloginpwd', "simple123")
+ ->press('#rcmloginsubmit')
+ ->waitFor('#logo')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Inbox');
+
+ $browser->press('.contacts')
+ ->waitUntil('!rcmail.busy')
+ ->assertVisible('#directorylist')
+ ->assertVisible('.addressbook.personal')
+ ->assertSee('Contacts');
+
+ $browser->press('.button-calendar')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Calendar');
+
+ //TODO requires the default folders to be created
+ // $browser->press('.button-files')
+ // ->waitUntil('!rcmail.busy')
+ // ->assertSeeIn('#files-folder-list', 'Files');
+
+ $browser->press('.button-notes')
+ ->waitUntil('!rcmail.busy')
+ ->assertSeeIn('#notebooks-content', 'Notes');
+
+ $browser->press('.button-tasklist')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Tasks');
+
+ $browser->press('.settings')
+ ->waitUntil('!rcmail.busy')
+ ->assertSee('Activesync');
+ // print($browser->dump());
+ });
+ }
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Apr 5, 3:28 PM (8 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
18833791
Default Alt Text
D3989.1775402885.diff (26 KB)
Attached To
Mode
D3989: Added infrastructure tests for Activesync/CalDav/WOPI/Roundcube & fixed the well-known redirect for caldav.
Attached
Detach File
Event Timeline