mirror of
https://github.com/flarum/core.git
synced 2025-08-09 01:46:35 +02:00
Optimize tag permissions (#126)
The new implementation generates a subquery of IDs instead of sending big arrays of data to/from the database. This massively speeds up performance.
This commit is contained in:
committed by
GitHub
parent
5235dda1e4
commit
712286151f
78
extensions/tags/.github/workflows/test.yml
vendored
Normal file
78
extensions/tags/.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
name: Tests
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
php: [7.3, 7.4, '8.0']
|
||||||
|
service: ['mysql:5.7', mariadb]
|
||||||
|
prefix: ['', flarum_]
|
||||||
|
|
||||||
|
include:
|
||||||
|
- service: 'mysql:5.7'
|
||||||
|
db: MySQL
|
||||||
|
- service: mariadb
|
||||||
|
db: MariaDB
|
||||||
|
- prefix: flarum_
|
||||||
|
prefixStr: (prefix)
|
||||||
|
|
||||||
|
exclude:
|
||||||
|
- php: 7.3
|
||||||
|
service: 'mysql:5.7'
|
||||||
|
prefix: flarum_
|
||||||
|
- php: 7.3
|
||||||
|
service: mariadb
|
||||||
|
prefix: flarum_
|
||||||
|
- php: 8.0
|
||||||
|
service: 'mysql:5.7'
|
||||||
|
prefix: flarum_
|
||||||
|
- php: 8.0
|
||||||
|
service: mariadb
|
||||||
|
prefix: flarum_
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: ${{ matrix.service }}
|
||||||
|
ports:
|
||||||
|
- 13306:3306
|
||||||
|
|
||||||
|
name: 'PHP ${{ matrix.php }} / ${{ matrix.db }} ${{ matrix.prefixStr }}'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
|
||||||
|
- name: Setup PHP
|
||||||
|
uses: shivammathur/setup-php@v2
|
||||||
|
with:
|
||||||
|
php-version: ${{ matrix.php }}
|
||||||
|
coverage: xdebug
|
||||||
|
extensions: curl, dom, gd, json, mbstring, openssl, pdo_mysql, tokenizer, zip
|
||||||
|
tools: phpunit, composer:v2
|
||||||
|
|
||||||
|
# The authentication alter is necessary because newer mysql versions use the `caching_sha2_password` driver,
|
||||||
|
# which isn't supported prior to PHP7.4
|
||||||
|
# When we drop support for PHP7.3, we should remove this from the setup.
|
||||||
|
- name: Create MySQL Database
|
||||||
|
run: |
|
||||||
|
sudo systemctl start mysql
|
||||||
|
mysql -uroot -proot -e 'CREATE DATABASE flarum_test;' --port 13306
|
||||||
|
mysql -uroot -proot -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';" --port 13306
|
||||||
|
|
||||||
|
- name: Install Composer dependencies
|
||||||
|
run: composer install
|
||||||
|
|
||||||
|
- name: Setup Composer tests
|
||||||
|
run: composer test:setup
|
||||||
|
env:
|
||||||
|
DB_PORT: 13306
|
||||||
|
DB_PASSWORD: root
|
||||||
|
DB_PREFIX: ${{ matrix.prefix }}
|
||||||
|
|
||||||
|
- name: Run Composer tests
|
||||||
|
run: composer test
|
||||||
|
env:
|
||||||
|
COMPOSER_PROCESS_TIMEOUT: 600
|
@@ -2,7 +2,9 @@
|
|||||||
"name": "flarum/tags",
|
"name": "flarum/tags",
|
||||||
"description": "Organize discussions into a hierarchy of tags and categories.",
|
"description": "Organize discussions into a hierarchy of tags and categories.",
|
||||||
"type": "flarum-extension",
|
"type": "flarum-extension",
|
||||||
"keywords": ["discussion"],
|
"keywords": [
|
||||||
|
"discussion"
|
||||||
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/flarum/core/issues",
|
"issues": "https://github.com/flarum/core/issues",
|
||||||
@@ -24,6 +26,11 @@
|
|||||||
"Flarum\\Tags\\": "src/"
|
"Flarum\\Tags\\": "src/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Flarum\\Tags\\Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "0.1.x-dev"
|
"dev-master": "0.1.x-dev"
|
||||||
@@ -37,5 +44,24 @@
|
|||||||
"color": "#fff"
|
"color": "#fff"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": [
|
||||||
|
"@test:unit",
|
||||||
|
"@test:integration"
|
||||||
|
],
|
||||||
|
"test:unit": "phpunit -c tests/phpunit.unit.xml",
|
||||||
|
"test:integration": "phpunit -c tests/phpunit.integration.xml",
|
||||||
|
"test:setup": "@php tests/integration/setup.php"
|
||||||
|
},
|
||||||
|
"scripts-descriptions": {
|
||||||
|
"test": "Runs all tests.",
|
||||||
|
"test:unit": "Runs all unit tests.",
|
||||||
|
"test:integration": "Runs all integration tests.",
|
||||||
|
"test:setup": "Sets up a database for use with integration tests. Execute this only once."
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"flarum/core": "0.1.x-dev",
|
||||||
|
"flarum/testing": "*@dev"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -37,9 +37,8 @@ class GlobalPolicy extends AbstractPolicy
|
|||||||
if ($actor->hasPermission($ability) && $actor->hasPermission('bypassTagCounts')) {
|
if ($actor->hasPermission($ability) && $actor->hasPermission('bypassTagCounts')) {
|
||||||
return $this->allow();
|
return $this->allow();
|
||||||
}
|
}
|
||||||
|
$enoughPrimary = Tag::queryIdsWhereCan(Tag::query()->getQuery(), $actor, $ability, true, false)->count() >= $this->settings->get('flarum-tags.min_primary_tags');
|
||||||
$enoughPrimary = count(Tag::getIdsWhereCan($actor, $ability, true, false)) >= $this->settings->get('flarum-tags.min_primary_tags');
|
$enoughSecondary = Tag::queryIdsWhereCan(Tag::query()->getQuery(), $actor, $ability, false, true)->count() >= $this->settings->get('flarum-tags.min_secondary_tags');
|
||||||
$enoughSecondary = count(Tag::getIdsWhereCan($actor, $ability, false, true)) >= $this->settings->get('flarum-tags.min_secondary_tags');
|
|
||||||
|
|
||||||
if ($enoughPrimary && $enoughSecondary) {
|
if ($enoughPrimary && $enoughSecondary) {
|
||||||
return $this->allow();
|
return $this->allow();
|
||||||
|
@@ -27,7 +27,9 @@ class ScopeDiscussionVisibility
|
|||||||
->whereNotIn('discussions.id', function ($query) use ($actor) {
|
->whereNotIn('discussions.id', function ($query) use ($actor) {
|
||||||
return $query->select('discussion_id')
|
return $query->select('discussion_id')
|
||||||
->from('discussion_tag')
|
->from('discussion_tag')
|
||||||
->whereIn('tag_id', Tag::getIdsWhereCannot($actor, 'viewDiscussions'));
|
->whereNotIn('tag_id', function ($query) use ($actor) {
|
||||||
|
Tag::queryIdsWhereCan($query->from('tags'), $actor, 'viewDiscussions');
|
||||||
|
});
|
||||||
})
|
})
|
||||||
->orWhere(function ($query) use ($actor) {
|
->orWhere(function ($query) use ($actor) {
|
||||||
$query->whereVisibleTo($actor, 'viewDiscussionsInRestrictedTags');
|
$query->whereVisibleTo($actor, 'viewDiscussionsInRestrictedTags');
|
||||||
|
@@ -32,7 +32,9 @@ class ScopeDiscussionVisibilityForAbility
|
|||||||
$query->whereIn('discussions.id', function ($query) use ($actor, $ability) {
|
$query->whereIn('discussions.id', function ($query) use ($actor, $ability) {
|
||||||
return $query->select('discussion_id')
|
return $query->select('discussion_id')
|
||||||
->from('discussion_tag')
|
->from('discussion_tag')
|
||||||
->whereIn('tag_id', Tag::getIdsWhereCan($actor, 'discussion.'.$ability));
|
->whereIn('tag_id', function ($query) use ($actor, $ability) {
|
||||||
|
Tag::queryIdsWhereCan($query->from('tags'), $actor, 'discussion.'.$ability);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -28,7 +28,9 @@ class ScopeFlagVisibility
|
|||||||
->whereNotExists(function ($query) use ($actor) {
|
->whereNotExists(function ($query) use ($actor) {
|
||||||
return $query->selectRaw('1')
|
return $query->selectRaw('1')
|
||||||
->from('discussion_tag')
|
->from('discussion_tag')
|
||||||
->whereIn('tag_id', Tag::getIdsWhereCannot($actor, 'discussion.viewFlags'))
|
->whereNotIn('tag_id', function ($query) use ($actor) {
|
||||||
|
Tag::queryIdsWhereCan($query->from('tags'), $actor, 'discussion.viewFlags');
|
||||||
|
})
|
||||||
->whereColumn('discussions.id', 'discussion_id');
|
->whereColumn('discussions.id', 'discussion_id');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,8 @@ class ScopeTagVisibility
|
|||||||
*/
|
*/
|
||||||
public function __invoke(User $actor, Builder $query)
|
public function __invoke(User $actor, Builder $query)
|
||||||
{
|
{
|
||||||
$query->whereNotIn('id', Tag::getIdsWhereCannot($actor, 'viewDiscussions'));
|
$query->whereIn('id', function ($query) use ($actor) {
|
||||||
|
Tag::queryIdsWhereCan($query, $actor, 'viewDiscussions');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -15,6 +15,7 @@ use Flarum\Discussion\Discussion;
|
|||||||
use Flarum\Group\Permission;
|
use Flarum\Group\Permission;
|
||||||
use Flarum\User\User;
|
use Flarum\User\User;
|
||||||
use Illuminate\Database\Eloquent\Builder;
|
use Illuminate\Database\Eloquent\Builder;
|
||||||
|
use Illuminate\Database\Query\Builder as QueryBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @property int $id
|
* @property int $id
|
||||||
@@ -190,45 +191,70 @@ class Tag extends AbstractModel
|
|||||||
Permission::where('permission', 'like', "tag{$this->id}.%")->delete();
|
Permission::where('permission', 'like', "tag{$this->id}.%")->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static function getIdsWherePermission(User $user, string $permission, bool $condition = true, bool $includePrimary = true, bool $includeSecondary = true): array
|
protected static function buildPermissionSubquery($base, $isAdmin, $hasGlobalPermission, $tagIdsWithPermission)
|
||||||
{
|
{
|
||||||
static $tags;
|
$base
|
||||||
|
->from('tags as perm_tags')
|
||||||
|
->select('perm_tags.id');
|
||||||
|
|
||||||
if (! $tags) {
|
// This needs to be a special case, as `tagIdsWithPermissions`
|
||||||
$tags = static::with('parent')->get();
|
// won't include admin perms (which are all perms by default).
|
||||||
|
if ($isAdmin) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$ids = [];
|
$base->where(function ($query) use ($tagIdsWithPermission) {
|
||||||
$hasGlobalPermission = $user->hasPermission($permission);
|
$query
|
||||||
|
->where('perm_tags.is_restricted', true)
|
||||||
|
->whereIn('perm_tags.id', $tagIdsWithPermission);
|
||||||
|
});
|
||||||
|
|
||||||
$canForTag = function (self $tag) use ($user, $permission, $hasGlobalPermission) {
|
if ($hasGlobalPermission) {
|
||||||
return ($hasGlobalPermission && ! $tag->is_restricted) || ($tag->is_restricted && $user->hasPermission('tag'.$tag->id.'.'.$permission));
|
$base->orWhere('perm_tags.is_restricted', false);
|
||||||
};
|
|
||||||
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$can = $canForTag($tag);
|
|
||||||
|
|
||||||
if ($can && $tag->parent) {
|
|
||||||
$can = $canForTag($tag->parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
$isPrimary = $tag->position !== null && ! $tag->parent;
|
|
||||||
|
|
||||||
if ($can === $condition && ($includePrimary && $isPrimary || $includeSecondary && ! $isPrimary)) {
|
|
||||||
$ids[] = $tag->id;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $ids;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getIdsWhereCan(User $user, string $permission, bool $includePrimary = true, bool $includeSecondary = true): array
|
public static function queryIdsWhereCan(QueryBuilder $base, User $user, string $currPermission, bool $includePrimary = true, bool $includeSecondary = true): QueryBuilder
|
||||||
{
|
{
|
||||||
return static::getIdsWherePermission($user, $permission, true, $includePrimary, $includeSecondary);
|
$hasGlobalPermission = $user->hasPermission($currPermission);
|
||||||
}
|
$isAdmin = $user->isAdmin();
|
||||||
|
$allPermissions = $user->getPermissions();
|
||||||
|
|
||||||
public static function getIdsWhereCannot(User $user, string $permission, bool $includePrimary = true, bool $includeSecondary = true): array
|
$tagIdsWithPermission = collect($allPermissions)
|
||||||
{
|
->filter(function ($permission) use ($currPermission) {
|
||||||
return static::getIdsWherePermission($user, $permission, false, $includePrimary, $includeSecondary);
|
return substr($permission, 0, 3) === 'tag' && strpos($permission, $currPermission) !== false;
|
||||||
|
})
|
||||||
|
->map(function ($permission) {
|
||||||
|
$scopeFragment = explode('.', $permission, 2)[0];
|
||||||
|
|
||||||
|
return substr($scopeFragment, 3);
|
||||||
|
})
|
||||||
|
->values();
|
||||||
|
|
||||||
|
$validTags = $base
|
||||||
|
->from('tags as s_tags')
|
||||||
|
->where(function ($query) use ($isAdmin, $hasGlobalPermission, $tagIdsWithPermission) {
|
||||||
|
$query
|
||||||
|
->whereIn('s_tags.id', function ($query) use ($isAdmin, $hasGlobalPermission, $tagIdsWithPermission) {
|
||||||
|
static::buildPermissionSubquery($query, $isAdmin, $hasGlobalPermission, $tagIdsWithPermission);
|
||||||
|
})
|
||||||
|
->where(function ($query) use ($isAdmin, $hasGlobalPermission, $tagIdsWithPermission) {
|
||||||
|
$query
|
||||||
|
->whereIn('s_tags.parent_id', function ($query) use ($isAdmin, $hasGlobalPermission, $tagIdsWithPermission) {
|
||||||
|
static::buildPermissionSubquery($query, $isAdmin, $hasGlobalPermission, $tagIdsWithPermission);
|
||||||
|
})
|
||||||
|
->orWhere('s_tags.parent_id', null);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->where(function ($query) use ($includePrimary, $includeSecondary) {
|
||||||
|
if (! $includePrimary) {
|
||||||
|
$query->where('s_tags.position', '=', null);
|
||||||
|
}
|
||||||
|
if (! $includeSecondary) {
|
||||||
|
$query->where('s_tags.position', '!=', null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return $validTags->select('s_tags.id');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
0
extensions/tags/tests/fixtures/.gitkeep
vendored
Normal file
0
extensions/tags/tests/fixtures/.gitkeep
vendored
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tags\Tests\integration;
|
||||||
|
|
||||||
|
trait RetrievesRepresentativeTags
|
||||||
|
{
|
||||||
|
protected function tags()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
['id' => 1, 'name' => 'Primary 1', 'slug' => 'primary-1', 'position' => 0, 'parent_id' => null],
|
||||||
|
['id' => 2, 'name' => 'Primary 2', 'slug' => 'primary-2', 'position' => 1, 'parent_id' => null],
|
||||||
|
['id' => 3, 'name' => 'Primary 2 Child 1', 'slug' => 'primary-2-child-1', 'position' => 2, 'parent_id' => 2],
|
||||||
|
['id' => 4, 'name' => 'Primary 2 Child 2', 'slug' => 'primary-2-child-2', 'position' => 3, 'parent_id' => 2],
|
||||||
|
['id' => 5, 'name' => 'Primary 2 Child Restricted', 'slug' => 'primary-2-child-restricted', 'position' => 4, 'parent_id' => 2, 'is_restricted' => true],
|
||||||
|
['id' => 6, 'name' => 'Primary Restricted', 'slug' => 'primary-restricted', 'position' => 5, 'parent_id' => null, 'is_restricted' => true],
|
||||||
|
['id' => 7, 'name' => 'Primary Restricted Child 1', 'slug' => 'primary-restricted-child-1', 'position' => 6, 'parent_id' => 6],
|
||||||
|
['id' => 8, 'name' => 'Primary Restricted Child Restricted', 'slug' => 'primary-restricted-child-restricted', 'position' => 7, 'parent_id' => 6, 'is_restricted' => true],
|
||||||
|
['id' => 9, 'name' => 'Secondary 1', 'slug' => 'secondary-1', 'position' => null, 'parent_id' => null],
|
||||||
|
['id' => 10, 'name' => 'Secondary 2', 'slug' => 'secondary-2', 'position' => null, 'parent_id' => null],
|
||||||
|
['id' => 11, 'name' => 'Secondary Restricted', 'slug' => 'secondary-restricted', 'position' => null, 'parent_id' => null, 'is_restricted' => true],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
265
extensions/tags/tests/integration/api/discussions/CreateTest.php
Normal file
265
extensions/tags/tests/integration/api/discussions/CreateTest.php
Normal file
@@ -0,0 +1,265 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tags\Tests\integration\api\discussions;
|
||||||
|
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Tags\Tests\integration\RetrievesRepresentativeTags;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
|
||||||
|
class CreateTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
use RetrievesRepresentativeTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-tags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'tags' => $this->tags(),
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag5.viewDiscussions'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag8.viewDiscussions'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag8.startDiscussion'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag11.viewDiscussions'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag11.startDiscussion'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_can_create_discussion_without_tags()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_cant_create_discussion_without_tags()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(422, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_can_create_discussion_in_primary_tag()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'tags' => [
|
||||||
|
'data' => [
|
||||||
|
['type' => 'tags', 'id' => 1]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_cant_create_discussion_in_primary_tag_where_can_view_but_cant_start()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'tags' => [
|
||||||
|
'data' => [
|
||||||
|
['type' => 'tags', 'id' => 5]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(403, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_can_create_discussion_in_tag_where_can_view_and_can_start()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'tags' => [
|
||||||
|
'data' => [
|
||||||
|
['type' => 'tags', 'id' => 1],
|
||||||
|
['type' => 'tags', 'id' => 11]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_cant_create_discussion_in_child_tag_without_parent_tag()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'tags' => [
|
||||||
|
'data' => [
|
||||||
|
['type' => 'tags', 'id' => 4]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(422, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_can_create_discussion_in_child_tag_with_parent_tag()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'tags' => [
|
||||||
|
'data' => [
|
||||||
|
['type' => 'tags', 'id' => 3],
|
||||||
|
['type' => 'tags', 'id' => 4]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(422, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function primary_tag_required_by_default()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'title' => 'test - too-obscure',
|
||||||
|
'content' => 'predetermined content for automated testing - too-obscure',
|
||||||
|
],
|
||||||
|
'relationships' => [
|
||||||
|
'tags' => [
|
||||||
|
'data' => [
|
||||||
|
['type' => 'tags', 'id' => 11]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(422, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
}
|
107
extensions/tags/tests/integration/api/tags/CreateTest.php
Normal file
107
extensions/tags/tests/integration/api/tags/CreateTest.php
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tags\Tests\integration\api\tags;
|
||||||
|
|
||||||
|
use Flarum\Tags\Tag;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class CreateTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-tags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function normal_user_cant_create_tag()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request(
|
||||||
|
'POST',
|
||||||
|
'/api/tags',
|
||||||
|
['authenticatedAs' => 2]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(403, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_cannot_create_tag_without_data()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/tags', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
'json' => [],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(422, $response->getStatusCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_can_create_tag()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('POST', '/api/tags', [
|
||||||
|
'authenticatedAs' => 1,
|
||||||
|
'json' => [
|
||||||
|
'data' => [
|
||||||
|
'attributes' => [
|
||||||
|
'name' => 'Dev Blog',
|
||||||
|
'slug' => 'dev-blog',
|
||||||
|
'description' => 'Follow Flarum development!',
|
||||||
|
'color' => '#123456'
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(201, $response->getStatusCode());
|
||||||
|
|
||||||
|
// Verify API response body
|
||||||
|
$data = json_decode($response->getBody(), true);
|
||||||
|
$this->assertEquals('Dev Blog', Arr::get($data, 'data.attributes.name'));
|
||||||
|
$this->assertEquals('dev-blog', Arr::get($data, 'data.attributes.slug'));
|
||||||
|
$this->assertEquals('Follow Flarum development!', Arr::get($data, 'data.attributes.description'));
|
||||||
|
$this->assertEquals('#123456', Arr::get($data, 'data.attributes.color'));
|
||||||
|
$this->assertNull(Arr::get($data, 'data.attributes.icon'));
|
||||||
|
|
||||||
|
// Verify database entry
|
||||||
|
$tag = Tag::all()->last();
|
||||||
|
$this->assertEquals('Dev Blog', $tag->name);
|
||||||
|
$this->assertEquals('dev-blog', $tag->slug);
|
||||||
|
$this->assertEquals('Follow Flarum development!', $tag->description);
|
||||||
|
$this->assertEquals('#123456', $tag->color);
|
||||||
|
$this->assertNull($tag->icon);
|
||||||
|
}
|
||||||
|
}
|
116
extensions/tags/tests/integration/authorization/PolicyTest.php
Normal file
116
extensions/tags/tests/integration/authorization/PolicyTest.php
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tags\Tests\integration\authorization;
|
||||||
|
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Tags\Tests\integration\RetrievesRepresentativeTags;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Flarum\User\User;
|
||||||
|
|
||||||
|
class PolicyTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
use RetrievesRepresentativeTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-tags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'tags' => $this->tags(),
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function cant_start_discussion_globally_if_permission_not_granted()
|
||||||
|
{
|
||||||
|
$this->database()->table('group_permission')->where('permission', 'startDiscussion')->delete();
|
||||||
|
|
||||||
|
$this->assertFalse(User::find(2)->can('startDiscussion'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function can_start_discussion_globally_if_allowed_in_primary_tag()
|
||||||
|
{
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag6.startDiscussion'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->database()->table('group_permission')->where('permission', 'startDiscussion')->delete();
|
||||||
|
|
||||||
|
$this->assertTrue(User::find(2)->can('startDiscussion'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function cant_start_discussion_globally_if_allowed_in_child_tag_only()
|
||||||
|
{
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag8.startDiscussion'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->database()->table('group_permission')->where('permission', 'startDiscussion')->delete();
|
||||||
|
|
||||||
|
$this->assertFalse(User::find(2)->can('startDiscussion'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function cant_start_discussion_globally_if_allowed_in_secondary_tag()
|
||||||
|
{
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag11.startDiscussion'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->database()->table('group_permission')->where('permission', 'startDiscussion')->delete();
|
||||||
|
|
||||||
|
$this->assertFalse(User::find(2)->can('startDiscussion'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function can_start_discussion_globally_if_allowed_in_secondary_tag_and_minimums_adjusted()
|
||||||
|
{
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag11.startDiscussion'],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->setting('flarum-tags.min_primary_tags', 0);
|
||||||
|
$this->setting('flarum-tags.min_secondary_tags', 1);
|
||||||
|
|
||||||
|
$this->database()->table('group_permission')->where('permission', 'startDiscussion')->delete();
|
||||||
|
|
||||||
|
$this->assertTrue(User::find(2)->can('startDiscussion'));
|
||||||
|
}
|
||||||
|
}
|
16
extensions/tags/tests/integration/setup.php
Normal file
16
extensions/tags/tests/integration/setup.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use Flarum\Testing\integration\Setup\SetupScript;
|
||||||
|
|
||||||
|
require __DIR__.'/../../vendor/autoload.php';
|
||||||
|
|
||||||
|
$setup = new SetupScript();
|
||||||
|
|
||||||
|
$setup->run();
|
@@ -0,0 +1,127 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tags\Tests\integration\visibility;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Tags\Tests\integration\RetrievesRepresentativeTags;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class DiscussionVisibilityTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
use RetrievesRepresentativeTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-tags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'discussions' => [
|
||||||
|
['id' => 1, 'title' => 'no tags', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 2, 'title' => 'open tags', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 3, 'title' => 'open tag, restricted child tag', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 4, 'title' => 'open tag, one restricted secondary tag', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
['id' => 5, 'title' => 'all closed', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
|
||||||
|
],
|
||||||
|
'posts' => [
|
||||||
|
['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 3, 'discussion_id' => 3, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 4, 'discussion_id' => 4, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
['id' => 5, 'discussion_id' => 5, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p></p></t>'],
|
||||||
|
],
|
||||||
|
'discussion_tag' => [
|
||||||
|
['discussion_id' => 2, 'tag_id' => 1],
|
||||||
|
['discussion_id' => 3, 'tag_id' => 2],
|
||||||
|
['discussion_id' => 3, 'tag_id' => 5],
|
||||||
|
['discussion_id' => 4, 'tag_id' => 1],
|
||||||
|
['discussion_id' => 4, 'tag_id' => 11],
|
||||||
|
['discussion_id' => 5, 'tag_id' => 6],
|
||||||
|
['discussion_id' => 5, 'tag_id' => 7],
|
||||||
|
['discussion_id' => 5, 'tag_id' => 8],
|
||||||
|
],
|
||||||
|
'tags' => $this->tags(),
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag5.viewDiscussions'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag8.viewDiscussions'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag11.viewDiscussions']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_sees_all()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 1
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '2', '3', '4', '5'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_sees_where_allowed()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/discussions', [
|
||||||
|
'authenticatedAs' => 2
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
// 5 isnt included because parent access doesnt necessarily give child access
|
||||||
|
// 6, 7, 8 aren't included because child access shouldnt work unless parent
|
||||||
|
// access is also given.
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '2', '3', '4'], $ids);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function guest_cant_see_restricted_or_children_of_restricted()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/discussions')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$ids = Arr::pluck($data, 'id');
|
||||||
|
$this->assertEqualsCanonicalizing(['1', '2'], $ids);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This file is part of Flarum.
|
||||||
|
*
|
||||||
|
* For detailed copyright and license information, please view the
|
||||||
|
* LICENSE file that was distributed with this source code.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Flarum\Tags\Tests\integration\visibility;
|
||||||
|
|
||||||
|
use Flarum\Group\Group;
|
||||||
|
use Flarum\Tags\Tests\integration\RetrievesRepresentativeTags;
|
||||||
|
use Flarum\Testing\integration\RetrievesAuthorizedUsers;
|
||||||
|
use Flarum\Testing\integration\TestCase;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
|
class TagVisibilityTest extends TestCase
|
||||||
|
{
|
||||||
|
use RetrievesAuthorizedUsers;
|
||||||
|
use RetrievesRepresentativeTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
$this->extension('flarum-tags');
|
||||||
|
|
||||||
|
$this->prepareDatabase([
|
||||||
|
'tags' => $this->tags(),
|
||||||
|
'users' => [
|
||||||
|
$this->normalUser(),
|
||||||
|
],
|
||||||
|
'group_permission' => [
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag8.viewDiscussions'],
|
||||||
|
['group_id' => Group::MEMBER_ID, 'permission' => 'tag11.viewDiscussions']
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function admin_sees_all()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/tags', [
|
||||||
|
'authenticatedAs' => 1
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
$this->assertEquals(['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'], Arr::pluck($data, 'id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function user_sees_where_allowed()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/tags', [
|
||||||
|
'authenticatedAs' => 2
|
||||||
|
])
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
// 5 isnt included because parent access doesnt necessarily give child access
|
||||||
|
// 6, 7, 8 aren't included because child access shouldnt work unless parent
|
||||||
|
// access is also given.
|
||||||
|
$this->assertEquals(['1', '2', '3', '4', '9', '10', '11'], Arr::pluck($data, 'id'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
*/
|
||||||
|
public function guest_cant_see_restricted_or_children_of_restricted()
|
||||||
|
{
|
||||||
|
$response = $this->send(
|
||||||
|
$this->request('GET', '/api/tags')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
$data = json_decode($response->getBody()->getContents(), true)['data'];
|
||||||
|
|
||||||
|
// Order-independent comparison
|
||||||
|
$this->assertEquals(['1', '2', '3', '4', '9', '10'], Arr::pluck($data, 'id'));
|
||||||
|
}
|
||||||
|
}
|
24
extensions/tags/tests/phpunit.integration.xml
Normal file
24
extensions/tags/tests/phpunit.integration.xml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="true"
|
||||||
|
stopOnFailure="false"
|
||||||
|
>
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">../src/</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Flarum Integration Tests">
|
||||||
|
<directory suffix="Test.php">./integration</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
</phpunit>
|
27
extensions/tags/tests/phpunit.unit.xml
Normal file
27
extensions/tags/tests/phpunit.unit.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
|
||||||
|
backupGlobals="false"
|
||||||
|
backupStaticAttributes="false"
|
||||||
|
colors="true"
|
||||||
|
convertErrorsToExceptions="true"
|
||||||
|
convertNoticesToExceptions="true"
|
||||||
|
convertWarningsToExceptions="true"
|
||||||
|
processIsolation="false"
|
||||||
|
stopOnFailure="false"
|
||||||
|
>
|
||||||
|
<coverage processUncoveredFiles="true">
|
||||||
|
<include>
|
||||||
|
<directory suffix=".php">../src/</directory>
|
||||||
|
</include>
|
||||||
|
</coverage>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="Flarum Unit Tests">
|
||||||
|
<directory suffix="Test.php">./unit</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<listeners>
|
||||||
|
<listener class="\Mockery\Adapter\Phpunit\TestListener" />
|
||||||
|
</listeners>
|
||||||
|
</phpunit>
|
0
extensions/tags/tests/unit/.gitkeep
Normal file
0
extensions/tags/tests/unit/.gitkeep
Normal file
Reference in New Issue
Block a user