Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions server/backend/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ lint = [
]

tests = [
"jsonschema>=4.23.0",
"pytest>=9.0.3",
"pytest-asyncio>=1.3.0",
"httpx",
Expand Down
6 changes: 3 additions & 3 deletions server/backend/src/cq_server/repositories/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
from datetime import UTC, datetime

from cq.models import KnowledgeUnit
from cq.scoring import calculate_relevance
from sqlalchemy.exc import IntegrityError

from ..core.db import Database
from ..scoring import calculate_relevance
from ._normalize import normalize_domains
from ._queries import (
DELETE_UNIT_DOMAINS,
Expand Down Expand Up @@ -119,7 +119,7 @@ def _insert_sync(self, unit: KnowledgeUnit) -> None:
INSERT_UNIT,
{
"id": unit.id,
"data": unit.model_dump_json(),
"data": unit.model_dump_json(exclude_none=True),
"created_at": created_at,
"tier": unit.tier.value,
},
Expand Down Expand Up @@ -176,7 +176,7 @@ def _update_sync(self, unit: KnowledgeUnit) -> None:
with self._db.engine.begin() as conn:
cursor = conn.execute(
UPDATE_UNIT_DATA,
{"id": unit.id, "data": unit.model_dump_json(), "tier": unit.tier.value},
{"id": unit.id, "data": unit.model_dump_json(exclude_none=True), "tier": unit.tier.value},
)
if cursor.rowcount == 0:
raise KeyError(f"Knowledge unit not found: {unit.id}")
Expand Down
89 changes: 0 additions & 89 deletions server/backend/src/cq_server/scoring.py

This file was deleted.

2 changes: 1 addition & 1 deletion server/backend/src/cq_server/services/knowledge.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
from __future__ import annotations

from cq.models import Context, FlagReason, Insight, KnowledgeUnit, Tier, create_knowledge_unit
from cq.scoring import apply_confirmation, apply_flag

from ..exceptions import InvalidDomainError, KnowledgeUnitNotFoundError
from ..models.knowledge import StatsResponse
from ..repositories import KnowledgeRepository, normalize_domains
from ..scoring import apply_confirmation, apply_flag


class KnowledgeService:
Expand Down
49 changes: 49 additions & 0 deletions server/backend/tests/test_schema_oracle.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
"""Tests that validate backend-serialised units against the canonical schema."""

from __future__ import annotations

import json
from datetime import UTC, datetime

import cq_schema
import jsonschema
from cq.models import Context, Evidence, Flag, FlagReason, Insight, KnowledgeUnit, Tier, create_knowledge_unit


def _make_full_unit() -> KnowledgeUnit:
now = datetime.now(UTC)
base = create_knowledge_unit(
domains=["databases"],
insight=Insight(summary="s", detail="d", action="a"),
context=Context(languages=["python"], frameworks=["sqlalchemy"], pattern="orm"),
tier=Tier.PRIVATE,
created_by="test-agent",
)
return base.model_copy(
update={
"evidence": Evidence(
confidence=0.7,
confirmations=2,
first_observed=now,
last_confirmed=now,
),
"flags": [Flag(reason=FlagReason.STALE, timestamp=now)],
}
)


def test_full_knowledge_unit_validates_against_canonical_schema() -> None:
unit = _make_full_unit()
schema = cq_schema.load_schema("knowledge_unit")
payload = json.loads(unit.model_dump_json(exclude_none=True))
jsonschema.validate(instance=payload, schema=schema)
Comment thread
peteski22 marked this conversation as resolved.


def test_minimal_knowledge_unit_validates_against_canonical_schema() -> None:
unit = create_knowledge_unit(
domains=["databases"],
insight=Insight(summary="s", detail="d", action="a"),
)
schema = cq_schema.load_schema("knowledge_unit")
payload = json.loads(unit.model_dump_json(exclude_none=True))
jsonschema.validate(instance=payload, schema=schema)
65 changes: 0 additions & 65 deletions server/backend/tests/test_scoring.py

This file was deleted.

24 changes: 22 additions & 2 deletions server/backend/tests/test_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
``knowledge_repo`` / ``reviews_repo`` fixtures instead.
"""

import json
import sqlite3
from datetime import UTC, datetime, timedelta
from typing import Any
Expand All @@ -19,8 +20,7 @@
Tier,
create_knowledge_unit,
)

from cq_server.scoring import apply_confirmation, apply_flag
from cq.scoring import apply_confirmation, apply_flag

from .conftest import _RepoBundle

Expand Down Expand Up @@ -61,6 +61,13 @@ async def _insert_and_approve(store: _RepoBundle, **overrides: Any) -> Knowledge
return unit


def _load_stored_unit_json(store: _RepoBundle, unit_id: str) -> dict[str, Any]:
with store._engine.connect() as conn:
row = conn.exec_driver_sql("SELECT data FROM knowledge_units WHERE id = ?", (unit_id,)).fetchone()
assert row is not None
return json.loads(row[0])


class TestInsertAndGet:
async def test_insert_and_retrieve(self, store: _RepoBundle) -> None:
unit = _make_unit()
Expand Down Expand Up @@ -92,6 +99,12 @@ async def test_insert_persists_normalized_domains_in_blob(self, store: _RepoBund
assert retrieved is not None
assert retrieved.domains == ["databases", "performance"]

async def test_insert_omits_none_fields_in_stored_json(self, store: _RepoBundle) -> None:
unit = _make_unit()
await store.insert(unit)
persisted = _load_stored_unit_json(store, unit.id)
assert "superseded_by" not in persisted


class TestUpdate:
async def test_update_persists_changes(self, store: _RepoBundle) -> None:
Expand Down Expand Up @@ -124,6 +137,13 @@ async def test_update_persists_normalized_domains_in_blob(self, store: _RepoBund
assert retrieved is not None
assert retrieved.domains == ["databases", "performance"]

async def test_update_omits_none_fields_in_stored_json(self, store: _RepoBundle) -> None:
unit = _make_unit()
await store.insert(unit)
await store.update(unit)
persisted = _load_stored_unit_json(store, unit.id)
assert "superseded_by" not in persisted


class TestQuery:
async def test_returns_matching_units(self, store: _RepoBundle) -> None:
Expand Down
Loading
Loading