Commit Graph

18 Commits

Author SHA1 Message Date
Ulas Kalayci 0dc303b81a feat(contacts): add multi-value fields support to PUT /contacts/:id
Extend PUT /contacts/:id route to support updating phones, emails, and
addresses arrays with replacement semantics. Uses atomic transactions
to DELETE all existing multi-values then INSERT new ones. Validates
all multi-value fields before updating. Response includes full contact
with multi-value fields via loadMultiValueFields() helper.

Backward compatible: multi-value fields only replaced when present in
request body. Scalar fields (name, category, etc.) continue to work
independently.

Tests added:
- Update with multi-value fields (replacement semantics verified)
- Validation error on invalid phone data (400)
- Backward compatibility: update without multi-values preserves them

All 109 tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:31:01 +02:00
Ulas Kalayci 966a6d46e3 feat(contacts): add POST /contacts with multi-value fields
Implements Task 12: Extend POST /contacts to accept and persist phones,
emails, and addresses arrays. Uses atomic transactions to ensure all
related records are created together or rolled back on error.

- Validation: validatePhones/Emails/Addresses before insert
- Transaction: db.transaction() for atomic Contact + Multi-Values
- Backward compatible: Multi-value fields are optional
- Refactoring: Extracted loadMultiValueFields() helper (DRY)
- Response includes all multi-value fields with generated IDs

Tests: 3 new tests (create with multi-values, validation, backward compat)
TDD workflow: RED → GREEN → REFACTOR → Commit

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:25:18 +02:00
Ulas Kalayci fe8af33568 feat(contacts): add GET /contacts/:id with multi-value fields
Implements Task 11: Extend GET /contacts/:id to include phones, emails,
and addresses arrays. Each multi-value field is queried from its respective
table (contact_phones, contact_emails, contact_addresses) and mapped to
camelCase response format with isPrimary boolean conversion.

Tests: 2 new tests (contact with multi-values, empty arrays)
TDD workflow: RED → GREEN → Commit

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 18:18:07 +02:00
Ulas Kalayci 674fe796b3 feat(cardav): implement POST /accounts/:id/sync endpoint
Adds route to sync all enabled addressbooks for an account with mock support for tests.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:54:03 +02:00
Ulas Kalayci 9ec7fda6b0 feat(cardav): implement PUT /addressbooks/:id endpoint
Adds route to toggle addressbook enabled/disabled state with bool validation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:47:26 +02:00
Ulas Kalayci c078a48884 feat(cardav): implement POST /accounts/:id/addressbooks/refresh endpoint
Add addressbook refresh route. Loads account from DB, delegates to
CardDAVSync.discoverAddressbooks() to run PROPFIND, then returns
updated addressbook list from DB.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:13:14 +02:00
Ulas Kalayci 12e8edf4c3 feat(cardav): implement GET /accounts/:id/addressbooks endpoint
Add addressbook listing route. Queries carddav_addressbook_selection
table directly, ordered by name. Returns empty array if account has
no addressbooks.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:11:53 +02:00
Ulas Kalayci dd5ac8812c feat(cardav): implement POST /accounts/:id/test endpoint
Add connection test route. Loads account from DB and delegates to
CardDAVSync.testConnection() to verify credentials and discover
addressbooks without saving.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:10:25 +02:00
Ulas Kalayci ca92cb2a86 feat(cardav): implement DELETE /accounts/:id endpoint
Add account deletion route with ID validation. Delegates to
CardDAVSync.deleteAccount() which cascades to addressbooks and
contacts via foreign key constraints.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 17:04:39 +02:00
Ulas Kalayci f7eb73b835 feat(cardav): implement POST /accounts endpoint
Add account creation route with validation. Delegates to
CardDAVSync.addAccount() which creates account and discovers
addressbooks. Returns 201 with account + addressbooks array.

