073d3346ef
- MentionAutocompleteField: use \z instead of $ so dropdown closes after Enter - MentionDirectory: handle path-less feed URLs (e.g. social.orgro.org → orgro) via penultimate host segment heuristic - Extract derivedNick logic to public inferredNick(from:) in OrgSocialKit/Util - Add URLNickTests covering 16 real relay URL patterns (vhost, root, path-less, deep GitHub/Codeberg, tilde, custom filename) - Bump build to 1.3 (16)
129 lines
5.1 KiB
Swift
129 lines
5.1 KiB
Swift
import Foundation
|
||
import Testing
|
||
@testable import OrgSocialKit
|
||
|
||
/// Tests for `inferredNick(from:)` — the placeholder-nick heuristic used by
|
||
/// mention autocomplete for relay-only candidates whose profiles have not been
|
||
/// fetched yet. Cases are derived from real feeds registered on the public relay.
|
||
@Suite("inferredNick – relay URL patterns")
|
||
struct URLNickTests {
|
||
|
||
// MARK: - Path-less host (e.g. social.orgro.org)
|
||
|
||
@Test("path-less host: penultimate segment of the hostname")
|
||
func pathlessHost() {
|
||
// https://social.orgro.org → "orgro"
|
||
#expect(nick("https://social.orgro.org") == "orgro")
|
||
}
|
||
|
||
@Test("path-less host with subdomain: ignores leading segment")
|
||
func pathlessHostSubdomain() {
|
||
// Hypothetical: https://org.social.example.com → "example"
|
||
#expect(nick("https://org.social.example.com") == "example")
|
||
}
|
||
|
||
// MARK: - /social.org at root (single path segment)
|
||
|
||
@Test("root social.org: falls back to host")
|
||
func rootSocialOrg() {
|
||
// https://rossabaker.com/social.org → "rossabaker.com"
|
||
#expect(nick("https://rossabaker.com/social.org") == "rossabaker.com")
|
||
}
|
||
|
||
@Test("root social.org on srht: falls back to host")
|
||
func srhtRootSocialOrg() {
|
||
// https://gnomon.srht.site/social.org → "gnomon.srht.site"
|
||
#expect(nick("https://gnomon.srht.site/social.org") == "gnomon.srht.site")
|
||
}
|
||
|
||
@Test("root social.org on github.io: falls back to host")
|
||
func githubIoRoot() {
|
||
// https://thesolarprincess.github.io/social.org → "thesolarprincess.github.io"
|
||
#expect(nick("https://thesolarprincess.github.io/social.org") == "thesolarprincess.github.io")
|
||
}
|
||
|
||
// MARK: - /user/social.org (two-segment path, standard vhost pattern)
|
||
|
||
@Test("vhost-style: second-to-last segment is the username")
|
||
func vhostUsername() {
|
||
// https://host.org-social.org/andros/social.org → "andros"
|
||
#expect(nick("https://host.org-social.org/andros/social.org") == "andros")
|
||
}
|
||
|
||
@Test("vhost-style: preserves case")
|
||
func vhostPreservesCase() {
|
||
// https://host.org-social.org/BALKIN/social.org → "BALKIN"
|
||
#expect(nick("https://host.org-social.org/BALKIN/social.org") == "BALKIN")
|
||
}
|
||
|
||
@Test("two-segment path on personal domain")
|
||
func twoSegmentPersonal() {
|
||
// https://zweili.ch/org-social/social.org → "org-social"
|
||
#expect(nick("https://zweili.ch/org-social/social.org") == "org-social")
|
||
}
|
||
|
||
@Test("tilde path on personal domain")
|
||
func tildePath() {
|
||
// https://muse-amuse.in/~punchagan/social.org → "~punchagan"
|
||
#expect(nick("https://muse-amuse.in/~punchagan/social.org") == "~punchagan")
|
||
}
|
||
|
||
@Test("github pages two-segment: subfolder name")
|
||
func githubPagesSubfolder() {
|
||
// https://xlymian.github.io/org-social/social.org → "org-social"
|
||
#expect(nick("https://xlymian.github.io/org-social/social.org") == "org-social")
|
||
}
|
||
|
||
// MARK: - Custom filename (ends in .org but not social.org)
|
||
|
||
@Test("custom .org filename: second-to-last segment is the nick")
|
||
func customOrgFilename() {
|
||
// https://cybervalley.org/org-social-leandro/org-social.org → "org-social-leandro"
|
||
#expect(nick("https://cybervalley.org/org-social-leandro/org-social.org") == "org-social-leandro")
|
||
}
|
||
|
||
@Test("custom .org filename on github pages")
|
||
func customOrgFilenameGithubPages() {
|
||
// https://dharmatech.github.io/org-social-dharmatech/org-social.org → "org-social-dharmatech"
|
||
#expect(nick("https://dharmatech.github.io/org-social-dharmatech/org-social.org") == "org-social-dharmatech")
|
||
}
|
||
|
||
@Test("wrbutros codeberg page: root .org filename falls back to host")
|
||
func codebergPageRootOrgFile() {
|
||
// https://wrbutros.codeberg.page/org-social.org → "wrbutros.codeberg.page"
|
||
#expect(nick("https://wrbutros.codeberg.page/org-social.org") == "wrbutros.codeberg.page")
|
||
}
|
||
|
||
// MARK: - Deep paths (GitHub raw, Codeberg raw)
|
||
|
||
@Test("deep github raw path: segment before the filename")
|
||
func githubRawDeep() {
|
||
// https://raw.githubusercontent.com/fengbainuo/org-social/refs/heads/main/social.org → "main"
|
||
#expect(nick("https://raw.githubusercontent.com/fengbainuo/org-social/refs/heads/main/social.org") == "main")
|
||
}
|
||
|
||
@Test("deep codeberg raw path: segment before the filename")
|
||
func codebergRawDeep() {
|
||
// https://codeberg.org/mester/CosasSociales/raw/branch/main/social.org → "main"
|
||
#expect(nick("https://codeberg.org/mester/CosasSociales/raw/branch/main/social.org") == "main")
|
||
}
|
||
|
||
// MARK: - Non-.org path segments
|
||
|
||
@Test("non-.org final segment: last segment used as-is")
|
||
func nonOrgFinalSegment() {
|
||
// Hypothetical: https://example.com/feeds/alice → "alice"
|
||
#expect(nick("https://example.com/feeds/alice") == "alice")
|
||
}
|
||
|
||
// MARK: - Helpers
|
||
|
||
private func nick(_ urlString: String) -> String {
|
||
guard let url = URL(string: urlString) else {
|
||
Issue.record("Invalid URL: \(urlString)")
|
||
return ""
|
||
}
|
||
return inferredNick(from: url)
|
||
}
|
||
}
|