Merge pull request #187 from creative-commoners/pulls/3.0/subsites-compat

API Update Subsite integration, remove Polyhome variant
This commit is contained in:
Dylan Wagstaff 2017-12-05 15:10:45 +13:00 committed by GitHub
commit 3e491088b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 377 additions and 524 deletions

View File

@ -12,6 +12,8 @@ matrix:
env: DB=MYSQL PHPUNIT_TEST=1 env: DB=MYSQL PHPUNIT_TEST=1
- php: 7.1 - php: 7.1
env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1 env: DB=MYSQL PHPUNIT_COVERAGE_TEST=1
- php: 7.1
env: DB=MYSQL PHPUNIT_TEST=1 SUBSITES=1
- php: 7.2 - php: 7.2
env: DB=MYSQL PHPUNIT_TEST=1 env: DB=MYSQL PHPUNIT_TEST=1
@ -21,7 +23,7 @@ before_script:
- composer validate - composer validate
- composer require --no-update symbiote/silverstripe-queuedjobs ^4.0 - composer require --no-update symbiote/silverstripe-queuedjobs ^4.0
# todo: Add Subsites in - if [[ $SUBSITES ]]; then composer require --no-update silverstripe/subsites 2.0.x-dev; fi
- composer require --no-update silverstripe/installer 4.0.x-dev - composer require --no-update silverstripe/installer 4.0.x-dev
- composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile - composer install --prefer-dist --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile

View File

@ -16,7 +16,6 @@ mappings:
SearchUpdater_ObjectHandler: SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater_ObjectHandler SearchUpdater_ObjectHandler: SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater_ObjectHandler
SearchVariant: SilverStripe\FullTextSearch\Search\Variants\SearchVariant SearchVariant: SilverStripe\FullTextSearch\Search\Variants\SearchVariant
SearchVariant_Caller: SilverStripe\FullTextSearch\Search\Variants\SearchVariant_Caller SearchVariant_Caller: SilverStripe\FullTextSearch\Search\Variants\SearchVariant_Caller
SearchVariantSiteTreeSubsitesPolyhome: SilverStripe\FullTextSearch\Search\Variants\SearchVariantSiteTreeSubsitesPolyhome
SearchVariantSubsites: SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites SearchVariantSubsites: SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites
SearchVariantVersioned: SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned SearchVariantVersioned: SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned
SolrReindexBase: SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexBase SolrReindexBase: SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexBase

View File