Add _mockTestConnection() helper for testing CardDAV routes without
real server connections.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:54:52 +02:00
Ulas Kalayci 930800eed9 fix(cardav): improve router security and test coverage
- Remove error message leakage (return generic 'Interner Fehler')
- Remove unused imports (str, collectErrors, MAX_TITLE, MAX_URL)
- Add _resetTestDatabase() for proper test cleanup
- Add test for populated accounts case with shape validation
- Import 'after' from node:test for proper teardown

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:51:21 +02:00
Ulas Kalayci cf68bff25f feat(cardav): create cardav router with GET /accounts
Add CardDAV API router with GET /accounts endpoint. Returns all
CardDAV accounts from database via cardav-sync service. Added test
infrastructure with _setTestDatabase helper for isolated API testing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-05-04 16:47:24 +02:00
Ulas Kalayci a71547562e feat(contacts): add multi-value array validators
Add validatePhones, validateEmails, validateAddresses for CardDAV
multi-value contact fields. Validates array structure, required
fields, type checks, and max lengths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 13:01:22 +02:00
Ulas Kalayci 8f78ed6fa2 fix: Isolate Contact Merge Logic tests via suite-level before hook
All 4 previously interdependent tests now use shared aliceContact from
before() hook. 54/54 tests passing with full isolation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:32:15 +02:00
Ulas Kalayci a38c2c84fd Fix test interdependencies and remove duplicate test suite in test-carddav.js
- Make all tests independent by creating their own test data
- Remove duplicate Account Management Operations suite (lines 744-801)
- Each test now creates its own accounts instead of relying on previous tests
- Fix unique constraint violations by using distinct account URLs
- All 54 tests now pass independently

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:02:42 +02:00
Ulas Kalayci 96b4f43aff test: add comprehensive account and addressbook management tests
Add test coverage for CardDAV account management, addressbook discovery UPSERT logic, and contact merge scenarios. Tests verify plain-text password storage, duplicate prevention, CASCADE SET NULL on account deletion, addressbook toggle functionality, and NULL-only field updates during sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 11:53:01 +02:00
Ulas Kalayci 689b479b2d Implement CardDAV sync service with account and contact management
- Add server/services/cardav-sync.js with full CardDAV functionality
- Implement account management (add, delete, list, test connection)
- Implement addressbook discovery and selection toggle
- Add vCard parser with support for all standard fields (FN, N, TEL, EMAIL, ADR, ORG, TITLE, URL, BDAY, PHOTO, NICKNAME, NOTE, CATEGORIES)
- Implement smart merge logic for contact deduplication (UID match, email/phone match)
- Handle multi-value fields (phones, emails, addresses) with primary flag preservation
- Add comprehensive tests for vCard parsing and database operations
- All 46 tests passing

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 11:34:25 +02:00
Ulas Kalayci 3f77fdb11d Fix Migration 30 code quality issues
C1 (CRITICAL): Refactor test-carddav.js to import migrations
- Import MIGRATIONS from server/db.js instead of duplicating SQL
- Export MIGRATIONS array from server/db.js
- Use better-sqlite3 to apply Migration 30 dynamically
- Ensures future changes to Migration 30 are reflected in tests

I1 (IMPORTANT): Add UNIQUE index on carddav_uid
- Create idx_contacts_carddav_uid_unique partial index
- Prevents duplicate CardDAV contacts per account+addressbook
- WHERE clause excludes NULL values (manual contacts allowed)
- Add test to verify unique constraint enforcement

I3 (IMPORTANT): Rename cardav → carddav (RFC 6352 compliance)
- Table: cardav_accounts → carddav_accounts
- Table: cardav_addressbook_selection → carddav_addressbook_selection
- Column: contacts.cardav_* → contacts.carddav_*
- Index: idx_cardav_* → idx_carddav_*
- Test file: test-cardav.js → test-carddav.js
- Package.json: test:cardav → test:carddav
- All test assertions updated

All 25 tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 10:55:30 +02:00