135 lines
5 KiB
PHP
135 lines
5 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
// petition-map.php defines cache-path constants and functions.
|
||
|
|
// petitionMapBuildData() takes an explicit $csvPath so it can be tested
|
||
|
|
// with temp files; the cache constants are not touched by these tests.
|
||
|
|
//
|
||
|
|
// Context is already stubbed in PetitionFormTest.php (loaded first alphabetically),
|
||
|
|
// so we do not redeclare it here.
|
||
|
|
require_once CUSTOM_DIR . '/plugins/page/petition-map.php';
|
||
|
|
|
||
|
|
// CSV header row shared across tests
|
||
|
|
const PETITION_MAP_CSV_HEADER = ['timestamp', 'email', 'firstname', 'surname', 'region', 'display', 'status', 'token', 'token_created', 'ip_hash'];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Write rows to a temp file and return its path.
|
||
|
|
* The caller is responsible for unlinking it.
|
||
|
|
*/
|
||
|
|
function mapTestCsv(array $rows): string
|
||
|
|
{
|
||
|
|
$path = tempnam(sys_get_temp_dir(), 'map_test_');
|
||
|
|
$fp = fopen($path, 'w');
|
||
|
|
foreach ($rows as $row) {
|
||
|
|
fputcsv($fp, $row, ',', '"', '');
|
||
|
|
}
|
||
|
|
fclose($fp);
|
||
|
|
return $path;
|
||
|
|
}
|
||
|
|
|
||
|
|
// --- petitionMapBuildData: missing / empty file ---
|
||
|
|
|
||
|
|
it('returns empty data when CSV does not exist', function () {
|
||
|
|
$result = petitionMapBuildData('/tmp/nonexistent-petition-map-test.csv');
|
||
|
|
expect($result['total'])->toBe(0);
|
||
|
|
expect($result)->toHaveKey('generated');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns zero total for a CSV with only pending signatures', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Ola', 'Hansen', 'oslo', 'semi', 'pending', 'tok1', time(), 'hash1'],
|
||
|
|
]);
|
||
|
|
expect(petitionMapBuildData($path)['total'])->toBe(0);
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('returns zero total for a CSV with only the header', function () {
|
||
|
|
$path = mapTestCsv([PETITION_MAP_CSV_HEADER]);
|
||
|
|
expect(petitionMapBuildData($path)['total'])->toBe(0);
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
// --- petitionMapBuildData: counting confirmed signatures ---
|
||
|
|
|
||
|
|
it('counts confirmed signatures and groups them by region', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||
|
|
[time(), 'b@b.com', 'Ola', 'Nilsen', 'oslo', 'semi', 'confirmed', 't2', time(), 'h2'],
|
||
|
|
[time(), 'c@b.com', 'Per', 'Olsen', 'bergen', 'semi', 'confirmed', 't3', time(), 'h3'],
|
||
|
|
]);
|
||
|
|
$result = petitionMapBuildData($path);
|
||
|
|
expect($result['total'])->toBe(3);
|
||
|
|
expect($result['regions']['oslo']['count'])->toBe(2);
|
||
|
|
expect($result['regions']['bergen']['count'])->toBe(1);
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('ignores non-confirmed rows (pending, deleted, etc.)', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||
|
|
[time(), 'b@b.com', 'Ola', 'Nilsen', 'oslo', 'semi', 'pending', 't2', time(), 'h2'],
|
||
|
|
[time(), 'c@b.com', 'Per', 'Olsen', 'oslo', 'semi', 'deleted', 't3', time(), 'h3'],
|
||
|
|
]);
|
||
|
|
$result = petitionMapBuildData($path);
|
||
|
|
expect($result['total'])->toBe(1);
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('skips confirmed entries that have no region', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Kari', 'Hansen', '', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||
|
|
]);
|
||
|
|
expect(petitionMapBuildData($path)['total'])->toBe(0);
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
// --- petitionMapBuildData: name handling ---
|
||
|
|
|
||
|
|
it('uses only the first word of the firstname', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Kari Marte', 'Hansen', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||
|
|
]);
|
||
|
|
$signer = petitionMapBuildData($path)['regions']['oslo']['signers'][0];
|
||
|
|
expect($signer['n'])->toBe('Kari');
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('sets name to null and anonymous flag true for anonymous display', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Kari', 'Hansen', 'oslo', 'anonymous', 'confirmed', 't1', time(), 'h1'],
|
||
|
|
]);
|
||
|
|
$signer = petitionMapBuildData($path)['regions']['oslo']['signers'][0];
|
||
|
|
expect($signer['n'])->toBeNull();
|
||
|
|
expect($signer['a'])->toBeTrue();
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('sets anonymous flag false for non-anonymous display', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'a@b.com', 'Lars', 'Berg', 'oslo', 'semi', 'confirmed', 't1', time(), 'h1'],
|
||
|
|
]);
|
||
|
|
$signer = petitionMapBuildData($path)['regions']['oslo']['signers'][0];
|
||
|
|
expect($signer['a'])->toBeFalse();
|
||
|
|
unlink($path);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('does not include emails, surnames, tokens, or ip_hashes in the output', function () {
|
||
|
|
$path = mapTestCsv([
|
||
|
|
PETITION_MAP_CSV_HEADER,
|
||
|
|
[time(), 'secret@b.com', 'Lars', 'SecretSurname', 'oslo', 'semi', 'confirmed', 'secret-token', time(), 'secret-hash'],
|
||
|
|
]);
|
||
|
|
$result = petitionMapBuildData($path);
|
||
|
|
$json = json_encode($result);
|
||
|
|
expect($json)->not->toContain('secret@b.com');
|
||
|
|
expect($json)->not->toContain('SecretSurname');
|
||
|
|
expect($json)->not->toContain('secret-token');
|
||
|
|
expect($json)->not->toContain('secret-hash');
|
||
|
|
unlink($path);
|
||
|
|
});
|