1
1
Fork 0
mirror of https://code.mensbeam.com/MensBeam/Arsse.git synced 2024-12-22 21:22:40 +00:00

Add export-to-file wrapper for OPML

This commit is contained in:
J. King 2019-04-01 16:54:14 -04:00
parent 35e79d53a9
commit deea294f8a
7 changed files with 128 additions and 0 deletions

View file

@ -86,6 +86,8 @@ abstract class AbstractException extends \Exception {
"Feed/Exception.xmlEntity" => 10512, "Feed/Exception.xmlEntity" => 10512,
"Feed/Exception.subscriptionNotFound" => 10521, "Feed/Exception.subscriptionNotFound" => 10521,
"Feed/Exception.unsupportedFeedFormat" => 10522, "Feed/Exception.unsupportedFeedFormat" => 10522,
"ImportExport/Exception.fileUnwritable" => 10604,
"ImportExport/Exception.fileUncreatable" => 10605,
]; ];
public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) { public function __construct(string $msgID = "", $vars = null, \Throwable $e = null) {

View file

@ -0,0 +1,10 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\ImportExport;
class Exception extends \JKingWeb\Arsse\AbstractException {
}

View file

@ -7,9 +7,13 @@ declare(strict_types=1);
namespace JKingWeb\Arsse\ImportExport; namespace JKingWeb\Arsse\ImportExport;
use JKingWeb\Arsse\Arsse; use JKingWeb\Arsse\Arsse;
use JKingWeb\Arsse\User\Exception as UserException;
class OPML { class OPML {
public function export(string $user, bool $flat = false): string { public function export(string $user, bool $flat = false): string {
if (!Arsse::$user->exists($user)) {
throw new UserException("doesNotExist", ["action" => __FUNCTION__, "user" => $user]);
}
$tags = []; $tags = [];
$folders = []; $folders = [];
$parents = [0 => null]; $parents = [0 => null];
@ -68,4 +72,14 @@ class OPML {
// return the serialization // return the serialization
return $document->saveXML(); return $document->saveXML();
} }
public function exportFile(string $file, string $user, bool $flat = false): bool {
$data = $this->export($user, $flat);
if (!@file_put_contents($file, $data)) {
// if it fails throw an exception
$err = file_exists($file) ? "fileUnwritable" : "fileUncreatable";
throw new Exception($err, ['file' => $file, 'format' => str_replace(__NAMESPACE__."\\", "", __CLASS__)]);
}
return true;
}
} }

View file

@ -155,4 +155,14 @@ return [
'Exception.JKingWeb/Arsse/Feed/Exception.xmlEntity' => 'Refused to parse feed "{url}" because it contains an XXE attack', 'Exception.JKingWeb/Arsse/Feed/Exception.xmlEntity' => 'Refused to parse feed "{url}" because it contains an XXE attack',
'Exception.JKingWeb/Arsse/Feed/Exception.subscriptionNotFound' => 'Unable to find a feed at location "{url}"', 'Exception.JKingWeb/Arsse/Feed/Exception.subscriptionNotFound' => 'Unable to find a feed at location "{url}"',
'Exception.JKingWeb/Arsse/Feed/Exception.unsupportedFeedFormat' => 'Feed "{url}" is of an unsupported format', 'Exception.JKingWeb/Arsse/Feed/Exception.unsupportedFeedFormat' => 'Feed "{url}" is of an unsupported format',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUncreatable' =>
'Insufficient permissions to write {type, select,
OPML {OPML}
other {"{type}"}
} export to file "{file}"',
'Exception.JKingWeb/Arsse/ImportExport/Exception.fileUnwritable' =>
'Insufficient permissions to write {type, select,
OPML {OPML}
other {"{type}"}
} export to existing file "{file}"',
]; ];

View file

