added pytest test suite (174 tests)
174 tests covering URL normalization, FTS5 query sanitization, SSRF/CSRF guards, sharing-mode logic, DB schema and upsert paths, handler end-to-end flows, and gateway body-size / mesh-whitelist guards. Each recent bug-fix commit (6ffd38d,1bc695f,8dffd8c) has an explicit regression test in test_regressions.py. One xfail documents a minor latent bug in clean_url where port 80 is not stripped from upgraded https URLs.
This commit is contained in:
parent
5936610e33
commit
d6f418d27c
18 changed files with 1673 additions and 0 deletions
90
tests/test_db_schema.py
Normal file
90
tests/test_db_schema.py
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
"""Tests for `init_db` and the settings key-value store.
|
||||
|
||||
`init_db` is called unconditionally on startup, so it must be idempotent
|
||||
and create every table/trigger the rest of the app expects.
|
||||
"""
|
||||
from db import get_db, return_db, init_db, get_setting, set_setting, get_site_name
|
||||
|
||||
|
||||
EXPECTED_TABLES = {
|
||||
"pages", "links", "settings", "subscriptions",
|
||||
"remote_pages", "tags", "page_tags", "chunks",
|
||||
# FTS5 virtual tables:
|
||||
"pages_fts", "remote_pages_fts",
|
||||
}
|
||||
|
||||
|
||||
def test_all_expected_tables_exist(temp_db):
|
||||
db = get_db()
|
||||
try:
|
||||
rows = db.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type IN ('table') AND name NOT LIKE 'sqlite_%'"
|
||||
).fetchall()
|
||||
names = {r["name"] for r in rows}
|
||||
finally:
|
||||
return_db(db)
|
||||
missing = EXPECTED_TABLES - names
|
||||
assert not missing, f"tables missing after init_db: {missing}"
|
||||
|
||||
|
||||
def test_fts_triggers_exist(temp_db):
|
||||
db = get_db()
|
||||
try:
|
||||
rows = db.execute(
|
||||
"SELECT name FROM sqlite_master WHERE type = 'trigger'"
|
||||
).fetchall()
|
||||
names = {r["name"] for r in rows}
|
||||
finally:
|
||||
return_db(db)
|
||||
# These triggers keep pages_fts in sync with pages on insert/update/delete.
|
||||
for trigger in ("pages_ai", "pages_ad", "pages_au"):
|
||||
assert trigger in names, f"missing trigger {trigger}"
|
||||
|
||||
|
||||
def test_init_db_is_idempotent(temp_db):
|
||||
"""Running init_db twice on the same DB must not error or duplicate anything."""
|
||||
init_db()
|
||||
init_db() # second call should be a no-op
|
||||
db = get_db()
|
||||
try:
|
||||
count = db.execute(
|
||||
"SELECT count(*) FROM sqlite_master WHERE name = 'pages'"
|
||||
).fetchone()[0]
|
||||
finally:
|
||||
return_db(db)
|
||||
assert count == 1
|
||||
|
||||
|
||||
def test_get_setting_returns_default_when_missing(temp_db):
|
||||
assert get_setting("nonexistent", "fallback") == "fallback"
|
||||
assert get_setting("nonexistent") == ""
|
||||
|
||||
|
||||
def test_set_setting_then_get(temp_db):
|
||||
set_setting("site_name", "my-personal-index")
|
||||
assert get_setting("site_name") == "my-personal-index"
|
||||
|
||||
|
||||
def test_set_setting_updates_existing(temp_db):
|
||||
set_setting("key", "first")
|
||||
set_setting("key", "second")
|
||||
assert get_setting("key") == "second"
|
||||
|
||||
|
||||
def test_get_site_name_has_default(temp_db):
|
||||
assert get_site_name() == "tinyweb"
|
||||
|
||||
|
||||
def test_get_site_name_reflects_override(temp_db):
|
||||
set_setting("site_name", "custom-site")
|
||||
assert get_site_name() == "custom-site"
|
||||
|
||||
|
||||
def test_foreign_keys_pragma_enabled(temp_db):
|
||||
"""Pool connections should have foreign_keys=ON so CASCADE deletes work."""
|
||||
db = get_db()
|
||||
try:
|
||||
row = db.execute("PRAGMA foreign_keys").fetchone()
|
||||
finally:
|
||||
return_db(db)
|
||||
assert row[0] == 1
|
||||
Loading…
Add table
Add a link
Reference in a new issue