Files
plezy/test/utils/url_utils_test.dart
edde746 75279ca196 refactor(discovery): expand server url candidates through one shared helper
Turning what a user typed into URLs worth probing lived inside
JellyfinEndpointDiscovery, next to a private copy of the scheme-detection
regex that url_utils already had. A second backend needs the same rules
with a different default port, and the existing copy carried two latent
defects: input with no host came back unchanged, so a candidate that can
never resolve went to the prober, and a query string survived into the
base URL that request paths are appended to.

expandBaseUrlCandidates takes the ordered guesses a backend wants — a
scheme plus the port to try when the user typed none — and owns the rest.
A typed port beats a guessed one, so host:8096 collapses to one candidate
per scheme and the same guess list covers both bare hosts and hosts with
ports. Candidates are built field by field rather than through
Uri.replace, whose null query means "keep mine", so query and fragment
are dropped for real. Blank input and input with no host expand to
nothing.

JellyfinEndpointDiscovery passes its existing order, so its probe and
persistence behaviour is unchanged, and hasUrlScheme replaces the regex
copy.
2026-08-25 03:07:14 +02:00

90 lines
3.4 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/utils/url_utils.dart';
void main() {
group('stripTrailingSlash', () {
test('removes a single trailing slash', () {
expect(stripTrailingSlash('https://host/'), 'https://host');
});
test('trims whitespace and leaves slashless input unchanged', () {
expect(stripTrailingSlash(' https://host '), 'https://host');
expect(stripTrailingSlash(''), '');
});
});
group('canonicalizeBaseUrl', () {
test('lowercases a mixed-case scheme (#1465)', () {
// FFmpeg's protocol lookup is case-sensitive; "Https://" reaching the
// player as a raw string fails with "Protocol not found".
expect(canonicalizeBaseUrl('Https://jellyfin.example.com'), 'https://jellyfin.example.com');
expect(canonicalizeBaseUrl('HTTPS://jellyfin.example.com'), 'https://jellyfin.example.com');
});
test('only touches the scheme, not host/path/query', () {
expect(canonicalizeBaseUrl('HTTP://Host.Example.com:8096/JellyFin'), 'http://Host.Example.com:8096/JellyFin');
});
test('strips trailing slash and trims whitespace', () {
expect(canonicalizeBaseUrl(' Https://host:8096/jellyfin/ '), 'https://host:8096/jellyfin');
});
test('leaves schemeless input unchanged', () {
expect(canonicalizeBaseUrl('host:8096'), 'host:8096');
expect(canonicalizeBaseUrl('Host.example.com'), 'Host.example.com');
expect(canonicalizeBaseUrl(''), '');
});
});
group('expandBaseUrlCandidates', () {
const guesses = <BaseUrlGuess>[
(scheme: 'https', port: null),
(scheme: 'http', port: null),
(scheme: 'http', port: 5055),
];
test('an explicit scheme is authoritative and comes back alone', () {
expect(expandBaseUrlCandidates('HTTPS://seerr.example.com/', guesses: guesses), ['https://seerr.example.com']);
expect(expandBaseUrlCandidates('http://192.168.1.5:5055', guesses: guesses), ['http://192.168.1.5:5055']);
});
test('applies every guess in order to a bare host', () {
expect(expandBaseUrlCandidates('seerr.example.com', guesses: guesses), [
'https://seerr.example.com',
'http://seerr.example.com',
'http://seerr.example.com:5055',
]);
});
test('a typed port beats the guessed one and collapses the duplicates', () {
expect(expandBaseUrlCandidates('192.168.1.5:5055', guesses: guesses), [
'https://192.168.1.5:5055',
'http://192.168.1.5:5055',
]);
});
test('keeps a sub-path on every candidate and drops query and fragment', () {
expect(expandBaseUrlCandidates('example.com/seerr?a=1#top', guesses: guesses), [
'https://example.com/seerr',
'http://example.com/seerr',
'http://example.com:5055/seerr',
]);
});
test('input with nothing to reach expands to nothing', () {
expect(expandBaseUrlCandidates(' ', guesses: guesses), isEmpty);
// Hostless: no guess could build a URL that resolves anywhere.
expect(expandBaseUrlCandidates('/seerr', guesses: guesses), isEmpty);
});
});
group('hasUrlScheme', () {
test('recognizes only a real leading scheme', () {
expect(hasUrlScheme('https://host'), isTrue);
expect(hasUrlScheme('jellyfin+tls://host'), isTrue);
expect(hasUrlScheme('host:8096'), isFalse);
expect(hasUrlScheme('host/https://nested'), isFalse);
});
});
}