Extract Knowledge Base into a plugin (reference non-asset plugin)
First feature extracted from core into a plugin per "plugin is the product", mirroring the notifications plugin. KB is a NON-asset plugin: it contributes a model + blueprint + nav item but registers no AssetType. - plugins/knowledgebase/: manifest.json (api_prefix /api/knowledgebase, no deps), models/ (KnowledgeBase, contract-pure imports via shopdb.api), api/ (the blueprint, same routes/prefix so the frontend is unchanged), plugin.py (get_blueprint + get_models + get_navigation_items). - De-cored: removed shopdb/core/models/knowledgebase.py + api/knowledgebase.py, their __init__ exports, and 'knowledgebase' from CORE_BLUEPRINT_NAMES; dropped the hardcoded KB nav item from dashboard.py (now via the plugin nav hook). - search.py and reports.py lazy-import KnowledgeBase from the plugin and degrade gracefully (search skips via _require_enabled when disabled; kb-popularity report returns 503 if the plugin is absent). - Registered in instance/plugins.json (enabled). The knowledgebase table stays in the core Alembic chain (bundled-plugin schema folded into core, ADR-004); the model just maps it. KB was never in the shopdb.api contract surface, so no __contract_version__ bump. Pinned with characterization tests first (test_knowledgebase.py); they pass unchanged against the plugin blueprint. 163 tests pass, naming green, app boots 7 bundled plugins, KB endpoint/nav/search verified live. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
34
tests/test_core/test_knowledgebase.py
Normal file
34
tests/test_core/test_knowledgebase.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""Characterization tests for the Knowledge Base API.
|
||||
|
||||
Written before extracting Knowledge Base into a plugin (pinning-flask-behavior):
|
||||
the endpoints must behave identically whether KB is a core blueprint or a plugin
|
||||
blueprint, since both register at /api/knowledgebase.
|
||||
"""
|
||||
|
||||
|
||||
def test_create_list_get_article(client, db, auth_headers):
|
||||
"""Create an article, see it in the list, fetch it by id."""
|
||||
created = client.post('/api/knowledgebase',
|
||||
json={'shortdescription': 'How to reset a printer',
|
||||
'linkurl': 'https://kb.example/printer-reset',
|
||||
'keywords': 'printer reset'},
|
||||
headers=auth_headers)
|
||||
assert created.status_code == 201, created.get_json()
|
||||
linkid = created.get_json()['data']['linkid']
|
||||
|
||||
listing = client.get('/api/knowledgebase', headers=auth_headers)
|
||||
assert listing.status_code == 200
|
||||
ids = [a['linkid'] for a in listing.get_json()['data']]
|
||||
assert linkid in ids
|
||||
|
||||
fetched = client.get(f'/api/knowledgebase/{linkid}', headers=auth_headers)
|
||||
assert fetched.status_code == 200
|
||||
assert fetched.get_json()['data']['shortdescription'] == 'How to reset a printer'
|
||||
|
||||
|
||||
def test_create_requires_shortdescription(client, db, auth_headers):
|
||||
"""shortdescription is required."""
|
||||
resp = client.post('/api/knowledgebase',
|
||||
json={'linkurl': 'https://kb.example/x'},
|
||||
headers=auth_headers)
|
||||
assert resp.status_code == 400
|
||||
Reference in New Issue
Block a user