1
0
mirror of https://github.com/flarum/core.git synced 2025-08-27 18:10:34 +02:00

Compare commits

..

2 Commits

Author SHA1 Message Date
Sami Mazouz
d087396494 fix: title scoring issues with group by clause
Signed-off-by: Sami Mazouz <sychocouldy@gmail.com>
2023-03-04 16:54:09 +01:00
Sami Mazouz
0d53660eeb test: fulltext search scores properly and prioritizes titles
Signed-off-by: Sami Mazouz <sychocouldy@gmail.com>
2023-03-04 16:53:55 +01:00
12 changed files with 243 additions and 115 deletions

View File

@@ -142,7 +142,8 @@
"mockery/mockery": "^1.4",
"phpunit/phpunit": "^9.0",
"phpstan/phpstan": ">=1.8.11 < 1.9.0",
"nunomaduro/larastan": "^1.0"
"nunomaduro/larastan": "^1.0",
"symfony/var-dumper": "*"
},
"config": {
"sort-packages": true

2
extensions/flags/js/dist/forum.js generated vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,6 @@
import type Flag from '../forum/models/Flag';
import type FlagListState from '../forum/states/FlagListState';
import type Mithril from 'mithril';
import type ItemList from 'flarum/common/utils/ItemList';
import Flag from '../forum/models/Flag';
import FlagListState from '../forum/states/FlagListState';
import Mithril from 'mithril';
declare module 'flarum/common/models/Post' {
export default interface Post {
@@ -18,8 +17,6 @@ declare module 'flarum/forum/ForumApplication' {
declare module 'flarum/forum/components/Post' {
export default interface Post {
dismissFlag: (body: any) => Promise<any>;
flagActionItems: () => ItemList<Mithril.Children>;
flagReason: (flag: Flag) => Mithril.Children;
}
}

View File

@@ -1,14 +1,10 @@
import { extend } from 'flarum/common/extend';
import app from 'flarum/forum/app';
import Post from 'flarum/forum/components/Post';
import CommentPost from 'flarum/forum/components/CommentPost';
import Button from 'flarum/common/components/Button';
import ItemList from 'flarum/common/utils/ItemList';
import PostControls from 'flarum/forum/utils/PostControls';
import humanTime from 'flarum/common/utils/humanTime';
import isObject from 'flarum/common/utils/isObject';
import type Flag from './models/Flag';
import type Mithril from 'mithril';
export default function () {
extend(Post.prototype, 'elementAttrs', function (attrs) {
@@ -25,7 +21,7 @@ export default function () {
this.subtree.invalidate();
if (app.flags.cache) {
app.flags.cache.some((flag: Flag, i: number) => {
app.flags.cache.some((flag, i) => {
if (flag.post() === post) {
app.flags.cache.splice(i, 1);
@@ -43,8 +39,6 @@ export default function () {
return true;
}
return false;
});
}
@@ -56,17 +50,16 @@ export default function () {
};
Post.prototype.flagActionItems = function () {
const items = new ItemList<Mithril.Children>();
const items = new ItemList();
const controls = PostControls.destructiveControls(this.attrs.post, this);
const controls = PostControls.destructiveControls(this.attrs.post);
Object.keys(controls.items).forEach((k) => {
const item = controls.get(k);
const attrs = isObject(item) && 'attrs' in item ? item.attrs : {};
const attrs = controls.get(k).attrs;
attrs.className = 'Button';
extend(attrs, 'onclick', () => this.dismissFlag({}));
extend(attrs, 'onclick', () => this.dismissFlag());
});
items.add('controls', <div className="ButtonGroup">{controls.toArray()}</div>);
@@ -88,16 +81,12 @@ export default function () {
if (!flags.length) return;
if (post.isHidden() && this instanceof CommentPost) {
this.revealContent = true;
}
if (!isObject(vdom) || !('unshift' in vdom)) return;
if (post.isHidden()) this.revealContent = true;
vdom.unshift(
<div className="Post-flagged">
<div className="Post-flagged-flags">
{flags.map((flag: Flag) => (
{flags.map((flag) => (
<div className="Post-flagged-flag">{this.flagReason(flag)}</div>
))}
</div>
@@ -122,7 +111,5 @@ export default function () {
detail ? <span className="Post-flagged-detail">{detail}</span> : '',
];
}
return null;
};
}

View File

@@ -1,6 +1,4 @@
export default class FlagListState {
index = 0;
constructor(app) {
this.app = app;

View File

@@ -88,7 +88,8 @@
"wikimedia/less.php": "^3.2"
},
"require-dev": {
"flarum/testing": "^1.0.0"
"flarum/testing": "^1.0.0",
"symfony/var-dumper": "*"
},
"autoload": {
"psr-4": {

View File

@@ -18,7 +18,6 @@ core:
custom_footer_text: => core.ref.custom_footer_text
custom_header_heading: Custom Header
custom_header_text: => core.ref.custom_header_text
custom_styles_cannot_use_less_features: "The @import and data-uri features are not allowed in custom LESS."
custom_styles_heading: Custom Styles
custom_styles_text: Customize your forum's appearance by adding your own Less/CSS code to be applied on top of Flarum's default styles.
dark_mode_label: Dark Mode

View File

@@ -30,7 +30,8 @@ class FulltextGambit implements GambitInterface
$query = $search->getQuery();
$grammar = $query->getGrammar();
$discussionSubquery = Discussion::select('id')
$discussionSubquery = Discussion::query()
->selectRaw('id as discussion_id')
->selectRaw('NULL as score')
->selectRaw('first_post_id as most_relevant_post_id')
->whereRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (? IN BOOLEAN MODE)', [$bit]);
@@ -52,17 +53,19 @@ class FulltextGambit implements GambitInterface
// discussions that have a relevant title or that contain relevant posts.
$query
->addSelect('posts_ft.most_relevant_post_id')
->addSelect('posts_ft.score')
->selectRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) as title_score', [$bit])
->join(
new Expression('('.$subquery->toSql().') '.$grammar->wrapTable('posts_ft')),
'posts_ft.discussion_id',
'=',
'discussions.id'
)
->groupBy('discussions.id')
->groupBy('posts_ft.discussion_id', 'title_score')
->addBinding($subquery->getBindings(), 'join');
$search->setDefaultSort(function ($query) use ($grammar, $bit) {
$query->orderByRaw('MATCH('.$grammar->wrap('discussions.title').') AGAINST (?) desc', [$bit]);
$search->setDefaultSort(function ($query) {
$query->orderBy('title_score', 'desc');
$query->orderBy('posts_ft.score', 'desc');
});

View File

@@ -21,7 +21,6 @@ use Illuminate\Filesystem\FilesystemAdapter;
use League\Flysystem\Adapter\NullAdapter;
use League\Flysystem\Filesystem;
use Less_Exception_Parser;
use Symfony\Contracts\Translation\TranslatorInterface;
/**
* @internal
@@ -62,15 +61,6 @@ class ValidateCustomLess
return;
}
// Restrict what features can be used in custom LESS
if (isset($event->settings['custom_less']) && preg_match('/@import|data-uri\s*\(/i', $event->settings['custom_less'])) {
$translator = $this->container->make(TranslatorInterface::class);
throw new ValidationException([
'custom_less' => $translator->trans('core.admin.appearance.custom_styles_cannot_use_less_features')
]);
}
// We haven't saved the settings yet, but we want to trial a full
// recompile of the CSS to see if this custom LESS will break
// anything. In order to do that, we will temporarily override the

View File

@@ -27,9 +27,21 @@ class ListWithFulltextSearchTest extends TestCase
$this->database()->rollBack();
$this->populateSimpleTestData();
$this->populateRealDataForScoreTests();
// We need to call these again, since we rolled back the transaction started by `::app()`.
$this->database()->beginTransaction();
$this->populateDatabase();
}
private function populateSimpleTestData(): void
{
// We need to insert these outside of a transaction, because FULLTEXT indexing,
// which is needed for search, doesn't happen in transactions.
// We clean it up explcitly at the end.
// We clean it up explicitly at the end.
$this->database()->table('discussions')->insert([
['id' => 1, 'title' => 'lightsail in title', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
['id' => 2, 'title' => 'lightsail in title too', 'created_at' => Carbon::createFromDate(2020, 01, 01)->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1],
@@ -48,11 +60,130 @@ class ListWithFulltextSearchTest extends TestCase
['id' => 6, 'discussion_id' => 4, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>తెలుగు</p></t>'],
['id' => 7, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>支持中文吗</p></t>'],
]);
}
// We need to call these again, since we rolled back the transaction started by `::app()`.
$this->database()->beginTransaction();
private function populateRealDataForScoreTests(): void
{
// Disable forign key constraints temporarily
$this->database()->statement('SET FOREIGN_KEY_CHECKS=0');
$this->populateDatabase();
$this->database()->table('discussions')->insert([
['id' => 7, 'title' => 'User should be able to promote the visible, foreground, non-saved window to a saved window', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1, 'first_post_id' => 8],
['id' => 8, 'title' => 'Moving Tab in Saved Window to New Window Destroys Non-Saved Window', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1, 'first_post_id' => 16],
['id' => 9, 'title' => 'Current saved window is renamed when moving normal window to saved window area', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1, 'first_post_id' => 18],
['id' => 10, 'title' => 'Save bookmarks edit page window position is not saved', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1, 'first_post_id' => 19],
['id' => 11, 'title' => 'Favicons in the saved bookmarks flash on and off', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1, 'first_post_id' => 20],
['id' => 12, 'title' => 'User should be able to switch \'in-place\' between non-saved windows', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'comment_count' => 1, 'first_post_id' => 23],
]);
$this->database()->table('posts')->insert([
['id' => 8, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>Unless I\'m missing something, currently there does not appear to be a way to promote a non-saved window to a saved window. A user must be click the \'+\' button in the window menu dropdown, then re-open all open tabs in the current window in the new saved window. That doesn\'t sound fun if one has multiple tabs open.</p>
<p>This would be ameliorated if it was possible move tabs to saved windows without physical drag and drop but that doesn\'t seem possible as well, see User should be able to move tabs from visible window to non-visible saved windows</p></t>'],
['id' => 9, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>open sidebar<br/>
switch to either Tabs or Windows view in sidebar<br/>
right click on window title in side bar (eg. "3 Tabs")<br/>
click "Save..."<br/>
OR<br/>
click once on window title in sidebar (eg. "3 Tabs")<br/>
rename window in-place<br/>
I think this is how it is supposed to work.</p>
<p>For me, at least, this funcionality doesn\'t work due to (I imagine) a bug...<br/>
Unless I\'ve misunderstood you?</p></t>'],
['id' => 10, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<r><p>Are you refering to this?</p>
<p><URL url="https://orionfeedback.org/d/4258-window-switcher-i-am-able-to-drag-the-unsaved-window-into-saved-windows-and-it-breaks-the-ui-until-restart">https://orionfeedback.org/d/4258-window-switcher-i-am-able-to-drag-the-unsaved-window-into-saved-windows-and-it-breaks-the-ui-until-restart</URL></p></r>'],
['id' => 11, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>robrecord Yes! Wasn\'t apparent that was the trick. Thanks for pointing it out.<br/>
Vlad My other logged issue seems related to that one</p></t>'],
['id' => 12, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>b3noit Which one?</p>
<p>Is there anything still being asked here?</p></t>'],
['id' => 13, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<r><p>Vlad This one <URL url="https://orionfeedback.org/d/4288-moving-tab-in-saved-window-to-new-window-destroys-non-saved-window">https://orionfeedback.org/d/4288-moving-tab-in-saved-window-to-new-window-destroys-non-saved-window</URL></p>
<p>The post above yours clarified how the UI is supposed to be used here</p></r>'],
['id' => 14, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t>
<p>b3noit So is there an ask here or I can close this?</p></t>'],
['id' => 15, 'discussion_id' => 7, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t>
<p>b3noit ping</p></t>'],
['id' => 26, 'discussion_id' => 12, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => "<t><p>Visible, non-saved windows cannot participate in 'in-place' window switching, they can only be switched via the OS window manager. Its unclear why only saved windows can participate, at the least non-saved windows should be able to opt-in so one window can switch between both saved and non-saved windows.</p></t>"],
['id' => 25, 'discussion_id' => 12, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>@dino can you explain why we decided this?</p>
</t>'],
['id' => 24, 'discussion_id' => 12, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>Vlad b3noit Because when anyone creates new Window (by New Window menu), user will always expect, there will be a separate Window as per any native app. So when having separate Window (for unsaved windows), why in-place switching needed. Lots of users still prefer working with Windows (as this term is very obvious for any OS), sometime side-by-side, sometime by using external display.</p>
<p>Also any modern browser uses Tab-Groups for in-place switching, but they treat traditional Windows in same way. So instead of breaking Native terminology and flow of OS, we preferred to continue with Windows.</p>
<p>Also if some user still feels to use all in-place switching, then that user can create new Windows as new in-place group by clicking "+" from window manager &#128578;</p></t>'],
['id' => 23, 'discussion_id' => 12, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => "<t><p>dino Vlad My thing about the solution of 'well, make a new saved window that does participate in in-place switching' is that:</p>
<p>there's no convenient affordance to move tabs from a visible, non-saved window to a saved window<br/>
there's no convenient affordance to simply transform a non-saved, visible window to a saved window that can interact with in-place switching<br/>
I don't see a reason why a user can't opt in a window into being able to be switched in-place<br/>
From where I'm standing, unless I'm missing something, the abstraction of a window and a group of tabs are two different things; it appears that in the case of Orion, there's an assumption about the relationship between a collection of tabs and windows such that, just like in programming languages with different 'colored' functions (e.g. async in JS, C#, etc), here you have the case of 'colored' windows.</p></t>"],
['id' => 22, 'discussion_id' => 11, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => "<t><p>No and actually it's OK I found it. It was some corrupted pref files. The ones relating to the web icons. I had to reestablish some of my preferences for the app but otherwise not much hassle!</p>
</t>"],
['id' => 21, 'discussion_id' => 11, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>can u provide a video?</p>
</t>'],
['id' => 20, 'discussion_id' => 11, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>Steps to reproduce:<br/>
&lt;Include steps to reproduce the bug; Did you try using Compatibility mode? If applicable, does Safari behave in the same way?&gt;</p>
<p>Expected behavior:<br/>
&lt;What you expected to happen?&gt;</p>
<p>Orion, OS version; hardware type:<br/>
Orion version Version 0.99.121-beta (WebKit 614.1.20)<br/>
MacOS version 12.6 (21G115) Monterey<br/>
M1 Mac mini (M1, 2020)<br/>
Image/Video:<br/>
&lt;Copy/paste or drag and drop to upload images or videos (up to 20MB)&gt;</p>
<p>The bookmarks in my Orion usually show favicons of the sites after I visit them. Today even after clearing cache and restarting the browser several times they are still flashing. Any ideas?</p></t>'],
['id' => 19, 'discussion_id' => 10, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>Steps to reproduce:<br/>
&lt;Include steps to reproduce the bug; Did you try using Compatibility mode? If applicable, does Safari behave in the same way?&gt;</p>
<p>Expected behavior:<br/>
Position is saved.</p>
<p>Orion and macOS:<br/>
&lt;Version&gt;</p>
<p>Image/Video:</p></t>'],
['id' => 18, 'discussion_id' => 9, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => "<t><p>Steps to reproduce:</p>
<p>Have one open window.<br/>
Have a saved window.<br/>
Using the toolbar icon, move the regular window to the saved window<br/>
Expected behavior:<br/>
Window is moved to saved area, doesn't affect other saved windows.<br/>
Orion, OS version; hardware type:<br/>
Version 0.99.123.1-beta (WebKit 615.1.16.1)<br/>
MacBook Pro (macOS Monterey 12.6.2 build 21G320)</p>
<p>Image/Video:</p>
</t>"],
['id' => 17, 'discussion_id' => 8, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>b3noit Can you please record a video of this?</p></t>'],
['id' => 16, 'discussion_id' => 8, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 1, 'type' => 'comment', 'content' => '<t><p>Steps to reproduce:</p>
<p>Initialized a new non-saved window one tab<br/>
Initialize a new saved window with one tab<br/>
Switch in-place to saved window<br/>
Right tab and select move tab to New Window<br/>
Tab moves to non-saved window and replaces previously open tab in non-saved window<br/>
Expected behavior:<br/>
Tab should not replace non-saved window tab</p>
<p>Orion, OS version; hardware type:<br/>
0.99.123 on 12.6.3</p></t>'],
]);
}
/**
@@ -87,6 +218,27 @@ class ListWithFulltextSearchTest extends TestCase
$this->assertEqualsCanonicalizing(['2', '1', '3'], $ids, 'IDs do not match');
}
/**
* @test
*/
public function search_prioritizes_title_search_score_over_post()
{
$response = $this->send(
$this->request('GET', '/api/discussions')
->withQueryParams([
'filter' => ['q' => 'saved bookmarks'],
'include' => 'mostRelevantPost',
])
);
$data = json_decode($response->getBody()->getContents(), true);
$ids = array_map(function ($row) {
return $row['id'];
}, $data['data']);
$this->assertEquals(['11', '10', '7', '8', '9', '12'], $ids, 'IDs do not match');
}
/**
* @test
*/

View File

@@ -74,43 +74,43 @@ class GroupSearchTest extends TestCase
]);
$response = $this->createRequest(['admin'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$response = $this->createRequest(['mod'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
$response = $this->createRequest(['admins'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$response = $this->createRequest(['mods'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
$response = $this->createRequest(['1'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$response = $this->createRequest(['4'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
}
/**
@@ -126,10 +126,10 @@ class GroupSearchTest extends TestCase
$this->createHiddenUser();
$response = $this->createRequest(['99'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
}
/**
@@ -144,13 +144,13 @@ class GroupSearchTest extends TestCase
]);
$this->createMultipleUsersAndGroups();
$response = $this->createRequest(['1', '4', '5', '6', '99'], 2);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$this->assertCount(4, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(4, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertEquals(4, $responseBodyContents['included'][1]['id']);
$this->assertEquals(5, $responseBodyContents['included'][2]['id']);
$this->assertEquals(6, $responseBodyContents['included'][3]['id']);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(4, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(4, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$this->assertEquals(4, $responseBodyContents->included[1]->id);
$this->assertEquals(5, $responseBodyContents->included[2]->id);
$this->assertEquals(6, $responseBodyContents->included[3]->id);
}
/**
@@ -159,43 +159,43 @@ class GroupSearchTest extends TestCase
public function admin_gets_correct_results_group()
{
$response = $this->createRequest(['admin'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$response = $this->createRequest(['mod'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
$response = $this->createRequest(['admins'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$response = $this->createRequest(['mods'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
$response = $this->createRequest(['1'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$response = $this->createRequest(['4'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(0, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertArrayNotHasKey('included', $responseBodyContents, json_encode($responseBodyContents));
$this->assertCount(0, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertObjectNotHasAttribute('included', $responseBodyContents, json_encode($responseBodyContents));
}
/**
@@ -205,11 +205,11 @@ class GroupSearchTest extends TestCase
{
$this->createHiddenUser();
$response = $this->createRequest(['99'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(1, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(99, $responseBodyContents['included'][0]['id']);
$this->assertCount(1, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(1, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(99, $responseBodyContents->included[0]->id);
}
/**
@@ -220,14 +220,14 @@ class GroupSearchTest extends TestCase
$this->createMultipleUsersAndGroups();
$this->createHiddenUser();
$response = $this->createRequest(['1', '4', '5', '6', '99'], 1);
$responseBodyContents = json_decode($response->getBody()->getContents(), true);
$this->assertCount(5, $responseBodyContents['data'], json_encode($responseBodyContents));
$this->assertCount(5, $responseBodyContents['included'], json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents['included'][0]['id']);
$this->assertEquals(99, $responseBodyContents['included'][1]['id']);
$this->assertEquals(4, $responseBodyContents['included'][2]['id']);
$this->assertEquals(5, $responseBodyContents['included'][3]['id']);
$this->assertEquals(6, $responseBodyContents['included'][4]['id']);
$responseBodyContents = json_decode($response->getBody()->getContents());
$this->assertCount(5, $responseBodyContents->data, json_encode($responseBodyContents));
$this->assertCount(5, $responseBodyContents->included, json_encode($responseBodyContents));
$this->assertEquals(1, $responseBodyContents->included[0]->id);
$this->assertEquals(99, $responseBodyContents->included[1]->id);
$this->assertEquals(4, $responseBodyContents->included[2]->id);
$this->assertEquals(5, $responseBodyContents->included[3]->id);
$this->assertEquals(6, $responseBodyContents->included[4]->id);
}
private function createRequest(array $group, int $userId = null)