mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 03:49:26 +00:00
feat(hunt): feature table, producers, labels, metric, artifact, the DSPy hunt program, runner and CLI
hunter-learning-loop groups 1-3 (first increment). The feature table is the contract: one row per touched function with diff, source, semantic and taxonomy columns, provenance per producer, labels by the benchmark's rule. Producers read the repository at head only (a planted advisory id and commit text never reach a feature). The hunt program is a DSPy ReAct loop over a signature whose inputs are the unmasked columns in a fixed order; answers outside the closed family list or citing unseen lines score as unknown. The runner writes records the benchmark scorer reads, resumable by change, program id and mask, and refuses the held-out pilot unless the run is a report run. Research extra [hunt] = dspy, pandas; the core install is unchanged. 43 tests on a scripted model, no network. Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
e55e44d981
commit
d9d6335ab3
@@ -0,0 +1,73 @@
|
||||
"""Checks for the versioned hunter table."""
|
||||
|
||||
import builtins
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from openultrasast.hunt.table import SCHEMA_VERSION, FeatureTable
|
||||
|
||||
|
||||
def row(label=1):
|
||||
return {
|
||||
"change_id": "c", "repo": "r", "head": "h", "path": "app.py", "line": 1,
|
||||
"kind": "vulnerability", "fold": 0, "label": label, "family": "injection",
|
||||
"fix_span": [1, 3], "label_ref": "case", "verification": None,
|
||||
"feature.diff.distance": 0, "provenance.diff.ok": True,
|
||||
"provenance.diff.reason": None, "provenance.diff.version": "1", "provenance.diff.bytes_read": 10,
|
||||
}
|
||||
|
||||
|
||||
def test_round_trip_digest_and_mask(tmp_path):
|
||||
table = FeatureTable([row(), row(None), row(0)])
|
||||
path = tmp_path / "table.jsonl"
|
||||
table.write_jsonl(path)
|
||||
restored = FeatureTable.read_jsonl(path)
|
||||
assert restored.rows == table.rows
|
||||
assert restored.digest() == table.digest()
|
||||
assert SCHEMA_VERSION == 1
|
||||
assert len(table.labelled().rows) == 2
|
||||
masked = table.mask(["diff"])
|
||||
assert masked.masked == ("diff",)
|
||||
assert masked.feature_columns() == []
|
||||
assert table.feature_columns() == ["feature.diff.distance"]
|
||||
assert masked.digest() != table.digest()
|
||||
masked.write_jsonl(path)
|
||||
assert FeatureTable.read_jsonl(path).masked == ("diff",)
|
||||
assert table.mask(["feature.diff.distance"]).feature_columns() == []
|
||||
assert table.digest() == FeatureTable([dict(reversed(list(row().items()))), row(None), row(0)]).digest()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("extra", [{"unknown": 1}, {"feature.diff.typo": 1}, {"provenance.diff.secret": 1}])
|
||||
def test_unknown_columns_rejected(extra):
|
||||
with pytest.raises(ValueError):
|
||||
FeatureTable([{**row(), **extra}])
|
||||
|
||||
|
||||
def test_missing_provenance_rejected():
|
||||
data = row()
|
||||
del data["provenance.diff.ok"]
|
||||
with pytest.raises(ValueError, match="provenance"):
|
||||
FeatureTable([data])
|
||||
|
||||
|
||||
def test_schema_version_and_empty_table(tmp_path):
|
||||
path = tmp_path / "table.jsonl"
|
||||
FeatureTable([]).write_jsonl(path)
|
||||
assert FeatureTable.read_jsonl(path).rows == []
|
||||
path.write_text(json.dumps({"schema_version": 99, "masked": []}) + "\n")
|
||||
with pytest.raises(ValueError, match="schema"):
|
||||
FeatureTable.read_jsonl(path)
|
||||
|
||||
|
||||
def test_pandas_is_optional(monkeypatch):
|
||||
real_import = builtins.__import__
|
||||
|
||||
def importing(name, *args, **kwargs):
|
||||
if name == "pandas":
|
||||
raise ImportError("missing")
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", importing)
|
||||
with pytest.raises(RuntimeError, match="pandas.*hunt"):
|
||||
FeatureTable([row()]).to_pandas()
|
||||
Reference in New Issue
Block a user