"""Tests for CRX with dotted symbols (e.g. 'foo.bar', 'request.body'). These tests expose the fundamental bug: CRX outputs grammars where '.' means both concatenation AND is part of symbol names. The parser splits on all dots, breaking matches. Every test here MUST pass after the fix. They all fail before. """ from bex.crx import CRX from bex.ensemble import _matches class TestCRXDotInSymbol: """CRX must handle symbols containing dots.""" def test_single_dotted_symbol(self): """One symbol with a dot: ['foo.bar'].""" crx = CRX() g = crx.infer([['foo.bar']]) assert g is not None assert _matches(g, ['foo.bar']) def test_two_dotted_symbols(self): """Two different dotted symbols: ['foo.bar', 'baz.qux'].""" crx = CRX() g = crx.infer([['foo.bar', 'baz.qux']]) assert g is not None assert _matches(g, ['foo.bar', 'baz.qux']) def test_dotted_symbol_repeated(self): """Dotted symbol appears multiple times: ['foo.bar', 'foo.bar', 'baz'].""" crx = CRX() g = crx.infer([['foo.bar', 'foo.bar', 'baz']]) assert g is not None assert _matches(g, ['foo.bar', 'foo.bar', 'baz']) def test_dotted_symbols_multiple_sequences(self): """Multiple sequences with dotted symbols.""" crx = CRX() seqs = [ ['return.capability', 'invoke.request'], ['return.capability', 'invoke'], ] g = crx.infer(seqs) assert g is not None for seq in seqs: assert _matches(g, seq), f"Grammar {g!r} should match {seq}" def test_dotted_symbol_alternation(self): """Dotted symbols in alternation: ['a.b', 'a.c'].""" crx = CRX() g = crx.infer([['a.b'], ['a.c']]) assert g is not None assert _matches(g, ['a.b']) assert _matches(g, ['a.c']) def test_dotted_symbol_optional(self): """Optional dotted symbol: some sequences have it, some don't.""" crx = CRX() seqs = [ ['a.b', 'c'], ['c'], ] g = crx.infer(seqs) assert g is not None for seq in seqs: assert _matches(g, seq), f"Grammar {g!r} should match {seq}" def test_dotted_symbol_plus(self): """Repeated dotted symbol: ['a.b', 'a.b', 'a.b', 'c'].""" crx = CRX() seqs = [ ['a.b', 'a.b', 'a.b', 'c'], ['a.b', 'a.b', 'c'], ['a.b', 'c'], ] g = crx.infer(seqs) assert g is not None for seq in seqs: assert _matches(g, seq), f"Grammar {g!r} should match {seq}" def test_realistic_method_chain(self): """Realistic: method call chains like Kotlin/Python.""" crx = CRX() seqs = [ ['request.body', 'validate', 'save'], ['request.body', 'validate', 'return'], ['request.body', 'save'], ] g = crx.infer(seqs) assert g is not None for seq in seqs: assert _matches(g, seq), f"Grammar {g!r} should match {seq}" def test_multiple_dots_in_symbol(self): """Symbol with multiple dots: 'a.b.c'.""" crx = CRX() seqs = [ ['a.b.c', 'd.e.f'], ['a.b.c', 'g'], ] g = crx.infer(seqs) assert g is not None for seq in seqs: assert _matches(g, seq), f"Grammar {g!r} should match {seq}" def test_mixed_dotted_and_plain(self): """Mix of dotted and plain symbols.""" crx = CRX() seqs = [ ['foo.bar', 'baz', 'qux.quux'], ['foo.bar', 'baz'], ['baz', 'qux.quux'], ] g = crx.infer(seqs) assert g is not None for seq in seqs: assert _matches(g, seq), f"Grammar {g!r} should match {seq}" class TestEnsembleDotInSymbol: """Ensemble (CRX + scoring) must handle dotted symbols.""" def test_ensemble_dotted_symbols(self): """infer_ensemble with dotted symbols.""" from bex.ensemble import infer_ensemble seqs = [ ['request.body', 'validate', 'save'], ['request.body', 'validate', 'return'], ['request.body', 'save'], ] result = infer_ensemble(seqs) assert result['best'] is not None grammar = result['best']['grammar'] for seq in seqs: assert _matches(grammar, seq), \ f"Grammar {grammar!r} should match {seq}" def test_scoring_dotted_symbols(self): """lang_size_score with dotted symbols should not crash.""" from bex.ensemble import infer_ensemble seqs = [ ['foo.bar', 'baz.qux'], ['foo.bar', 'baz'], ] result = infer_ensemble(seqs) assert result['best'] is not None # Just checking it doesn't crash — the score should be finite score = result['best']['mdl_score'] assert isinstance(score, (int, float)) assert score < float('inf')