Skip to content

Commit f89731a

Browse files
committed
[UPD] rel v4.0.0
1 parent 854eee5 commit f89731a

20 files changed

Lines changed: 2095 additions & 26 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@
1010
- NodeSet2.xml code generator: typed DTOs, PHP enums, binary codecs, registrar with dependency resolution.
1111
- Server address space dump to NodeSet2.xml.
1212
- Server certificate trust management from the terminal.
13+
- **272 tests** (253 unit + 19 integration), 592 assertions, **99.9% code coverage**.

CONTRIBUTING.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ docker compose up -d
3535

3636
## Running Tests
3737

38+
The project currently has **272 tests** (253 unit + 19 integration) with 592 assertions and **99.9% code coverage**.
39+
3840
```bash
3941
# All tests
4042
./vendor/bin/pest
@@ -48,7 +50,7 @@ docker compose up -d
4850
# A specific test file
4951
./vendor/bin/pest tests/Unit/ArgvParserTest.php
5052

51-
# With coverage report
53+
# With coverage report (requires pcov)
5254
php -d pcov.enabled=1 ./vendor/bin/pest --coverage
5355
```
5456

@@ -163,6 +165,7 @@ composer format:check
163165
- Use Pest PHP syntax (not PHPUnit)
164166
- Group integration tests with `->group('integration')`
165167
- Use `TestHelper::safeDisconnect()` in `finally` blocks for integration tests
168+
- **Code coverage must remain >= 99.5%.** Run `php -d pcov.enabled=1 ./vendor/bin/pest --coverage` and verify the total before submitting. Pull requests that drop coverage below this threshold will not be merged
166169

