Skip to content

Commit 582bdc3

Browse files
committed
test: Add comprehensive preview deployment port and path tests
Add missing edge case test for root path (/) and expand test coverage for preview FQDN generation. Tests verify that ports and paths are correctly preserved in preview URLs while excluding root paths. Fixes #2184
1 parent ecd4c51 commit 582bdc3

File tree

2 files changed

+134
-6
lines changed

2 files changed

+134
-6
lines changed

app/Models/ApplicationPreview.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,21 @@ public function generate_preview_fqdn()
7777
if ($this->application->fqdn) {
7878
if (str($this->application->fqdn)->contains(',')) {
7979
$url = Url::fromString(str($this->application->fqdn)->explode(',')[0]);
80-
$preview_fqdn = getFqdnWithoutPort(str($this->application->fqdn)->explode(',')[0]);
8180
} else {
8281
$url = Url::fromString($this->application->fqdn);
83-
if ($this->fqdn) {
84-
$preview_fqdn = getFqdnWithoutPort($this->fqdn);
85-
}
8682
}
8783
$template = $this->application->preview_url_template;
8884
$host = $url->getHost();
8985
$schema = $url->getScheme();
86+
$portInt = $url->getPort();
87+
$port = $portInt !== null ? ':'.$portInt : '';
88+
$urlPath = $url->getPath();
89+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
9090
$random = new Cuid2;
9191
$preview_fqdn = str_replace('{{random}}', $random, $template);
9292
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
9393
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
94-
$preview_fqdn = "$schema://$preview_fqdn";
94+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
9595
$this->fqdn = $preview_fqdn;
9696
$this->save();
9797
}
@@ -147,11 +147,13 @@ public function generate_preview_fqdn_compose()
147147
$schema = $url->getScheme();
148148
$portInt = $url->getPort();
149149
$port = $portInt !== null ? ':'.$portInt : '';
150+
$urlPath = $url->getPath();
151+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
150152
$random = new Cuid2;
151153
$preview_fqdn = str_replace('{{random}}', $random, $template);
152154
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
153155
$preview_fqdn = str_replace('{{pr_id}}', $this->pull_request_id, $preview_fqdn);
154-
$preview_fqdn = "$schema://$preview_fqdn{$port}";
156+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
155157
$preview_domains[] = $preview_fqdn;
156158
}
157159

tests/Unit/PreviewDeploymentPortTest.php

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,129 @@
133133
expect($port)->toBe($case['expected']);
134134
}
135135
});
136+
137+
// Tests for path preservation in preview URLs
138+
// @see https://github.com/coollabsio/coolify/issues/2184#issuecomment-3638971221
139+
140+
it('generates preview FQDN with port and path preserved', function () {
141+
$domain = 'https://api.example.com:3000/api/v1';
142+
$url = Url::fromString($domain);
143+
$template = '{{pr_id}}.{{domain}}';
144+
$pullRequestId = 42;
145+
146+
$host = $url->getHost();
147+
$schema = $url->getScheme();
148+
$portInt = $url->getPort();
149+
$port = $portInt !== null ? ':'.$portInt : '';
150+
$urlPath = $url->getPath();
151+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
152+
153+
$preview_fqdn = str_replace('{{random}}', 'abc123', $template);
154+
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
155+
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
156+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
157+
158+
expect($preview_fqdn)->toBe('https://42.api.example.com:3000/api/v1');
159+
});
160+
161+
it('generates preview FQDN with path only (no port)', function () {
162+
$domain = 'https://api.example.com/api';
163+
$url = Url::fromString($domain);
164+
$template = '{{pr_id}}.{{domain}}';
165+
$pullRequestId = 99;
166+
167+
$host = $url->getHost();
168+
$schema = $url->getScheme();
169+
$portInt = $url->getPort();
170+
$port = $portInt !== null ? ':'.$portInt : '';
171+
$urlPath = $url->getPath();
172+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
173+
174+
$preview_fqdn = str_replace('{{random}}', 'abc123', $template);
175+
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
176+
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
177+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
178+
179+
expect($preview_fqdn)->toBe('https://99.api.example.com/api');
180+
});
181+
182+
it('handles multiple domains with different ports and paths', function () {
183+
$domains = [
184+
'https://app.example.com:3000/dashboard',
185+
'https://api.example.com:8080/api/v1',
186+
'https://web.example.com/admin',
187+
'https://static.example.com',
188+
];
189+
190+
$preview_fqdns = [];
191+
$template = 'pr-{{pr_id}}.{{domain}}';
192+
$pullRequestId = 123;
193+
194+
foreach ($domains as $domain) {
195+
$url = Url::fromString($domain);
196+
$host = $url->getHost();
197+
$schema = $url->getScheme();
198+
$portInt = $url->getPort();
199+
$port = $portInt !== null ? ':'.$portInt : '';
200+
$urlPath = $url->getPath();
201+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
202+
203+
$preview_fqdn = str_replace('{{random}}', 'xyz', $template);
204+
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
205+
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
206+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
207+
$preview_fqdns[] = $preview_fqdn;
208+
}
209+
210+
expect($preview_fqdns[0])->toBe('https://pr-123.app.example.com:3000/dashboard');
211+
expect($preview_fqdns[1])->toBe('https://pr-123.api.example.com:8080/api/v1');
212+
expect($preview_fqdns[2])->toBe('https://pr-123.web.example.com/admin');
213+
expect($preview_fqdns[3])->toBe('https://pr-123.static.example.com');
214+
});
215+
216+
it('preserves trailing slash in URL path', function () {
217+
$domain = 'https://api.example.com/api/';
218+
$url = Url::fromString($domain);
219+
$template = '{{pr_id}}.{{domain}}';
220+
$pullRequestId = 55;
221+
222+
$host = $url->getHost();
223+
$schema = $url->getScheme();
224+
$portInt = $url->getPort();
225+
$port = $portInt !== null ? ':'.$portInt : '';
226+
$urlPath = $url->getPath();
227+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
228+
229+
$preview_fqdn = str_replace('{{random}}', 'abc123', $template);
230+
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
231+
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
232+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
233+
234+
// Trailing slash is preserved: /api/ stays as /api/
235+
expect($preview_fqdn)->toBe('https://55.api.example.com/api/');
236+
});
237+
238+
it('excludes root path from preview FQDN', function () {
239+
// Edge case: URL with explicit root path "/" should NOT append the slash
240+
$domain = 'https://example.com/';
241+
$url = Url::fromString($domain);
242+
$template = '{{pr_id}}.{{domain}}';
243+
$pullRequestId = 42;
244+
245+
$host = $url->getHost();
246+
$schema = $url->getScheme();
247+
$portInt = $url->getPort();
248+
$port = $portInt !== null ? ':'.$portInt : '';
249+
$urlPath = $url->getPath();
250+
$path = ($urlPath !== '' && $urlPath !== '/') ? $urlPath : '';
251+
252+
$preview_fqdn = str_replace('{{random}}', 'abc123', $template);
253+
$preview_fqdn = str_replace('{{domain}}', $host, $preview_fqdn);
254+
$preview_fqdn = str_replace('{{pr_id}}', $pullRequestId, $preview_fqdn);
255+
$preview_fqdn = "$schema://$preview_fqdn{$port}{$path}";
256+
257+
// Root path "/" should be excluded, resulting in no trailing slash
258+
expect($urlPath)->toBe('/');
259+
expect($path)->toBe('');
260+
expect($preview_fqdn)->toBe('https://42.example.com');
261+
});

0 commit comments

Comments
 (0)