@ -79,7 +79,10 @@ OPML_EXPORT_SERIALIZATION;
OPML_EXPORT_SERIALIZATION; OPML_EXPORT_SERIALIZATION;
public function setUp() { public function setUp() {
self::clearData();
Arsse::$db = \Phake::mock(\JKingWeb\Arsse\Database::class); Arsse::$db = \Phake::mock(\JKingWeb\Arsse\Database::class);
Arsse::$user = \Phake::mock(\JKingWeb\Arsse\User::class);
\Phake::when(Arsse::$user)->exists->thenReturn(true);
} }
public function testExportToOpml() { public function testExportToOpml() {
@ -95,4 +98,10 @@ OPML_EXPORT_SERIALIZATION;
\Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags)); \Phake::when(Arsse::$db)->tagSummarize("john.doe@example.com")->thenReturn(new Result($this->tags));
$this->assertXmlStringEqualsXmlString($this->serializationFlat, (new OPML)->export("john.doe@example.com", true)); $this->assertXmlStringEqualsXmlString($this->serializationFlat, (new OPML)->export("john.doe@example.com", true));
} }
public function testExportToOpmlAMissingUser() {
\Phake::when(Arsse::$user)->exists->thenReturn(false);
$this->assertException("doesNotExist", "User");
(new OPML)->export("john.doe@example.com");
}
} }

View file

@ -0,0 +1,82 @@
<?php
/** @license MIT
* Copyright 2017 J. King, Dustin Wilson et al.
* See LICENSE and AUTHORS files for details */
declare(strict_types=1);
namespace JKingWeb\Arsse\TestCase\ImportExport;
use JKingWeb\Arsse\ImportExport\OPML;
use JKingWeb\Arsse\ImportExport\Exception;
use org\bovigo\vfs\vfsStream;
/** @covers \JKingWeb\Arsse\ImportExport\OPML<extended> */
class TestOPMLFile extends \JKingWeb\Arsse\Test\AbstractTest {
protected $vfs;
protected $path;
protected $opml;
public function setUp() {
self::clearData();
// create a mock OPML processor with stubbed underlying import/export routines
$this->opml = \Phake::partialMock(OPML::class);
\Phake::when($this->opml)->export->thenReturn("OPML_FILE");
$this->vfs = vfsStream::setup("root", null, [
'exportGoodFile' => "",
'exportGoodDir' => [],
'exportBadFile' => "",
'exportBadDir' => [],
]);
$this->path = $this->vfs->url()."/";
// make the "bad" entries inaccessible
chmod($this->path."exportBadFile", 0000);
chmod($this->path."exportBadDir", 0000);
}
public function tearDown() {
$this->path = null;
$this->vfs = null;
$this->opml = null;
self::clearData();
}
/** @dataProvider provideFileExports */
public function testExportOpmlToAFile(string $file, string $user, bool $flat, $exp) {
$path = $this->path.$file;
try {
if ($exp instanceof \JKingWeb\Arsse\AbstractException) {
$this->assertException($exp);
$this->opml->exportFile($path, $user, $flat);
} else {
$this->assertSame($exp, $this->opml->exportFile($path, $user, $flat));
$this->assertSame("OPML_FILE", $this->vfs->getChild($file)->getContent());
}
} finally {
\Phake::verify($this->opml)->export($user, $flat);
}
}
public function provideFileExports() {
$createException = new Exception("fileUncreatable");
$writeException = new Exception("fileUnwritable");
return [
["exportGoodFile", "john.doe@example.com", true, true],
["exportGoodFile", "john.doe@example.com", false, true],
["exportGoodFile", "jane.doe@example.com", true, true],
["exportGoodFile", "jane.doe@example.com", false, true],
["exportGoodDir/file", "john.doe@example.com", true, true],
["exportGoodDir/file", "john.doe@example.com", false, true],
["exportGoodDir/file", "jane.doe@example.com", true, true],
["exportGoodDir/file", "jane.doe@example.com", false, true],
["exportBadFile", "john.doe@example.com", true, $writeException],
["exportBadFile", "john.doe@example.com", false, $writeException],
["exportBadFile", "jane.doe@example.com", true, $writeException],
["exportBadFile", "jane.doe@example.com", false, $writeException],
["exportBadDir/file", "john.doe@example.com", true, $createException],
["exportBadDir/file", "john.doe@example.com", false, $createException],
["exportBadDir/file", "jane.doe@example.com", true, $createException],
["exportBadDir/file", "jane.doe@example.com", false, $createException],
];
}
}

View file

@ -115,6 +115,7 @@
</testsuite> </testsuite>
<testsuite name="Import/Export"> <testsuite name="Import/Export">
<file>cases/ImportExport/TestOPML.php</file> <file>cases/ImportExport/TestOPML.php</file>
<file>cases/ImportExport/TestOPMLFile.php</file>
</testsuite> </testsuite>
</testsuites> </testsuites>
</phpunit> </phpunit>