Resolves more formats of URLs correctly: absolute, root-relative, relative, parent-relative, and URLs with query strings and fragments.
150 lines
7.6 KiB
C++
150 lines
7.6 KiB
C++
#include <cstdio>
|
|
#include <string>
|
|
|
|
#include "../../src/util/UrlUtils.h"
|
|
|
|
static int testsPassed = 0;
|
|
static int testsFailed = 0;
|
|
|
|
#define ASSERT_EQ(a, b) \
|
|
do { \
|
|
if ((a) != (b)) { \
|
|
fprintf(stderr, " FAIL: %s:%d: %s != %s\n", __FILE__, __LINE__, #a, #b); \
|
|
testsFailed++; \
|
|
return; \
|
|
} \
|
|
} while (0)
|
|
|
|
#define PASS() testsPassed++
|
|
|
|
void testAbsoluteUrlRemainsUnchanged() {
|
|
printf("testAbsoluteUrlRemainsUnchanged...\n");
|
|
ASSERT_EQ(
|
|
UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "https://example.com/books/download/test.epub"),
|
|
"https://example.com/books/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "//cdn.example.com/books/test.epub"),
|
|
"https://cdn.example.com/books/test.epub");
|
|
PASS();
|
|
}
|
|
|
|
void testRootRelativeUrlUsesHostRoot() {
|
|
printf("testRootRelativeUrlUsesHostRoot...\n");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "/books/download/test.epub"),
|
|
"https://catalog.example.com/books/download/test.epub");
|
|
PASS();
|
|
}
|
|
|
|
void testRelativeUrlUsesFeedDirectory() {
|
|
printf("testRelativeUrlUsesFeedDirectory...\n");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "download/test.epub"),
|
|
"https://catalog.example.com/opds/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub", "download/test.epub"),
|
|
"https://catalog.example.com/opds/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub/", "download/test.epub"),
|
|
"https://catalog.example.com/opds/sub/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "images//cover.jpg"),
|
|
"https://catalog.example.com/opds/images//cover.jpg");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "subdir/"),
|
|
"https://catalog.example.com/opds/subdir/");
|
|
PASS();
|
|
}
|
|
|
|
void testParentRelativeUrlResolvesCorrectly() {
|
|
printf("testParentRelativeUrlResolvesCorrectly...\n");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub/feed.xml", "../download/test.epub"),
|
|
"https://catalog.example.com/opds/download/test.epub");
|
|
PASS();
|
|
}
|
|
|
|
void testUrlsWithQueryAndFragmentResolveCorrectly() {
|
|
printf("testUrlsWithQueryAndFragmentResolveCorrectly...\n");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub?auth=1", "download/test.epub"),
|
|
"https://catalog.example.com/opds/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub/#section", "download/test.epub?format=epub#start"),
|
|
"https://catalog.example.com/opds/sub/download/test.epub?format=epub#start");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml?auth=1#top", "download/test.epub"),
|
|
"https://catalog.example.com/opds/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "/books?id=/../cover"),
|
|
"https://catalog.example.com/books?id=/../cover");
|
|
ASSERT_EQ(
|
|
UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "download/test.epub?next=/../cover#frag/../x"),
|
|
"https://catalog.example.com/opds/download/test.epub?next=/../cover#frag/../x");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml", "download/test.epub#frag?x=1"),
|
|
"https://catalog.example.com/opds/download/test.epub#frag?x=1");
|
|
PASS();
|
|
}
|
|
|
|
void testRootRelativeUrlsWithQueryAndFragmentResolveCorrectly() {
|
|
printf("testRootRelativeUrlsWithQueryAndFragmentResolveCorrectly...\n");
|
|
ASSERT_EQ(
|
|
UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml?auth=1", "/books/download/test.epub?format=epub"),
|
|
"https://catalog.example.com/books/download/test.epub?format=epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub/#section", "/books/download/test.epub#start"),
|
|
"https://catalog.example.com/books/download/test.epub#start");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/sub?auth=1#top",
|
|
"/books/download/test.epub?format=epub#start"),
|
|
"https://catalog.example.com/books/download/test.epub?format=epub#start");
|
|
PASS();
|
|
}
|
|
|
|
void testHostOnlyBaseUrlsWithQueryAndFragmentResolveCorrectly() {
|
|
printf("testHostOnlyBaseUrlsWithQueryAndFragmentResolveCorrectly...\n");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com?auth=1", "download/test.epub"),
|
|
"https://catalog.example.com/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com#top", "/books/download/test.epub"),
|
|
"https://catalog.example.com/books/download/test.epub");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com?auth=1", "?page=2"), "https://catalog.example.com?page=2");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com#top", "#latest"), "https://catalog.example.com#latest");
|
|
PASS();
|
|
}
|
|
|
|
void testQueryAndFragmentReferencesAreRfcCompliant() {
|
|
printf("testQueryAndFragmentReferencesAreRfcCompliant...\n");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml?auth=1", "#top"),
|
|
"https://catalog.example.com/opds/feed.xml?auth=1#top");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com/opds/feed.xml?auth=1#old", "?page=2"),
|
|
"https://catalog.example.com/opds/feed.xml?page=2");
|
|
ASSERT_EQ(UrlUtils::buildUrl("https://catalog.example.com?auth=1#old", "#latest"),
|
|
"https://catalog.example.com?auth=1#latest");
|
|
PASS();
|
|
}
|
|
|
|
void testExtractHostnameHandlesPortsAndIpv6() {
|
|
printf("testExtractHostnameHandlesPortsAndIpv6...\n");
|
|
ASSERT_EQ(UrlUtils::extractHostname("https://catalog.example.com:8080/path"), "catalog.example.com");
|
|
ASSERT_EQ(UrlUtils::extractHostname("catalog.example.com:8080/opds"), "catalog.example.com");
|
|
ASSERT_EQ(UrlUtils::extractHostname("https://user:pass@[2001:db8::1]:8080/path?x=1#frag"), "2001:db8::1");
|
|
ASSERT_EQ(UrlUtils::extractHostname("http://[fe80::1234]#top"), "fe80::1234");
|
|
PASS();
|
|
}
|
|
|
|
void testExtractHostnameRejectsMalformedInputs() {
|
|
printf("testExtractHostnameRejectsMalformedInputs...\n");
|
|
ASSERT_EQ(UrlUtils::extractHostname("https:///path"), "");
|
|
ASSERT_EQ(UrlUtils::extractHostname("http://[]"), "");
|
|
ASSERT_EQ(UrlUtils::extractHostname("http://@/x"), "");
|
|
ASSERT_EQ(UrlUtils::extractHostname("http://[::1"), "");
|
|
ASSERT_EQ(UrlUtils::extractHostname("http://[::1]x"), "");
|
|
ASSERT_EQ(UrlUtils::extractHostname("mailto:user@example.com"), "");
|
|
ASSERT_EQ(UrlUtils::extractHostname("urn:isbn:1234567890"), "");
|
|
PASS();
|
|
}
|
|
|
|
int main() {
|
|
printf("=== OPDS URL Utils Tests ===\n\n");
|
|
|
|
testAbsoluteUrlRemainsUnchanged();
|
|
testRootRelativeUrlUsesHostRoot();
|
|
testRelativeUrlUsesFeedDirectory();
|
|
testParentRelativeUrlResolvesCorrectly();
|
|
testUrlsWithQueryAndFragmentResolveCorrectly();
|
|
testRootRelativeUrlsWithQueryAndFragmentResolveCorrectly();
|
|
testHostOnlyBaseUrlsWithQueryAndFragmentResolveCorrectly();
|
|
testQueryAndFragmentReferencesAreRfcCompliant();
|
|
testExtractHostnameHandlesPortsAndIpv6();
|
|
testExtractHostnameRejectsMalformedInputs();
|
|
|
|
printf("\n=== Results: %d passed, %d failed ===\n", testsPassed, testsFailed);
|
|
return testsFailed > 0 ? 1 : 0;
|
|
}
|