Skip to content

Commit d02ca4c

Browse files
committed
fix: resolve OpenAPI spec to absolute path and add port check
- Fixes 500 errors by resolving the specification path to an absolute path before starting the mock server. - Adds pre-start port availability check to provide clearer error messages for port conflicts. - Includes validation for the specification file during module initialization. - Updates GEMINI.md with lessons learned.
1 parent f572a78 commit d02ca4c

2 files changed

Lines changed: 42 additions & 1 deletion

File tree

GEMINI.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,15 @@ composer cs:fix # Fix coding standards
6464
* `phpstan.neon`: PHPStan configuration (Level 8).
6565
* `.php-cs-fixer.php`: Coding style rules.
6666
* `grumphp.yml`: Automation task definitions.
67+
68+
## Lessons Learned & Troubleshooting
69+
70+
### Absolute Path Resolution for OpenAPI Specifications
71+
The mock server process is started with its own `vendor` directory as the working directory (`cwd`) to facilitate binary execution. This means any relative paths for the `OPENAPI_SPEC` environment variable will be resolved against that library path, leading to `500 Internal Server Error` (CONFIG_ERROR) if the specification is not found.
72+
- **Rule:** Always resolve the `spec` configuration to an **absolute path** (using `realpath()`) before passing it to the server process.
73+
74+
### Port Availability
75+
To prevent confusing errors where the mock server seems to be running but does not behave as expected (e.g., if another process is already listening on the same port), the module now performs a pre-start check.
76+
- **Implementation:** `fsockopen()` is used to verify that the configured port is free before attempting to start the built-in PHP server.
77+
- **Behavior:** A descriptive `RuntimeException` is thrown if the port is already in use.
78+

src/OpenApiServerMock.php

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ public function _initialize(): void
5454

5555
if ($this->config['startServer']) {
5656
$this->validatePath((string) $this->config['path']);
57+
$this->validateSpec((string) $this->config['spec']);
58+
}
59+
}
60+
61+
private function validateSpec(string $spec): void
62+
{
63+
if (empty($spec) || !file_exists($spec)) {
64+
throw new ModuleConfigException($this, "OpenAPI specification file not found at '{$spec}'.");
5765
}
5866
}
5967

@@ -119,6 +127,10 @@ public function _afterSuite(): void
119127

120128
protected function startMockServer(): void
121129
{
130+
if ($this->isPortInUse()) {
131+
throw new RuntimeException("Port {$this->config['port']} is already in use. Cannot start mock server.");
132+
}
133+
122134
$binPath = $this->config['path'] . '/bin/openapi-mock-server';
123135
if (!file_exists($binPath)) {
124136
$binPath = $this->config['path'] . '/public/index.php';
@@ -129,8 +141,13 @@ protected function startMockServer(): void
129141
throw new RuntimeException('PHP executable not found.');
130142
}
131143

144+
$specPath = (string) $this->config['spec'];
145+
if (file_exists($specPath)) {
146+
$specPath = (string) realpath($specPath);
147+
}
148+
132149
$command = [$phpBinary, '-S', "{$this->config['host']}:{$this->config['port']}", $binPath];
133-
$env = ['OPENAPI_SPEC' => (string) $this->config['spec']];
150+
$env = ['OPENAPI_SPEC' => $specPath];
134151

135152
$this->process = new Process($command, (string) $this->config['path'], $env);
136153
$this->process->start();
@@ -141,6 +158,18 @@ protected function startMockServer(): void
141158
}
142159
}
143160

161+
private function isPortInUse(): bool
162+
{
163+
$fp = @fsockopen($this->config['host'], (int) $this->config['port'], $errno, $errstr, 0.1);
164+
if ($fp) {
165+
fclose($fp);
166+
167+
return true;
168+
}
169+
170+
return false;
171+
}
172+
144173
private function waitForServer(): bool
145174
{
146175
$start = time();

0 commit comments

Comments
 (0)