diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-19 14:01:38 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-19 14:01:38 +0200 |
| commit | d2495a2c4b89fbbfc18cefec83ae96cabdd745e2 (patch) | |
| tree | 463e4d11b1c742475aac07155818c9c64a1e43e5 /packages/meshbay-node/tests/test_node_settings_defaults.py | |
| parent | f8223293a211a87c92b1fed80f5ca53660f6b26c (diff) | |
| parent | 933daccbcfde7705413d3a10db87d910c650ed42 (diff) | |
| download | meshbay-d2495a2c4b89fbbfc18cefec83ae96cabdd745e2.tar.gz | |
Merge origin/main: the operator's upload ceiling beside the disk-thread work
One conflict, in §15.3's open list, and it was two changes agreeing rather than
disagreeing: this side removed the rows for the third-party search bound and the
node-announcement bound because both are now built (AV27, AV28), while the other
side kept them and added a new one. Resolved by keeping what is genuinely still
open — per-device revocation having no CLI — and leaving the two closed.
`webrtc_server.py` merged without conflict but the two sides met inside one
function: `_upload_chunk` gained the operator's `max_upload_gb` ceiling from
there and the per-group lock and `off_disk` calls from here. Read back rather
than trusted: the operator's ceiling now sits inside the critical section that
keeps chunk ordering, and the unlink beside it goes to the disk thread with
everything else.
2893 passed. The twelve `test_sticky_header.py[firefox]` setup errors are the
open Firefox on this machine, as before.
Diffstat (limited to 'packages/meshbay-node/tests/test_node_settings_defaults.py')
| -rw-r--r-- | packages/meshbay-node/tests/test_node_settings_defaults.py | 85 |
1 files changed, 85 insertions, 0 deletions
diff --git a/packages/meshbay-node/tests/test_node_settings_defaults.py b/packages/meshbay-node/tests/test_node_settings_defaults.py new file mode 100644 index 0000000..f5d7856 --- /dev/null +++ b/packages/meshbay-node/tests/test_node_settings_defaults.py @@ -0,0 +1,85 @@ +""" +Three lists name the same node settings, and they must agree. + +The resolver (`Roster.node_settings`) answers for a set of keys; the reader +(`config.node_settings_defaults`) supplies what node.toml says for each; the +writer (`ops.NODE_SETTING_WRITERS`) says which may be set and how each is +validated. Nothing in the code path errors when the reader covers fewer keys +than the resolver answers for: the missing one resolves to None, is written +back onto the config, and the consumer falls through to its own default. So +node.toml is parsed, validated — and ignored. + +That is what happened to `max_concurrent_downloads` and +`max_concurrent_uploads`, and it was invisible because the value it fell back +to was the same 8 the file suggests. Only an operator who set something else +would ever have seen it, and then only as pools that did not match the file. +""" +import pytest +from meshbay_node.config import NodeConfig, load_config, node_settings_defaults +from meshbay_node.ops import NODE_SETTING_WRITERS +from meshbay_node.roster import Roster + + +def test_the_reader_covers_every_setting_the_resolver_answers_for(): + assert set(node_settings_defaults()) == set(Roster.node_setting_keys()) + + +def test_the_writer_covers_every_setting_the_resolver_answers_for(): + assert set(NODE_SETTING_WRITERS) == set(Roster.node_setting_keys()) + + +def test_no_default_is_none(): + """A None here is indistinguishable from a key that is missing.""" + missing = [k for k, v in node_settings_defaults().items() if v is None] + assert not missing + + +@pytest.mark.asyncio +async def test_node_toml_survives_startup_with_no_panel_override(tmp_path): + """The whole bug, at the level an operator meets it.""" + conf = tmp_path / "node.toml" + conf.write_text( + '[hub]\nurl = "https://example.invalid"\nusername = "op"\n\n' + '[node]\n' + 'max_concurrent_downloads = 3\n' + 'max_concurrent_uploads = 2\n' + 'max_concurrent_streams = 5\n' + 'max_upload_gb = 4\n', + encoding="utf-8") + nd = load_config(conf).node + + roster = Roster(db_path=tmp_path / "roster.db") + await roster.open() + try: + effective = await roster.node_settings(node_settings_defaults(nd)) + finally: + await roster.close() + + assert effective["max_concurrent_downloads"] == 3 + assert effective["max_concurrent_uploads"] == 2 + assert effective["max_concurrent_streams"] == 5 + assert effective["max_upload_gb"] == 4.0 + + +@pytest.mark.asyncio +async def test_a_panel_override_still_wins_over_the_file(tmp_path): + conf = tmp_path / "node.toml" + conf.write_text( + '[hub]\nurl = "https://example.invalid"\nusername = "op"\n\n' + '[node]\nmax_concurrent_downloads = 3\n', encoding="utf-8") + nd = load_config(conf).node + + roster = Roster(db_path=tmp_path / "roster.db") + await roster.open() + try: + await roster.set_node_setting(Roster.SETTING_MAX_DOWNLOADS, "6", "op") + effective = await roster.node_settings(node_settings_defaults(nd)) + finally: + await roster.close() + + assert effective["max_concurrent_downloads"] == 6 + + +def test_an_absent_config_falls_back_to_the_dataclass_not_to_literals(): + """`node_status` used to repeat the numbers by hand, and they went stale.""" + assert node_settings_defaults(None) == node_settings_defaults(NodeConfig()) |