Refactor vCard validation and canonicalize VERSION handling
Move whole-card cardinality checks into overloaded validate helpers in the vCard 3 and vCard 4 modules, and have readVCard invoke those validators after parsing instead of hard-coding property checks in the shared parser. For vCard 3, validate property cardinality directly from propertyCardMap and add regression coverage for duplicate single-cardinality properties. For vCard 4, validate from propertyCardMap as well, while preserving ALTID semantics for single-cardinality properties so alternate representations remain valid and duplicate ungrouped properties are rejected. Rework VERSION handling to treat it as parser metadata rather than stored card content. Parsing still requires exactly one canonical VERSION line and rejects missing or duplicate occurrences, but parsed cards no longer retain VC3_Version or VC4_Version objects in content. Accessors and serializers continue to expose and emit the canonical version for the concrete card type, which keeps parsed and programmatically constructed cards on the same model. Update the vCard 3 and vCard 4 test suites to cover the new validation path, confirm VERSION is not persisted in content, and adjust fixture expectations to match the canonical VERSION model. AI-Assisted: yes AI-Tool: OpenAI Codex / gpt-5.4 xhigh
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import std/[options, strutils, times, unittest]
|
||||
import std/[options, sequtils, strutils, times, unittest]
|
||||
import zero_functional
|
||||
|
||||
import vcard
|
||||
@@ -57,6 +57,7 @@ suite "vcard/vcard3":
|
||||
"BEGIN:vCard\r\n" &
|
||||
"VERSION:3.0\r\n" &
|
||||
"FN:Frank Dawson\r\n" &
|
||||
"N:Dawson;Frank;;;\r\n" &
|
||||
"ORG:Lotus Development Corporation\r\n" &
|
||||
"ADR;TYPE=WORK,POSTAL,PARCEL:;;6544 Battleford Drive\r\n" &
|
||||
" ;Raleigh;NC;27613-3502;U.S.A.\r\n" &
|
||||
@@ -71,6 +72,7 @@ suite "vcard/vcard3":
|
||||
"BEGIN:vCard\r\n" &
|
||||
"VERSION:3.0\r\n" &
|
||||
"FN:Tim Howes\r\n" &
|
||||
"N:Howes;Tim;;;\r\n" &
|
||||
"ORG:Netscape Communications Corp.\r\n" &
|
||||
"ADR;TYPE=WORK:;;501 E. Middlefield Rd.;Mountain View;\r\n" &
|
||||
" CA; 94043;U.S.A.\r\n" &
|
||||
@@ -104,6 +106,25 @@ suite "vcard/vcard3":
|
||||
"VERSION:3.0",
|
||||
"FN:John Smith"))
|
||||
|
||||
test "spec: parser rejects duplicate single-cardinality vCard 3 properties":
|
||||
expect(VCardParsingError):
|
||||
discard parseVCards(vcard3Doc(
|
||||
"VERSION:3.0",
|
||||
"FN:John Smith",
|
||||
"N:Smith;John;;;",
|
||||
"BDAY:2000-01-01",
|
||||
"BDAY:2000-01-02"))
|
||||
|
||||
test "spec: VERSION is not stored as vCard 3 content":
|
||||
let parsed = parseSingleVCard3(vcard3Doc(
|
||||
"VERSION:3.0",
|
||||
"FN:John Smith",
|
||||
"N:Smith;John;;;"))
|
||||
|
||||
check:
|
||||
parsed.version.value == "3.0"
|
||||
parsed.content.countIt(it of VC3_Version) == 0
|
||||
|
||||
test "spec: simple text values decode RFC 2426 escapes when parsing":
|
||||
let parsed = parseSingleVCard3(vcard3Doc(
|
||||
"VERSION:3.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import std/[options, strutils, tables, unittest]
|
||||
import std/[options, sequtils, strutils, tables, unittest]
|
||||
import zero_functional
|
||||
|
||||
import vcard
|
||||
@@ -203,6 +203,7 @@ suite "vcard/vcard4":
|
||||
madeUpProp.value == "Sample value for my made-up prop."
|
||||
|
||||
let cardWithAltBdayStr = testVCardTemplate % [(
|
||||
"FN:Simon Perreault\r\n" &
|
||||
"BDAY;VALUE=text;ALTID=1:20th century\r\n" &
|
||||
"BDAY;VALUE=date-and-or-time;ALTID=1:19650321\r\n"
|
||||
)]
|
||||
@@ -210,6 +211,14 @@ suite "vcard/vcard4":
|
||||
test "single-cardinality properties allow multiples with ALTID":
|
||||
check parseVCards(cardWithAltBdayStr).len == 1
|
||||
|
||||
test "single-cardinality properties reject multiples without ALTID":
|
||||
expect(VCardParsingError):
|
||||
discard parseVCards(testVCardTemplate % [(
|
||||
"FN:Simon Perreault\r\n" &
|
||||
"BDAY;VALUE=text:20th century\r\n" &
|
||||
"BDAY;VALUE=date-and-or-time:19650321\r\n"
|
||||
)])
|
||||
|
||||
let hasAltBdays = cast[VCard4](parseVCards(cardWithAltBdayStr)[0])
|
||||
|
||||
test "properties with cardinality 1 and altids return the first found by default":
|
||||
@@ -222,6 +231,7 @@ suite "vcard/vcard4":
|
||||
check:
|
||||
hasAltBdays.content.len == 3
|
||||
hasAltBdays.bday.isSome
|
||||
hasAltBdays.content.countIt(it of VC4_Version) == 0
|
||||
|
||||
let allBdays = allAlternatives[VC4_Bday](hasAltBdays)
|
||||
check:
|
||||
|
||||
Reference in New Issue
Block a user