# uriparser 0.9.8 — RFC 3986 URI parsing (BRINTOS-218). # # The port ships one runnable artifact, the `uriparse` CLI, so every case here # drives the library through it. `uriparse` prints one labelled field per line # and exits 0 on a parse, 1 on a syntax error, which makes both halves of RFC # 3986 conformance — what parses, and what must NOT — directly assertable. # # The cases are grouped: presence, the five components of a full URI, the three # host forms (reg-name / IPv4 / IPv6), the schemes that carry no authority, the # relative-reference and absolute-path rules, and the two rejection cases. Every # expectation was taken from a native x86_64 build of the same 0.9.8 source, not # from the RFC alone — so a red here is the platform's, not the test's. . "$BST_LIB" require uriparse uriparser/present bst_ok uriparser/present U='https://user:pw@example.com:8080/a/b/../c?x=1&y=2#frag' # --- the tool runs at all, and reports its own usage ------------------------ # uriparse with no argument is an error by design (rc=1), so this asserts the # usage text on the failing path rather than through check_out. usage=$(uriparse 2>&1) case "$usage" in *"Usage: uriparse"*) bst_ok uriparser/usage ;; *) bst_notok uriparser/usage "want:'Usage: uriparse' got:$(_bst_1line "$usage")" ;; esac # --- the five components of a full hierarchical URI ------------------------- check_out uriparser/scheme 'scheme: https' uriparse "$U" check_out uriparser/userinfo 'userInfo: user:pw' uriparse "$U" check_out uriparser/host 'hostText: example.com' uriparse "$U" check_out uriparser/port 'portText: 8080' uriparse "$U" check_out uriparser/query 'query: x=1&y=2' uriparse "$U" check_out uriparser/fragment 'fragment: frag' uriparse "$U" # Path segmentation is the part a naive splitter gets wrong: the dot-segment # '..' must survive parsing as its own segment (removal is a separate, # caller-driven normalisation step, not something the parser does). check_out uriparser/path-segments '.. pathSeg: ..' uriparse "$U" # --- the three host forms --------------------------------------------------- # A reg-name host is asserted above. These two are the literal-address forms, # which uriparse reports through a second, typed field (hostData.ip4/ip6) — # that field is the evidence the address was actually recognised as an address # rather than passed through as text. check_out uriparser/host-ipv4 'hostData.ip4: 192.0.2.1' uriparse 'http://192.0.2.1/p' check_out uriparser/host-ipv6 'hostData.ip6: 2001:db8::1' uriparse 'http://[2001:db8::1]:80/x' # --- schemes with no authority ---------------------------------------------- # mailto: and urn: put everything after the colon in the path, with no host at # all. Asserting the path content (rather than merely rc=0) is what catches a # parser that silently invented an authority. check_out uriparser/no-authority-mailto '.. pathSeg: a@b.example' uriparse 'mailto:a@b.example' check_out uriparser/no-authority-urn '.. pathSeg: isbn:0451450523' uriparse 'urn:isbn:0451450523' # --- relative reference, and the absolutePath contract ---------------------- # A bare '/just/a/path' is a relative reference with an absolute path, and is # the ONLY shape for which uriparse reports absolutePath: true — the flag is # defined as always false once a host is present, however leading-slashed the # path looks. Both halves are asserted so the pair cannot drift apart. check_out uriparser/relative-ref 'absolutePath: true' uriparse '/just/a/path' check_out uriparser/abspath-false 'absolutePath: false' uriparse "$U" # --- percent-encoding and case are preserved, not interpreted --------------- # RFC 3986 leaves %2F distinct from '/' at the parse layer, and scheme/host case # folding is a normalisation step the parser must not perform on its own. If # either were "helpfully" decoded here, the segment would split in two. check_out uriparser/pct-preserved '.. pathSeg: A%2fB' uriparse 'HTTP://Example.COM/A%2fB' check_out uriparser/case-preserved 'scheme: HTTP' uriparse 'HTTP://Example.COM/A%2fB' # --- multiple URIs in one invocation ---------------------------------------- # uriparse takes "URI [..]" and must parse each independently; this catches # state left behind between parses. multi=$(uriparse 'http://a.example/1' 'http://b.example/2' 2>&1) case "$multi" in *'hostText: a.example'*) case "$multi" in *'hostText: b.example'*) bst_ok uriparser/multi-arg ;; *) bst_notok uriparser/multi-arg "second URI missing: $(_bst_1line "$multi")" ;; esac ;; *) bst_notok uriparser/multi-arg "first URI missing: $(_bst_1line "$multi")" ;; esac # --- what must be REJECTED -------------------------------------------------- # Strict RFC 3986 compliance is as much about refusal as acceptance. Both of # these exit 1 and name the offending offset; asserting the rc alone would pass # for a tool that failed for an unrelated reason, so the message is checked too. bad=$(uriparse '://bad' 2>&1); bad_rc=$? if [ "$bad_rc" -eq 1 ]; then case "$bad" in *'Failure:'*) bst_ok uriparser/reject-no-scheme ;; *) bst_notok uriparser/reject-no-scheme "rc=1 but no Failure line: $(_bst_1line "$bad")" ;; esac else bst_notok uriparser/reject-no-scheme "rc=$bad_rc want=1 (malformed URI was accepted)" fi # A bracketed host that is not a valid IPv6 literal must not be accepted as one. check_rc uriparser/reject-bad-ipv6 1 uriparse 'http://[2001:db8:::zz]/x'