Complete LinkSyncServer and LinkSyncExtension implementation

LinkSyncServer:
- Fix app.py imports, add CORS middleware, lifespan events
- Create api/routes.py router aggregator
- Create config/settings.py for centralized configuration
- Rewrite models/base.py with proper relationships and serialization
- Rewrite all API endpoints with real DB integration (auth, links, collections, sync, queries, tags)
- Add admin endpoints (user management, stats, audit log)
- Complete query parser with recursive descent and proper precedence
- Complete query executor with set operations and field filters
- Set up Alembic migrations with initial schema
- Create web interface (templates, CSS, JS)
- Add 42 passing tests (auth, links, collections, queries)
- Add deploy.ps1 and deploy.sh scripts
- Update README with deployment workflow

LinkSyncExtension:
- Create utils/api.js (REST client with retries, auth, error handling)
- Create utils/sync.js (3 sync modes + conflict detection)
- Create utils/collection.js (collection management)
- Create utils/query-engine.js (client-side query parser)
- Rewrite background.js (sync loop, bookmark events, message routing)
- Rewrite popup.js (tabs, settings modal, notifications, CRUD)
- Update popup.html (tabbed interface, query builder, modal)
- Update popup.css (full redesign)
- Create content/content.js (page metadata extraction)
- Create options.html/js (dedicated settings page)
- Generate icons (48x48, 96x96)
- Update manifest.json (host permissions, content scripts, options)
- Create AGENTS.md
This commit is contained in:
DavidSaylor
2026-05-19 13:21:26 -05:00
parent c5d3912070
commit 09d30427f4
54 changed files with 5918 additions and 3177 deletions

View File

@@ -0,0 +1,83 @@
"""
LinkSyncServer - Collection API Tests
"""
import pytest
from fastapi.testclient import TestClient
class TestCollections:
def test_create_collection(self, client: TestClient, auth_headers: dict, sample_collection_data: dict):
response = client.post("/api/collections/", json=sample_collection_data, headers=auth_headers)
assert response.status_code == 201
data = response.json()
assert data["name"] == sample_collection_data["name"]
assert data["query_type"] == "static"
def test_list_collections(self, client: TestClient, auth_headers: dict, sample_collection_data: dict):
client.post("/api/collections/", json=sample_collection_data, headers=auth_headers)
response = client.get("/api/collections/", headers=auth_headers)
assert response.status_code == 200
assert isinstance(response.json(), list)
def test_get_collection(self, client: TestClient, auth_headers: dict, sample_collection_data: dict):
create_resp = client.post("/api/collections/", json=sample_collection_data, headers=auth_headers)
collection_id = create_resp.json()["id"]
response = client.get(f"/api/collections/{collection_id}", headers=auth_headers)
assert response.status_code == 200
assert response.json()["id"] == collection_id
def test_update_collection(self, client: TestClient, auth_headers: dict, sample_collection_data: dict):
create_resp = client.post("/api/collections/", json=sample_collection_data, headers=auth_headers)
collection_id = create_resp.json()["id"]
response = client.put(
f"/api/collections/{collection_id}",
json={"name": "Updated Name"},
headers=auth_headers,
)
assert response.status_code == 200
assert response.json()["name"] == "Updated Name"
def test_delete_collection(self, client: TestClient, auth_headers: dict, sample_collection_data: dict):
create_resp = client.post("/api/collections/", json=sample_collection_data, headers=auth_headers)
collection_id = create_resp.json()["id"]
response = client.delete(f"/api/collections/{collection_id}", headers=auth_headers)
assert response.status_code == 200
assert response.json()["deleted_id"] == collection_id
def test_add_links_to_collection(
self, client: TestClient, auth_headers: dict, sample_bookmark_data: dict, sample_collection_data: dict
):
bm_resp = client.post("/api/links/", json=sample_bookmark_data, headers=auth_headers)
bookmark_id = bm_resp.json()["id"]
col_resp = client.post("/api/collections/", json=sample_collection_data, headers=auth_headers)
collection_id = col_resp.json()["id"]
response = client.post(
f"/api/collections/{collection_id}/add-links",
json=[bookmark_id],
headers=auth_headers,
)
assert response.status_code == 200
assert response.json()["added_count"] == 1
def test_remove_links_from_collection(
self, client: TestClient, auth_headers: dict, sample_bookmark_data: dict, sample_collection_data: dict
):
bm_resp = client.post("/api/links/", json=sample_bookmark_data, headers=auth_headers)
bookmark_id = bm_resp.json()["id"]
col_data = sample_collection_data.copy()
col_data["link_ids"] = [bookmark_id]
col_resp = client.post("/api/collections/", json=col_data, headers=auth_headers)
collection_id = col_resp.json()["id"]
response = client.request(
"DELETE",
f"/api/collections/{collection_id}/remove-links",
json=[bookmark_id],
headers=auth_headers,
)
assert response.status_code == 200
assert response.json()["removed_count"] == 1