mirror of
https://github.com/Niko1221/Strata.git
synced 2026-11-10 03:41:36 -10:00
One line under the start banner: the config's engine options without the model's file paths, and the server's fields (host:port, an API key set or not, GPUs, draft subset, fit_max_tokens, thinking budget). The key itself is never printed.
146 lines
6.9 KiB
Python
146 lines
6.9 KiB
Python
"""Tests for `setup.py --update` (#475, UPDATE.bat / update.sh): it refreshes what a start would - the Python packages,
|
|
the engine, each model's config and draft subset - and never starts the model. Every outside effect is mocked: no
|
|
GPU, no downloads, nothing written outside a temp folder.
|
|
|
|
python -m unittest tools.test_setup_update
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import contextlib
|
|
import io
|
|
import json
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from unittest import mock
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(ROOT))
|
|
import setup # noqa: E402
|
|
|
|
|
|
class Update(unittest.TestCase):
|
|
def config(self, d: Path, **extra) -> Path:
|
|
rt = d / "mtp" / "rt"
|
|
rt.mkdir(parents=True)
|
|
p = d / "strata-iq3_s.json"
|
|
p.write_text(json.dumps({"model_name": "Qwen IQ3_S", "exe": str(d / "engine" / setup.EXE),
|
|
"args": ["--native", "x", "--mtp", str(rt)], **extra}), encoding="utf-8")
|
|
return p
|
|
|
|
def run_update(self, have, argv=()):
|
|
a = mock.Mock(**{"build": False, "prebuilt": "URL", **dict(argv)})
|
|
out = io.StringIO()
|
|
with mock.patch.object(setup, "pip_install") as pip, \
|
|
mock.patch.object(setup, "update_installed_engine") as eng, \
|
|
mock.patch.object(setup, "refresh_draft_vocab") as dv, \
|
|
mock.patch.object(setup, "start") as start, \
|
|
mock.patch("subprocess.call") as call, \
|
|
contextlib.redirect_stdout(out):
|
|
rc = setup.update_install(have, a)
|
|
return rc, pip, eng, dv, start, call, out.getvalue()
|
|
|
|
def test_refreshes_without_starting(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p = self.config(Path(d), draft_vocab="en")
|
|
rc, pip, eng, dv, start, call, out = self.run_update([p])
|
|
self.assertEqual(rc, 0)
|
|
pip.assert_called_once()
|
|
eng.assert_called_once_with("URL")
|
|
dv.assert_called_once()
|
|
self.assertEqual(dv.call_args[0][1], "en") # the model's own subset is kept
|
|
start.assert_not_called()
|
|
call.assert_not_called()
|
|
self.assertIn("Strata is updated", out)
|
|
|
|
def test_build_keeps_the_compiled_engine_path(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p = self.config(Path(d))
|
|
rc, pip, eng, dv, *_ = self.run_update([p], {"build": True})
|
|
self.assertEqual(rc, 0)
|
|
eng.assert_not_called()
|
|
self.assertEqual(dv.call_args[0][1], "cjk")
|
|
|
|
def test_nothing_installed(self):
|
|
rc, pip, eng, dv, start, call, out = self.run_update([])
|
|
self.assertEqual(rc, 0)
|
|
for m in (pip, eng, dv, start, call):
|
|
m.assert_not_called()
|
|
self.assertIn("START-HERE.bat", out)
|
|
|
|
def test_a_json_that_is_no_model_config_is_skipped(self):
|
|
"""#549: a strata-*.json without "args" (not written by setup) stopped update.sh with KeyError: 'args'."""
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p = self.config(Path(d))
|
|
other = Path(d) / "strata-notes.json"
|
|
other.write_text(json.dumps({"note": "mine"}), encoding="utf-8")
|
|
broken = Path(d) / "strata-cut.json"
|
|
broken.write_text("{\"args\": [", encoding="utf-8")
|
|
rc, pip, eng, dv, start, call, out = self.run_update([other, broken, p])
|
|
self.assertEqual(rc, 0, out)
|
|
self.assertIn('skipped strata-notes.json (no "exe" or "args"): it is not a Strata model config', out)
|
|
self.assertIn("skipped strata-cut.json (not valid JSON)", out)
|
|
dv.assert_called_once() # the real model is still refreshed
|
|
self.assertIn("Qwen IQ3_S: up to date", out)
|
|
self.assertIn("Strata is updated", out)
|
|
|
|
def test_installed_configs_lists_only_model_configs(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p = self.config(Path(d))
|
|
(Path(d) / "strata-notes.json").write_text("{}", encoding="utf-8")
|
|
with mock.patch.object(setup, "ROOT", Path(d)), contextlib.redirect_stdout(io.StringIO()):
|
|
self.assertEqual(setup.installed_configs(), [p])
|
|
|
|
def test_main_update_never_starts(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
p = self.config(Path(d))
|
|
with mock.patch.object(sys, "argv", ["setup.py", "--update"]), \
|
|
mock.patch.object(setup, "data_folder", return_value=(Path(d), [])), \
|
|
mock.patch.object(setup, "installed_configs", return_value=[p]), \
|
|
mock.patch.object(setup, "update_install", return_value=0) as up, \
|
|
mock.patch.object(setup, "start") as start, \
|
|
contextlib.redirect_stdout(io.StringIO()):
|
|
self.assertEqual(setup.main(), 0)
|
|
up.assert_called_once()
|
|
start.assert_not_called()
|
|
|
|
|
|
class SettingsLine(unittest.TestCase):
|
|
"""#564: a start prints the settings it uses (the engine options without the model's paths, and the server's
|
|
fields), so a change made by hand to strata-<model>.json shows without reading the log."""
|
|
|
|
CFG = {"exe": "x", "host": "0.0.0.0", "port": 8081, "api_key": "secret", "gpu": [0, 1], "fit_max_tokens": True,
|
|
"args": ["--native", "E:\\Strata\\packs\\iq3_s", "--mtp", "/s/mtp/rt", "m.gguf", "--kv", "int8",
|
|
"--kv-resident", "32768", "--spec-min-p", "0.5", "--vram-reserve-mib", "2048", "--mmap-experts",
|
|
"--prefill", "auto"]}
|
|
|
|
def test_summary(self):
|
|
s = setup.settings_summary(self.CFG)
|
|
self.assertEqual(s, "--kv int8 --kv-resident 32768 --spec-min-p 0.5 --vram-reserve-mib 2048 --mmap-experts "
|
|
"--prefill auto; server 0.0.0.0:8081, api key set, gpu 0,1, fit_max_tokens true")
|
|
self.assertNotIn("secret", s)
|
|
self.assertIn("127.0.0.1:9000", setup.settings_summary({"args": []}, 9000))
|
|
|
|
def test_a_start_prints_it(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
exe = Path(d) / "strata.exe"
|
|
exe.write_bytes(b"")
|
|
p = Path(d) / "strata-iq3_s.json"
|
|
args = [x for x in self.CFG["args"] if x != "m.gguf"] # no model file here
|
|
p.write_text(json.dumps({**self.CFG, "exe": str(exe), "gpu": 0, "args": args}), encoding="utf-8")
|
|
out = io.StringIO()
|
|
with mock.patch.object(setup, "gpus", lambda: []), \
|
|
mock.patch.object(setup, "refresh_draft_vocab"), \
|
|
mock.patch.object(setup, "is_wsl", lambda: False), \
|
|
mock.patch.object(setup.subprocess, "call", return_value=0), \
|
|
contextlib.redirect_stdout(out):
|
|
setup.start(p, None, open_browser=False, yes=True)
|
|
text = " ".join(out.getvalue().split())
|
|
self.assertIn("Settings (strata-iq3_s.json): --kv int8 --kv-resident 32768", text)
|
|
self.assertIn("--vram-reserve-mib 2048", text)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|