Skip to content

Commit 4333410

Browse files
committed
Add round-trip conversion tests and refactor EmailAddress
- Add comprehensive RoundTripTests for RFC conversions - Refactor EmailAddress+RFC2822 implementation - Update EmailAddress core implementation - Improve RFC conversion implementations - Update RFC2822 conversion tests
1 parent 324b0fc commit 4333410

File tree

7 files changed

+98
-12
lines changed

7 files changed

+98
-12
lines changed

Sources/EmailAddress/EmailAddress+RFC2822.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ extension RFC_2822.AddrSpec {
2121
// Try to use RFC 5322 representation if available (most compatible)
2222
if let rfc5322 = emailAddress.rfc5322 {
2323
try self.init(
24-
localPart: rfc5322.localPart.description,
24+
localPart: String(describing: rfc5322.localPart),
2525
domain: rfc5322.domain.name
2626
)
2727
} else {
2828
// Fall back to RFC 6531 (most permissive)
2929
try self.init(
30-
localPart: emailAddress.rfc6531.localPart.description,
30+
localPart: String(describing: emailAddress.rfc6531.localPart),
3131
domain: emailAddress.rfc6531.domain.name
3232
)
3333
}

Sources/EmailAddress/EmailAddress.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,12 +91,12 @@ extension EmailAddress {
9191
/// The local part (before @)
9292
public var localPart: String {
9393
if let rfc5321 = rfc5321 {
94-
return rfc5321.localPart.description
94+
return String(describing: rfc5321.localPart)
9595
}
9696
if let rfc5322 = rfc5322 {
97-
return rfc5322.localPart.description
97+
return String(describing: rfc5322.localPart)
9898
}
99-
return rfc6531.localPart.description
99+
return String(describing: rfc6531.localPart)
100100
}
101101
}
102102

Sources/EmailAddress/RFC_5321+RFC_5322.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extension RFC_5321.EmailAddress {
1313
public init(_ rfc5322: RFC_5322.EmailAddress) throws {
1414
try self.init(
1515
displayName: rfc5322.displayName,
16-
localPart: .init(rfc5322.localPart.description),
16+
localPart: .init(String(describing: rfc5322.localPart)),
1717
domain: rfc5322.domain
1818
)
1919
}
@@ -30,7 +30,7 @@ extension RFC_5322.EmailAddress {
3030
public init(_ rfc5321: RFC_5321.EmailAddress) throws {
3131
try self.init(
3232
displayName: rfc5321.displayName,
33-
localPart: .init(rfc5321.localPart.description),
33+
localPart: .init(String(describing: rfc5321.localPart)),
3434
domain: rfc5321.domain
3535
)
3636
}

Sources/EmailAddress/RFC_5321+RFC_6531.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ extension RFC_5321.EmailAddress {
1414
public init(_ rfc6531: RFC_6531.EmailAddress) throws {
1515
try self.init(
1616
displayName: rfc6531.displayName,
17-
localPart: .init(rfc6531.localPart.description),
17+
localPart: .init(String(describing: rfc6531.localPart)),
1818
domain: .init(rfc6531.domain.name)
1919
)
2020
}
@@ -31,7 +31,7 @@ extension RFC_6531.EmailAddress {
3131
public init(_ rfc5321: RFC_5321.EmailAddress) throws {
3232
try self.init(
3333
displayName: rfc5321.displayName,
34-
localPart: .init(rfc5321.localPart.description),
34+
localPart: .init(String(describing: rfc5321.localPart)),
3535
domain: rfc5321.domain
3636
)
3737
}

Sources/EmailAddress/RFC_5322+RFC_6531.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ extension RFC_5322.EmailAddress {
1313
public init(_ rfc6531: RFC_6531.EmailAddress) throws {
1414
try self.init(
1515
displayName: rfc6531.displayName,
16-
localPart: .init(rfc6531.localPart.description),
16+
localPart: .init(String(describing: rfc6531.localPart)),
1717
domain: rfc6531.domain
1818
)
1919
}
@@ -30,7 +30,7 @@ extension RFC_6531.EmailAddress {
3030
public init(_ rfc5322: RFC_5322.EmailAddress) throws {
3131
try self.init(
3232
displayName: rfc5322.displayName,
33-
localPart: .init(rfc5322.localPart.description),
33+
localPart: .init(String(describing: rfc5322.localPart)),
3434
domain: rfc5322.domain
3535
)
3636
}

Tests/EmailAddress Tests/RFC2822ConversionTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct RFC2822ConversionTests {
2222
let addrSpec = try RFC_2822.AddrSpec(localPart: "test", domain: "example.org")
2323
let email = try EmailAddress(addrSpec)
2424

25-
#expect(email.rfc5322?.localPart.stringValue == "test")
25+
#expect(email.rfc5322?.localPart.description == "test")
2626
#expect(email.rfc5322?.domain.name == "example.org")
2727
}
2828

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//
2+
// RoundTripTests.swift
3+
// EmailAddress Tests
4+
//
5+
// Tests for round-trip conversions between RFC formats
6+
//
7+
8+
import Foundation
9+
@testable import EmailAddress
10+
import RFC_5321
11+
import RFC_5322
12+
import RFC_6531
13+
import Testing
14+
15+
@Suite("Round-Trip Conversion Tests")
16+
struct RoundTripTests {
17+
18+
@Test("RFC 5321 -> EmailAddress -> RFC 5321")
19+
func rfc5321RoundTrip() throws {
20+
let original = try RFC_5321.EmailAddress("[email protected]")
21+
let emailAddress = try EmailAddress(rfc5321: original)
22+
23+
guard let converted = emailAddress.rfc5321 else {
24+
throw EmailAddress.Error.conversionFailure
25+
}
26+
27+
#expect(converted.addressValue == original.addressValue)
28+
#expect(converted.displayName == original.displayName)
29+
#expect(converted.localPart.description == original.localPart.description)
30+
#expect(converted.domain.name == original.domain.name)
31+
}
32+
33+
@Test("RFC 5322 -> EmailAddress -> RFC 5322")
34+
func rfc5322RoundTrip() throws {
35+
let original = try RFC_5322.EmailAddress("John Doe <[email protected]>")
36+
let emailAddress = try EmailAddress(rfc5322: original)
37+
38+
guard let converted = emailAddress.rfc5322 else {
39+
throw EmailAddress.Error.conversionFailure
40+
}
41+
42+
#expect(converted.addressValue == original.addressValue)
43+
#expect(converted.displayName == original.displayName)
44+
}
45+
46+
@Test("RFC 6531 -> EmailAddress -> RFC 6531")
47+
func rfc6531RoundTrip() throws {
48+
let original = try RFC_6531.EmailAddress("用户@example.com")
49+
let emailAddress = EmailAddress(rfc6531: original)
50+
let converted = emailAddress.rfc6531
51+
52+
#expect(converted.addressValue == original.addressValue)
53+
#expect(converted.displayName == original.displayName)
54+
}
55+
56+
@Test("ASCII email has all RFC format representations")
57+
func asciiEmailAllFormats() throws {
58+
// Initialize from string - using init with components to avoid ambiguity
59+
let emailAddress = try EmailAddress(localPart: "test", domain: "example.com")
60+
61+
#expect(emailAddress.isASCII == true)
62+
#expect(emailAddress.rfc5321 != nil)
63+
#expect(emailAddress.rfc5322 != nil)
64+
65+
// Verify all formats produce the same address value
66+
let rfc5321Value = emailAddress.rfc5321?.addressValue
67+
let rfc5322Value = emailAddress.rfc5322?.addressValue
68+
let rfc6531Value = emailAddress.rfc6531.addressValue
69+
70+
#expect(rfc5321Value == "[email protected]")
71+
#expect(rfc5322Value == "[email protected]")
72+
#expect(rfc6531Value == "[email protected]")
73+
}
74+
75+
@Test("Internationalized email only has RFC 6531 format")
76+
func internationalizedEmailFormat() throws {
77+
// Create via RFC 6531 directly to avoid ambiguity
78+
let rfc6531 = try RFC_6531.EmailAddress("用户@example.com")
79+
let emailAddress = EmailAddress(rfc6531: rfc6531)
80+
81+
#expect(emailAddress.isInternationalized == true)
82+
#expect(emailAddress.rfc5321 == nil)
83+
#expect(emailAddress.rfc5322 == nil)
84+
#expect(emailAddress.rfc6531.addressValue == "用户@example.com")
85+
}
86+
}

0 commit comments

Comments
 (0)