test(helpdesk): add OData injection rejection tests and meetings cache key coverage

Adds 11 tests for BcODataGateway covering getMeetingsForCustomer (empty,
whitespace, not-configured), OData injection rejection for all customerNo
methods (meetings, contract lines, escalation tickets, controlling), and
valid input acceptance. Also adds meetingsKey format and inclusion tests
for DebitorCacheControl.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-13 15:16:57 +02:00
parent a0cb345e04
commit 3827b09205
2 changed files with 110 additions and 0 deletions

View File

@@ -314,4 +314,102 @@ class BcODataGatewayTest extends TestCase
$results = $gateway->getTicketsForControlling('10610'); $results = $gateway->getTicketsForControlling('10610');
$this->assertSame([], $results); $this->assertSame([], $results);
} }
// ── Meetings: empty / whitespace / not-configured ──────────────
public function testGetMeetingsForCustomerReturnsEmptyForEmptyNo(): void
{
$gateway = $this->createGatewayWithSettings();
$results = $gateway->getMeetingsForCustomer('');
$this->assertSame([], $results);
}
public function testGetMeetingsForCustomerReturnsEmptyForWhitespace(): void
{
$gateway = $this->createGatewayWithSettings();
$results = $gateway->getMeetingsForCustomer(' ');
$this->assertSame([], $results);
}
public function testGetMeetingsForCustomerReturnsEmptyWhenNotConfigured(): void
{
$gateway = $this->createGatewayWithSettings(false);
$results = $gateway->getMeetingsForCustomer('10610');
$this->assertSame([], $results);
}
// ── OData injection rejection on customerNo methods ────────────
public function testGetMeetingsForCustomerRejectsODataInjection(): void
{
$gateway = $this->createGatewayWithSettings();
$this->expectException(\InvalidArgumentException::class);
$gateway->getMeetingsForCustomer("10254' or '1'='1");
}
public function testGetContractLinesForCustomerRejectsODataInjection(): void
{
$gateway = $this->createGatewayWithSettings();
$this->expectException(\InvalidArgumentException::class);
$gateway->getContractLinesForCustomer("10254' or '1'='1");
}
public function testGetEscalationTicketsForCustomerRejectsODataInjection(): void
{
$gateway = $this->createGatewayWithSettings();
$this->expectException(\InvalidArgumentException::class);
$gateway->getEscalationTicketsForCustomer("10254' or '1'='1");
}
public function testGetTicketsForControllingRejectsODataInjection(): void
{
$gateway = $this->createGatewayWithSettings();
$this->expectException(\InvalidArgumentException::class);
$gateway->getTicketsForControlling("10254' or '1'='1");
}
public function testGetMeetingsForCustomerRejectsParenthesesInjection(): void
{
$gateway = $this->createGatewayWithSettings();
$this->expectException(\InvalidArgumentException::class);
$gateway->getMeetingsForCustomer("') or true or contains(Name,'");
}
public function testGetContractLinesForCustomerRejectsParenthesesInjection(): void
{
$gateway = $this->createGatewayWithSettings();
$this->expectException(\InvalidArgumentException::class);
$gateway->getContractLinesForCustomer("10254) or (1 eq 1");
}
// ── Valid customerNo values still pass strict validation ───────
public function testGetMeetingsForCustomerAcceptsValidCustomerNo(): void
{
$gateway = $this->createGatewayWithSettings();
$threw = false;
try {
$gateway->getMeetingsForCustomer('10254');
} catch (\InvalidArgumentException) {
$this->fail('Valid customer number should pass strict input validation');
} catch (\RuntimeException) {
$threw = true;
}
// Either cURL failed (expected) or returned empty — both are fine
$this->assertTrue(true);
}
public function testGetContractLinesForCustomerAcceptsCustomerNoWithDash(): void
{
$gateway = $this->createGatewayWithSettings();
$threw = false;
try {
$gateway->getContractLinesForCustomer('10254-A');
} catch (\InvalidArgumentException) {
$this->fail('Customer number with dash should pass strict input validation');
} catch (\RuntimeException) {
$threw = true;
}
$this->assertTrue(true);
}
} }

View File

@@ -71,4 +71,16 @@ class DebitorCacheControlTest extends TestCase
$keys = DebitorCacheControl::allDebitorCacheKeys('7', '10610'); $keys = DebitorCacheControl::allDebitorCacheKeys('7', '10610');
$this->assertContains('module.helpdesk.debitor.v2.controlling-tickets.7.10610', $keys); $this->assertContains('module.helpdesk.debitor.v2.controlling-tickets.7.10610', $keys);
} }
public function testMeetingsKeyFormat(): void
{
$key = DebitorCacheControl::meetingsKey('3', '10254');
$this->assertSame('module.helpdesk.debitor.v2.meetings.3.10254', $key);
}
public function testAllDebitorCacheKeysIncludesMeetingsKey(): void
{
$keys = DebitorCacheControl::allDebitorCacheKeys('3', '10254');
$this->assertContains('module.helpdesk.debitor.v2.meetings.3.10254', $keys);
}
} }