File size: 5,249 Bytes
c938f2d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | <?php
/**
* Matomo - free/libre analytics platform
*
* @link https://matomo.org
* @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
*/
declare(strict_types=1);
namespace Piwik\Plugins\BotTracking\tests\Unit;
use PHPUnit\Framework\TestCase;
use Piwik\DataTable;
use Piwik\DataTable\Row;
use Piwik\Plugins\BotTracking\Metrics;
use Piwik\Plugins\BotTracking\RecordBuilders\AIChatbotFavouredPages;
/**
* Covers the pure union logic behind the Favoured Pages records (the merge that used to live in the
* deleted FavouredPagesMerger). The query and archiving glue are exercised by the system ApiTest.
*
* @group BotTracking
* @group AIChatbotFavouredPages
* @group Plugins
*/
class AIChatbotFavouredPagesTest extends TestCase
{
/**
* @param array<int, array{string, int}> $rows list of [label, value]
*/
private static function table(string $valueColumn, array $rows): DataTable
{
$table = new DataTable();
foreach ($rows as [$label, $value]) {
$table->addRow(new Row([Row::COLUMNS => ['label' => $label, $valueColumn => $value]]));
}
return $table;
}
/**
* The normal (non-summary) rows as label => [human, ai].
*
* @return array<string, array{int, int}>
*/
private static function asMap(DataTable $table): array
{
$out = [];
foreach ($table->getRows() as $row) {
if ($row->isSummaryRow()) {
continue;
}
$out[(string) $row->getColumn('label')] = [
(int) $row->getColumn(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS),
(int) $row->getColumn(Metrics::COLUMN_AI_CHATBOT_REQUESTS),
];
}
return $out;
}
public function testMergesOverlappingHumanOnlyAndAiOnlyUrls(): void
{
$human = self::table(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS, [
['example.com/both', 100],
['example.com/human-only', 40],
]);
$ai = self::table(Metrics::COLUMN_REQUESTS, [
['example.com/both', 7],
['example.com/ai-only', 12],
]);
$merged = AIChatbotFavouredPages::mergeHumanAndAiTables($human, $ai);
self::assertSame([
'example.com/both' => [100, 7],
'example.com/ai-only' => [0, 12],
'example.com/human-only' => [40, 0],
], self::asMap($merged));
}
public function testMissingSidesDefaultToZero(): void
{
$merged = AIChatbotFavouredPages::mergeHumanAndAiTables(
self::table(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS, [['example.com/h', 5]]),
self::table(Metrics::COLUMN_REQUESTS, [['example.com/a', 9]])
);
self::assertSame(['example.com/a' => [0, 9], 'example.com/h' => [5, 0]], self::asMap($merged));
}
public function testEmptyLabelsAreSkipped(): void
{
$merged = AIChatbotFavouredPages::mergeHumanAndAiTables(
self::table(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS, [['', 5], ['example.com/h', 3]]),
self::table(Metrics::COLUMN_REQUESTS, [['', 9]])
);
self::assertSame(['example.com/h' => [3, 0]], self::asMap($merged));
}
public function testCombinesTruncationTailIntoUnscoredOthersRow(): void
{
$human = self::table(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS, [['example.com/h', 3]]);
$humanSummary = new Row([Row::COLUMNS => ['label' => 'Others', Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS => 999]]);
$humanSummary->setIsSummaryRow();
$human->addSummaryRow($humanSummary);
$ai = self::table(Metrics::COLUMN_REQUESTS, [['example.com/a', 4]]);
$aiSummary = new Row([Row::COLUMNS => ['label' => 'Others', Metrics::COLUMN_REQUESTS => 888]]);
$aiSummary->setIsSummaryRow();
$ai->addSummaryRow($aiSummary);
$merged = AIChatbotFavouredPages::mergeHumanAndAiTables($human, $ai);
// The normal per-URL union is unaffected by the truncation tails.
self::assertSame(['example.com/a' => [0, 4], 'example.com/h' => [3, 0]], self::asMap($merged));
// Both per-side "Others" tails combine into one summary row carrying both sums...
$others = $merged->getSummaryRow();
self::assertInstanceOf(Row::class, $others);
self::assertSame(999, (int) $others->getColumn(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS));
self::assertSame(888, (int) $others->getColumn(Metrics::COLUMN_AI_CHATBOT_REQUESTS));
// ...and it is left unscored (no discrepancy score is computed for an aggregate of many URLs).
self::assertFalse($others->getColumn(Metrics::COLUMN_DISCREPANCY_SCORE));
}
public function testProducesCanonicalColumnSet(): void
{
$merged = AIChatbotFavouredPages::mergeHumanAndAiTables(
self::table(Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS, [['example.com/h', 5]]),
self::table(Metrics::COLUMN_REQUESTS, [['example.com/h', 2]])
);
$row = $merged->getFirstRow();
self::assertSame(
['label', Metrics::COLUMN_UNIQUE_HUMAN_PAGEVIEWS, Metrics::COLUMN_AI_CHATBOT_REQUESTS],
array_keys($row->getColumns())
);
}
}
|