nim-vcard/tests/tvcard4.nim
Jonathan Bernard 98c300fee2 vcard4: Complete implementation.
- Parsers and serializers are now present for all property types.
- Tests exist to cover parsing for most value types. Many property types
  share the same parsing logic based on their value type. We have
  created unit tests to cover each value type, not neccesarily all
  properties individually.
2023-05-02 22:37:23 -05:00

251 lines
8.0 KiB
Nim

import std/[options, strutils, tables, unittest]
import zero_functional
import ./vcard
import ./vcard/vcard4
suite "vcard/vcard4":
test "vcard4/private tests":
runVcard4PrivateTests()
let v4ExampleStr = readFile("tests/allen.foster.v4.vcf")
let testVCardTemplate =
"BEGIN:VCARD\r\n" &
"VERSION:4.0\r\n" &
"$#" &
"END:VCARD\r\n"
test "parseVCard4":
check parseVCards(v4ExampleStr).len == 1
test "parseVCard4File":
check parseVCardsFromFile("tests/allen.foster.v4.vcf").len == 1
# TODO: remove cast after finishing VCard4 implementation
let v4Ex = cast[VCard4](parseVCards(v4ExampleStr)[0])
test "RFC 6350 author's VCard":
let vcardStr =
"BEGIN:VCARD\r\n" &
"VERSION:4.0\r\n" &
"FN:Simon Perreault\r\n" &
"N:Perreault;Simon;;;ing. jr,M.Sc.\r\n" &
"BDAY:--0203\r\n" &
"ANNIVERSARY:20090808T1430-0500\r\n" &
"GENDER:M\r\n" &
"LANG;PREF=1:fr\r\n" &
"LANG;PREF=2:en\r\n" &
"ORG;TYPE=work:Viagenie\r\n" &
"ADR;TYPE=work:;Suite D2-630;2875 Laurier;\r\n" &
" Quebec;QC;G1V 2M2;Canada\r\n" &
"TEL;VALUE=uri;TYPE=\"work,voice\";PREF=1:tel:+1-418-656-9254;ext=102\r\n" &
"TEL;VALUE=uri;TYPE=\"work,cell,voice,video,text\":tel:+1-418-262-6501\r\n" &
"EMAIL;TYPE=work:simon.perreault@viagenie.ca\r\n" &
"GEO;TYPE=work:geo:46.772673,-71.282945\r\n" &
"KEY;TYPE=work;VALUE=uri:\r\n" &
" http://www.viagenie.ca/simon.perreault/simon.asc\r\n" &
"TZ:-0500\r\n" &
"URL;TYPE=home:http://nomis80.org\r\n" &
"END:VCARD\r\n"
let vcards = parseVCards(vcardStr)
check vcards.len == 1
let sp = cast[VCard4](vcards[0])
check:
sp.fn.len == 1
sp.fn[0].value == "Simon Perreault"
sp.gender.isSome
sp.gender.get.sex == some(VC4_Sex.Male)
sp.gender.get.genderIdentity.isNone
sp.lang.len == 2
sp.lang --> map(it.value) == @["fr", "en"]
test "custom properties are serialized":
let email = newVC4_Email(
value ="john.smith@testco.test",
types = @["work", "internet"],
params = @[("PREF", @["1"]), ("X-ATTACHMENT-LIMIT", @["25MB"])])
check serialize(email) ==
"EMAIL;X-ATTACHMENT-LIMIT=25MB;TYPE=work,internet;PREF=1:john.smith@testco.test"
test "can parse properties with escaped characters":
check v4Ex.note.len == 1
let note = v4Ex.note[0]
check note.value ==
"This is an example, for clarity; in text value cases the parser " &
"will recognize escape values for ',', '\\', and newlines. For " &
"example:" &
"\n\t123 Flagstaff Road" &
"\n\tPlaceville, MA"
test "can parse parameters with escaped characters":
let prop = v4Ex.customProp("X-CUSTOM-EXAMPLE")[0]
check prop.value ==
"This is an example, for clarity; in straight value cases, the parser " &
"does not recognize any escape values, as the meaning of the content " &
"is implementation-specific."
let param1 = prop.params --> filter(it.name == "PARAM")
let label = prop.params --> filter(it.name == "LABEL")
check:
param1.len == 1
param1[0].values == @["How one says, \"Hello.\""]
label.len == 1
label[0].values == @["^top\nsecond line"]
test "Data URIs are parsed correctly":
let expectedB64 = readFile("tests/allen.foster.jpg.uri")
check:
v4Ex.photo.len == 2
v4Ex.photo[0].altId == some("1")
v4Ex.photo[0].value ==
"https://tile.loc.gov/storage-services/service/pnp/bellcm/02200/02297r.jpg"
v4Ex.photo[0].valueType == some("uri")
v4Ex.photo[1].altId == some("1")
v4Ex.photo[1].value == expectedB64
v4Ex.photo[1].valueType.isNone
test "URI-type properties are parsed correctly":
# Covers SOURCE, PHOTO, IMPP, GEO, LOGO, MEMBER, SOUND, URL, FBURL,
# CALADRURI, and CALURI
check:
v4Ex.source.len == 1
v4Ex.source[0].value == "https://carddav.fosters.test/allen.vcf"
v4Ex.source[0].valueType == some("uri")
v4Ex.url.len == 1
v4Ex.url[0].value == "https://allen.fosters.test/"
test "URI-type properties are serialized correctly":
# Covers SOURCE, PHOTO, IMPP, GEO, LOGO, MEMBER, SOUND, URL, FBURL,
# CALADRURI, and CALURI
let src = newVC4_Source(value="https://carddav.example.test/john-smith.vcf")
check serialize(src) == "SOURCE:https://carddav.example.test/john-smith.vcf"
test "Single-text properties are parsed correctly":
# Covers KIND, XML, FN, NICKNAME, EMAIL, LANG, TZ, TITLE, ROLE, ORG, NOTE,
# PRODID, and VERSION
check:
v4Ex.kind.isSome
v4Ex.kind.get.value == "individual"
v4Ex.nickname.len == 2
v4Ex.nickname[0].value == @["Jack Jr."]
v4Ex.nickname[1].value == @["Doc A"]
v4Ex.fn.len == 1
v4Ex.fn[0].value == "Dr. Allen Foster"
v4Ex.email.len == 2
v4Ex.email[0].value == "jack.foster@company.test"
v4Ex.email[0].types == @["work"]
test "URI or Text properties are parsed correctly":
# Covers TEL, RELATED, UID, KEY
check:
v4Ex.tel.len == 3
v4ex.tel[0].types == @[$VC4_TelType.ttCell]
v4Ex.tel[0].value == "+1 555-123-4567"
v4Ex.tel[2].types == @[$VC4_TelType.ttWork,$VC4_TelType.ttVoice]
v4Ex.tel[2].valueType == some($vtUri)
v4Ex.tel[2].value == "tel:+1-555-874-1234"
test "N is parsed correctly":
check:
v4Ex.n.isSome
v4Ex.n.get.given == @["Jack"]
v4Ex.n.get.family == @["Foster"]
v4Ex.n.get.additional == @["John", "Allen"]
v4Ex.n.get.prefixes == @["Dr."]
v4Ex.n.get.suffixes == @["II"]
test "BDAY is parsed correctly":
check:
v4Ex.bday.isSome
v4Ex.bday.get.value == "--1224"
v4Ex.bday.get.year.isNone
v4Ex.bday.get.month == some(12)
v4Ex.bday.get.day == some(24)
test "ANNIVERSARY is parsed correctly":
check:
v4Ex.anniversary.isSome
v4Ex.anniversary.get.value == "20140612T163000-0500"
v4Ex.anniversary.get.year == some(2014)
v4Ex.anniversary.get.hour == some(16)
v4Ex.anniversary.get.minute == some(30)
v4Ex.anniversary.get.timezone == some("-0500")
test "GENDER is parsed correctly":
check:
v4Ex.gender.isSome
v4Ex.gender.get.sex == some(VC4_Sex.Male)
v4Ex.gender.get.genderIdentity == some("male")
#[
test "CATEGORIES is parsed correctly":
test "REV is parsed correctly":
test "CLIENTPIDMAP is parsed correctly":
]#
test "unknown properties are parsed correctly":
check v4Ex.customProp("MADE-UP-PROP").len == 1
let madeUpProp = v4Ex.customProp("MADE-UP-PROP")[0]
check:
madeUpProp.name == "MADE-UP-PROP"
madeUpProp.value == "Sample value for my made-up prop."
let cardWithAltBdayStr = testVCardTemplate % [(
"BDAY;VALUE=text;ALTID=1:20th century\r\n" &
"BDAY;VALUE=date-and-or-time;ALTID=1:19650321\r\n"
)]
test "single-cardinality properties allow multiples with ALTID":
check parseVCards(cardWithAltBdayStr).len == 1
let hasAltBdays = cast[VCard4](parseVCards(cardWithAltBdayStr)[0])
test "properties with cardinality 1 and altids return the first found by default":
check:
hasAltBdays.bday.isSome
hasAltBdays.bday.get.value == "20th century"
hasAltBdays.bday.get.year.isNone
test "allAlternatives":
check:
hasAltBdays.content.len == 3
hasAltBdays.bday.isSome
let allBdays = allAlternatives[VC4_Bday](hasAltBdays)
check:
allBdays.len == 1
allBdays.contains("1")
allBdays["1"].len == 2
let bday0 = allBdays["1"][0]
check:
bday0.value == "20th century"
bday0.year.isNone
bday0.month.isNone
bday0.day.isNone
bday0.hour.isNone
bday0.minute.isNone
bday0.second.isNone
bday0.timezone.isNone
let bday1 = allBDays["1"][1]
check:
bday1.value == "19650321"
bday1.year == some(1965)
bday1.month == some(3)
bday1.day == some(21)
bday1.hour.isNone
bday1.minute.isNone
bday1.second.isNone
test "PREF ordering":
check:
v4Ex.nickname --> map(it.value) == @[@["Jack Jr."], @["Doc A"]]
v4Ex.nickname.inPrefOrder --> map(it.value) == @[@["Doc A"], @["Jack Jr."]]