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.
90 lines
2.6 KiB
Python
90 lines
2.6 KiB
Python
"""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
|