activity-core/tests/test_frontend_patterns.py

123 lines
5.4 KiB
Python
Raw Permalink Normal View History

import copy
import hashlib
import json
from datetime import datetime, timezone
from pathlib import Path
import pytest
import yaml
from activity_core.context_resolvers import frontend_patterns as f
from activity_core.definition_parser import parse_file
def event(kind='real'):
return dict(schema_version=1, use_id='demo:task:decision', event_id='first',
previous_event_sha256=None, recorded_at='2026-08-20T12:00:00Z', kind=kind,
consumer=dict(repo='demo', revision='a'*40, task_ref='workplans/task', producer='agent', run_ref='run:1'),
reference=dict(version='0.1', packaging_revision='b'*40, manifest_sha256='c'*64),
context=dict(problem='Choose frontend boundaries', constraints=[], considered=['CAND-0001'], selected=['CAND-0001']),
decision=dict(action='applied', reason='Fits the task'),
outcome=dict(value='unknown', basis='consumer-report', stage='decision', expectation='Fewer coupling defects', observation=None, evidence_refs=[]))
def fixtures(monkeypatch, events, duplicate=False):
blobs = [yaml.safe_dump(e).encode() for e in events]
entries = [dict(path=f'receipts/{i}.yaml', sha256=hashlib.sha256(b).hexdigest()) for i,b in enumerate(blobs)]
if duplicate and entries:
entries.append(entries[0])
def raw(repo, sha, path):
if repo == 'frontend-patterns':
return json.dumps(dict(schema_version=1,sources=[dict(repo='demo', branch='main', index='receipts/index.json')])).encode()
if path.endswith('index.json'):
return json.dumps(dict(schema_version=1,receipts=entries)).encode()
return blobs[int(path.split('/')[-1].split('.')[0])]
monkeypatch.setattr(f,'revision',lambda *args: 'd'*40)
monkeypatch.setattr(f,'raw',raw)
return entries
def test_unknown_real_duplicate_and_synthetic_separate(monkeypatch):
synthetic = event('synthetic'); synthetic['use_id']='fixture:task:decision'; synthetic['event_id']='fixture'
fixtures(monkeypatch,[event(),synthetic],True)
report=f.collect()
assert report['real_uses']==1 and report['synthetic_uses']==1
assert report['duplicates']==1 and report['outcomes']=={'unknown':1}
assert report['model_calls']==0 and report['execution_ready'] is False
def test_delayed_outcome_and_month_boundary(monkeypatch):
root=event(); follow=copy.deepcopy(root)
follow.update(event_id='later',previous_event_sha256=hashlib.sha256(yaml.safe_dump(root).encode()).hexdigest(),recorded_at='2026-09-05T00:00:00Z')
follow['outcome'].update(value='helpful',observation='Test passed',evidence_refs=['tests/result'])
fixtures(monkeypatch,[follow,root])
report=f.collect('monthly',datetime(2026,9,10,tzinfo=timezone.utc))
assert report['outcomes']=={'unknown':1}
assert f.collect()['outcomes']=={'helpful':1}
assert f.collect()['signals'][0]['verification_required'] is True
def test_identity_collision_quarantines_use(monkeypatch):
conflict=copy.deepcopy(event()); conflict['decision']['reason']='different'
fixtures(monkeypatch,[event(),conflict])
report=f.collect()
assert report['real_uses']==0 and report['status']=='degraded'
assert 'quarantined_event_lineage' in report['errors']
def test_missing_predecessor_quarantines_use(monkeypatch):
e=event(); e['previous_event_sha256']='e'*64
fixtures(monkeypatch,[e])
assert f.collect()['real_uses']==0
@pytest.mark.parametrize('value',['helpful','harmful','mixed','no-effect'])
def test_outcomes_need_evidence(value):
e=event(); e['outcome']['value']=value
with pytest.raises(ValueError): f.validate_event(e,'demo')
e['outcome'].update(observation='Observed result',evidence_refs=['result'])
f.validate_event(e,'demo')
@pytest.mark.parametrize('action',['rejected','no-match'])
def test_empty_selection_valid_for_abstention(action):
e=event(); e['context']['selected']=[]; e['decision']['action']=action
f.validate_event(e,'demo')
@pytest.mark.parametrize('path',['../secret','/etc/passwd','x//y','x/./y','x?token=a','https://evil/x'])
def test_reject_unsafe_path(path):
assert not f.path_ok(path)
def test_unavailable_is_not_no_data(monkeypatch):
monkeypatch.setattr(f,'revision',lambda *args: (_ for _ in ()).throw(ValueError('unavailable')))
report=f.collect()
assert report['status']=='degraded' and report['errors']
def test_empty_roster_is_no_consumers(monkeypatch):
monkeypatch.setattr(f,'revision',lambda *args:'a'*40)
monkeypatch.setattr(f,'raw',lambda *args:b'{"schema_version":1,"sources":[]}')
report=f.collect()
assert report['status']=='no_consumers' and report['eligible_tasks'] is None
assert len(f.FrontendPatternsResolver().resolve('daily',None,{})) < 3900
def test_over_limit_is_explicit(monkeypatch):
entries=fixtures(monkeypatch,[event()])
entries.extend([entries[0]]*100)
report=f.collect()
assert report['status']=='degraded' and report['receipt_events']==0
def test_domain_definitions_parse():
manifest = Path(__file__).parents[1] / 'k8s/railiance/20-runtime.yaml'
cm = next(d for d in yaml.safe_load_all(manifest.read_text()) if d and d.get('metadata', {}).get('name') == 'actcore-external-activity-definitions')
import tempfile
for name, body in cm['data'].items():
if not name.startswith('frontend-patterns-'): continue
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp) / name
path.write_text(body)
d=parse_file(path)
assert d.instructions[0]['model']=='deterministic'