Skip to content

Commit f9b60af

Browse files
carlbennettclaude
andcommitted
Add PHPUnit tests for UrlFormatter, Event, News\Category, and Packet
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f7eed2c commit f9b60af

4 files changed

Lines changed: 857 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
<?php
2+
3+
namespace BNETDocs\Tests\Libraries\Core;
4+
5+
use \BNETDocs\Libraries\Core\UrlFormatter;
6+
use \PHPUnit\Framework\TestCase;
7+
8+
class UrlFormatterTest extends TestCase
9+
{
10+
protected function setUp(): void
11+
{
12+
// Provide a stable request context for every test.
13+
putenv('HTTP_HOST=test.example.com');
14+
putenv('DOCUMENT_URI=/foo/bar');
15+
putenv('QUERY_STRING=');
16+
putenv('HTTPS'); // unset
17+
putenv('SERVER_PORT'); // unset
18+
}
19+
20+
protected function tearDown(): void
21+
{
22+
putenv('HTTP_HOST');
23+
putenv('SERVER_NAME');
24+
putenv('HOST');
25+
putenv('DOCUMENT_URI');
26+
putenv('QUERY_STRING');
27+
putenv('HTTPS');
28+
putenv('SERVER_PORT');
29+
}
30+
31+
// Scheme detection
32+
33+
public function testHttpWhenNoHttpsEnvAndPortNotSet(): void
34+
{
35+
$url = UrlFormatter::format('/path');
36+
$this->assertStringStartsWith('http://', $url);
37+
}
38+
39+
public function testHttpsWhenServerPortIs443(): void
40+
{
41+
putenv('SERVER_PORT=443');
42+
$url = UrlFormatter::format('/path');
43+
$this->assertStringStartsWith('https://', $url);
44+
}
45+
46+
public function testHttpsWhenHttpsIsOn(): void
47+
{
48+
putenv('HTTPS=on');
49+
$url = UrlFormatter::format('/path');
50+
$this->assertStringStartsWith('https://', $url);
51+
}
52+
53+
public function testHttpsWhenHttpsIsYes(): void
54+
{
55+
putenv('HTTPS=yes');
56+
$url = UrlFormatter::format('/path');
57+
$this->assertStringStartsWith('https://', $url);
58+
}
59+
60+
public function testHttpsWhenHttpsIsTrue(): void
61+
{
62+
putenv('HTTPS=true');
63+
$url = UrlFormatter::format('/path');
64+
$this->assertStringStartsWith('https://', $url);
65+
}
66+
67+
public function testHttpsWhenHttpsIsOne(): void
68+
{
69+
putenv('HTTPS=1');
70+
$url = UrlFormatter::format('/path');
71+
$this->assertStringStartsWith('https://', $url);
72+
}
73+
74+
public function testHttpWhenHttpsIsOff(): void
75+
{
76+
putenv('HTTPS=off');
77+
$url = UrlFormatter::format('/path');
78+
$this->assertStringStartsWith('http://', $url);
79+
}
80+
81+
// Host resolution
82+
83+
public function testUsesCurrentHostForAbsolutePath(): void
84+
{
85+
$url = UrlFormatter::format('/page');
86+
$this->assertStringContainsString('test.example.com', $url);
87+
}
88+
89+
// Absolute path
90+
91+
public function testAbsolutePathUsedAsIs(): void
92+
{
93+
$url = UrlFormatter::format('/new/path');
94+
$this->assertSame('http://test.example.com/new/path', $url);
95+
}
96+
97+
// Relative path
98+
99+
public function testRelativePathSplicedIntoCurrentDirectory(): void
100+
{
101+
// DOCUMENT_URI=/foo/bar → dirname = /foo (starts with /)
102+
// The code does: '/' . ($dir . '/' . $path) = '/' . '/foo/relative' = '//foo/relative'
103+
// This double-slash is a known quirk of the implementation.
104+
$url = UrlFormatter::format('relative');
105+
$this->assertSame('http://test.example.com//foo/relative', $url);
106+
}
107+
108+
// Query string
109+
110+
public function testQueryStringPreserved(): void
111+
{
112+
$url = UrlFormatter::format('/path?key=value&other=123');
113+
$this->assertSame('http://test.example.com/path?key=value&other=123', $url);
114+
}
115+
116+
public function testNoQueryStringProducesNoQuestionMark(): void
117+
{
118+
$url = UrlFormatter::format('/path');
119+
$this->assertStringNotContainsString('?', $url);
120+
}
121+
122+
// Protocol-relative URL (//host/path)
123+
124+
public function testProtocolRelativeUsesCurrentScheme(): void
125+
{
126+
$url = UrlFormatter::format('//other.example.com/page');
127+
$this->assertSame('http://other.example.com/page', $url);
128+
}
129+
130+
public function testProtocolRelativeOverridesHost(): void
131+
{
132+
$url = UrlFormatter::format('//cdn.example.com/asset.js');
133+
$this->assertStringContainsString('cdn.example.com', $url);
134+
$this->assertStringNotContainsString('test.example.com', $url);
135+
}
136+
137+
// Full URL with explicit scheme
138+
139+
public function testFullUrlPreservesSchemeAndHost(): void
140+
{
141+
$url = UrlFormatter::format('https://cdn.example.com/asset.js');
142+
$this->assertSame('https://cdn.example.com/asset.js', $url);
143+
}
144+
145+
public function testFullUrlWithQueryString(): void
146+
{
147+
$url = UrlFormatter::format('https://cdn.example.com/asset.js?v=1');
148+
$this->assertSame('https://cdn.example.com/asset.js?v=1', $url);
149+
}
150+
151+
// Empty input falls back to current path
152+
153+
public function testEmptyValueFallsBackToCurrentPath(): void
154+
{
155+
$url = UrlFormatter::format('');
156+
$this->assertSame('http://test.example.com/foo/bar', $url);
157+
}
158+
}
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
<?php
2+
3+
namespace BNETDocs\Tests\Libraries\EventLog;
4+
5+
use \BNETDocs\Libraries\EventLog\Event;
6+
use \BNETDocs\Libraries\EventLog\EventTypes;
7+
use \LengthException;
8+
use \OutOfBoundsException;
9+
use \PHPUnit\Framework\TestCase;
10+
11+
class EventTest extends TestCase
12+
{
13+
private Event $event;
14+
15+
protected function setUp(): void
16+
{
17+
// null id → allocate() returns true early without touching the database.
18+
$this->event = new Event(null);
19+
}
20+
21+
// Default state after null construction
22+
23+
public function testDefaultIdIsNull(): void
24+
{
25+
$this->assertNull($this->event->getId());
26+
}
27+
28+
public function testDefaultTypeIdIsLogNote(): void
29+
{
30+
$this->assertSame(EventTypes::LOG_NOTE, $this->event->getTypeId());
31+
}
32+
33+
public function testDefaultIpAddressIsNull(): void
34+
{
35+
$this->assertNull($this->event->getIPAddress());
36+
}
37+
38+
public function testDefaultMetaDataIsNull(): void
39+
{
40+
$this->assertNull($this->event->getMetaData());
41+
}
42+
43+
public function testDefaultUserIdIsNull(): void
44+
{
45+
$this->assertNull($this->event->getUserId());
46+
}
47+
48+
public function testDefaultDateTimeIsNull(): void
49+
{
50+
$this->assertNull($this->event->getDateTime());
51+
}
52+
53+
// getURI — null when id is null
54+
55+
public function testGetUriIsNullWhenIdIsNull(): void
56+
{
57+
$this->assertNull($this->event->getURI());
58+
}
59+
60+
// getTypeName — delegates to EventType::__toString()
61+
62+
public function testGetTypeNameForLogNote(): void
63+
{
64+
$this->assertSame('Log Note', $this->event->getTypeName());
65+
}
66+
67+
public function testGetTypeNameAfterTypeIdChange(): void
68+
{
69+
$this->event->setTypeId(EventTypes::USER_CREATED);
70+
$this->assertSame('User Created', $this->event->getTypeName());
71+
}
72+
73+
// setId — bounds validation
74+
75+
public function testSetIdNullIsAllowed(): void
76+
{
77+
$this->event->setId(null);
78+
$this->assertNull($this->event->getId());
79+
}
80+
81+
public function testSetIdValidPositiveIsAllowed(): void
82+
{
83+
$this->event->setId(42);
84+
$this->assertSame(42, $this->event->getId());
85+
}
86+
87+
public function testSetIdNegativeThrowsOutOfBounds(): void
88+
{
89+
$this->expectException(OutOfBoundsException::class);
90+
$this->event->setId(-1);
91+
}
92+
93+
// setTypeId — bounds validation
94+
95+
public function testSetTypeIdValidIsAllowed(): void
96+
{
97+
$this->event->setTypeId(EventTypes::PACKET_CREATED);
98+
$this->assertSame(EventTypes::PACKET_CREATED, $this->event->getTypeId());
99+
}
100+
101+
public function testSetTypeIdNegativeThrowsOutOfBounds(): void
102+
{
103+
$this->expectException(OutOfBoundsException::class);
104+
$this->event->setTypeId(-1);
105+
}
106+
107+
// setIPAddress — format and length validation
108+
109+
public function testSetIPAddressValidIPv4(): void
110+
{
111+
$this->event->setIPAddress('127.0.0.1');
112+
$this->assertSame('127.0.0.1', $this->event->getIPAddress());
113+
}
114+
115+
public function testSetIPAddressValidIPv6(): void
116+
{
117+
$this->event->setIPAddress('::1');
118+
$this->assertSame('::1', $this->event->getIPAddress());
119+
}
120+
121+
public function testSetIPAddressFullIPv6(): void
122+
{
123+
$this->event->setIPAddress('2001:db8::1');
124+
$this->assertSame('2001:db8::1', $this->event->getIPAddress());
125+
}
126+
127+
public function testSetIPAddressNullIsAllowed(): void
128+
{
129+
$this->event->setIPAddress('127.0.0.1');
130+
$this->event->setIPAddress(null);
131+
$this->assertNull($this->event->getIPAddress());
132+
}
133+
134+
public function testSetIPAddressInvalidStringThrowsLengthException(): void
135+
{
136+
$this->expectException(LengthException::class);
137+
$this->event->setIPAddress('not-an-ip');
138+
}
139+
140+
public function testSetIPAddressEmptyStringThrowsLengthException(): void
141+
{
142+
$this->expectException(LengthException::class);
143+
$this->event->setIPAddress('');
144+
}
145+
146+
// setMetaData
147+
148+
public function testSetMetaDataNullIsAllowed(): void
149+
{
150+
$this->event->setMetaData(null);
151+
$this->assertNull($this->event->getMetaData());
152+
}
153+
154+
public function testSetMetaDataArrayIsAllowed(): void
155+
{
156+
$this->event->setMetaData(['key' => 'value']);
157+
$this->assertSame(['key' => 'value'], $this->event->getMetaData());
158+
}
159+
160+
public function testSetMetaDataScalarIsAllowed(): void
161+
{
162+
$this->event->setMetaData('hello');
163+
$this->assertSame('hello', $this->event->getMetaData());
164+
}
165+
166+
// setUserId
167+
168+
public function testSetUserIdNullIsAllowed(): void
169+
{
170+
$this->event->setUserId(null);
171+
$this->assertNull($this->event->getUserId());
172+
}
173+
174+
public function testSetUserIdPositiveIsAllowed(): void
175+
{
176+
$this->event->setUserId(99);
177+
$this->assertSame(99, $this->event->getUserId());
178+
}
179+
180+
public function testSetUserIdNegativeThrowsOutOfBounds(): void
181+
{
182+
$this->expectException(OutOfBoundsException::class);
183+
$this->event->setUserId(-1);
184+
}
185+
186+
// setDateTime
187+
188+
public function testSetDateTimeFromString(): void
189+
{
190+
$this->event->setDateTime('2024-06-01 12:00:00');
191+
$this->assertNotNull($this->event->getDateTime());
192+
$this->assertSame('2024-06-01 12:00:00', $this->event->getDateTime()->format('Y-m-d H:i:s'));
193+
}
194+
195+
public function testSetDateTimeNullClearsValue(): void
196+
{
197+
$this->event->setDateTime('2024-01-01');
198+
$this->event->setDateTime(null);
199+
$this->assertNull($this->event->getDateTime());
200+
}
201+
202+
public function testSetDateTimeFromInterface(): void
203+
{
204+
$dt = new \DateTimeImmutable('2024-03-15 08:00:00', new \DateTimeZone('UTC'));
205+
$this->event->setDateTime($dt);
206+
$this->assertSame('2024-03-15', $this->event->getDateTime()->format('Y-m-d'));
207+
}
208+
209+
// jsonSerialize
210+
211+
public function testJsonSerializeHasExpectedKeys(): void
212+
{
213+
$data = $this->event->jsonSerialize();
214+
$this->assertArrayHasKey('datetime', $data);
215+
$this->assertArrayHasKey('id', $data);
216+
$this->assertArrayHasKey('ip_address', $data);
217+
$this->assertArrayHasKey('meta_data', $data);
218+
$this->assertArrayHasKey('type_id', $data);
219+
$this->assertArrayHasKey('user_id', $data);
220+
}
221+
222+
public function testJsonSerializeDefaultValues(): void
223+
{
224+
$data = $this->event->jsonSerialize();
225+
$this->assertNull($data['id']);
226+
$this->assertNull($data['ip_address']);
227+
$this->assertNull($data['meta_data']);
228+
$this->assertSame(EventTypes::LOG_NOTE, $data['type_id']);
229+
$this->assertNull($data['user_id']);
230+
}
231+
}

0 commit comments

Comments
 (0)