@ -13,7 +13,6 @@ Adds support for fulltext search engines like Sphinx and Solr to SilverStripe CM
## Requirements ## Requirements
* SilverStripe 4.0+ * SilverStripe 4.0+
* (optional) [silverstripe-phockito](https://github.com/hafriedlander/silverstripe-phockito) (for testing)
**Note:** For SilverStripe 3.x, please use the [2.x release line](https://github.com/silverstripe/silverstripe-fulltextsearch/tree/2). **Note:** For SilverStripe 3.x, please use the [2.x release line](https://github.com/silverstripe/silverstripe-fulltextsearch/tree/2).

8
_config/tests.yml Normal file
View File

@ -0,0 +1,8 @@
---
Name: fulltextsearchtests
---
SilverStripe\Core\Injector\Injector:
SilverStripe\Dev\State\SapphireTestState:
properties:
States:
fulltextsearch: '%$SilverStripe\FullTextSearch\Tests\State\FullTextSearchState'

View File

@ -1,4 +1,5 @@
<?php <?php
namespace SilverStripe\FullTextSearch\Search; namespace SilverStripe\FullTextSearch\Search;
use SilverStripe\Core\ClassInfo; use SilverStripe\Core\ClassInfo;

View File

@ -4,6 +4,7 @@ namespace SilverStripe\FullTextSearch\Search\Variants;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\Core\ClassInfo; use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Configurable;
use SilverStripe\FullTextSearch\Utils\CombinationsArrayIterator; use SilverStripe\FullTextSearch\Utils\CombinationsArrayIterator;
use ReflectionClass; use ReflectionClass;
@ -13,6 +14,16 @@ use ReflectionClass;
*/ */
abstract class SearchVariant abstract class SearchVariant
{ {
use Configurable;
/**
* Whether this variant is enabled
*
* @config
* @var boolean
*/
private static $enabled = true;
public function __construct() public function __construct()
{ {
} }
@ -30,7 +41,7 @@ abstract class SearchVariant
*/ */
public function appliesToEnvironment() public function appliesToEnvironment()
{ {
return true; return $this->config()->get('enabled');
} }
/** /**
@ -117,6 +128,14 @@ abstract class SearchVariant
} }
} }
/**
* Clear the cached variants
*/
public static function clear_variant_cache()
{
self::$class_variants = [];
}
/** Holds a cache of SearchVariant_Caller instances, one for each class/includeSubclasses setting */ /** Holds a cache of SearchVariant_Caller instances, one for each class/includeSubclasses setting */
protected static $call_instances = array(); protected static $call_instances = array();
@ -175,14 +194,14 @@ abstract class SearchVariant
/** /**
* Activate all the states in the passed argument * Activate all the states in the passed argument
* @static * @static
* @param (array) $state. A set of (string)$variantClass => (any)$state pairs , e.g. as returned by * @param array $state A set of (string)$variantClass => (any)$state pairs , e.g. as returned by
* SearchVariant::current_state() * SearchVariant::current_state()
* @return void * @return void
*/ */
public static function activate_state($state) public static function activate_state($state)
{ {
foreach (self::variants() as $variant => $instance) { foreach (self::variants() as $variant => $instance) {
if (isset($state[$variant])) { if (isset($state[$variant]) && $instance->appliesToEnvironment()) {
$instance->activateState($state[$variant]); $instance->activateState($state[$variant]);
} }
} }

View File

@ -1,103 +0,0 @@
<?php
namespace SilverStripe\FullTextSearch\Search\Variants;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\FullTextSearch\Search\SearchIntrospection;
if (!class_exists('Subsite') || !class_exists('SubsitePolyhome')) {
return;
}
class SearchVariantSiteTreeSubsitesPolyhome extends SearchVariant
{
public function appliesToEnvironment()
{
return class_exists('Subsite') && class_exists('SubsitePolyhome');
}
public function appliesTo($class, $includeSubclasses)
{
return SearchIntrospection::has_extension($class, 'SiteTreeSubsitesPolyhome', $includeSubclasses);
}
public function currentState()
{
return Subsite::currentSubsiteID();
}
public function reindexStates()
{
static $ids = null;
if ($ids === null) {
$ids = array(0);
foreach (DataObject::get('Subsite') as $subsite) {
$ids[] = $subsite->ID;
}
}
return $ids;
}
public function activateState($state)
{
if (Controller::has_curr()) {
Subsite::changeSubsite($state);
} else {
// TODO: This is a nasty hack - calling Subsite::changeSubsite after request ends
// throws error because no current controller to access session on
$_REQUEST['SubsiteID'] = $state;
}
}
public function alterDefinition($class, $index)
{
$self = get_class($this);
$this->addFilterField($index, '_subsite', array(
'name' => '_subsite',
'field' => '_subsite',
'fullfield' => '_subsite',
'base' => DataObject::getSchema()->baseDataClass($class),
'origin' => $class,
'type' => 'Int',
'lookup_chain' => array(array('call' => 'variant', 'variant' => $self, 'method' => 'currentState'))
));
}
public function alterQuery($query, $index)
{
$subsite = Subsite::currentSubsiteID();
$query->filter('_subsite', array($subsite, SearchQuery::$missing));
}
public static $subsites = null;
/**
* We need _really_ complicated logic to find just the changed subsites (because we use versions there's no explicit
* deletes, just new versions with different members) so just always use all of them
*/
public function extractManipulationWriteState(&$writes)
{
$self = get_class($this);
foreach ($writes as $key => $write) {
if (!$this->appliesTo($write['class'], true)) {
continue;
}
if (self::$subsites === null) {
$query = new SQLSelect('ID', 'Subsite');
self::$subsites = array_merge(array('0'), $query->execute()->column());
}
$next = array();
foreach ($write['statefulids'] as $i => $statefulid) {
foreach (self::$subsites as $subsiteID) {
$next[] = array('id' => $statefulid['id'], 'state' => array_merge($statefulid['state'], array($self => $subsiteID)));
}
}
$writes[$key]['statefulids'] = $next;
}
}
}

View File

@ -2,13 +2,21 @@
namespace SilverStripe\FullTextSearch\Search\Variants; namespace SilverStripe\FullTextSearch\Search\Variants;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\FullTextSearch\Search\SearchIntrospection;
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
use SilverStripe\ORM\Queries\SQLSelect; use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Permission; use SilverStripe\Security\Permission;
use SilverStripe\FullTextSearch\Search\SearchIntrospection; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery; use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Subsites\Extensions\GroupSubsites;
use SilverStripe\Subsites\Extensions\FileSubsites;
use SilverStripe\Subsites\Extensions\SiteConfigSubsites;
use SilverStripe\Subsites\State\SubsiteState;
if (!class_exists('Subsite')) { if (!class_exists(Subsite::class)) {
return; return;
} }
@ -16,22 +24,26 @@ class SearchVariantSubsites extends SearchVariant
{ {
public function appliesToEnvironment() public function appliesToEnvironment()
{ {
return class_exists('Subsite'); return class_exists(Subsite::class) && parent::appliesToEnvironment();
} }
public function appliesTo($class, $includeSubclasses) public function appliesTo($class, $includeSubclasses)
{ {
if (!$this->appliesToEnvironment()) {
return false;
}
// Include all DataExtensions that contain a SubsiteID. // Include all DataExtensions that contain a SubsiteID.
// TODO: refactor subsites to inherit a common interface, so we can run introspection once only. // TODO: refactor subsites to inherit a common interface, so we can run introspection once only.
return SearchIntrospection::has_extension($class, 'SiteTreeSubsites', $includeSubclasses) || return SearchIntrospection::has_extension($class, SiteTreeSubsites::class, $includeSubclasses)
SearchIntrospection::has_extension($class, 'GroupSubsites', $includeSubclasses) || || SearchIntrospection::has_extension($class, GroupSubsites::class, $includeSubclasses)
SearchIntrospection::has_extension($class, 'FileSubsites', $includeSubclasses) || || SearchIntrospection::has_extension($class, FileSubsites::class, $includeSubclasses)
SearchIntrospection::has_extension($class, 'SiteConfigSubsites', $includeSubclasses); || SearchIntrospection::has_extension($class, SiteConfigSubsites::class, $includeSubclasses);
} }
public function currentState() public function currentState()
{ {
return (string)Subsite::currentSubsiteID(); return (string) SubsiteState::singleton()->getSubsiteId();
} }
public function reindexStates() public function reindexStates()
@ -39,9 +51,9 @@ class SearchVariantSubsites extends SearchVariant
static $ids = null; static $ids = null;
if ($ids === null) { if ($ids === null) {
$ids = array('0'); $ids = ['0'];
foreach (DataObject::get('Subsite') as $subsite) { foreach (Subsite::get() as $subsite) {
$ids[] = (string)$subsite->ID; $ids[] = (string) $subsite->ID;
} }
} }
@ -50,26 +62,34 @@ class SearchVariantSubsites extends SearchVariant
public function activateState($state) public function activateState($state)
{ {
// We always just set the $_GET variable rather than store in Session - this always works, has highest priority if (!$this->appliesToEnvironment()) {
// in Subsite::currentSubsiteID() and doesn't persist unlike Subsite::changeSubsite return;
$_GET['SubsiteID'] = $state; }
Permission::flush_permission_cache();
// Note: Setting directly to the SubsiteState because we don't want the subsite ID to be persisted
// like Subsite::changeSubsite would do.
SubsiteState::singleton()->setSubsiteId($state);
Permission::reset();
} }
public function alterDefinition($class, $index) public function alterDefinition($class, $index)
{ {
$self = get_class($this); $self = get_class($this);
if (!$this->appliesTo($class, true)) {
return;
}
// Add field to root // Add field to root
$this->addFilterField($index, '_subsite', array( $this->addFilterField($index, '_subsite', [
'name' => '_subsite', 'name' => '_subsite',
'field' => '_subsite', 'field' => '_subsite',
'fullfield' => '_subsite', 'fullfield' => '_subsite',
'base' => DataObject::getSchema()->baseDataClass($class), 'base' => DataObject::getSchema()->baseDataClass($class),
'origin' => $class, 'origin' => $class,
'type' => 'Int', 'type' => 'Int',
'lookup_chain' => array(array('call' => 'variant', 'variant' => $self, 'method' => 'currentState')) 'lookup_chain' => [['call' => 'variant', 'variant' => $self, 'method' => 'currentState']],
)); ]);
} }
/** /**
@ -83,12 +103,12 @@ class SearchVariantSubsites extends SearchVariant
*/ */
public function alterQuery($query, $index) public function alterQuery($query, $index)
{ {
if ($this->isFieldFiltered('_subsite', $query)) { if ($this->isFieldFiltered('_subsite', $query) || !$this->appliesToEnvironment()) {
return; return;
} }
$subsite = Subsite::currentSubsiteID(); $subsite = $this->currentState();
$query->filter('_subsite', array($subsite, SearchQuery::$missing)); $query->filter('_subsite', [$subsite, SearchQuery::$missing]);
} }
/** /**
@ -98,8 +118,9 @@ class SearchVariantSubsites extends SearchVariant
public function extractManipulationWriteState(&$writes) public function extractManipulationWriteState(&$writes)
{ {
$self = get_class($this); $self = get_class($this);
$query = new SQLSelect('"ID"', '"Subsite"'); $tableName = DataObject::getSchema()->tableName(Subsite::class);
$subsites = array_merge(array('0'), $query->execute()->column()); $query = SQLSelect::create('"ID"', '"' . $tableName . '"');
$subsites = array_merge(['0'], $query->execute()->column());
foreach ($writes as $key => $write) { foreach ($writes as $key => $write) {
$applies = $this->appliesTo($write['class'], true); $applies = $this->appliesTo($write['class'], true);
@ -107,25 +128,27 @@ class SearchVariantSubsites extends SearchVariant
continue; continue;
} }
if (isset($write['fields']['SiteTree:SubsiteID'])) { if (isset($write['fields'][SiteTree::class . ':SubsiteID'])) {
$subsitesForWrite = array($write['fields']['SiteTree:SubsiteID']); $subsitesForWrite = [$write['fields'][SiteTree::class . ':SubsiteID']];
} // files in subsite 0 should be in all subsites as they are global } elseif (isset($write['fields'][File::class . ':SubsiteID'])
elseif (isset($write['fields']['File:SubsiteID']) && intval($write['fields']['File:SubsiteID']) !== 0) { && (int) $write['fields'][File::class . ':SubsiteID'] !== 0
$subsitesForWrite = array($write['fields']['File:SubsiteID']); ) {
// files in subsite 0 should be in all subsites as they are global
$subsitesForWrite = [$write['fields'][File::class . ':SubsiteID']];
} else { } else {
$subsitesForWrite = $subsites; $subsitesForWrite = $subsites;
} }
$next = array(); $next = [];
foreach ($write['statefulids'] as $i => $statefulid) { foreach ($write['statefulids'] as $i => $statefulid) {
foreach ($subsitesForWrite as $subsiteID) { foreach ($subsitesForWrite as $subsiteID) {
$next[] = array( $next[] = [
'id' => $statefulid['id'], 'id' => $statefulid['id'],
'state' => array_merge( 'state' => array_merge(
$statefulid['state'], $statefulid['state'],
array($self => (string)$subsiteID) [$self => (string) $subsiteID]
) ),
); ];
} }
} }
$writes[$key]['statefulids'] = $next; $writes[$key]['statefulids'] = $next;

View File

@ -12,6 +12,10 @@ class SearchVariantVersioned extends SearchVariant
{ {
public function appliesTo($class, $includeSubclasses) public function appliesTo($class, $includeSubclasses)
{ {
if (!$this->appliesToEnvironment()) {
return false;
}
return SearchIntrospection::has_extension($class, Versioned::class, $includeSubclasses); return SearchIntrospection::has_extension($class, Versioned::class, $includeSubclasses);
} }
@ -57,8 +61,6 @@ class SearchVariantVersioned extends SearchVariant
public function extractManipulationState(&$manipulation) public function extractManipulationState(&$manipulation)
{ {
$self = get_class($this);
foreach ($manipulation as $table => $details) { foreach ($manipulation as $table => $details) {
$class = $details['class']; $class = $details['class'];
$stage = Versioned::DRAFT; $stage = Versioned::DRAFT;
@ -70,7 +72,7 @@ class SearchVariantVersioned extends SearchVariant
if (ClassInfo::exists($class) && $this->appliesTo($class, false)) { if (ClassInfo::exists($class) && $this->appliesTo($class, false)) {
$manipulation[$table]['class'] = $class; $manipulation[$table]['class'] = $class;
$manipulation[$table]['state'][$self] = $stage; $manipulation[$table]['state'][get_class($this)] = $stage;
} }
} }
} }
@ -83,10 +85,9 @@ class SearchVariantVersioned extends SearchVariant
if (ClassInfo::exists($class) && $this->appliesTo($class, false)) { if (ClassInfo::exists($class) && $this->appliesTo($class, false)) {
$table = $class; $table = $class;
$self = get_class($this);
foreach ($ids as $i => $statefulid) { foreach ($ids as $i => $statefulid) {
$ids[$i]['state'][$self] = $suffix ? $suffix : Versioned::DRAFT; $ids[$i]['state'][get_class($this)] = $suffix ?: Versioned::DRAFT;
} }
} }
} }

View File

@ -1,4 +1,5 @@
<?php <?php
namespace SilverStripe\FullTextSearch\Solr\Services; namespace SilverStripe\FullTextSearch\Solr\Services;
class Solr4Service extends SolrService class Solr4Service extends SolrService

View File

@ -2,12 +2,11 @@
namespace SilverStripe\FullTextSearch\Tests; namespace SilverStripe\FullTextSearch\Tests;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\FullTextSearch\Search\FullTextSearch; use SilverStripe\FullTextSearch\Search\FullTextSearch;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\Core\Config\Config;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessor_QueuedJobService; use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessor_QueuedJobService;
use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessorTest_Index; use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessorTest_Index;
use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessorTest_Object; use SilverStripe\FullTextSearch\Tests\BatchedProcessorTest\BatchedProcessorTest_Object;
@ -16,6 +15,9 @@ use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateQueuedJobProcessor
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateBatchedProcessor; use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateBatchedProcessor;
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater; use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned; use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\Versioned\Versioned;
use Symbiote\QueuedJobs\Services\QueuedJob;
use Symbiote\QueuedJobs\Services\QueuedJobService; use Symbiote\QueuedJobs\Services\QueuedJobService;
/** /**
@ -29,37 +31,30 @@ class BatchedProcessorTest extends SapphireTest
BatchedProcessorTest_Object::class BatchedProcessorTest_Object::class
); );
protected $illegalExtensions = array( protected static $illegal_extensions = [
'SiteTree' => array( SiteTree::class => [
'SiteTreeSubsites', SiteTreeSubsites::class,
'Translatable' ],
) ];
);
public function setUpOnce() public static function setUpBeforeClass()
{ {
// Disable illegal extensions if skipping this test // Disable illegal extensions if skipping this test
if (class_exists('Subsite') || !interface_exists('Symbiote\QueuedJobs\Services\QueuedJob')) { if (class_exists(Subsite::class) || !interface_exists(QueuedJob::class)) {
$this->illegalExtensions = array(); static::$illegal_extensions = [];
} }
parent::setUpOnce(); parent::setUpBeforeClass();
} }
protected function setUp() protected function setUp()
{ {
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false);
parent::setUp(); parent::setUp();
if (!interface_exists('Symbiote\QueuedJobs\Services\QueuedJob')) { if (!interface_exists(QueuedJob::class)) {
$this->skipTest = true;
$this->markTestSkipped("These tests need the QueuedJobs module installed to run"); $this->markTestSkipped("These tests need the QueuedJobs module installed to run");
} }
Config::modify()->set(QueuedJobService::class, 'use_shutdown_function', false); if (class_exists(Subsite::class)) {
if (class_exists('Subsite')) {
$this->skipTest = true;
$this->markTestSkipped(get_class() . ' skipped when running with subsites'); $this->markTestSkipped(get_class() . ' skipped when running with subsites');
} }
@ -69,7 +64,7 @@ class BatchedProcessorTest extends SapphireTest
Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_soft_cap', 0); Config::modify()->set(SearchUpdateBatchedProcessor::class, 'batch_soft_cap', 0);
Config::modify()->set(SearchUpdateCommitJobProcessor::class, 'cooldown', 600); Config::modify()->set(SearchUpdateCommitJobProcessor::class, 'cooldown', 600);
Versioned::set_stage("Stage"); Versioned::set_stage(Versioned::DRAFT);
Injector::inst()->registerService(new BatchedProcessor_QueuedJobService(), QueuedJobService::class); Injector::inst()->registerService(new BatchedProcessor_QueuedJobService(), QueuedJobService::class);

View File

@ -22,8 +22,6 @@ class SearchUpdaterTest extends SapphireTest
protected function setUp() protected function setUp()
{ {
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false);
parent::setUp(); parent::setUp();
if (self::$index === null) { if (self::$index === null) {

View File

@ -1,74 +0,0 @@
<?php
namespace SilverStripe\FullTextSearch\Tests;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
use SilverStripe\FullTextSearch\Tests\SearchVariantSiteTreeSubsitesPolyhomeTest\SearchVariantSiteTreeSubsitesPolyhomeTest_Index;
use SilverStripe\FullTextSearch\Tests\SearchVariantSiteTreeSubsitesPolyhomeTest\SearchVariantSiteTreeSubsitesPolyhomeTest_Item;
class SearchVariantSiteTreeSubsitesPolyhomeTest extends SapphireTest
{
private static $index = null;
private static $subsite_a = null;
private static $subsite_b = null;
public function setUp()
{
parent::setUp();
// Check subsites installed
if (!class_exists('Subsite') || !class_exists('SubsitePolyhome')) {
return $this->markTestSkipped('The subsites polyhome module is not installed');
}
if (self::$index === null) {
self::$index = singleton('SearchVariantSiteTreeSubsitesPolyhomeTest_Index');
}
if (self::$subsite_a === null) {
self::$subsite_a = new Subsite();
self::$subsite_a->write();
self::$subsite_b = new Subsite();
self::$subsite_b->write();
}
FullTextSearch::force_index_list(self::$index);
SearchUpdater::clear_dirty_indexes();
}
public function testSavingDirect()
{
// Initial add
$item = new SearchVariantSiteTreeSubsitesPolyhomeTest_Item();
$item->write();
SearchUpdater::flush_dirty_indexes();
$this->assertEquals(self::$index->getAdded(array('ID', '_subsite')), array(
array('ID' => $item->ID, '_subsite' => 0)
));
// Check that adding to subsites works
self::$index->reset();
$item->setField('AddToSubsite[0]', 1);
$item->setField('AddToSubsite['.(self::$subsite_a->ID).']', 1);
$item->write();
SearchUpdater::flush_dirty_indexes();
$this->assertEquals(self::$index->getAdded(array('ID', '_subsite')), array(
array('ID' => $item->ID, '_subsite' => 0),
array('ID' => $item->ID, '_subsite' => self::$subsite_a->ID)
));
$this->assertEquals(self::$index->deleted, array(
array('base' => 'SiteTree', 'id' => $item->ID, 'state' => array(
'SearchVariantVersioned' => 'Stage', 'SearchVariantSiteTreeSubsitesPolyhome' => self::$subsite_b->ID
))
));
}
}

View File

@ -1,14 +0,0 @@
<?php
namespace SilverStripe\FullTextSearch\Tests\SearchVariantSiteTreeSubsitesPolyhomeTest;
use SilverStripe\FullTextSearch\Search\Indexes\SearchIndex_Recording;
class SearchVariantSiteTreeSubsitesPolyhomeTest_Index extends SearchIndex_Recording
{
public function init()
{
$this->addClass(SearchVariantSiteTreeSubsitesPolyhomeTest_Item::class);
$this->addFilterField('TestText');
}
}

View File

@ -1,15 +0,0 @@
<?php
namespace SilverStripe\FullTextSearch\Tests\SearchVariantSiteTreeSubsitesPolyhomeTest;
use SilverStripe\CMS\Model\SiteTree;
class SearchVariantSiteTreeSubsitesPolyhomeTest_Item extends SiteTree
{
private static $table_name = 'SearchVariantSiteTreeSubsitesPolyhomeTest_Item';
// TODO: Currently theres a failure if you addClass a non-table class
private static $db = array(
'TestText' => 'Varchar'
);
}

View File

@ -2,32 +2,40 @@
namespace SilverStripe\FullTextSearch\Tests; namespace SilverStripe\FullTextSearch\Tests;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\FullTextSearch\Search\FullTextSearch;
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateProcessor;
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_Container;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex;
use SilverStripe\Subsites\Model\Subsite;
class SearchVariantSubsiteTest extends SapphireTest class SearchVariantSubsiteTest extends SapphireTest
{ {
private static $index = null; private static $index = null;
protected function setUp()
public function setUp()
{ {
parent::setUp(); parent::setUp();
// Check versioned available // Check versioned available
if (!class_exists('Subsite')) { if (!class_exists(Subsite::class)) {
return $this->markTestSkipped('The subsites module is not installed'); return $this->markTestSkipped('The subsites module is not installed');
} }
if (self::$index === null) { if (self::$index === null) {
self::$index = singleton('SearchVariantSubsiteTest'); self::$index = singleton(static::class);
} }
SearchUpdater::bind_manipulation_capture(); SearchUpdater::bind_manipulation_capture();
Config::inst()->update('Injector', 'SearchUpdateProcessor', array( Config::modify()->set(Injector::class, SearchUpdateProcessor::class, [
'class' => 'SearchUpdateImmediateProcessor' 'class' => SearchUpdateImmediateProcessor::class
)); ]);
FullTextSearch::force_index_list(self::$index); FullTextSearch::force_index_list(self::$index);
SearchUpdater::clear_dirty_indexes(); SearchUpdater::clear_dirty_indexes();
@ -41,13 +49,13 @@ class SearchVariantSubsiteTest extends SapphireTest
//typical behaviour: nobody is explicitly filtering on subsite, so the search variant adds a filter to the query //typical behaviour: nobody is explicitly filtering on subsite, so the search variant adds a filter to the query
$this->assertArrayNotHasKey('_subsite', $query->require); $this->assertArrayNotHasKey('_subsite', $query->require);
$variant = new SearchVariantSubsites(); $variant = new SearchVariantSubsites();
$variant->alterDefinition('SearchUpdaterTest_Container', $index); $variant->alterDefinition(SearchUpdaterTest_Container::class, $index);
$variant->alterQuery($query, $index); $variant->alterQuery($query, $index);
//check that the "default" query has been put in place: it's not empty, and we're searching on Subsite ID:0 and //check that the "default" query has been put in place: it's not empty, and we're searching on Subsite ID:0 and
// an object of SearchQuery::missing // an object of SearchQuery::missing
$this->assertNotEmpty($query->require['_subsite']); $this->assertNotEmpty($query->require['_subsite']);
$this->assertEquals(0, $query->require['_subsite'][0]); $this->assertEmpty($query->require['_subsite'][0]);
//check that SearchQuery::missing is set (by default, it is an object of stdClass) //check that SearchQuery::missing is set (by default, it is an object of stdClass)
$this->assertInstanceOf('stdClass', $query->require['_subsite'][1]); $this->assertInstanceOf('stdClass', $query->require['_subsite'][1]);
@ -71,7 +79,7 @@ class SearchVariantSubsiteTest extends SapphireTest
//apply the search variant's definition and query //apply the search variant's definition and query
$variant = new SearchVariantSubsites(); $variant = new SearchVariantSubsites();
$variant->alterDefinition('SearchUpdaterTest_Container', $index); $variant->alterDefinition(SearchUpdaterTest_Container::class, $index);
//the protected function isFieldFiltered is implicitly tested here //the protected function isFieldFiltered is implicitly tested here
$variant->alterQuery($query, $index); $variant->alterQuery($query, $index);

View File

@ -28,8 +28,6 @@ class SearchVariantVersionedTest extends SapphireTest
protected function setUp() protected function setUp()
{ {
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false);
parent::setUp(); parent::setUp();
if (self::$index === null) { if (self::$index === null) {
@ -130,4 +128,15 @@ class SearchVariantVersionedTest extends SapphireTest
array('ID' => $item->ID, '_versionedstage' => 'Live') array('ID' => $item->ID, '_versionedstage' => 'Live')
), $index->getAdded(array('ID', '_versionedstage'))); ), $index->getAdded(array('ID', '_versionedstage')));
} }
public function testCanBeDisabledViaConfig()
{
$variant = new SearchVariantVersioned;
Config::modify()->set(SearchVariantVersioned::class, 'enabled', true);
$this->assertTrue($variant->appliesToEnvironment());
Config::modify()->set(SearchVariantVersioned::class, 'enabled', false);
$this->assertFalse($variant->appliesToEnvironment());
}
} }

View File

@ -2,20 +2,32 @@
namespace SilverStripe\FullTextSearch\Tests; namespace SilverStripe\FullTextSearch\Tests;
use Apache_Solr_Document;
use Page;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Image;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\FullTextSearch\Search\FullTextSearch;
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateImmediateProcessor;
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateProcessor;
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
use SilverStripe\FullTextSearch\Solr\Services\Solr4Service;
use SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest\SolrIndexSubsitesTest_Index; use SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest\SolrIndexSubsitesTest_Index;
use SilverStripe\FullTextSearch\Tests\SolrIndexVersionedTest\SolrDocumentMatcher;
if (class_exists('\Phockito')) { use SilverStripe\ORM\DataObject;
\Phockito::include_hamcrest(false); use SilverStripe\Subsites\Model\Subsite;
} use SilverStripe\Versioned\Versioned;
/** /**
* Subsite specific solr testing * Subsite specific solr testing
*/ */
class SolrIndexSubsitesTest extends SapphireTest class SolrIndexSubsitesTest extends SapphireTest
{ {
// @todo protected static $fixture_file = 'SolrIndexSubsitesTest/SolrIndexSubsitesTest.yml';
// protected static $fixture_file = 'SolrIndexSubsitesTest/SolrIndexSubsitesTest.yml';
/** /**
* @var SolrIndexSubsitesTest_Index * @var SolrIndexSubsitesTest_Index
@ -26,31 +38,28 @@ class SolrIndexSubsitesTest extends SapphireTest
protected function setUp() protected function setUp()
{ {
// Prevent parent::setUp() crashing on db build
if (!class_exists(Subsite::class)) {
static::$fixture_file = null;
}
parent::setUp(); parent::setUp();
// Prevent parent::setUp() crashing on db build if (!class_exists(Subsite::class)) {
if (!class_exists('Subsite')) {
$this->skipTest = true;
$this->markTestSkipped("These tests need the Subsite module installed to run"); $this->markTestSkipped("These tests need the Subsite module installed to run");
} }
$this->server = $_SERVER; $this->server = $_SERVER;
if (!class_exists('\Phockito')) {
$this->skipTest = true;
$this->markTestSkipped("These tests need the \Phockito module installed to run");
return;
}
if (self::$index === null) { if (self::$index === null) {
self::$index = singleton('SolrIndexSubsitesTest_Index'); self::$index = singleton(SolrIndexSubsitesTest_Index::class);
} }
SearchUpdater::bind_manipulation_capture(); SearchUpdater::bind_manipulation_capture();
Config::modify()->set('Injector', 'SearchUpdateProcessor', array( Config::modify()->set(Injector::class, SearchUpdateProcessor::class, [
'class' => 'SearchUpdateImmediateProcessor' 'class' => SearchUpdateImmediateProcessor::class,
)); ]);
FullTextSearch::force_index_list(self::$index); FullTextSearch::force_index_list(self::$index);
SearchUpdater::clear_dirty_indexes(); SearchUpdater::clear_dirty_indexes();
@ -67,7 +76,9 @@ class SolrIndexSubsitesTest extends SapphireTest
protected function getServiceMock() protected function getServiceMock()
{ {
return \Phockito::mock('Solr4Service'); return $this->getMockBuilder(Solr4Service::class)
->setMethods(['addDocument', 'commit'])
->getMock();
} }
/** /**
@ -83,7 +94,9 @@ class SolrIndexSubsitesTest extends SapphireTest
$variants = array(); $variants = array();
// Check subsite // Check subsite
if (class_exists('Subsite') && DataObject::getSchema()->hasOneComponent($object->getClassName(), 'Subsite')) { if (class_exists(Subsite::class)
&& DataObject::getSchema()->hasOneComponent($object->getClassName(), 'Subsite')
) {
$variants[] = '"SearchVariantSubsites":"' . $subsiteID. '"'; $variants[] = '"SearchVariantSubsites":"' . $subsiteID. '"';
} }
@ -91,7 +104,7 @@ class SolrIndexSubsitesTest extends SapphireTest
if ($stage) { if ($stage) {
$variants[] = '"SearchVariantVersioned":"' . $stage . '"'; $variants[] = '"SearchVariantVersioned":"' . $stage . '"';
} }
return $id.'-'.$class.'-{'.implode(',', $variants).'}'; return $id . '-' . $class . '-{' . implode(',', $variants) . '}';
} }
public function testPublishing() public function testPublishing()
@ -100,146 +113,163 @@ class SolrIndexSubsitesTest extends SapphireTest
$serviceMock = $this->getServiceMock(); $serviceMock = $this->getServiceMock();
self::$index->setService($serviceMock); self::$index->setService($serviceMock);
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
// Add records to first subsite // Add records to first subsite
Versioned::reading_stage('Stage'); Versioned::set_stage(Versioned::DRAFT);
$_SERVER['HTTP_HOST'] = 'www.subsite1.com'; $_SERVER['HTTP_HOST'] = 'www.subsite1.com';
\Phockito::reset($serviceMock);
$file = new File(); $file = new File();
$file->Title = 'My File'; $file->Title = 'My File';
$file->SubsiteID = $subsite1->ID; $file->SubsiteID = $subsite1->ID;
$file->write(); $file->write();
$page = new Page(); $page = new Page();
$page->Title = 'My Page'; $page->Title = 'My Page';
$page->SubsiteID = $subsite1->ID; $page->SubsiteID = $subsite1->ID;
$page->write(); $page->write();
SearchUpdater::flush_dirty_indexes();
$doc1 = new SolrDocumentMatcher(array( $doc1 = new Apache_Solr_Document([
'_documentid' => $this->getExpectedDocumentId($page, $subsite1->ID, 'Stage'), '_documentid' => $this->getExpectedDocumentId($page, $subsite1->ID, 'Stage'),
'ClassName' => 'Page', 'ClassName' => 'Page',
'SiteTree_Title' => 'My Page', 'SiteTree_Title' => 'My Page',
'_versionedstage' => 'Stage', '_versionedstage' => 'Stage',
'_subsite' => $subsite1->ID '_subsite' => $subsite1->ID,
)); ]);
$doc2 = new SolrDocumentMatcher(array(
$doc2 = new Apache_Solr_Document([
'_documentid' => $this->getExpectedDocumentId($file, $subsite1->ID), '_documentid' => $this->getExpectedDocumentId($file, $subsite1->ID),
'ClassName' => 'File', 'ClassName' => File::class,
'File_Title' => 'My File', 'File_Title' => 'My File',
'_subsite' => $subsite1->ID '_subsite' => $subsite1->ID,
)); ]);
\Phockito::verify($serviceMock)->addDocument($doc1);
\Phockito::verify($serviceMock)->addDocument($doc2); $serviceMock
->expects($this->exactly(2))
->method('addDocument')
->withConsecutive($doc1, $doc2);
SearchUpdater::flush_dirty_indexes();
} }
public function testCorrectSubsiteIDOnPageWrite() public function testCorrectSubsiteIDOnPageWrite()
{ {
$mockWrites = array( $mockWrites = [
'3367:SiteTree:a:1:{s:22:"SearchVariantVersioned";s:4:"Live";}' => array( '3367:SiteTree:a:1:{s:22:"SearchVariantVersioned";s:4:"Live";}' => [
'base' => 'SiteTree', 'base' => 'SilverStripe\\CMS\\Model\\SiteTree',
'class' => 'Page', 'class' => 'Page',
'id' => 3367, 'id' => 3367,
'statefulids' => array( 'statefulids' => [
array( [
'id' => 3367, 'id' => 3367,
'state' => array( 'state' => [
'SearchVariantVersioned' => 'Live', 'SearchVariantVersioned' => 'Live',
), ],
), ],
), ],
'fields' => array( 'fields' => [
'SiteTree:ClassName' => 'Page', 'SilverStripe\\CMS\\Model\\SiteTree:ClassName' => 'Page',
'SiteTree:LastEdited' => '2016-12-08 23:55:30', 'SilverStripe\\CMS\\Model\\SiteTree:LastEdited' => '2016-12-08 23:55:30',
'SiteTree:Created' => '2016-11-30 05:23:58', 'SilverStripe\\CMS\\Model\\SiteTree:Created' => '2016-11-30 05:23:58',
'SiteTree:URLSegment' => 'test', 'SilverStripe\\CMS\\Model\\SiteTree:URLSegment' => 'test',
'SiteTree:Title' => 'Test Title', 'SilverStripe\\CMS\\Model\\SiteTree:Title' => 'Test Title',
'SiteTree:Content' => '<p>test content</p>', 'SilverStripe\\CMS\\Model\\SiteTree:Content' => '<p>test content</p>',
'SiteTree:MetaDescription' => 'a solr test', 'SilverStripe\\CMS\\Model\\SiteTree:MetaDescription' => 'a solr test',
'SiteTree:ShowInMenus' => 1, 'SilverStripe\\CMS\\Model\\SiteTree:ShowInMenus' => 1,
'SiteTree:ShowInSearch' => 1, 'SilverStripe\\CMS\\Model\\SiteTree:ShowInSearch' => 1,
'SiteTree:Sort' => 77, 'SilverStripe\\CMS\\Model\\SiteTree:Sort' => 77,
'SiteTree:HasBrokenFile' => 0, 'SilverStripe\\CMS\\Model\\SiteTree:HasBrokenFile' => 0,
'SiteTree:HasBrokenLink' => 0, 'SilverStripe\\CMS\\Model\\SiteTree:HasBrokenLink' => 0,
'SiteTree:CanViewType' => 'Inherit', 'SilverStripe\\CMS\\Model\\SiteTree:CanViewType' => 'Inherit',
'SiteTree:CanEditType' => 'Inherit', 'SilverStripe\\CMS\\Model\\SiteTree:CanEditType' => 'Inherit',
'SiteTree:Locale' => 'en_NZ', 'SilverStripe\\CMS\\Model\\SiteTree:Locale' => 'en_NZ',
'SiteTree:SubsiteID' => 0, 'SilverStripe\\CMS\\Model\\SiteTree:SubsiteID' => 0,
'Page:ID' => 3367, 'Page:ID' => 3367,
'Page:MetaKeywords' => null, 'Page:MetaKeywords' => null,
), ],
), ],
); ];
$variant = new SearchVariantSubsites(); $variant = new SearchVariantSubsites();
$tmpMockWrites = $mockWrites; $tmpMockWrites = $mockWrites;
$variant->extractManipulationWriteState($tmpMockWrites); $variant->extractManipulationWriteState($tmpMockWrites);
foreach ($tmpMockWrites as $mockWrite) { foreach ($tmpMockWrites as $mockWrite) {
$this->assertCount(1, $mockWrite['statefulids']); $this->assertCount(1, $mockWrite['statefulids']);
$statefulIDs = array_shift($mockWrite['statefulids']); $statefulIDs = array_shift($mockWrite['statefulids']);
$this->assertEquals(0, $statefulIDs['state']['SearchVariantSubsites']);
$this->assertArrayHasKey(SearchVariantSubsites::class, $statefulIDs['state']);
$this->assertEquals(0, $statefulIDs['state'][SearchVariantSubsites::class]);
} }
$subsite = $this->objFromFixture('Subsite', 'subsite1'); $subsite = $this->objFromFixture(Subsite::class, 'subsite1');
$tmpMockWrites = $mockWrites; $tmpMockWrites = $mockWrites;
$tmpMockWrites['3367:SiteTree:a:1:{s:22:"SearchVariantVersioned";s:4:"Live";}']['fields']['SiteTree:SubsiteID'] = $subsite->ID; $tmpMockWrites['3367:SiteTree:a:1:{s:22:"SearchVariantVersioned";s:4:"Live";}']['fields'][SiteTree::class . ':SubsiteID'] = $subsite->ID;
$variant->extractManipulationWriteState($tmpMockWrites); $variant->extractManipulationWriteState($tmpMockWrites);
foreach ($tmpMockWrites as $mockWrite) { foreach ($tmpMockWrites as $mockWrite) {
$this->assertCount(1, $mockWrite['statefulids']); $this->assertCount(1, $mockWrite['statefulids']);
$statefulIDs = array_shift($mockWrite['statefulids']); $statefulIDs = array_shift($mockWrite['statefulids']);
$this->assertEquals($subsite->ID, $statefulIDs['state']['SearchVariantSubsites']);
$this->assertArrayHasKey(SearchVariantSubsites::class, $statefulIDs['state']);
$this->assertEquals($subsite->ID, $statefulIDs['state'][SearchVariantSubsites::class]);
} }
} }
public function testCorrectSubsiteIDOnFileWrite() public function testCorrectSubsiteIDOnFileWrite()
{ {
$subsiteIDs = array('0') + $this->allFixtureIDs('Subsite'); $subsiteIDs = ['0'] + $this->allFixtureIDs(Subsite::class);
$mockWrites = array( $mockWrites = [
'35910:File:a:0:{}' => array( '35910:File:a:0:{}' => [
'base' => 'File', 'base' => File::class,
'class' => 'File', 'class' => File::class,
'id' => 35910, 'id' => 35910,
'statefulids' => array( 'statefulids' => [
array( [
'id' => 35910, 'id' => 35910,
'state' => array(), 'state' => [],
), ],
), ],
'fields' => array( 'fields' => [
'File:ClassName' => 'Image', File::class . ':ClassName' => Image::class,
'File:ShowInSearch' => 1, File::class . ':ShowInSearch' => 1,
'File:ParentID' => 26470, File::class . ':ParentID' => 26470,
'File:Filename' => 'assets/Uploads/pic.jpg', File::class . ':Filename' => 'assets/Uploads/pic.jpg',
'File:Name' => 'pic.jpg', File::class . ':Name' => 'pic.jpg',
'File:Title' => 'pic', File::class . ':Title' => 'pic',
'File:SubsiteID' => 0, File::class . ':SubsiteID' => 0,
'File:OwnerID' => 661, File::class . ':OwnerID' => 661,
'File:CurrentVersionID' => 22038, File::class . ':CurrentVersionID' => 22038,
'File:LastEdited' => '2016-12-09 00:35:13', File::class . ':LastEdited' => '2016-12-09 00:35:13',
), ],
), ],
); ];
$variant = new SearchVariantSubsites(); $variant = new SearchVariantSubsites();
$tmpMockWrites = $mockWrites; $tmpMockWrites = $mockWrites;
$variant->extractManipulationWriteState($tmpMockWrites); $variant->extractManipulationWriteState($tmpMockWrites);
foreach ($tmpMockWrites as $mockWrite) { foreach ($tmpMockWrites as $mockWrite) {
$this->assertCount(count($subsiteIDs), $mockWrite['statefulids']); $this->assertCount(count($subsiteIDs), $mockWrite['statefulids']);
foreach ($mockWrite['statefulids'] as $statefulIDs) { foreach ($mockWrite['statefulids'] as $statefulIDs) {
$this->assertTrue( $this->assertContains(
in_array($statefulIDs['state']['SearchVariantSubsites'], $subsiteIDs), $statefulIDs['state'][SearchVariantSubsites::class],
sprintf('Failed to assert that %s is in list of valid subsites: %s', $statefulIDs['state']['SearchVariantSubsites'], implode(', ', $subsiteIDs)) $subsiteIDs,
sprintf(
'Failed to assert that %s is in list of valid subsites: %s',
$statefulIDs['state'][SearchVariantSubsites::class],
implode(', ', $subsiteIDs)
)
); );
} }
} }
$subsite = $this->objFromFixture('Subsite', 'subsite1'); $subsite = $this->objFromFixture(Subsite::class, 'subsite1');
$tmpMockWrites = $mockWrites; $tmpMockWrites = $mockWrites;
$tmpMockWrites['35910:File:a:0:{}']['fields']['File:SubsiteID'] = $subsite->ID; $tmpMockWrites['35910:File:a:0:{}']['fields'][File::class . ':SubsiteID'] = $subsite->ID;
$variant->extractManipulationWriteState($tmpMockWrites); $variant->extractManipulationWriteState($tmpMockWrites);
foreach ($tmpMockWrites as $mockWrite) { foreach ($tmpMockWrites as $mockWrite) {
$this->assertCount(1, $mockWrite['statefulids']); $this->assertCount(1, $mockWrite['statefulids']);
$statefulIDs = array_shift($mockWrite['statefulids']); $statefulIDs = array_shift($mockWrite['statefulids']);
$this->assertEquals($subsite->ID, $statefulIDs['state']['SearchVariantSubsites']); $this->assertEquals($subsite->ID, $statefulIDs['state'][SearchVariantSubsites::class]);
} }
} }
} }

View File

@ -1,16 +1,17 @@
Subsite: SilverStripe\Subsites\Model\Subsite:
main: main:
Title: Template Title: Template
subsite1: subsite1:
Title: 'Subsite1 Template' Title: 'Subsite1 Template'
subsite2: subsite2:
Title: 'Subsite2 Template' Title: 'Subsite2 Template'
SubsiteDomain:
SilverStripe\Subsites\Model\SubsiteDomain:
subsite1: subsite1:
SubsiteID: =>Subsite.subsite1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
Domain: www.subsite1.com Domain: www.subsite1.com
Protocol: automatic Protocol: automatic
subsite2: subsite2:
SubsiteID: =>Subsite.subsite2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
Domain: www.subsite2.com Domain: www.subsite2.com
Protocol: automatic Protocol: automatic

View File

@ -2,9 +2,9 @@
namespace SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest; namespace SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest;
use SilverStripe\FullTextSearch\Solr\SolrIndex;
use SilverStripe\Assets\File; use SilverStripe\Assets\File;
use SilverStripe\CMS\Model\SiteTree; use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\FullTextSearch\Solr\SolrIndex;
class SolrIndexSubsitesTest_Index extends SolrIndex class SolrIndexSubsitesTest_Index extends SolrIndex
{ {

View File

@ -7,18 +7,20 @@ use SilverStripe\Core\Environment;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Kernel; use SilverStripe\Core\Kernel;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelationIndex; use SilverStripe\FullTextSearch\Search\Queries\SearchQuery;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelationInheritedIndex; use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex; use SilverStripe\FullTextSearch\Solr\Services\Solr3Service;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex2;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_BoostedIndex;
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_Container; use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_Container;
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasOne; use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasOne;
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasMany; use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_HasMany;
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_ManyMany; use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_ManyMany;
use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_OtherContainer; use SilverStripe\FullTextSearch\Tests\SearchUpdaterTest\SearchUpdaterTest_OtherContainer;
use SilverStripe\FullTextSearch\Search\Queries\SearchQuery; use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelationIndex;
use SilverStripe\FullTextSearch\Solr\Services\Solr3Service; use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_AmbiguousRelationInheritedIndex;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_BoostedIndex;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex;
use SilverStripe\FullTextSearch\Tests\SolrIndexTest\SolrIndexTest_FakeIndex2;
use SilverStripe\Subsites\Model\Subsite;
class SolrIndexTest extends SapphireTest class SolrIndexTest extends SapphireTest
{ {
@ -151,6 +153,10 @@ class SolrIndexTest extends SapphireTest
*/ */
public function testBoostedField() public function testBoostedField()
{ {
if (class_exists(Subsite::class)) {
Config::modify()->set(SearchVariantSubsites::class, 'enabled', false);
}
/** @var Solr3Service|PHPUnit_Framework_MockObject_MockObject $serviceMock */ /** @var Solr3Service|PHPUnit_Framework_MockObject_MockObject $serviceMock */
$serviceMock = $this->getMockBuilder(Solr3Service::class) $serviceMock = $this->getMockBuilder(Solr3Service::class)
->setMethods(['search']) ->setMethods(['search'])
@ -162,8 +168,11 @@ class SolrIndexTest extends SapphireTest
$this->equalTo('+term'), $this->equalTo('+term'),
$this->anything(), $this->anything(),
$this->anything(), $this->anything(),
$this->equalTo(['qf' => SearchUpdaterTest_Container::class . '_Field1^1.5 ' . SearchUpdaterTest_Container::class . '_Field2^2.1 _text', $this->equalTo([
'fq' => '+(_versionedstage:"" (*:* -_versionedstage:[* TO *]))']), 'qf' => SearchUpdaterTest_Container::class . '_Field1^1.5 '
. SearchUpdaterTest_Container::class . '_Field2^2.1 _text',
'fq' => '+(_versionedstage:"" (*:* -_versionedstage:[* TO *]))',
]),
$this->anything() $this->anything()
)->willReturn($this->getFakeRawSolrResponse()); )->willReturn($this->getFakeRawSolrResponse());

View File

@ -2,8 +2,10 @@
namespace SilverStripe\FullTextSearch\Tests; namespace SilverStripe\FullTextSearch\Tests;
use Apache_Solr_Document;
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\FullTextSearch\Search\FullTextSearch; use SilverStripe\FullTextSearch\Search\FullTextSearch;
use SilverStripe\FullTextSearch\Search\SearchIntrospection; use SilverStripe\FullTextSearch\Search\SearchIntrospection;
@ -15,23 +17,28 @@ use SilverStripe\FullTextSearch\Tests\SolrIndexVersionedTest\SolrVersionedTest_I
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateProcessor; use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateProcessor;
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateImmediateProcessor; use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateImmediateProcessor;
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater; use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned; use SilverStripe\FullTextSearch\Search\Variants\SearchVariantVersioned;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Versioned\Versioned; use SilverStripe\Versioned\Versioned;
class SolrIndexVersionedTest extends SapphireTest class SolrIndexVersionedTest extends SapphireTest
{ {
protected $usesDatabase = true;
protected $oldMode = null; protected $oldMode = null;
protected static $index = null; protected static $index = null;
protected static $extra_dataobjects = array( protected static $extra_dataobjects = [
SearchVariantVersionedTest_Item::class, SearchVariantVersionedTest_Item::class,
SolrIndexVersionedTest_Object::class SolrIndexVersionedTest_Object::class,
); ];
protected function setUp() protected function setUp()
{ {
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false); // Need to be set before parent::setUp() since they're executed before the tests start
Config::modify()->set(SearchVariantSubsites::class, 'enabled', false);
parent::setUp(); parent::setUp();
@ -41,18 +48,18 @@ class SolrIndexVersionedTest extends SapphireTest
SearchUpdater::bind_manipulation_capture(); SearchUpdater::bind_manipulation_capture();
Config::modify()->set(Injector::class, SearchUpdateProcessor::class, array( Config::modify()->set(Injector::class, SearchUpdateProcessor::class, [
'class' => SearchUpdateImmediateProcessor::class 'class' => SearchUpdateImmediateProcessor::class
)); ]);
FullTextSearch::force_index_list(self::$index); FullTextSearch::force_index_list(self::$index);
SearchUpdater::clear_dirty_indexes(); SearchUpdater::clear_dirty_indexes();
$this->oldMode = Versioned::get_reading_mode(); $this->oldMode = Versioned::get_reading_mode();
Versioned::set_stage('Stage'); Versioned::set_stage(Versioned::DRAFT);
} }
public function tearDown() protected function tearDown()
{ {
Versioned::set_reading_mode($this->oldMode); Versioned::set_reading_mode($this->oldMode);
parent::tearDown(); parent::tearDown();
@ -80,13 +87,7 @@ class SolrIndexVersionedTest extends SapphireTest
{ {
$id = $object->ID; $id = $object->ID;
$class = DataObject::getSchema()->baseDataClass($object); $class = DataObject::getSchema()->baseDataClass($object);
// Prevent subsites from breaking tests return $id.'-'.$class.'-{'.json_encode(SearchVariantVersioned::class).':"'.$stage.'"}';
// TODO: Subsites currently isn't migrated. This needs to be fixed when subsites is fixed.
$subsites = '';
if (class_exists('Subsite') && DataObject::getSchema()->hasOneComponent($object->getClassName(), 'Subsite')) {
$subsites = '"SearchVariantSubsites":"0",';
}
return $id.'-'.$class.'-{'.$subsites. json_encode(SearchVariantVersioned::class) . ':"'.$stage.'"}';
} }
/** /**
@ -98,12 +99,12 @@ class SolrIndexVersionedTest extends SapphireTest
*/ */
protected function getSolrDocument($class, $object, $value, $stage) protected function getSolrDocument($class, $object, $value, $stage)
{ {
$doc = new \Apache_Solr_Document(); $doc = new Apache_Solr_Document();
$doc->setField('_documentid', $this->getExpectedDocumentId($object, $stage)); $doc->setField('_documentid', $this->getExpectedDocumentId($object, $stage));
$doc->setField('ClassName', $class); $doc->setField('ClassName', $class);
$doc->setField(DataObject::getSchema()->baseDataClass($class) . '_TestText', $value); $doc->setField(DataObject::getSchema()->baseDataClass($class) . '_TestText', $value);
$doc->setField('_versionedstage', $stage); $doc->setField('_versionedstage', $stage);
$doc->setField('ID', $object->ID); $doc->setField('ID', (int) $object->ID);
$doc->setField('ClassHierarchy', SearchIntrospection::hierarchy($class)); $doc->setField('ClassHierarchy', SearchIntrospection::hierarchy($class));
$doc->setFieldBoost('ID', false); $doc->setFieldBoost('ID', false);
$doc->setFieldBoost('ClassHierarchy', false); $doc->setFieldBoost('ClassHierarchy', false);
@ -114,89 +115,52 @@ class SolrIndexVersionedTest extends SapphireTest
public function testPublishing() public function testPublishing()
{ {
// Check that write updates Stage // Check that write updates Stage
Versioned::set_stage('Stage'); Versioned::set_stage(Versioned::DRAFT);
$item = new SearchVariantVersionedTest_Item(array('TestText' => 'Foo')); $item = new SearchVariantVersionedTest_Item(array('TestText' => 'Foo'));
$item->write(); $item->write();
$object = new SolrIndexVersionedTest_Object(array('TestText' => 'Bar')); $object = new SolrIndexVersionedTest_Object(array('TestText' => 'Bar'));
$object->write(); $object->write();
$doc1 = $this->getSolrDocument(SearchVariantVersionedTest_Item::class, $item, 'Foo', 'Stage'); $doc1 = $this->getSolrDocument(SearchVariantVersionedTest_Item::class, $item, 'Foo', Versioned::DRAFT);
$doc2 = $this->getSolrDocument(SolrIndexVersionedTest_Object::class, $object, 'Bar', 'Stage'); $doc2 = $this->getSolrDocument(SolrIndexVersionedTest_Object::class, $object, 'Bar', Versioned::DRAFT);
// Ensure correct call is made to Solr // Ensure correct call is made to Solr
$this->getServiceMock(['addDocument', 'commit']) $this->getServiceMock(['addDocument', 'commit'])
->expects($this->exactly(2)) ->expects($this->exactly(2))
->method('addDocument') ->method('addDocument')
->withConsecutive( ->withConsecutive(
[ [$this->equalTo($doc1)],
$this->equalTo($doc1), [$this->equalTo($doc2)]
$this->anything(),
$this->anything(),
$this->anything(),
$this->anything()
],
[
$this->equalTo($doc2),
$this->anything(),
$this->anything(),
$this->anything(),
$this->anything()
]
); );
SearchUpdater::flush_dirty_indexes(); SearchUpdater::flush_dirty_indexes();
// Check that write updates Live // Check that write updates Live
Versioned::set_stage('Stage'); Versioned::set_stage(Versioned::DRAFT);
$item = new SearchVariantVersionedTest_Item(array('TestText' => 'Foo')); $item = new SearchVariantVersionedTest_Item(array('TestText' => 'Foo'));
$item->write(); $item->write();
$item->copyVersionToStage('Stage', 'Live'); $item->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$object = new SolrIndexVersionedTest_Object(array('TestText' => 'Bar')); $object = new SolrIndexVersionedTest_Object(array('TestText' => 'Bar'));
$object->write(); $object->write();
$object->copyVersionToStage('Stage', 'Live'); $object->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$doc1 = $this->getSolrDocument(SearchVariantVersionedTest_Item::class, $item, 'Foo', 'Stage'); $doc1 = $this->getSolrDocument(SearchVariantVersionedTest_Item::class, $item, 'Foo', Versioned::DRAFT);
$doc2 = $this->getSolrDocument(SearchVariantVersionedTest_Item::class, $item, 'Foo', 'Live'); $doc2 = $this->getSolrDocument(SearchVariantVersionedTest_Item::class, $item, 'Foo', Versioned::LIVE);
$doc3 = $this->getSolrDocument(SolrIndexVersionedTest_Object::class, $object, 'Bar', 'Stage'); $doc3 = $this->getSolrDocument(SolrIndexVersionedTest_Object::class, $object, 'Bar', Versioned::DRAFT);
$doc4 = $this->getSolrDocument(SolrIndexVersionedTest_Object::class, $object, 'Bar', 'Live'); $doc4 = $this->getSolrDocument(SolrIndexVersionedTest_Object::class, $object, 'Bar', Versioned::LIVE);
// Ensure correct call is made to Solr // Ensure correct call is made to Solr
$this->getServiceMock(['addDocument', 'commit']) $this->getServiceMock(['addDocument', 'commit'])
->expects($this->exactly(4)) ->expects($this->exactly(4))
->method('addDocument') ->method('addDocument')
->withConsecutive( ->withConsecutive(
[ [$doc1],
$this->equalTo($doc1), [$doc2],
$this->anything(), [$doc3],
$this->anything(), [$doc4]
$this->anything(),
$this->anything()
],
[
$this->equalTo($doc2),
$this->anything(),
$this->anything(),
$this->anything(),
$this->anything()
],
[
$this->equalTo($doc3),
$this->anything(),
$this->anything(),
$this->anything(),
$this->anything()
],
[
$this->equalTo($doc4),
$this->anything(),
$this->anything(),
$this->anything(),
$this->anything()
]
); );
SearchUpdater::flush_dirty_indexes(); SearchUpdater::flush_dirty_indexes();
@ -205,12 +169,12 @@ class SolrIndexVersionedTest extends SapphireTest
public function testDelete() public function testDelete()
{ {
// Delete the live record (not the stage) // Delete the live record (not the stage)
Versioned::set_stage('Stage'); Versioned::set_stage(Versioned::DRAFT);
$item = new SearchVariantVersionedTest_Item(array('TestText' => 'Too')); $item = new SearchVariantVersionedTest_Item(array('TestText' => 'Too'));
$item->write(); $item->write();
$item->copyVersionToStage('Stage', 'Live'); $item->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
Versioned::set_stage('Live'); Versioned::set_stage(Versioned::LIVE);
$id = clone $item; $id = clone $item;
$item->delete(); $item->delete();
@ -218,16 +182,16 @@ class SolrIndexVersionedTest extends SapphireTest
$this->getServiceMock(['addDocument', 'commit', 'deleteById']) $this->getServiceMock(['addDocument', 'commit', 'deleteById'])
->expects($this->exactly(1)) ->expects($this->exactly(1))
->method('deleteById') ->method('deleteById')
->with($this->equalTo($this->getExpectedDocumentId($id, 'Live'))); ->with($this->getExpectedDocumentId($id, Versioned::LIVE));
SearchUpdater::flush_dirty_indexes(); SearchUpdater::flush_dirty_indexes();
// Delete the stage record // Delete the stage record
Versioned::set_stage('Stage'); Versioned::set_stage(Versioned::DRAFT);
$item = new SearchVariantVersionedTest_Item(array('TestText' => 'Too')); $item = new SearchVariantVersionedTest_Item(array('TestText' => 'Too'));
$item->write(); $item->write();
$item->copyVersionToStage('Stage', 'Live'); $item->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$id = clone $item; $id = clone $item;
$item->delete(); $item->delete();
@ -235,7 +199,7 @@ class SolrIndexVersionedTest extends SapphireTest
$this->getServiceMock(['addDocument', 'commit', 'deleteById']) $this->getServiceMock(['addDocument', 'commit', 'deleteById'])
->expects($this->exactly(1)) ->expects($this->exactly(1))
->method('deleteById') ->method('deleteById')
->with($this->equalTo($this->getExpectedDocumentId($id, 'Stage'))); ->with($this->getExpectedDocumentId($id, Versioned::DRAFT));
SearchUpdater::flush_dirty_indexes(); SearchUpdater::flush_dirty_indexes();
} }

View File

@ -1,39 +0,0 @@
<?php
namespace SilverStripe\FullTextSearch\Tests\SolrIndexVersionedTest;
if (!class_exists('\Phockito')) {
return;
}
\Phockito::include_hamcrest(false);
class SolrDocumentMatcher extends \Hamcrest_BaseMatcher
{
protected $properties;
public function __construct($properties)
{
$this->properties = $properties;
}
public function describeTo(\Hamcrest_Description $description)
{
$description->appendText('\Apache_Solr_Document with properties '.var_export($this->properties, true));
}
public function matches($item)
{
if (! ($item instanceof \Apache_Solr_Document)) {
return false;
}
foreach ($this->properties as $key => $value) {
if ($item->{$key} != $value) {
return false;
}
}
return true;
}
}

View File

@ -49,17 +49,13 @@ class SolrReindexQueuedTest extends SapphireTest
protected function setUp() protected function setUp()
{ {
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false);
parent::setUp(); parent::setUp();
if (!interface_exists('Symbiote\QueuedJobs\Services\QueuedJob')) { if (!interface_exists(QueuedJob::class)) {
$this->skipTest = true; $this->skipTest = true;
return $this->markTestSkipped("These tests need the QueuedJobs module installed to run"); return $this->markTestSkipped("These tests need the QueuedJobs module installed to run");
} }
Config::modify()->set(QueuedJobService::class, 'use_shutdown_function', false);
// Set queued handler for reindex // Set queued handler for reindex
Config::modify()->set(Injector::class, SolrReindexHandler::class, array( Config::modify()->set(Injector::class, SolrReindexHandler::class, array(
'class' => SolrReindexQueuedHandler::class 'class' => SolrReindexQueuedHandler::class

View File

@ -43,8 +43,6 @@ class SolrReindexTest extends SapphireTest
protected function setUp() protected function setUp()
{ {
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false);
parent::setUp(); parent::setUp();
// Set test handler for reindex // Set test handler for reindex

View File

@ -0,0 +1,37 @@
<?php
namespace SilverStripe\FullTextSearch\Tests\State;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Dev\State\TestState;
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
use SilverStripe\FullTextSearch\Search\Variants\SearchVariant;
use Symbiote\QueuedJobs\Services\QueuedJobService;
class FullTextSearchState implements TestState
{
public function setUp(SapphireTest $test)
{
// noop
}
public function tearDown(SapphireTest $test)
{
SearchVariant::clear_variant_cache();
}
public function setUpOnce($class)
{
Config::modify()->set(SearchUpdater::class, 'flush_on_shutdown', false);
if (class_exists(QueuedJobService::class)) {
Config::modify()->set(QueuedJobService::class, 'use_shutdown_function', false);
}
}
public function tearDownOnce($class)
{
// noop
}
}