87 lines
4.3 KiB
Python
87 lines
4.3 KiB
Python
import copy
|
|
import json
|
|
|
|
import pytest
|
|
|
|
from viano_bug_analysis.analysis.investigation_view import working_view, thin_view
|
|
from viano_bug_analysis.analysis.investigation_prompt import build_prompt
|
|
from viano_bug_analysis.case.contracts import validate
|
|
from viano_bug_analysis.tools import ToolError
|
|
from test_project_research import source
|
|
from test_investigation_view import context
|
|
|
|
|
|
def restore(view):
|
|
for entry in view['evidence'].values():
|
|
for capture in entry['content']['items']:
|
|
result = capture.get('result', {})
|
|
table = result.get('items')
|
|
if not isinstance(table, dict) or 'encoding' not in table:
|
|
continue
|
|
if table['encoding'] == 'source_line_table_v1':
|
|
result['items'] = [{**table['common'], **dict(zip(table['columns'], row))} for row in table['rows']]
|
|
else:
|
|
result['items'] = [{**table['common'], 'row': dict(zip(table['columns'], row))} for row in table['rows']]
|
|
return view
|
|
|
|
|
|
def test_tables_are_lossless_including_rows_limits_and_other_hypotheses():
|
|
c = context()
|
|
for eid, entry in c['evidence'].items():
|
|
entry['content']['items'] = [
|
|
{'query': {'kind': 'source.read', 'path': 'code.cpp', 'start_line': 1, 'end_line': 80},
|
|
'result': {'coverage': 'selected_lines', 'truncated': False,
|
|
'items': [{'path': 'code.cpp', 'sha256': 'a'*64, 'size': 1000,
|
|
'line': i, 'text': 'line '+str(i), 'text_truncated': False,
|
|
'typed_metadata': False if i % 2 else 0} for i in range(1, 81)]}},
|
|
{'query': {'kind': 'database.rows', 'table': 'example'},
|
|
'result': {'coverage': 'partial_filtered_rows', 'truncated': True, 'next_offset': 2,
|
|
'items': [{'table': 'example', 'row': {'id': 1, 'path': 'a'*64, 'value': None}},
|
|
{'table': 'example', 'row': {'id': 2, 'path': 'b'*64, 'value': False}}]}}]
|
|
original = copy.deepcopy(c)
|
|
compact = thin_view(c)
|
|
assert len(json.dumps(compact)) < len(json.dumps(working_view(c))) / 2
|
|
assert json.dumps(restore(copy.deepcopy(compact)), sort_keys=True) == json.dumps(working_view(c), sort_keys=True)
|
|
assert c == original
|
|
|
|
|
|
def test_nonuniform_rows_are_not_coerced_to_null():
|
|
c = context()
|
|
entry = next(iter(c['evidence'].values()))
|
|
entry['content']['items'] = [{'query': {'kind': 'database.rows'}, 'result': {'items': [
|
|
{'row': {'id': 1, 'value': None}}, {'row': {'id': 2}}]}}]
|
|
assert thin_view(c)['evidence'] == working_view(c)['evidence']
|
|
|
|
|
|
def test_archived_v4_requests_keep_their_original_working_view():
|
|
c = context()
|
|
c['prompt_version'] = 'investigation_project_v4'
|
|
request = build_prompt(c)
|
|
assert request['prompt_version'] == 'investigation_project_v4'
|
|
schema = request['output_schema']
|
|
assert json.loads(request['messages'][1]['content'])['input'] == working_view(c)
|
|
c['prompt_version'] = 'investigation_project_v5'
|
|
request = build_prompt(c)
|
|
assert request['prompt_version'] == 'investigation_project_v5'
|
|
assert request['output_schema'] == schema
|
|
assert json.loads(request['messages'][1]['content'])['input'] == thin_view(c)
|
|
|
|
|
|
def test_eof_returns_remaining_lines_and_empty_read_with_explicit_scope(source):
|
|
for start, expected in ((2, [2, 3]), (4, [])):
|
|
args = {'kind': 'source.read', 'path': 'module.cpp', 'start_line': start, 'end_line': 60}
|
|
result = source.query(args)
|
|
assert [i['line'] for i in result['items']] == expected
|
|
assert result['eof'] is True and result['total_lines'] == 3
|
|
assert result['truncated'] is False and result['coverage'] == 'selected_lines'
|
|
assert result['requested_end_line'] == 60
|
|
assert result['returned_end_line'] == (3 if expected else None)
|
|
validate({'format':'viano-project-capture','format_version':1,'query':args,
|
|
'origin':source.binding(),'result':result}, 'project-capture')
|
|
|
|
|
|
@pytest.mark.parametrize('start,end', [(0, 3), (1, 81), (4, 3), (True, 3), (1, 2.0)])
|
|
def test_eof_does_not_bypass_safety_limits(source, start, end):
|
|
with pytest.raises(ToolError, match='PROJECT_SOURCE_RANGE'):
|
|
source.query({'kind': 'source.read', 'path': 'module.cpp', 'start_line': start, 'end_line': end})
|