BusinessCard/BusinessCardTests/BusinessCardTests.swift

340 lines
12 KiB
Swift

import Foundation
import Testing
import SwiftData
@testable import BusinessCard
@MainActor
struct BusinessCardTests {
private func makeTestContainer() throws -> ModelContainer {
let config = ModelConfiguration(isStoredInMemoryOnly: true)
return try ModelContainer(for: BusinessCard.self, Contact.self, configurations: config)
}
@Test func vCardPayloadIncludesFields() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let card = BusinessCard(
displayName: "Test User",
role: "Developer",
company: "Test Corp",
bio: "A passionate developer"
)
context.insert(card)
// Add contact fields using the ContactField relationship
card.addContactField(.email, value: "test@example.com", title: "Work")
card.addContactField(.phone, value: "+1 555 123 4567", title: "Cell")
card.addContactField(.website, value: "example.com", title: "")
#expect(card.vCardPayload.contains("BEGIN:VCARD"))
#expect(card.vCardPayload.contains("FN:Test User"))
#expect(card.vCardPayload.contains("ORG:Test Corp"))
#expect(card.vCardPayload.contains("EMAIL;TYPE=WORK:test@example.com"))
#expect(card.vCardPayload.contains("TEL;TYPE=CELL:+1 555 123 4567"))
#expect(card.vCardPayload.contains("A passionate developer"))
#expect(card.vCardPayload.contains("END:VCARD"))
}
@Test func vCardPayloadIncludesStructuredName() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let card = BusinessCard(
prefix: "Dr.",
firstName: "John",
middleName: "Michael",
lastName: "Smith",
suffix: "Jr."
)
context.insert(card)
// N: LastName;FirstName;MiddleName;Prefix;Suffix
#expect(card.vCardPayload.contains("N:Smith;John;Michael;Dr.;Jr."))
#expect(card.vCardPayload.contains("FN:Dr. John Michael Smith Jr."))
}
@Test func vCardPayloadIncludesAllContactInfo() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let card = BusinessCard(
role: "CEO",
company: "Acme Inc",
prefix: "",
firstName: "Jane",
lastName: "Doe",
pronouns: "she/her",
department: "Executive",
headline: "Building the future",
bio: "Passionate leader",
accreditations: "MBA, CPA"
)
context.insert(card)
// Verify structured name
#expect(card.vCardPayload.contains("N:Doe;Jane;;;"))
#expect(card.vCardPayload.contains("FN:Jane Doe"))
// Verify org with department
#expect(card.vCardPayload.contains("ORG:Acme Inc;Executive"))
// Verify title
#expect(card.vCardPayload.contains("TITLE:CEO"))
// Verify note contains all info
#expect(card.vCardPayload.contains("Building the future"))
#expect(card.vCardPayload.contains("Passionate leader"))
#expect(card.vCardPayload.contains("Credentials: MBA\\, CPA"))
#expect(card.vCardPayload.contains("Pronouns: she/her"))
}
@Test func defaultCardSelectionUpdatesCards() async throws {
let container = try makeTestContainer()
let context = container.mainContext
// Insert cards directly instead of using samples (which might trigger other logic)
let card1 = BusinessCard(displayName: "Card One", role: "Role", company: "Company", isDefault: true)
let card2 = BusinessCard(displayName: "Card Two", role: "Role", company: "Company", isDefault: false)
context.insert(card1)
context.insert(card2)
try context.save()
let store = CardStore(modelContext: context)
#expect(store.cards.count >= 2)
store.setDefaultCard(card2)
#expect(store.selectedCardID == card2.id)
#expect(store.cards.filter { $0.isDefault }.count == 1)
#expect(store.cards.first { $0.isDefault }?.id == card2.id)
}
@Test func contactsSearchFiltersByNameOrCompany() async throws {
let container = try makeTestContainer()
let context = container.mainContext
// Insert contacts directly
let contact1 = Contact(name: "John Doe", role: "Developer", company: "Global Bank")
let contact2 = Contact(name: "Jane Smith", role: "Designer", company: "Tech Corp")
context.insert(contact1)
context.insert(contact2)
try context.save()
// Create store without triggering sample creation - just use the context
let descriptor = FetchDescriptor<Contact>()
let contacts = try context.fetch(descriptor)
// Filter manually to test the logic
let query = "Global"
let filtered = contacts.filter { $0.company.localizedStandardContains(query) }
#expect(filtered.count == 1)
#expect(filtered.first?.company == "Global Bank")
}
@Test func addCardIncreasesCardCount() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let store = CardStore(modelContext: context)
let initialCount = store.cards.count
let newCard = BusinessCard(
displayName: "New User",
role: "Manager",
company: "New Corp"
)
store.addCard(newCard)
#expect(store.cards.count == initialCount + 1)
}
@Test func deleteCardRemovesFromStore() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let card1 = BusinessCard(displayName: "Card One", role: "Role", company: "Company")
let card2 = BusinessCard(displayName: "Card Two", role: "Role", company: "Company")
context.insert(card1)
context.insert(card2)
try context.save()
let store = CardStore(modelContext: context)
let initialCount = store.cards.count
store.deleteCard(card2)
#expect(store.cards.count == initialCount - 1)
#expect(!store.cards.contains(where: { $0.id == card2.id }))
}
@Test func updateCardChangesProperties() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let card = BusinessCard(
displayName: "Original Name",
role: "Original Role",
company: "Original Company"
)
context.insert(card)
try context.save()
let store = CardStore(modelContext: context)
card.displayName = "Updated Name"
card.role = "Updated Role"
store.updateCard(card)
let updatedCard = store.cards.first(where: { $0.id == card.id })
#expect(updatedCard?.displayName == "Updated Name")
#expect(updatedCard?.role == "Updated Role")
}
@Test func recordShareCreatesContact() async throws {
let container = try makeTestContainer()
let context = container.mainContext
// Manually insert a contact and test recordShare logic
let newContact = Contact(
name: "New Contact",
role: "CEO",
company: "Partner Inc",
cardLabel: "Work"
)
context.insert(newContact)
try context.save()
let descriptor = FetchDescriptor<Contact>()
let contacts = try context.fetch(descriptor)
#expect(contacts.count >= 1)
#expect(contacts.first(where: { $0.name == "New Contact" }) != nil)
}
@Test func recordShareUpdatesExistingContact() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let existingContact = Contact(
name: "Existing Contact",
role: "Manager",
company: "Partner Inc",
cardLabel: "Personal"
)
context.insert(existingContact)
try context.save()
// Update the contact
existingContact.cardLabel = "Work"
existingContact.lastSharedDate = .now
try context.save()
let descriptor = FetchDescriptor<Contact>()
let contacts = try context.fetch(descriptor)
let updated = contacts.first(where: { $0.name == "Existing Contact" })
#expect(updated?.cardLabel == "Work")
}
@Test func themeAssignmentWorks() async throws {
let card = BusinessCard()
card.theme = .midnight
#expect(card.themeName == "Midnight")
#expect(card.theme == .midnight)
card.theme = .ocean
#expect(card.themeName == "Ocean")
}
@Test func layoutStyleAssignmentWorks() async throws {
let card = BusinessCard()
card.layoutStyle = .split
#expect(card.layoutStyleRawValue == "split")
#expect(card.layoutStyle == .split)
card.layoutStyle = .photo
#expect(card.layoutStyleRawValue == "photo")
}
// Tests for high priority features
@Test func contactNotesAndTags() async throws {
let contact = Contact(
name: "Test Contact",
notes: "Met at conference",
tags: "VIP, client, tech"
)
#expect(contact.tagList.count == 3)
#expect(contact.tagList.contains("VIP"))
#expect(contact.tagList.contains("client"))
#expect(contact.tagList.contains("tech"))
}
@Test func contactFollowUpStatus() async throws {
let contact = Contact(name: "Test")
#expect(!contact.hasFollowUp)
#expect(!contact.isFollowUpOverdue)
contact.followUpDate = .now.addingTimeInterval(86400) // Tomorrow
#expect(contact.hasFollowUp)
#expect(!contact.isFollowUpOverdue)
contact.followUpDate = .now.addingTimeInterval(-86400) // Yesterday
#expect(contact.isFollowUpOverdue)
}
@Test func contactFromVCardParsing() async throws {
let vCardData = """
BEGIN:VCARD
VERSION:3.0
FN:John Smith
ORG:Acme Corp
TITLE:CEO
EMAIL;TYPE=work:john@acme.com
TEL;TYPE=work:+1 555 123 4567
END:VCARD
"""
let contact = Contact.fromVCard(vCardData)
#expect(contact.name == "John Smith")
#expect(contact.company == "Acme Corp")
#expect(contact.role == "CEO")
#expect(contact.email == "john@acme.com")
#expect(contact.phone == "+1 555 123 4567")
#expect(contact.isReceivedCard)
#expect(contact.cardLabel == "Received")
}
@Test func addReceivedCardFromVCard() async throws {
let container = try makeTestContainer()
let context = container.mainContext
let vCardData = """
BEGIN:VCARD
VERSION:3.0
FN:Jane Doe
ORG:Test Inc
END:VCARD
"""
let contact = Contact.fromVCard(vCardData)
context.insert(contact)
try context.save()
let descriptor = FetchDescriptor<Contact>()
let contacts = try context.fetch(descriptor)
let received = contacts.first(where: { $0.name == "Jane Doe" })
#expect(received != nil)
#expect(received?.isReceivedCard == true)
}
}