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() 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() 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() 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() let contacts = try context.fetch(descriptor) let received = contacts.first(where: { $0.name == "Jane Doe" }) #expect(received != nil) #expect(received?.isReceivedCard == true) } }