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>
This commit is contained in:
Ulas Kalayci
2026-05-04 10:55:30 +02:00
parent 18310dbfe5
commit 3f77fdb11d
3 changed files with 147 additions and 160 deletions
+29 -24
View File
@@ -1082,33 +1082,33 @@ const MIGRATIONS = [
-- ========================================
-- CardDAV Accounts
-- ========================================
CREATE TABLE cardav_accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
cardav_url TEXT NOT NULL,
username TEXT NOT NULL,
password TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
last_sync TEXT,
UNIQUE(cardav_url, username)
CREATE TABLE carddav_accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
carddav_url TEXT NOT NULL,
username TEXT NOT NULL,
password TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
last_sync TEXT,
UNIQUE(carddav_url, username)
);
-- ========================================
-- CardDAV Addressbook Selection
-- ========================================
CREATE TABLE cardav_addressbook_selection (
id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER NOT NULL,
addressbook_url TEXT NOT NULL,
CREATE TABLE carddav_addressbook_selection (
id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER NOT NULL,
addressbook_url TEXT NOT NULL,
addressbook_name TEXT NOT NULL,
enabled INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
enabled INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
UNIQUE(account_id, addressbook_url),
FOREIGN KEY(account_id) REFERENCES cardav_accounts(id) ON DELETE CASCADE
FOREIGN KEY(account_id) REFERENCES carddav_accounts(id) ON DELETE CASCADE
);
CREATE INDEX idx_cardav_addressbook_account
ON cardav_addressbook_selection(account_id, enabled);
CREATE INDEX idx_carddav_addressbook_account
ON carddav_addressbook_selection(account_id, enabled);
-- ========================================
-- Extend Contacts Table for CardDAV
@@ -1119,14 +1119,19 @@ const MIGRATIONS = [
ALTER TABLE contacts ADD COLUMN website TEXT;
ALTER TABLE contacts ADD COLUMN photo TEXT;
ALTER TABLE contacts ADD COLUMN nickname TEXT;
ALTER TABLE contacts ADD COLUMN cardav_account_id INTEGER
REFERENCES cardav_accounts(id) ON DELETE SET NULL;
ALTER TABLE contacts ADD COLUMN cardav_uid TEXT;
ALTER TABLE contacts ADD COLUMN cardav_addressbook_url TEXT;
ALTER TABLE contacts ADD COLUMN carddav_account_id INTEGER
REFERENCES carddav_accounts(id) ON DELETE SET NULL;
ALTER TABLE contacts ADD COLUMN carddav_uid TEXT;
ALTER TABLE contacts ADD COLUMN carddav_addressbook_url TEXT;
CREATE INDEX idx_contacts_cardav_uid ON contacts(cardav_uid);
CREATE INDEX idx_contacts_carddav_uid ON contacts(carddav_uid);
CREATE INDEX idx_contacts_email ON contacts(email);
-- UNIQUE constraint for CardDAV UIDs (prevents duplicates per account+addressbook)
CREATE UNIQUE INDEX idx_contacts_carddav_uid_unique
ON contacts(carddav_account_id, carddav_addressbook_url, carddav_uid)
WHERE carddav_uid IS NOT NULL;
-- ========================================
-- Contact Phones (Multiple per Contact)
-- ========================================
@@ -1351,4 +1356,4 @@ function transaction(fn) {
init(); // auto-initialise when module is first imported
export { init, get, transaction, currentVersion, getPath, backupToFile, restoreFromFile };
export { init, get, transaction, currentVersion, getPath, backupToFile, restoreFromFile, MIGRATIONS };