Fix multi-database entity resolution capture validation
This commit is contained in:
1 parent
9c9d9d70d6
commit
a9b49bbb13
2 files changed
+43
-2
No files matched your search
@@ -436,8 +436,16 @@ class EntityResolver:
|
|||||||
captures=[]
|
captures=[]
|
||||||
for s,ref in zip(plan['searches'],search['capture_refs'],strict=True):
|
for s,ref in zip(plan['searches'],search['capture_refs'],strict=True):
|
||||||
c=read_json(reader,ref,'project-capture',self.owner.limits.max_context_bytes)
|
c=read_json(reader,ref,'project-capture',self.owner.limits.max_context_bytes)
|
||||||
if (ref != self.base(context)+'/captures/'+hashlib.sha256(reader.read_bytes(ref)).hexdigest()+'.json' or c['origin'] != initial['binding']
|
query=query_for(s,initial['policy'])
|
||||||
or c['query'] != query_for(s,initial['policy'])):
|
# Reconstruct the query-specific origin from the frozen binding, so
|
||||||
|
# completed cases remain verifiable without a live database provider.
|
||||||
|
binding=copy.deepcopy(initial['binding'])
|
||||||
|
if binding.get('kind') == 'mariadb' and (binding.get('additional_databases') or '.' in query['table']):
|
||||||
|
parts=query['table'].split('.')
|
||||||
|
database,table=(binding['database'],parts[0]) if len(parts)==1 else parts
|
||||||
|
binding.update(selected_database=database,selected_table=table)
|
||||||
|
if (ref != self.base(context)+'/captures/'+hashlib.sha256(reader.read_bytes(ref)).hexdigest()+'.json' or c['origin'] != binding
|
||||||
|
or c['query'] != query):
|
||||||
raise InterpretationError('ENTITY_RESOLUTION_CAPTURE_CHANGED')
|
raise InterpretationError('ENTITY_RESOLUTION_CAPTURE_CHANGED')
|
||||||
captures.append(c)
|
captures.append(c)
|
||||||
if search['candidates'] != candidates_for(plan,captures,initial['policy']):
|
if search['candidates'] != candidates_for(plan,captures,initial['policy']):
|
||||||
|
|||||||
@@ -126,6 +126,39 @@ def context_of(store):
|
|||||||
return build_context(json.loads(reader.read_bytes(ref)),ref,InterpretationLimits())
|
return build_context(json.loads(reader.read_bytes(ref)),ref,InterpretationLimits())
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('qualified', [False, True])
|
||||||
|
@pytest.mark.parametrize('wrong_origin', [False, True])
|
||||||
|
def test_multidatabase_capture_origin_and_portable_replay(built_case, tmp_path, qualified, wrong_origin):
|
||||||
|
class MultiDatabase(Database):
|
||||||
|
def binding(self):
|
||||||
|
return {'kind':'mariadb', 'database':'world', 'snapshot_id':self.snapshot,
|
||||||
|
'tables':['components'], 'additional_databases':{'characters':['components']}}
|
||||||
|
|
||||||
|
def binding_for(self, query):
|
||||||
|
database, table = query['table'].split('.') if '.' in query['table'] else ('world', query['table'])
|
||||||
|
return {**self.binding(), 'selected_database':'wrong' if wrong_origin else database,
|
||||||
|
'selected_table':table}
|
||||||
|
|
||||||
|
def validate(self, query):
|
||||||
|
assert query['table'] in {'components','characters.components'}
|
||||||
|
|
||||||
|
store=built_case[0]
|
||||||
|
model=Model(); service=service_for(store,MultiDatabase())
|
||||||
|
if qualified:
|
||||||
|
service.entity_resolution['catalogs'][0]['table']='characters.components'
|
||||||
|
if wrong_origin:
|
||||||
|
with pytest.raises(InterpretationError, match='ENTITY_RESOLUTION_CAPTURE_CHANGED'):
|
||||||
|
run(store,model,service)
|
||||||
|
return
|
||||||
|
result=run(store,model,service)
|
||||||
|
assert result.status=='COMPLETED'
|
||||||
|
assert PackageValidator().validate(result.package).valid
|
||||||
|
receiving=CheckpointStore(type(store.workspace)(tmp_path/'portable-multidb'))
|
||||||
|
receiving.intake(result.package)
|
||||||
|
again=run(receiving,model)
|
||||||
|
assert again.reused and len(model.requests)==3
|
||||||
|
|
||||||
|
|
||||||
def test_resolves_unrecorded_subject_preserves_case_and_portable_replay(built_case, tmp_path):
|
def test_resolves_unrecorded_subject_preserves_case_and_portable_replay(built_case, tmp_path):
|
||||||
store, _, built, _, _ = built_case
|
store, _, built, _, _ = built_case
|
||||||
with PackageReader(built.package) as reader:
|
with PackageReader(built.package) as reader:
|
||||||
|
|||||||
Reference in new issue
Block a user