167170
### Commits
168171

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,20 @@ opcua-cli read opc.tcp://localhost:4840 "i=2259" --debug-file=/tmp/opcua.log --j
278278
| **`php-opcua/opcua-cli`** | **CLI tool (this package)** |
279279
| [`php-opcua/opcua-client-nodeset`](https://github.com/php-opcua/opcua-client-nodeset) | Pre-built NodeSet2 types for 51 OPC UA companion specifications |
280280

281+
## Testing
282+
283+
The project includes 272 tests (253 unit + 19 integration) with 592 assertions and **99.9% code coverage**.
284+
285+
```bash
286+
# Run all tests
287+
./vendor/bin/pest
288+
289+
# Run with coverage report (requires pcov)
290+
php -d pcov.enabled=1 ./vendor/bin/pest --coverage
291+
```
292+
293+
Code coverage must remain >= 99.5%. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
294+
281295
## Requirements
282296

283297
- PHP >= 8.2

ROADMAP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
- [x] NodeSet2.xml code generator: typed DTOs, PHP enums, binary codecs, registrar
1313
- [x] Server address space dump to NodeSet2.xml
1414
- [x] Server certificate trust management from the terminal
15+
- [x] **272 tests** (253 unit + 19 integration), 592 assertions, 99.9% code coverage
1516

1617
## Planned
1718

composer.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"support": {
66
"issues": "https://github.com/php-opcua/opcua-cli/issues",
77
"source": "https://github.com/php-opcua/opcua-cli",
8-
"docs": "https://github.com/php-opcua/opcua-client/tree/master/doc"
8+
"docs": "https://github.com/php-opcua/opcua-cli/tree/master/doc"
99
},
1010
"type": "library",
1111
"bin": [
@@ -44,7 +44,7 @@
4444
],
4545
"require": {
4646
"php": "^8.2",
47-
"php-opcua/opcua-client": "@dev"
47+
"php-opcua/opcua-client": "^4.0"
4848
},
4949
"require-dev": {
5050
"pestphp/pest": "^3.0",

llms-full.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,30 @@ The dump output can be fed directly into generate:nodeset for end-to-end code ge
444444

445445
---
446446

447+
## 8. Testing
448+
449+
272 tests (253 unit + 19 integration), 592 assertions, 99.9% code coverage. Code coverage must remain >= 99.5% on every pull request.
450+
451+
Tests use [Pest PHP](https://pestphp.com/) and [pcov](https://github.com/krakjoe/pcov) for coverage.
452+
453+
```bash
454+
# All tests
455+
./vendor/bin/pest
456+
457+
# Unit tests only
458+
./vendor/bin/pest tests/Unit/
459+
460+
# Integration tests (requires opcua-test-suite running)
461+
./vendor/bin/pest tests/Integration/ --group=integration
462+
463+
# With coverage report
464+
php -d pcov.enabled=1 ./vendor/bin/pest --coverage
465+
```
466+
467+
Unit tests mock the OPC UA client via `PhpOpcua\Client\Testing\MockClient`. Integration tests connect to the [opcua-test-suite](https://github.com/php-opcua/opcua-test-suite) Docker containers.
468+
469+
---
470+
447471
## Related Packages
448472

449473
- php-opcua/opcua-client: pure PHP OPC UA client library — the foundation this CLI is built on

llms.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,15 @@ Options: --trust-store=PATH, --trust-policy=POLICY (fingerprint, fingerprint+exp
168168
- php-opcua/laravel-opcua: Laravel integration
169169
- php-opcua/opcua-session-manager: session persistence across PHP requests
170170

171+
## Testing
172+
173+
272 tests (253 unit + 19 integration), 592 assertions, 99.9% code coverage. Coverage must remain >= 99.5%.
174+
175+
```
176+
./vendor/bin/pest
177+
php -d pcov.enabled=1 ./vendor/bin/pest --coverage
178+
```
179+
171180
## Requirements
172181

173182
- PHP >= 8.2

src/Application.php

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -117,26 +117,48 @@ public function run(array $argv): int
117117

118118
return $exitCode;
119119
} catch (UntrustedCertificateException $e) {
120-
$output->error('Error: Server certificate not trusted.');
121-
$output->error(' Fingerprint: ' . $e->fingerprint);
122-
$output->writeln('');
123-
$output->writeln('To trust this certificate, run:');
124-
$output->writeln(' opcua-cli trust ' . ($parsed['arguments'][0] ?? '<endpoint>'));
125-
$output->writeln('');
126-
$output->writeln('To list trusted certificates:');
127-
$output->writeln(' opcua-cli trust:list');
128-
$output->writeln('');
129-
$output->writeln('To skip trust validation for this command:');
130-
$output->writeln(' opcua-cli ' . ($parsed['command'] ?? '') . ' ... --no-trust-policy');
131-
132-
return 1;
120+
return $this->handleUntrustedCertificate($e, $output, $parsed['arguments'][0] ?? '<endpoint>', $parsed['command'] ?? '');
133121
} catch (OpcUaException $e) {
134-
$output->error('Error: ' . $e->getMessage());
135-
136-
return 1;
122+
return $this->handleOpcUaException($e, $output);
137123
}
138124
}
139125

126+
/**
127+
* @param UntrustedCertificateException $e
128+
* @param OutputInterface $output
129+
* @param string $endpointUrl
130+
* @param string $commandName
131+
* @return int
132+
*/
133+
public function handleUntrustedCertificate(UntrustedCertificateException $e, OutputInterface $output, string $endpointUrl, string $commandName): int
134+
{
135+
$output->error('Error: Server certificate not trusted.');
136+
$output->error(' Fingerprint: ' . $e->fingerprint);
137+
$output->writeln('');
138+
$output->writeln('To trust this certificate, run:');
139+
$output->writeln(' opcua-cli trust ' . $endpointUrl);
140+
$output->writeln('');
141+
$output->writeln('To list trusted certificates:');
142+
$output->writeln(' opcua-cli trust:list');
143+
$output->writeln('');
144+
$output->writeln('To skip trust validation for this command:');
145+
$output->writeln(' opcua-cli ' . $commandName . ' ... --no-trust-policy');
146+
147+
return 1;
148+
}
149+
150+
/**
151+
* @param OpcUaException $e
152+
* @param OutputInterface $output
153+
* @return int
154+
*/
155+
public function handleOpcUaException(OpcUaException $e, OutputInterface $output): int
156+
{
157+
$output->error('Error: ' . $e->getMessage());
158+
159+
return 1;
160+
}
161+
140162
/**
141163
* @param array<string, string|bool> $options
142164
* @return OutputInterface

src/Commands/DumpNodesetCommand.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,7 @@ private function readNodeAttributes(OpcUaClientInterface $client, NodeId $nodeId
271271
* @param NodeId $nodeId
272272
* @return ?array
273273
*/
274-
private function readDataTypeDefinition(OpcUaClientInterface $client, NodeId $nodeId): ?array
274+
public function readDataTypeDefinition(OpcUaClientInterface $client, NodeId $nodeId): ?array
275275
{
276276
try {
277277
$dv = $client->read($nodeId, AttributeId::DataTypeDefinition);
@@ -301,7 +301,7 @@ private function readDataTypeDefinition(OpcUaClientInterface $client, NodeId $no
301301
* @param NodeId $nodeId
302302
* @return ?array
303303
*/
304-
private function findDefinitionFromDiscovery(OpcUaClientInterface $client, NodeId $nodeId): ?array
304+
public function findDefinitionFromDiscovery(OpcUaClientInterface $client, NodeId $nodeId): ?array
305305
{
306306
try {
307307
$refs = $client->browseAll($nodeId);
@@ -450,7 +450,7 @@ private function structureDefinitionToArray(\PhpOpcua\Client\Types\StructureDefi
450450
* @param \PhpOpcua\Client\Encoding\BinaryDecoder $decoder
451451
* @return ?array
452452
*/
453-
private function parseRawDefinition(\PhpOpcua\Client\Encoding\BinaryDecoder $decoder): ?array
453+
public function parseRawDefinition(\PhpOpcua\Client\Encoding\BinaryDecoder $decoder): ?array
454454
{
455455
try {
456456
$parsed = \PhpOpcua\Client\Encoding\StructureDefinitionParser::parse($decoder);

src/Commands/WatchCommand.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ private function watchPolling(OpcUaClientInterface $client, string $nodeIdStr, i
101101
if ($this->maxIterations !== null) {
102102
continue;
103103
}
104-
usleep($intervalMs * 1000);
104+
$this->sleep($intervalMs * 1000);
105105
}
106106

107107
return 0;
@@ -146,6 +146,15 @@ private function watchSubscription(OpcUaClientInterface $client, string $nodeIdS
146146
return 0;
147147
}
148148

149+
/**
150+
* @param int $microseconds
151+
* @return void
152+
*/
153+
protected function sleep(int $microseconds): void
154+
{
155+
usleep($microseconds);
156+
}
157+
149158
/**
150159
* @param mixed $value
151160
* @return string

0 commit comments

Comments
 (0)