e839678130
The module shipped without its 17-case spec, which was still sitting in the /tmp build dir where the room ground it — a module whose contract lives somewhere else is exactly the drift this work set out to remove. Repointed at lib/bullpen_modeltag and verified 17/17 against the shipped implementation.
154 lines
5.5 KiB
Python
154 lines
5.5 KiB
Python
"""Executable contract for lib/bullpen_modeltag.py (#118).
|
|
|
|
Written by @testdesigner over three rounds and one operator correction; see the commit
|
|
for what each round caught. Kept in the repo because a module without its contract is
|
|
exactly the drift this work existed to remove — it was living in a /tmp build dir.
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
|
|
sys.path.insert(0, os.path.join(
|
|
os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "lib"))
|
|
import bullpen_modeltag as modeltag # noqa: E402
|
|
|
|
|
|
def _norm(text):
|
|
return " ".join(text.split()).lower()
|
|
|
|
|
|
# The grinder derives its target file by matching repo filenames against THIS source
|
|
# (bin/bullpen-grinder, _infer_targets), so "bullpen_modeltag.py" must appear here
|
|
# verbatim - eine Harness-Anforderung an die Suite, KEINE Anforderung
|
|
# an das Modul. Frueher stand hier eine Pruefung auf den Modulquelltext; die war nur
|
|
# erfuellbar, indem man den Dateinamen in den Docstring klebt, und genau das ist
|
|
# passiert. Ein Vertrag, der sich so bestehen laesst, misst nichts.
|
|
|
|
|
|
def test_module_is_importable():
|
|
assert callable(modeltag.model_tag)
|
|
|
|
|
|
def test_local_prefix_is_free():
|
|
msg = modeltag.model_tag("[local] qwen3.6-coding")
|
|
flat = _norm(msg)
|
|
assert "qwen3.6-coding" in flat
|
|
assert "local" in flat
|
|
assert "unknown" not in flat
|
|
|
|
|
|
def test_free_prefix_is_free():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder")
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
assert "free" in flat
|
|
assert "unknown" not in flat
|
|
|
|
|
|
def test_paid_prefix_is_paid():
|
|
msg = modeltag.model_tag("[$] anthropic/claude-opus-4.8")
|
|
flat = _norm(msg)
|
|
assert "anthropic/claude-opus-4.8" in flat
|
|
assert "$" in flat or "paid" in flat or "kostenpflichtig" in flat
|
|
assert "unknown" not in flat
|
|
|
|
|
|
def test_missing_prefix_tier_unknown():
|
|
msg = modeltag.model_tag("bare-model-name")
|
|
flat = _norm(msg)
|
|
assert "bare-model-name" in flat
|
|
assert "unknown" in flat
|
|
|
|
|
|
def test_message_is_single_line():
|
|
msg = modeltag.model_tag("qwen3.6-coding")
|
|
assert "\n" not in msg.strip()
|
|
|
|
|
|
def test_served_none_equals_asked_no_switch():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder")
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
|
|
|
|
def test_served_defaults_to_asked():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder", served=None)
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
|
|
|
|
def test_served_gateway_named():
|
|
msg = modeltag.model_tag("qwen3.6-coding", gateway="aperture")
|
|
flat = _norm(msg)
|
|
assert "aperture" in flat
|
|
|
|
|
|
def test_default_gateway_named():
|
|
"""Ohne gateway-Argument muss trotzdem EIN Gateway benannt werden. Welches, ist
|
|
Sache der Verdrahtung - frueher stand hier ein Flotten-Hostname fest verdrahtet,
|
|
was den lokalen Namen zum eingebauten Vorgabewert machte."""
|
|
msg = modeltag.model_tag("qwen3.6-coding")
|
|
flat = _norm(msg)
|
|
assert "gateway" in flat
|
|
|
|
|
|
def test_served_different_from_asked_visible():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder", served="claude-opus-4.8")
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
assert "claude-opus-4.8" in flat
|
|
assert "switch" in flat or "wechsel" in flat or "->" in flat or "instead" in flat
|
|
|
|
|
|
def test_served_with_paid_prefix_tier_reflected():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder", served="[$] anthropic/claude-opus-4.8")
|
|
flat = _norm(msg)
|
|
assert "anthropic/claude-opus-4.8" in flat
|
|
assert "$" in flat or "paid" in flat or "kostenpflichtig" in flat
|
|
assert "unknown" not in flat
|
|
|
|
|
|
def test_same_model_different_tier_is_visible_switch():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder", served="[$] qwen/qwen3-coder")
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
assert "switch" in flat or "wechsel" in flat or "->" in flat or "instead" in flat
|
|
assert "$" in flat or "paid" in flat or "kostenpflichtig" in flat
|
|
|
|
|
|
def test_switch_shows_asked_tier_not_just_asked_name():
|
|
msg = modeltag.model_tag("[free] qwen/qwen3-coder", served="[$] anthropic/claude-opus-4.8")
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
assert "free" in flat
|
|
assert "$" in flat or "paid" in flat or "kostenpflichtig" in flat
|
|
assert "switch" in flat or "wechsel" in flat or "->" in flat or "instead" in flat
|
|
|
|
|
|
def test_served_without_prefix_same_name_no_switch():
|
|
msg = modeltag.model_tag("[local] deepseek-v4-flash-dspark", served="deepseek-v4-flash-dspark")
|
|
flat = _norm(msg)
|
|
assert "deepseek-v4-flash-dspark" in flat
|
|
assert "unknown" not in flat
|
|
assert "local" in flat
|
|
assert "->" not in flat
|
|
|
|
|
|
def test_served_without_prefix_different_name_shows_switch():
|
|
msg = modeltag.model_tag("[local] deepseek-v4-flash-dspark", served="deepseek-v4-flash-other")
|
|
flat = _norm(msg)
|
|
assert "deepseek-v4-flash-dspark" in flat
|
|
assert "deepseek-v4-flash-other" in flat
|
|
assert "switch" in flat or "wechsel" in flat or "->" in flat or "instead" in flat
|
|
|
|
|
|
def test_formatting_only_difference_is_no_switch():
|
|
"""[free]x und [free] x sind dasselbe Modell auf derselben Stufe - der Unterschied
|
|
ist ein Leerzeichen hinter dem Praefix. Ein roher asked != served Vergleich macht
|
|
daraus einen Wechsel auf sich selbst (@reviewer, empirisch belegt)."""
|
|
msg = modeltag.model_tag("[free]qwen/qwen3-coder", served="[free] qwen/qwen3-coder")
|
|
flat = _norm(msg)
|
|
assert "qwen/qwen3-coder" in flat
|
|
assert "free" in flat
|
|
assert not ("switch" in flat or "wechsel" in flat or "->" in flat or "instead" in flat)
|