303 lines
11 KiB
Python
303 lines
11 KiB
Python
"""Unit tests for scripts/bootstrap_db.py with a fake Mongo at the leaf.
|
|
|
|
The $jsonSchema validator's runtime behavior is server-side and is not
|
|
covered here; it is verified against the live cluster when the script runs.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "scripts"))
|
|
|
|
import bootstrap_db # noqa: E402
|
|
|
|
|
|
class FakeCollection:
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.docs = {}
|
|
self.indexes = []
|
|
|
|
def find_one(self, query, projection=None):
|
|
return self.docs.get(query["_id"])
|
|
|
|
def update_one(self, query, update, upsert=False):
|
|
class Result:
|
|
upserted_id = None
|
|
|
|
result = Result()
|
|
if query["_id"] not in self.docs and upsert:
|
|
self.docs[query["_id"]] = {"_id": query["_id"], **update["$setOnInsert"]}
|
|
result.upserted_id = query["_id"]
|
|
|
|
return result
|
|
|
|
def create_index(self, keys, **options):
|
|
self.indexes.append((keys, options))
|
|
return "_".join(f"{field}_{direction}" for field, direction in keys)
|
|
|
|
|
|
class FakeDatabase:
|
|
def __init__(self, existing=()):
|
|
self.collections = {name: FakeCollection(name) for name in existing}
|
|
self.options = {name: {} for name in existing}
|
|
self.created = []
|
|
self.commands = []
|
|
|
|
def list_collection_names(self):
|
|
return list(self.collections)
|
|
|
|
def list_collections(self, filter=None):
|
|
names = [filter["name"]] if filter else list(self.collections)
|
|
return [
|
|
{"name": n, "options": self.options[n]}
|
|
for n in names
|
|
if n in self.collections
|
|
]
|
|
|
|
def create_collection(self, name, **options):
|
|
self.collections[name] = FakeCollection(name)
|
|
self.options[name] = options
|
|
self.created.append((name, options))
|
|
|
|
def command(self, name, collection, **kwargs):
|
|
self.commands.append((name, collection, kwargs))
|
|
self.options[collection].update(kwargs)
|
|
|
|
def __getitem__(self, name):
|
|
return self.collections.setdefault(name, FakeCollection(name))
|
|
|
|
|
|
@pytest.fixture
|
|
def seed_root(tmp_path):
|
|
for mode in ("production", "testing"):
|
|
(tmp_path / mode).mkdir()
|
|
(tmp_path / mode / "woot.json").write_text(
|
|
json.dumps({"categories": ["tech"], "channels": {"50-60": "1"}})
|
|
)
|
|
(tmp_path / "production" / "bestbuy.json").write_text(
|
|
json.dumps(
|
|
{
|
|
"categories": {"a": "categoryid$a"},
|
|
"channels": {"60-70": "2"},
|
|
"extra": "ignored",
|
|
}
|
|
)
|
|
)
|
|
(tmp_path / "control.json").write_text(
|
|
json.dumps({"monitors": {"names": ["woot"], "settings": {}}})
|
|
)
|
|
return tmp_path
|
|
|
|
|
|
def test_ensure_collection_creates_absent_with_options():
|
|
db = FakeDatabase()
|
|
assert bootstrap_db.ensure_collection(db, "configs", validator={"x": 1}) is True
|
|
assert db.created == [("configs", {"validator": {"x": 1}})]
|
|
|
|
|
|
def test_ensure_collection_skips_existing():
|
|
db = FakeDatabase(existing=["configs"])
|
|
assert bootstrap_db.ensure_collection(db, "configs") is False
|
|
assert db.created == []
|
|
|
|
|
|
def test_ensure_collection_dry_run_writes_nothing():
|
|
db = FakeDatabase()
|
|
assert bootstrap_db.ensure_collection(db, "configs", dry_run=True) is True
|
|
assert db.created == []
|
|
|
|
|
|
def test_ensure_config_validator_applies_collmod_once():
|
|
db = FakeDatabase(existing=["configs"])
|
|
assert bootstrap_db.ensure_config_validator(db, "configs") is True
|
|
assert db.commands == [
|
|
("collMod", "configs", {"validator": bootstrap_db.CONFIG_VALIDATOR})
|
|
]
|
|
assert bootstrap_db.ensure_config_validator(db, "configs") is False
|
|
assert len(db.commands) == 1
|
|
|
|
|
|
def test_ensure_config_validator_dry_run_and_missing_collection():
|
|
db = FakeDatabase(existing=["configs"])
|
|
assert bootstrap_db.ensure_config_validator(db, "configs", dry_run=True) is True
|
|
assert db.commands == []
|
|
assert bootstrap_db.ensure_config_validator(db, "nope") is False
|
|
|
|
|
|
def test_load_seed_configs_rejects_list_channels(tmp_path):
|
|
(tmp_path / "woot.json").write_text(
|
|
json.dumps({"categories": [], "channels": {"50-60": ["1", "2"]}})
|
|
)
|
|
with pytest.raises(ValueError, match="single id strings"):
|
|
bootstrap_db.load_seed_configs(tmp_path)
|
|
|
|
|
|
def test_alert_indexes_match_consumer_spec():
|
|
alerts = FakeCollection("alerts")
|
|
bootstrap_db.ensure_alert_indexes(alerts)
|
|
assert alerts.indexes == [
|
|
([("claimed", 1), ("priority", -1), ("createdAt", 1)], {}),
|
|
(
|
|
[("product_id", 1)],
|
|
{"unique": True, "partialFilterExpression": {"claimed": False}},
|
|
),
|
|
]
|
|
|
|
|
|
def test_load_seed_configs_keys_by_stem_and_drops_extra_fields(seed_root):
|
|
docs = bootstrap_db.load_seed_configs(seed_root / "production")
|
|
assert set(docs) == {"bestbuy", "woot"}
|
|
assert docs["bestbuy"] == {
|
|
"categories": {"a": "categoryid$a"},
|
|
"channels": {"60-70": "2"},
|
|
}
|
|
|
|
|
|
def test_load_seed_configs_rejects_missing_keys(tmp_path):
|
|
(tmp_path / "bad.json").write_text(json.dumps({"categories": []}))
|
|
with pytest.raises(ValueError, match="channels"):
|
|
bootstrap_db.load_seed_configs(tmp_path)
|
|
|
|
|
|
def test_load_seed_configs_rejects_wrong_shape_for_monitor(tmp_path):
|
|
(tmp_path / "bestbuy.json").write_text(
|
|
json.dumps({"categories": ["abcat0"], "channels": {}})
|
|
)
|
|
with pytest.raises(ValueError, match="categories must be dict for bestbuy"):
|
|
bootstrap_db.load_seed_configs(tmp_path)
|
|
|
|
(tmp_path / "bestbuy.json").unlink()
|
|
(tmp_path / "woot.json").write_text(
|
|
json.dumps({"categories": {"tech": "tech"}, "channels": {}})
|
|
)
|
|
with pytest.raises(ValueError, match="categories must be list for woot"):
|
|
bootstrap_db.load_seed_configs(tmp_path)
|
|
|
|
|
|
def test_load_seed_configs_empty_dir(tmp_path):
|
|
assert bootstrap_db.load_seed_configs(tmp_path) == {}
|
|
|
|
|
|
def test_seed_configs_inserts_absent_and_preserves_existing():
|
|
configs = FakeCollection("configs")
|
|
configs.docs["woot"] = {"_id": "woot", "categories": ["edited"], "channels": {}}
|
|
docs = {
|
|
"woot": {"categories": ["tech"], "channels": {}},
|
|
"hp": {"categories": [], "channels": {}},
|
|
}
|
|
assert bootstrap_db.seed_configs(configs, docs) == ["hp"]
|
|
assert configs.docs["woot"]["categories"] == ["edited"]
|
|
assert configs.docs["hp"] == {"_id": "hp", "categories": [], "channels": {}}
|
|
|
|
|
|
def test_seed_configs_dry_run_reports_without_writing():
|
|
configs = FakeCollection("configs")
|
|
docs = {"hp": {"categories": [], "channels": {}}}
|
|
assert bootstrap_db.seed_configs(configs, docs, dry_run=True) == ["hp"]
|
|
assert configs.docs == {}
|
|
|
|
|
|
def test_load_control_seed_keys_by_id(seed_root):
|
|
docs = bootstrap_db.load_control_seed(seed_root / "control.json")
|
|
assert docs == {"monitors": {"names": ["woot"], "settings": {}}}
|
|
|
|
|
|
def test_load_control_seed_rejects_non_object_doc(tmp_path):
|
|
path = tmp_path / "control.json"
|
|
path.write_text(json.dumps({"monitors": ["woot"]}))
|
|
with pytest.raises(ValueError, match="monitors"):
|
|
bootstrap_db.load_control_seed(path)
|
|
|
|
|
|
def test_seed_control_inserts_absent_and_preserves_existing():
|
|
control = FakeCollection("control")
|
|
control.docs["monitors"] = {"_id": "monitors", "names": ["edited"]}
|
|
docs = {"monitors": {"names": ["woot"]}, "other": {"x": 1}}
|
|
assert bootstrap_db.seed_control(control, docs) == ["other"]
|
|
assert control.docs["monitors"]["names"] == ["edited"]
|
|
assert control.docs["other"] == {"_id": "other", "x": 1}
|
|
|
|
|
|
def test_seed_control_dry_run_reports_without_writing():
|
|
control = FakeCollection("control")
|
|
assert bootstrap_db.seed_control(control, {"monitors": {}}, dry_run=True) == [
|
|
"monitors"
|
|
]
|
|
assert control.docs == {}
|
|
|
|
|
|
def test_bootstrap_builds_both_modes(seed_root):
|
|
db = FakeDatabase()
|
|
summary = bootstrap_db.bootstrap(db, seed_root=seed_root)
|
|
|
|
created = dict(db.created)
|
|
for suffix in ("", "_testing"):
|
|
assert created[f"configs{suffix}"] == {
|
|
"validator": bootstrap_db.CONFIG_VALIDATOR
|
|
}
|
|
assert f"alerts{suffix}" in created
|
|
assert f"control{suffix}" in created
|
|
for monitor in bootstrap_db.MONITORS:
|
|
assert f"{monitor}{suffix}" in created
|
|
assert len(db[f"alerts{suffix}"].indexes) == 2
|
|
|
|
assert summary["seeded"]["configs"] == ["bestbuy", "woot"]
|
|
assert summary["seeded"]["configs_testing"] == ["woot"]
|
|
assert summary["seeded"]["control"] == ["monitors"]
|
|
assert summary["seeded"]["control_testing"] == ["monitors"]
|
|
assert db["control"].docs["monitors"]["names"] == ["woot"]
|
|
assert len(summary["created"]) == 2 * (3 + len(bootstrap_db.MONITORS))
|
|
|
|
|
|
def test_bootstrap_rerun_is_noop(seed_root):
|
|
db = FakeDatabase()
|
|
bootstrap_db.bootstrap(db, seed_root=seed_root)
|
|
db.created.clear()
|
|
summary = bootstrap_db.bootstrap(db, seed_root=seed_root)
|
|
assert db.created == []
|
|
assert db.commands == []
|
|
assert summary["created"] == []
|
|
assert summary["validators"] == []
|
|
assert summary["seeded"] == {
|
|
"configs": [],
|
|
"configs_testing": [],
|
|
"control": [],
|
|
"control_testing": [],
|
|
}
|
|
|
|
|
|
def test_shipped_control_seed_matches_registry_shape():
|
|
"""monitors doc: names list and per-name {min_percentage, ping_threshold}"""
|
|
|
|
docs = bootstrap_db.load_control_seed(bootstrap_db.CONTROL_SEED)
|
|
monitors = docs["monitors"]
|
|
assert set(monitors["settings"]) == set(monitors["names"])
|
|
for name, settings in monitors["settings"].items():
|
|
assert set(settings) == {"min_percentage", "ping_threshold"}, name
|
|
assert isinstance(settings["min_percentage"], int), name
|
|
assert 40 <= settings["min_percentage"] <= 100, name
|
|
assert settings["ping_threshold"] is None or isinstance(
|
|
settings["ping_threshold"], int
|
|
), name
|
|
|
|
|
|
def test_shipped_seed_files_load_and_match_validator_shape():
|
|
"""every checked-in seed doc has the shape the validator and setup_config want"""
|
|
|
|
for mode in ("production", "testing"):
|
|
docs = bootstrap_db.load_seed_configs(bootstrap_db.SEED_ROOT / mode)
|
|
assert docs, mode
|
|
for name, doc in docs.items():
|
|
categories, channels = doc["categories"], doc["channels"]
|
|
assert isinstance(categories, bootstrap_db.CATEGORY_SHAPES[name]), name
|
|
if isinstance(categories, dict):
|
|
assert all(isinstance(v, str) for v in categories.values()), name
|
|
else:
|
|
assert all(isinstance(v, str) for v in categories), name
|
|
for value in channels.values():
|
|
assert isinstance(value, str) and value.isdigit(), (name, value)
|